+ {this.state.error?.message.includes('Attempt to get default algod configuration')
+ ? 'Please make sure to set up your environment variables correctly. Create a .env file based on .env.template and fill in the required values. This controls the network and credentials for connections with Algod and Indexer.'
+ : this.state.error?.message}
+
+
+
+
+ )
+ }
+
+ return this.props.children
+ }
+}
+
+export default ErrorBoundary
diff --git a/tests_generated/test_default_preset/src/components/Transact.tsx b/tests_generated/test_default_preset/src/components/Transact.tsx
new file mode 100644
index 0000000..16bd932
--- /dev/null
+++ b/tests_generated/test_default_preset/src/components/Transact.tsx
@@ -0,0 +1,95 @@
+import * as algokit from '@algorandfoundation/algokit-utils'
+import { useWallet } from '@txnlab/use-wallet'
+import algosdk from 'algosdk'
+import { useSnackbar } from 'notistack'
+import { useState } from 'react'
+import { getAlgodConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs'
+
+interface TransactInterface {
+ openModal: boolean
+ setModalState: (value: boolean) => void
+}
+
+const Transact = ({ openModal, setModalState }: TransactInterface) => {
+ const [loading, setLoading] = useState(false)
+ const [receiverAddress, setReceiverAddress] = useState('')
+
+ const algodConfig = getAlgodConfigFromViteEnvironment()
+ const algodClient = algokit.getAlgoClient({
+ server: algodConfig.server,
+ port: algodConfig.port,
+ token: algodConfig.token,
+ })
+
+ const { enqueueSnackbar } = useSnackbar()
+
+ const { signer, activeAddress, signTransactions, sendTransactions } = useWallet()
+
+ const handleSubmitAlgo = async () => {
+ setLoading(true)
+
+ if (!signer || !activeAddress) {
+ enqueueSnackbar('Please connect wallet first', { variant: 'warning' })
+ return
+ }
+
+ const suggestedParams = await algodClient.getTransactionParams().do()
+
+ const transaction = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
+ from: activeAddress,
+ to: receiverAddress,
+ amount: 1e6,
+ suggestedParams,
+ })
+
+ const encodedTransaction = algosdk.encodeUnsignedTransaction(transaction)
+
+ const signedTransactions = await signTransactions([encodedTransaction])
+
+ const waitRoundsToConfirm = 4
+
+ try {
+ enqueueSnackbar('Sending transaction...', { variant: 'info' })
+ const { id } = await sendTransactions(signedTransactions, waitRoundsToConfirm)
+ enqueueSnackbar(`Transaction sent: ${id}`, { variant: 'success' })
+ setReceiverAddress('')
+ } catch (e) {
+ enqueueSnackbar('Failed to send transaction', { variant: 'error' })
+ }
+
+ setLoading(false)
+ }
+
+ return (
+
+ )
+}
+
+export default Transact
diff --git a/tests_generated/test_default_preset/src/contracts/README.md b/tests_generated/test_default_preset/src/contracts/README.md
new file mode 100644
index 0000000..e056b58
--- /dev/null
+++ b/tests_generated/test_default_preset/src/contracts/README.md
@@ -0,0 +1,13 @@
+## How to connect my web app with Algorand smart contracts?
+
+The following folder is reserved for the Algorand Application Clients. The clients are used to interact with instances of Algorand Smart Contracts (ASC1s) deployed on-chain.
+
+To integrate this react frontend template with your smart contracts codebase, perform the following steps:
+
+1. Generate the typed client using `algokit generate client -l typescript -o {path/to/this/folder}`
+2. The generated typescript client should be ready to be imported and used in this react frontend template, making it a full fledged dApp.
+
+### FAQ
+
+- **How to interact with the smart contract?**
+ - The generated client provides a set of functions that can be used to interact with the ABI (Application Binary Interface) compliant Algorand smart contract. For example, if the smart contract has a function called `hello`, the generated client will have a function called `hello` that can be used to interact with the smart contract. Refer to a [full-stack end-to-end starter template](https://github.com/algorandfoundation/algokit-fullstack-template) for a reference example on invoking and interacting with typescript typed clients generated.
diff --git a/tests_generated/test_default_preset/src/interfaces/network.ts b/tests_generated/test_default_preset/src/interfaces/network.ts
new file mode 100644
index 0000000..a458edc
--- /dev/null
+++ b/tests_generated/test_default_preset/src/interfaces/network.ts
@@ -0,0 +1,26 @@
+import { AlgoClientConfig } from '@algorandfoundation/algokit-utils/types/network-client'
+import type { TokenHeader } from 'algosdk/dist/types/client/urlTokenBaseHTTPClient'
+
+export interface AlgoViteClientConfig extends AlgoClientConfig {
+ /** Base URL of the server e.g. http://localhost, https://testnet-api.algonode.cloud/, etc. */
+ server: string
+ /** The port to use e.g. 4001, 443, etc. */
+ port: string | number
+ /** The token to use for API authentication (or undefined if none needed) - can be a string, or an object with the header key => value */
+ token: string | TokenHeader
+ /** String representing current Algorand Network type (testnet/mainnet and etc) */
+ network: string
+}
+
+export interface AlgoViteKMDConfig extends AlgoClientConfig {
+ /** Base URL of the server e.g. http://localhost, https://testnet-api.algonode.cloud/, etc. */
+ server: string
+ /** The port to use e.g. 4001, 443, etc. */
+ port: string | number
+ /** The token to use for API authentication (or undefined if none needed) - can be a string, or an object with the header key => value */
+ token: string | TokenHeader
+ /** KMD wallet name */
+ wallet: string
+ /** KMD wallet password */
+ password: string
+}
diff --git a/tests_generated/test_default_preset/src/main.tsx b/tests_generated/test_default_preset/src/main.tsx
new file mode 100644
index 0000000..adf72ec
--- /dev/null
+++ b/tests_generated/test_default_preset/src/main.tsx
@@ -0,0 +1,13 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import App from './App'
+import './styles/main.css'
+import ErrorBoundary from './components/ErrorBoundary'
+
+ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
+
+
+
+
+ ,
+)
diff --git a/tests_generated/test_default_preset/src/styles/main.css b/tests_generated/test_default_preset/src/styles/main.css
new file mode 100644
index 0000000..b5c61c9
--- /dev/null
+++ b/tests_generated/test_default_preset/src/styles/main.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/tests_generated/test_default_preset/src/utils/ellipseAddress.spec.tsx b/tests_generated/test_default_preset/src/utils/ellipseAddress.spec.tsx
new file mode 100644
index 0000000..2cbff10
--- /dev/null
+++ b/tests_generated/test_default_preset/src/utils/ellipseAddress.spec.tsx
@@ -0,0 +1,15 @@
+import { ellipseAddress } from './ellipseAddress'
+
+describe('ellipseAddress', () => {
+ it('should return ellipsed address with specified width', () => {
+ const address = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
+ const result = ellipseAddress(address, 4)
+ expect(result).toBe('aaaa...aaaa')
+ })
+
+ it('should return empty string when address is empty', () => {
+ const address = ''
+ const result = ellipseAddress(address)
+ expect(result).toBe('')
+ })
+})
diff --git a/tests_generated/test_default_preset/src/utils/ellipseAddress.ts b/tests_generated/test_default_preset/src/utils/ellipseAddress.ts
new file mode 100644
index 0000000..542f46f
--- /dev/null
+++ b/tests_generated/test_default_preset/src/utils/ellipseAddress.ts
@@ -0,0 +1,3 @@
+export function ellipseAddress(address = ``, width = 6): string {
+ return address ? `${address.slice(0, width)}...${address.slice(-width)}` : address
+}
diff --git a/tests_generated/test_default_preset/src/utils/network/getAlgoClientConfigs.ts b/tests_generated/test_default_preset/src/utils/network/getAlgoClientConfigs.ts
new file mode 100644
index 0000000..b5121f8
--- /dev/null
+++ b/tests_generated/test_default_preset/src/utils/network/getAlgoClientConfigs.ts
@@ -0,0 +1,41 @@
+import { AlgoViteClientConfig, AlgoViteKMDConfig } from '../../interfaces/network'
+
+export function getAlgodConfigFromViteEnvironment(): AlgoViteClientConfig {
+ if (!import.meta.env.VITE_ALGOD_SERVER) {
+ throw new Error('Attempt to get default algod configuration without specifying VITE_ALGOD_SERVER in the environment variables')
+ }
+
+ return {
+ server: import.meta.env.VITE_ALGOD_SERVER,
+ port: import.meta.env.VITE_ALGOD_PORT,
+ token: import.meta.env.VITE_ALGOD_TOKEN,
+ network: import.meta.env.VITE_ALGOD_NETWORK,
+ }
+}
+
+export function getIndexerConfigFromViteEnvironment(): AlgoViteClientConfig {
+ if (!import.meta.env.VITE_INDEXER_SERVER) {
+ throw new Error('Attempt to get default algod configuration without specifying VITE_INDEXER_SERVER in the environment variables')
+ }
+
+ return {
+ server: import.meta.env.VITE_INDEXER_SERVER,
+ port: import.meta.env.VITE_INDEXER_PORT,
+ token: import.meta.env.VITE_INDEXER_TOKEN,
+ network: import.meta.env.VITE_ALGOD_NETWORK,
+ }
+}
+
+export function getKmdConfigFromViteEnvironment(): AlgoViteKMDConfig {
+ if (!import.meta.env.VITE_KMD_SERVER) {
+ throw new Error('Attempt to get default kmd configuration without specifying VITE_KMD_SERVER in the environment variables')
+ }
+
+ return {
+ server: import.meta.env.VITE_KMD_SERVER,
+ port: import.meta.env.VITE_KMD_PORT,
+ token: import.meta.env.VITE_KMD_TOKEN,
+ wallet: import.meta.env.VITE_KMD_WALLET,
+ password: import.meta.env.VITE_KMD_PASSWORD,
+ }
+}
diff --git a/tests_generated/test_default_preset/src/vite-env.d.ts b/tests_generated/test_default_preset/src/vite-env.d.ts
new file mode 100644
index 0000000..67c2d30
--- /dev/null
+++ b/tests_generated/test_default_preset/src/vite-env.d.ts
@@ -0,0 +1,24 @@
+///
+
+interface ImportMetaEnv {
+ readonly VITE_ENVIRONMENT: string
+
+ readonly VITE_ALGOD_TOKEN: string
+ readonly VITE_ALGOD_SERVER: string
+ readonly VITE_ALGOD_PORT: string
+ readonly VITE_ALGOD_NETWORK: string
+
+ readonly VITE_INDEXER_TOKEN: string
+ readonly VITE_INDEXER_SERVER: string
+ readonly VITE_INDEXER_PORT: string
+
+ readonly VITE_KMD_TOKEN: string
+ readonly VITE_KMD_SERVER: string
+ readonly VITE_KMD_PORT: string
+ readonly VITE_KMD_PASSWORD: string
+ readonly VITE_KMD_WALLET: string
+}
+
+interface ImportMeta {
+ readonly env: ImportMetaEnv
+}
diff --git a/tests_generated/test_default_preset/tailwind.config.js b/tests_generated/test_default_preset/tailwind.config.js
new file mode 100644
index 0000000..a9f7a95
--- /dev/null
+++ b/tests_generated/test_default_preset/tailwind.config.js
@@ -0,0 +1,11 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: ['./src/**/*.{js,ts,jsx,tsx}'],
+ theme: {
+ extend: {},
+ },
+ daisyui: {
+ themes: ['lofi'],
+ },
+ plugins: [require('daisyui')],
+ }
diff --git a/tests_generated/test_default_preset/tests/example.spec.ts b/tests_generated/test_default_preset/tests/example.spec.ts
new file mode 100644
index 0000000..df83322
--- /dev/null
+++ b/tests_generated/test_default_preset/tests/example.spec.ts
@@ -0,0 +1,38 @@
+import { randomAccount } from '@algorandfoundation/algokit-utils'
+import { expect, test } from '@playwright/test'
+
+test.beforeEach(async ({ page }) => {
+ await page.goto('http://localhost:5173/')
+})
+
+test('has title', async ({ page }) => {
+ // Expect a title "to contain" a substring.
+ await expect(page).toHaveTitle('AlgoKit React Template')
+})
+
+test('get started link', async ({ page }) => {
+ await expect(page.getByTestId('getting-started')).toHaveText('Getting started')
+})
+
+test('authentication and dummy payment transaction', async ({ page }) => {
+ page.on('dialog', async (dialog) => {
+ dialog.message() === 'KMD password' ? await dialog.accept() : await dialog.dismiss()
+ })
+
+ // 1. Must be able to connect to a KMD wallet provider
+ await page.getByTestId('connect-wallet').click()
+ await page.getByTestId('kmd-connect').click()
+ await page.getByTestId('close-wallet-modal').click()
+
+ // 2. Must be able to send a dummy payment transaction
+ await page.getByTestId('transactions-demo').click()
+
+ const dummyAccount = randomAccount()
+ await page.getByTestId('receiver-address').fill(dummyAccount.addr)
+ await page.getByTestId('send-algo').click()
+
+ // 3. Must be able to see a notification that the transaction was sent
+ const notification = await page.getByText('Transaction sent:')
+ await notification.waitFor()
+ expect(notification).toBeTruthy()
+})
diff --git a/tests_generated/test_default_preset/tsconfig.json b/tests_generated/test_default_preset/tsconfig.json
new file mode 100644
index 0000000..5cde02a
--- /dev/null
+++ b/tests_generated/test_default_preset/tsconfig.json
@@ -0,0 +1,38 @@
+{
+ "compilerOptions": {
+ "target": "ES2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
+ "module": "ES2022" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
+ "declaration": true /* Generates corresponding '.d.ts' file. */,
+ "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */,
+ "sourceMap": true /* Generates corresponding '.map' file. */,
+ "strict": true /* Enable all strict type-checking options. */,
+ "noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
+ "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
+ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
+ "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
+ "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
+ "skipLibCheck": true /* Skip type checking of declaration files. */,
+ "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
+ "allowJs": false,
+ "allowSyntheticDefaultImports": true,
+ "moduleResolution": "Node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "outDir": "./dist/"
+ },
+ "include": [
+ "src/**/*.ts",
+ "src/**/*.tsx",
+ "vite.config.js",
+ "src/utils/ellipseAddress.spec.tsx",
+ "src/utils/ellipseAddress.spec.tsx",
+ "src/main.tsx",
+ ],
+ "references": [
+ {
+ "path": "./tsconfig.node.json"
+ }
+ ]
+}
diff --git a/tests_generated/test_default_preset/tsconfig.node.json b/tests_generated/test_default_preset/tsconfig.node.json
new file mode 100644
index 0000000..9d31e2a
--- /dev/null
+++ b/tests_generated/test_default_preset/tsconfig.node.json
@@ -0,0 +1,9 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "allowSyntheticDefaultImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/tests_generated/test_default_preset/vite.config.ts b/tests_generated/test_default_preset/vite.config.ts
new file mode 100644
index 0000000..5e5b6ee
--- /dev/null
+++ b/tests_generated/test_default_preset/vite.config.ts
@@ -0,0 +1,7 @@
+import react from '@vitejs/plugin-react-swc'
+import { defineConfig } from 'vite'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [react()],
+})