From 37a67fc8705f89cbc88f88fcc7980efffe7fe40e Mon Sep 17 00:00:00 2001 From: Neil Campbell Date: Mon, 25 Nov 2024 23:24:14 +0800 Subject: [PATCH] chore: update AppCall.tsx and regen templates (#36) --- .github/workflows/check-python.yaml | 5 + .../.algokit/.copier-answers.yml | 2 +- .../README.md | 20 +- .../poetry.toml | 1 + .../pyproject.toml | 2 +- .../smart_contracts/_helpers/build.py | 2 +- .../.algokit/.copier-answers.yml | 2 +- .../index.html | 3 - .../package.json | 29 +- .../src/components/AppCalls.tsx | 67 +- .../src/components/Transact.tsx | 35 +- .../src/contracts/HelloWorld.ts | 686 ++++++++------ .../tests/example.spec.ts | 8 +- .../vite.config.ts | 10 +- .../contracts/clients/CalculatorClient.ts | 2 +- .../.algokit/.copier-answers.yml | 2 +- .../index.html | 3 - .../package.json | 29 +- .../src/components/AppCalls.tsx | 53 +- .../src/components/Transact.tsx | 35 +- .../src/contracts/Calculator.ts | 878 ++++++++++-------- .../tests/example.spec.ts | 8 +- .../vite.config.ts | 10 +- .../.algokit/.copier-answers.yml | 2 +- .../{{ contract_name }}/deploy-config.ts.j2 | 28 +- .../starter_python_react-contracts/README.md | 9 + .../package.json | 9 +- .../poetry.toml | 1 + .../smart_contracts/_helpers/build.py | 2 +- .../hello_world/deploy-config.ts | 28 +- .../smart_contracts/index.ts | 2 + .../.algokit/.copier-answers.yml | 2 +- .../starter_python_react-frontend/index.html | 3 - .../package.json | 19 +- .../src/components/AppCalls.tsx | 67 +- .../src/components/Transact.tsx | 35 +- .../src/contracts/HelloWorld.ts | 686 ++++++++------ .../vite.config.ts | 10 +- .../contracts/clients/CalculatorClient.ts | 2 +- .../.algokit/.copier-answers.yml | 2 +- .../index.html | 3 - .../package.json | 19 +- .../src/components/AppCalls.tsx | 53 +- .../src/components/Transact.tsx | 35 +- .../src/contracts/Calculator.ts | 878 ++++++++++-------- .../vite.config.ts | 10 +- .../inject_content/AppCalls.tsx.jinja | 130 ++- 47 files changed, 2238 insertions(+), 1689 deletions(-) diff --git a/.github/workflows/check-python.yaml b/.github/workflows/check-python.yaml index 2f903f6..570b4d4 100644 --- a/.github/workflows/check-python.yaml +++ b/.github/workflows/check-python.yaml @@ -44,6 +44,11 @@ jobs: # set git user and email as test invoke git git config --global user.email "actions@github.com" && git config --global user.name "github-actions" + - name: Setup Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: '20.x' + - name: Run tests shell: bash run: | diff --git a/examples/production_python_react/projects/production_python_react-contracts/.algokit/.copier-answers.yml b/examples/production_python_react/projects/production_python_react-contracts/.algokit/.copier-answers.yml index 196fdd6..7315cb2 100644 --- a/examples/production_python_react/projects/production_python_react-contracts/.algokit/.copier-answers.yml +++ b/examples/production_python_react/projects/production_python_react-contracts/.algokit/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier; NEVER EDIT MANUALLY -_commit: 1.3.3 +_commit: 1.4.1 _src_path: gh:algorandfoundation/algokit-python-template author_email: None author_name: None diff --git a/examples/production_python_react/projects/production_python_react-contracts/README.md b/examples/production_python_react/projects/production_python_react-contracts/README.md index 21a3499..c6b106f 100644 --- a/examples/production_python_react/projects/production_python_react-contracts/README.md +++ b/examples/production_python_react/projects/production_python_react-contracts/README.md @@ -77,15 +77,7 @@ By default the template creates a single `HelloWorld` contract under hello_world By default the template instance does not contain any env files. Using [`algokit project deploy`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/project/deploy.md) against `localnet` | `testnet` | `mainnet` will use default values for `algod` and `indexer` unless overwritten via `.env` or `.env.{target_network}`. -To generate a new `.env` or `.env.{target_network}` file, run `algokit generate env-file`### Continuous Integration / Continuous Deployment (CI/CD) - -This project uses [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to define CI/CD workflows, which are located in the [.github/workflows](`../../.github/workflows`) folder. - -> Please note, if you instantiated the project with --workspace flag in `algokit init` it will automatically attempt to move the contents of the `.github` folder to the root of the workspace. - -### AlgoKit Workspaces - -To define custom `algokit project run` commands refer to [documentation](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/project/run.md). This allows orchestration of commands spanning across multiple projects within an algokit workspace based project (monorepo). +To generate a new `.env` or `.env.{target_network}` file, run `algokit generate env-file` ### Debugging Smart Contracts @@ -94,7 +86,15 @@ Refer to the commented header in the `__main__.py` file in the `smart_contracts` If you have opted in to include VSCode launch configurations in your project, you can also use the `Debug TEAL via AlgoKit AVM Debugger` launch configuration to interactively select an available trace file and launch the debug session for your smart contract. -For information on using and setting up the `AlgoKit AVM Debugger` VSCode extension refer [here](https://github.com/algorandfoundation/algokit-avm-vscode-debugger). To install the extension from the VSCode Marketplace, use the following link: [AlgoKit AVM Debugger extension](https://marketplace.visualstudio.com/items?itemName=algorandfoundation.algokit-avm-vscode-debugger). +For information on using and setting up the `AlgoKit AVM Debugger` VSCode extension refer [here](https://github.com/algorandfoundation/algokit-avm-vscode-debugger). To install the extension from the VSCode Marketplace, use the following link: [AlgoKit AVM Debugger extension](https://marketplace.visualstudio.com/items?itemName=algorandfoundation.algokit-avm-vscode-debugger).### Continuous Integration / Continuous Deployment (CI/CD) + +This project uses [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to define CI/CD workflows, which are located in the [.github/workflows](`../../.github/workflows`) folder. + +> Please note, if you instantiated the project with --workspace flag in `algokit init` it will automatically attempt to move the contents of the `.github` folder to the root of the workspace. + +### AlgoKit Workspaces + +To define custom `algokit project run` commands refer to [documentation](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/project/run.md). This allows orchestration of commands spanning across multiple projects within an algokit workspace based project (monorepo). #### Setting up GitHub for CI/CD workflow and TestNet deployment diff --git a/examples/production_python_react/projects/production_python_react-contracts/poetry.toml b/examples/production_python_react/projects/production_python_react-contracts/poetry.toml index ab1033b..5fcef8c 100644 --- a/examples/production_python_react/projects/production_python_react-contracts/poetry.toml +++ b/examples/production_python_react/projects/production_python_react-contracts/poetry.toml @@ -1,2 +1,3 @@ [virtualenvs] in-project = true +prefer-active-python = true diff --git a/examples/production_python_react/projects/production_python_react-contracts/pyproject.toml b/examples/production_python_react/projects/production_python_react-contracts/pyproject.toml index 2cc3238..85ab8e6 100644 --- a/examples/production_python_react/projects/production_python_react-contracts/pyproject.toml +++ b/examples/production_python_react/projects/production_python_react-contracts/pyproject.toml @@ -56,7 +56,7 @@ warn_redundant_casts = true warn_unused_ignores = true warn_return_any = true strict_equality = true -strict_concatenate = true +extra_checks = true disallow_any_unimported = true disallow_any_expr = true disallow_any_decorated = true diff --git a/examples/production_python_react/projects/production_python_react-contracts/smart_contracts/_helpers/build.py b/examples/production_python_react/projects/production_python_react-contracts/smart_contracts/_helpers/build.py index 0afee9c..3461154 100644 --- a/examples/production_python_react/projects/production_python_react-contracts/smart_contracts/_helpers/build.py +++ b/examples/production_python_react/projects/production_python_react-contracts/smart_contracts/_helpers/build.py @@ -31,7 +31,7 @@ def build(output_dir: Path, contract_path: Path) -> Path: contract_path.absolute(), f"--out-dir={output_dir}", "--output-arc32", - "--debug-level=0", + "--output-source-map", ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, diff --git a/examples/production_python_react/projects/production_python_react-frontend/.algokit/.copier-answers.yml b/examples/production_python_react/projects/production_python_react-frontend/.algokit/.copier-answers.yml index 0fe3e6f..9d5503f 100644 --- a/examples/production_python_react/projects/production_python_react-frontend/.algokit/.copier-answers.yml +++ b/examples/production_python_react/projects/production_python_react-frontend/.algokit/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier; NEVER EDIT MANUALLY -_commit: 1.0.8 +_commit: 1.0.9 _src_path: gh:algorandfoundation/algokit-react-frontend-template author_email: None author_name: None diff --git a/examples/production_python_react/projects/production_python_react-frontend/index.html b/examples/production_python_react/projects/production_python_react-frontend/index.html index a85566a..5a2ef4f 100644 --- a/examples/production_python_react/projects/production_python_react-frontend/index.html +++ b/examples/production_python_react/projects/production_python_react-frontend/index.html @@ -7,9 +7,6 @@
- diff --git a/examples/production_python_react/projects/production_python_react-frontend/package.json b/examples/production_python_react/projects/production_python_react-frontend/package.json index d665f4a..0dfa99a 100644 --- a/examples/production_python_react/projects/production_python_react-frontend/package.json +++ b/examples/production_python_react/projects/production_python_react-frontend/package.json @@ -8,21 +8,21 @@ "private": true, "type": "module", "engines": { - "node": ">=18.0", + "node": ">=20.0", "npm": ">=9.0" }, "devDependencies": { - "@algorandfoundation/algokit-client-generator": "^3.0.3", + "@algorandfoundation/algokit-client-generator": "^4.0.0", "@types/node": "^18.17.14", "@types/react": "^18.2.11", "@types/react-dom": "^18.2.4", "@vitejs/plugin-react": "^4.2.1", "autoprefixer": "^10.4.14", - "eslint": "^8.42.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-prettier": "^5.0.0", - "@typescript-eslint/eslint-plugin": "^6.5.0", - "@typescript-eslint/parser": "^6.5.0", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "@typescript-eslint/eslint-plugin": "^7.0.2", + "@typescript-eslint/parser": "^7.0.2", "postcss": "^8.4.24", "tailwindcss": "3.3.2", "ts-jest": "^29.1.1", @@ -31,16 +31,16 @@ "typescript": "^5.1.6", "@playwright/test": "^1.35.0", "playwright": "^1.35.0", - "vite": "^5.0.0" + "vite": "^5.0.0", + "vite-plugin-node-polyfills": "^0.22.0" }, "dependencies": { - "@walletconnect/modal-sign-html": "^2.6.1", - "@algorandfoundation/algokit-utils": "^6.0.2", + "@algorandfoundation/algokit-utils": "^7.0.0", "@blockshake/defly-connect": "^1.1.6", "@daffiwallet/connect": "^1.0.3", - "@perawallet/connect": "^1.3.1", - "@txnlab/use-wallet": "^2.4.0", - "algosdk": "^2.7.0", + "@perawallet/connect": "^1.3.4", + "@txnlab/use-wallet": "^2.8.2", + "algosdk": ">=2.9.0 <3.0", "daisyui": "^4.0.0", "notistack": "^3.0.1", "react": "^18.2.0", @@ -74,5 +74,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "overrides": { + "ws@>7.0.0 <7.5.9": "7.5.10" } } diff --git a/examples/production_python_react/projects/production_python_react-frontend/src/components/AppCalls.tsx b/examples/production_python_react/projects/production_python_react-frontend/src/components/AppCalls.tsx index 897b80c..1667fbc 100644 --- a/examples/production_python_react/projects/production_python_react-frontend/src/components/AppCalls.tsx +++ b/examples/production_python_react/projects/production_python_react-frontend/src/components/AppCalls.tsx @@ -1,12 +1,10 @@ -import * as algokit from '@algorandfoundation/algokit-utils' -import { TransactionSignerAccount } from '@algorandfoundation/algokit-utils/types/account' import { useWallet } from '@txnlab/use-wallet' import { useSnackbar } from 'notistack' import { useState } from 'react' -import { AppDetails } from '@algorandfoundation/algokit-utils/types/app-client' -import { HelloWorldClient } from '../contracts/HelloWorld' +import { HelloWorldFactory } from '../contracts/HelloWorld' import { OnSchemaBreak, OnUpdate } from '@algorandfoundation/algokit-utils/types/app' import { getAlgodConfigFromViteEnvironment, getIndexerConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' +import { AlgorandClient } from '@algorandfoundation/algokit-utils' interface AppCallsInterface { openModal: boolean @@ -16,22 +14,16 @@ interface AppCallsInterface { const AppCalls = ({ openModal, setModalState }: AppCallsInterface) => { const [loading, setLoading] = useState(false) const [contractInput, setContractInput] = useState('') + const { enqueueSnackbar } = useSnackbar() + const { signer, activeAddress } = useWallet() const algodConfig = getAlgodConfigFromViteEnvironment() - const algodClient = algokit.getAlgoClient({ - server: algodConfig.server, - port: algodConfig.port, - token: algodConfig.token, - }) const indexerConfig = getIndexerConfigFromViteEnvironment() - const indexer = algokit.getAlgoIndexerClient({ - server: indexerConfig.server, - port: indexerConfig.port, - token: indexerConfig.token, + const algorand = AlgorandClient.fromConfig({ + algodConfig, + indexerConfig, }) - - const { enqueueSnackbar } = useSnackbar() - const { signer, activeAddress } = useWallet() + algorand.setDefaultSigner(signer) const sendAppCall = async () => { setLoading(true) @@ -41,31 +33,38 @@ const AppCalls = ({ openModal, setModalState }: AppCallsInterface) => { // Instead, you would deploy your contract on your backend and reference it by id. // Given the simplicity of the starter contract, we are deploying it on the frontend // for demonstration purposes. - const appDetails = { - resolveBy: 'creatorAndName', - sender: { signer, addr: activeAddress } as TransactionSignerAccount, - creatorAddress: activeAddress, - findExistingUsing: indexer, - } as AppDetails + const factory = new HelloWorldFactory({ + defaultSender: activeAddress, + algorand, + }) + const deployResult = await factory + .deploy({ + onSchemaBreak: OnSchemaBreak.AppendApp, + onUpdate: OnUpdate.AppendApp, + }) + .catch((e: Error) => { + enqueueSnackbar(`Error deploying the contract: ${e.message}`, { variant: 'error' }) + setLoading(false) + return undefined + }) - const appClient = new HelloWorldClient(appDetails, algodClient) - const deployParams = { - onSchemaBreak: OnSchemaBreak.AppendApp, - onUpdate: OnUpdate.AppendApp, - } - await appClient.deploy(deployParams).catch((e: Error) => { - enqueueSnackbar(`Error deploying the contract: ${e.message}`, { variant: 'error' }) - setLoading(false) + if (!deployResult) { return - }) + } + + const { appClient } = deployResult - const response = await appClient.hello({ name: contractInput }).catch((e: Error) => { + const response = await appClient.send.hello({ args: { name: contractInput } }).catch((e: Error) => { enqueueSnackbar(`Error calling the contract: ${e.message}`, { variant: 'error' }) setLoading(false) - return + return undefined }) - enqueueSnackbar(`Response from the contract: ${response?.return}`, { variant: 'success' }) + if (!response) { + return + } + + enqueueSnackbar(`Response from the contract: ${response.return}`, { variant: 'success' }) setLoading(false) } diff --git a/examples/production_python_react/projects/production_python_react-frontend/src/components/Transact.tsx b/examples/production_python_react/projects/production_python_react-frontend/src/components/Transact.tsx index 16bd932..9e32a9e 100644 --- a/examples/production_python_react/projects/production_python_react-frontend/src/components/Transact.tsx +++ b/examples/production_python_react/projects/production_python_react-frontend/src/components/Transact.tsx @@ -1,6 +1,5 @@ -import * as algokit from '@algorandfoundation/algokit-utils' +import { algo, AlgorandClient } 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' @@ -15,15 +14,11 @@ const Transact = ({ openModal, setModalState }: TransactInterface) => { const [receiverAddress, setReceiverAddress] = useState('') const algodConfig = getAlgodConfigFromViteEnvironment() - const algodClient = algokit.getAlgoClient({ - server: algodConfig.server, - port: algodConfig.port, - token: algodConfig.token, - }) + const algorand = AlgorandClient.fromConfig({ algodConfig }) const { enqueueSnackbar } = useSnackbar() - const { signer, activeAddress, signTransactions, sendTransactions } = useWallet() + const { signer, activeAddress } = useWallet() const handleSubmitAlgo = async () => { setLoading(true) @@ -33,25 +28,15 @@ const Transact = ({ openModal, setModalState }: TransactInterface) => { 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' }) + const result = await algorand.send.payment({ + signer, + sender: activeAddress, + receiver: receiverAddress, + amount: algo(1), + }) + enqueueSnackbar(`Transaction sent: ${result.txIds[0]}`, { variant: 'success' }) setReceiverAddress('') } catch (e) { enqueueSnackbar('Failed to send transaction', { variant: 'error' }) diff --git a/examples/production_python_react/projects/production_python_react-frontend/src/contracts/HelloWorld.ts b/examples/production_python_react/projects/production_python_react-frontend/src/contracts/HelloWorld.ts index 48df604..58fcdab 100644 --- a/examples/production_python_react/projects/production_python_react-frontend/src/contracts/HelloWorld.ts +++ b/examples/production_python_react/projects/production_python_react-frontend/src/contracts/HelloWorld.ts @@ -1,305 +1,234 @@ /* eslint-disable */ -// @ts-nocheck /** * This file was automatically generated by @algorandfoundation/algokit-client-generator. * DO NOT MODIFY IT BY HAND. - * requires: @algorandfoundation/algokit-utils: ^2 + * requires: @algorandfoundation/algokit-utils: ^7 */ -import * as algokit from '@algorandfoundation/algokit-utils' -import type { - ABIAppCallArg, - AppCallTransactionResult, - AppCallTransactionResultOfType, - AppCompilationResult, - AppReference, - AppState, - AppStorageSchema, - CoreAppCallArgs, - RawAppCallArgs, - TealTemplateParams, -} from '@algorandfoundation/algokit-utils/types/app' -import type { - AppClientCallCoreParams, +import { AlgorandClientInterface } from '@algorandfoundation/algokit-utils/types/algorand-client-interface' +import { ABIReturn, AppReturn, SendAppTransactionResult } from '@algorandfoundation/algokit-utils/types/app' +import { Arc56Contract, getArc56ReturnValue, getABIStructFromABITuple } from '@algorandfoundation/algokit-utils/types/app-arc56' +import { + AppClient, + AppClientMethodCallParams, + AppClientParams, + AppClientBareCallParams, + CallOnComplete, AppClientCompilationParams, - AppClientDeployCoreParams, - AppDetails, - ApplicationClient, + ResolveAppClientByCreatorAndName, + ResolveAppClientByNetwork, + CloneAppClientParams, } from '@algorandfoundation/algokit-utils/types/app-client' -import type { AppSpec } from '@algorandfoundation/algokit-utils/types/app-spec' -import type { SendTransactionResult, TransactionToSign, SendTransactionFrom, SendTransactionParams } from '@algorandfoundation/algokit-utils/types/transaction' -import type { ABIResult, TransactionWithSigner } from 'algosdk' -import { Algodv2, OnApplicationComplete, Transaction, AtomicTransactionComposer, modelsv2 } from 'algosdk' -export const APP_SPEC: AppSpec = { - "hints": { - "hello(string)string": { - "call_config": { - "no_op": "CALL" - } - } - }, - "source": { - "approval": "I3ByYWdtYSB2ZXJzaW9uIDEwCgpzbWFydF9jb250cmFjdHMuaGVsbG9fd29ybGQuY29udHJhY3QuSGVsbG9Xb3JsZC5hcHByb3ZhbF9wcm9ncmFtOgogICAgY2FsbHN1YiBfX3B1eWFfYXJjNF9yb3V0ZXJfXwogICAgcmV0dXJuCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuX19wdXlhX2FyYzRfcm91dGVyX18oKSAtPiB1aW50NjQ6Cl9fcHV5YV9hcmM0X3JvdXRlcl9fOgogICAgcHJvdG8gMCAxCiAgICB0eG4gTnVtQXBwQXJncwogICAgYnogX19wdXlhX2FyYzRfcm91dGVyX19fYmFyZV9yb3V0aW5nQDUKICAgIG1ldGhvZCAiaGVsbG8oc3RyaW5nKXN0cmluZyIKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDAKICAgIG1hdGNoIF9fcHV5YV9hcmM0X3JvdXRlcl9fX2hlbGxvX3JvdXRlQDIKICAgIGludCAwCiAgICByZXRzdWIKCl9fcHV5YV9hcmM0X3JvdXRlcl9fX2hlbGxvX3JvdXRlQDI6CiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gaXMgbm90IGNyZWF0aW5nCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgY2FsbHN1YiBoZWxsbwogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGUgMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludCAxCiAgICByZXRzdWIKCl9fcHV5YV9hcmM0X3JvdXRlcl9fX2JhcmVfcm91dGluZ0A1OgogICAgdHhuIE9uQ29tcGxldGlvbgogICAgYm56IF9fcHV5YV9hcmM0X3JvdXRlcl9fX2FmdGVyX2lmX2Vsc2VAOQogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgICEKICAgIGFzc2VydCAvLyBpcyBjcmVhdGluZwogICAgaW50IDEKICAgIHJldHN1YgoKX19wdXlhX2FyYzRfcm91dGVyX19fYWZ0ZXJfaWZfZWxzZUA5OgogICAgaW50IDAKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvKG5hbWU6IGJ5dGVzKSAtPiBieXRlczoKaGVsbG86CiAgICBwcm90byAxIDEKICAgIGJ5dGUgIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCg==", - "clear": "I3ByYWdtYSB2ZXJzaW9uIDEwCgpzbWFydF9jb250cmFjdHMuaGVsbG9fd29ybGQuY29udHJhY3QuSGVsbG9Xb3JsZC5jbGVhcl9zdGF0ZV9wcm9ncmFtOgogICAgaW50IDEKICAgIHJldHVybgo=" - }, - "state": { - "global": { - "num_byte_slices": 0, - "num_uints": 0 - }, - "local": { - "num_byte_slices": 0, - "num_uints": 0 - } - }, - "schema": { - "global": { - "declared": {}, - "reserved": {} - }, - "local": { - "declared": {}, - "reserved": {} - } - }, - "contract": { - "name": "HelloWorld", - "methods": [ - { - "name": "hello", - "args": [ - { - "type": "string", - "name": "name" - } - ], - "readonly": false, - "returns": { - "type": "string" - } - } - ], - "networks": {} - }, - "bare_call_config": { - "no_op": "CREATE" - } -} +import { AppFactory, AppFactoryAppClientParams, AppFactoryResolveAppClientByCreatorAndNameParams, AppFactoryDeployParams, AppFactoryParams, CreateSchema } from '@algorandfoundation/algokit-utils/types/app-factory' +import { TransactionComposer, AppCallMethodCall, AppMethodCallTransactionArgument, SimulateOptions } from '@algorandfoundation/algokit-utils/types/composer' +import { SendParams, SendSingleTransactionResult, SendAtomicTransactionComposerResults } from '@algorandfoundation/algokit-utils/types/transaction' +import { Address, encodeAddress, modelsv2, OnApplicationComplete, Transaction, TransactionSigner } from 'algosdk' +import SimulateResponse = modelsv2.SimulateResponse + +export const APP_SPEC: Arc56Contract = {"arcs":[],"name":"HelloWorld","structs":{},"methods":[{"name":"hello","args":[{"name":"name","type":"string"}],"returns":{"type":"string"},"events":[],"actions":{"create":[],"call":["NoOp"]}}],"state":{"schema":{"global":{"ints":0,"bytes":0},"local":{"ints":0,"bytes":0}},"keys":{"global":{},"local":{},"box":{}},"maps":{"global":{},"local":{},"box":{}}},"source":{"approval":"I3ByYWdtYSB2ZXJzaW9uIDEwCgpzbWFydF9jb250cmFjdHMuaGVsbG9fd29ybGQuY29udHJhY3QuSGVsbG9Xb3JsZC5hcHByb3ZhbF9wcm9ncmFtOgogICAgaW50Y2Jsb2NrIDAgMQogICAgY2FsbHN1YiBfX3B1eWFfYXJjNF9yb3V0ZXJfXwogICAgcmV0dXJuCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuX19wdXlhX2FyYzRfcm91dGVyX18oKSAtPiB1aW50NjQ6Cl9fcHV5YV9hcmM0X3JvdXRlcl9fOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjUKICAgIC8vIGNsYXNzIEhlbGxvV29ybGQoQVJDNENvbnRyYWN0KToKICAgIHByb3RvIDAgMQogICAgdHhuIE51bUFwcEFyZ3MKICAgIGJ6IF9fcHV5YV9hcmM0X3JvdXRlcl9fX2JhcmVfcm91dGluZ0A1CiAgICBwdXNoYnl0ZXMgMHgwMmJlY2UxMSAvLyBtZXRob2QgImhlbGxvKHN0cmluZylzdHJpbmciCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAwCiAgICBtYXRjaCBfX3B1eWFfYXJjNF9yb3V0ZXJfX19oZWxsb19yb3V0ZUAyCiAgICBpbnRjXzAgLy8gMAogICAgcmV0c3ViCgpfX3B1eWFfYXJjNF9yb3V0ZXJfX19oZWxsb19yb3V0ZUAyOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjYKICAgIC8vIEBhYmltZXRob2QoKQogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBpcyBub3QgTm9PcAogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZXh0cmFjdCAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo2CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8KICAgIGR1cAogICAgbGVuCiAgICBpdG9iCiAgICBleHRyYWN0IDYgMgogICAgc3dhcAogICAgY29uY2F0CiAgICBwdXNoYnl0ZXMgMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMSAvLyAxCiAgICByZXRzdWIKCl9fcHV5YV9hcmM0X3JvdXRlcl9fX2JhcmVfcm91dGluZ0A1OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjUKICAgIC8vIGNsYXNzIEhlbGxvV29ybGQoQVJDNENvbnRyYWN0KToKICAgIHR4biBPbkNvbXBsZXRpb24KICAgIGJueiBfX3B1eWFfYXJjNF9yb3V0ZXJfX19hZnRlcl9pZl9lbHNlQDkKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICAhCiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIGNyZWF0aW5nCiAgICBpbnRjXzEgLy8gMQogICAgcmV0c3ViCgpfX3B1eWFfYXJjNF9yb3V0ZXJfX19hZnRlcl9pZl9lbHNlQDk6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgaW50Y18wIC8vIDAKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvKG5hbWU6IGJ5dGVzKSAtPiBieXRlczoKaGVsbG86CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6Ni03CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIC8vIGRlZiBoZWxsbyhzZWxmLCBuYW1lOiBTdHJpbmcpIC0+IFN0cmluZzoKICAgIHByb3RvIDEgMQogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjgKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBwdXNoYnl0ZXMgIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCg==","clear":"I3ByYWdtYSB2ZXJzaW9uIDEwCgpzbWFydF9jb250cmFjdHMuaGVsbG9fd29ybGQuY29udHJhY3QuSGVsbG9Xb3JsZC5jbGVhcl9zdGF0ZV9wcm9ncmFtOgogICAgcHVzaGludCAxIC8vIDEKICAgIHJldHVybgo="},"bareActions":{"create":["NoOp"],"call":[]}} as unknown as Arc56Contract -/** - * Defines an onCompletionAction of 'no_op' - */ -export type OnCompleteNoOp = { onCompleteAction?: 'no_op' | OnApplicationComplete.NoOpOC } -/** - * Defines an onCompletionAction of 'opt_in' - */ -export type OnCompleteOptIn = { onCompleteAction: 'opt_in' | OnApplicationComplete.OptInOC } -/** - * Defines an onCompletionAction of 'close_out' - */ -export type OnCompleteCloseOut = { onCompleteAction: 'close_out' | OnApplicationComplete.CloseOutOC } -/** - * Defines an onCompletionAction of 'delete_application' - */ -export type OnCompleteDelApp = { onCompleteAction: 'delete_application' | OnApplicationComplete.DeleteApplicationOC } -/** - * Defines an onCompletionAction of 'update_application' - */ -export type OnCompleteUpdApp = { onCompleteAction: 'update_application' | OnApplicationComplete.UpdateApplicationOC } -/** - * A state record containing a single unsigned integer - */ -export type IntegerState = { - /** - * Gets the state value as a BigInt. - */ - asBigInt(): bigint - /** - * Gets the state value as a number. - */ - asNumber(): number -} /** * A state record containing binary data */ -export type BinaryState = { +export interface BinaryState { /** * Gets the state value as a Uint8Array */ - asByteArray(): Uint8Array + asByteArray(): Uint8Array | undefined /** * Gets the state value as a string */ - asString(): string + asString(): string | undefined } -export type AppCreateCallTransactionResult = AppCallTransactionResult & Partial & AppReference -export type AppUpdateCallTransactionResult = AppCallTransactionResult & Partial +class BinaryStateValue implements BinaryState { + constructor(private value: Uint8Array | undefined) {} -export type AppClientComposeCallCoreParams = Omit & { - sendParams?: Omit + asByteArray(): Uint8Array | undefined { + return this.value + } + + asString(): string | undefined { + return this.value !== undefined ? Buffer.from(this.value).toString('utf-8') : undefined + } } -export type AppClientComposeExecuteParams = Pick -export type IncludeSchema = { +/** + * Expands types for IntelliSense so they are more human readable + * See https://stackoverflow.com/a/69288824 + */ +export type Expand = T extends (...args: infer A) => infer R + ? (...args: Expand) => Expand + : T extends infer O + ? { [K in keyof O]: O[K] } + : never + + +/** + * The argument types for the HelloWorld contract + */ +export type HelloWorldArgs = { /** - * Any overrides for the storage schema to request for the created app; by default the schema indicated by the app spec is used. + * The object representation of the arguments for each method */ - schema?: Partial + obj: { + 'hello(string)string': { + name: string + } + } + /** + * The tuple representation of the arguments for each method + */ + tuple: { + 'hello(string)string': [name: string] + } +} + +/** + * The return type for each method + */ +export type HelloWorldReturns = { + 'hello(string)string': string } /** * Defines the types of available calls and state of the HelloWorld smart contract. */ -export type HelloWorld = { +export type HelloWorldTypes = { /** * Maps method signatures / names to their argument and return types. */ methods: & Record<'hello(string)string' | 'hello', { - argsObj: { - name: string - } - argsTuple: [name: string] - returns: string + argsObj: HelloWorldArgs['obj']['hello(string)string'] + argsTuple: HelloWorldArgs['tuple']['hello(string)string'] + returns: HelloWorldReturns['hello(string)string'] }> } + /** - * Defines the possible abi call signatures + * Defines the possible abi call signatures. */ -export type HelloWorldSig = keyof HelloWorld['methods'] +export type HelloWorldSignatures = keyof HelloWorldTypes['methods'] /** - * Defines an object containing all relevant parameters for a single call to the contract. Where TSignature is undefined, a bare call is made + * Defines the possible abi call signatures for methods that return a non-void value. */ -export type TypedCallParams = { - method: TSignature - methodArgs: TSignature extends undefined ? undefined : Array -} & AppClientCallCoreParams & CoreAppCallArgs +export type HelloWorldNonVoidMethodSignatures = keyof HelloWorldTypes['methods'] extends infer T ? T extends keyof HelloWorldTypes['methods'] ? MethodReturn extends void ? never : T : never : never /** - * Defines the arguments required for a bare call + * Defines an object containing all relevant parameters for a single call to the contract. */ -export type BareCallArgs = Omit +export type CallParams = Expand< + Omit & + { + /** The args for the ABI method call, either as an ordered array or an object */ + args: Expand + } +> /** - * Maps a method signature from the HelloWorld smart contract to the method's arguments in either tuple of struct form + * Maps a method signature from the HelloWorld smart contract to the method's arguments in either tuple or struct form */ -export type MethodArgs = HelloWorld['methods'][TSignature]['argsObj' | 'argsTuple'] +export type MethodArgs = HelloWorldTypes['methods'][TSignature]['argsObj' | 'argsTuple'] /** * Maps a method signature from the HelloWorld smart contract to the method's return type */ -export type MethodReturn = HelloWorld['methods'][TSignature]['returns'] +export type MethodReturn = HelloWorldTypes['methods'][TSignature]['returns'] + /** - * A factory for available 'create' calls - */ -export type HelloWorldCreateCalls = (typeof HelloWorldCallFactory)['create'] -/** - * Defines supported create methods for this smart contract + * Defines supported create method params for this smart contract */ export type HelloWorldCreateCallParams = - | (TypedCallParams & (OnCompleteNoOp)) + | Expand /** * Defines arguments required for the deploy method. */ -export type HelloWorldDeployArgs = { - deployTimeParams?: TealTemplateParams +export type HelloWorldDeployParams = Expand & { /** - * A delegate which takes a create call factory and returns the create call params for this smart contract + * Create transaction parameters to use if a create needs to be issued as part of deployment; use `method` to define ABI call (if available) or leave out for a bare call (if available) */ - createCall?: (callFactory: HelloWorldCreateCalls) => HelloWorldCreateCallParams -} + createParams?: HelloWorldCreateCallParams +}> /** - * Exposes methods for constructing all available smart contract calls + * Exposes methods for constructing `AppClient` params objects for ABI calls to the HelloWorld smart contract */ -export abstract class HelloWorldCallFactory { - /** - * Gets available create call factories - */ - static get create() { - return { - /** - * Constructs a create call for the HelloWorld smart contract using a bare call - * - * @param params Any parameters for the call - * @returns A TypedCallParams object for the call - */ - bare(params: BareCallArgs & AppClientCallCoreParams & CoreAppCallArgs & AppClientCompilationParams & (OnCompleteNoOp) = {}) { - return { - method: undefined, - methodArgs: undefined, - ...params, - } - }, - } - } - +export abstract class HelloWorldParamsFactory { /** * Constructs a no op call for the hello(string)string ABI method * - * @param args Any args for the contract call - * @param params Any additional parameters for the call - * @returns A TypedCallParams object for the call + * @param params Parameters for the call + * @returns An `AppClientMethodCallParams` object for the call */ - static hello(args: MethodArgs<'hello(string)string'>, params: AppClientCallCoreParams & CoreAppCallArgs) { + static hello(params: CallParams & CallOnComplete): AppClientMethodCallParams & CallOnComplete { return { - method: 'hello(string)string' as const, - methodArgs: Array.isArray(args) ? args : [args.name], ...params, + method: 'hello(string)string' as const, + args: Array.isArray(params.args) ? params.args : [params.args.name], } } } /** - * A client to make calls to the HelloWorld smart contract + * A factory to create and deploy one or more instance of the HelloWorld smart contract and to create one or more app clients to interact with those (or other) app instances */ -export class HelloWorldClient { +export class HelloWorldFactory { /** - * The underlying `ApplicationClient` for when you want to have more flexibility + * The underlying `AppFactory` for when you want to have more flexibility */ - public readonly appClient: ApplicationClient - - private readonly sender: SendTransactionFrom | undefined + public readonly appFactory: AppFactory /** - * Creates a new instance of `HelloWorldClient` + * Creates a new instance of `HelloWorldFactory` * - * @param appDetails appDetails The details to identify the app to deploy - * @param algod An algod client instance - */ - constructor(appDetails: AppDetails, private algod: Algodv2) { - this.sender = appDetails.sender - this.appClient = algokit.getAppClient({ - ...appDetails, - app: APP_SPEC - }, algod) + * @param params The parameters to initialise the app factory with + */ + constructor(params: Omit) { + this.appFactory = new AppFactory({ + ...params, + appSpec: APP_SPEC, + }) } - + + /** The name of the app (from the ARC-32 / ARC-56 app spec or override). */ + public get appName() { + return this.appFactory.appName + } + + /** The ARC-56 app spec being used */ + get appSpec() { + return APP_SPEC + } + + /** A reference to the underlying `AlgorandClient` this app factory is using. */ + public get algorand(): AlgorandClientInterface { + return this.appFactory.algorand + } + /** - * Checks for decode errors on the AppCallTransactionResult and maps the return value to the specified generic type + * Returns a new `AppClient` client for an app instance of the given ID. * - * @param result The AppCallTransactionResult to be mapped - * @param returnValueFormatter An optional delegate to format the return value if required - * @returns The smart contract response with an updated return value + * Automatically populates appName, defaultSender and source maps from the factory + * if not specified in the params. + * @param params The parameters to create the app client + * @returns The `AppClient` */ - protected mapReturnValue(result: AppCallTransactionResult, returnValueFormatter?: (value: any) => TReturn): AppCallTransactionResultOfType & TResult { - if(result.return?.decodeError) { - throw result.return.decodeError - } - const returnValue = result.return?.returnValue !== undefined && returnValueFormatter !== undefined - ? returnValueFormatter(result.return.returnValue) - : result.return?.returnValue as TReturn | undefined - return { ...result, return: returnValue } as AppCallTransactionResultOfType & TResult + public getAppClientById(params: AppFactoryAppClientParams) { + return new HelloWorldClient(this.appFactory.getAppClientById(params)) } - + /** - * Calls the ABI method with the matching signature using an onCompletion code of NO_OP + * Returns a new `AppClient` client, resolving the app by creator address and name + * using AlgoKit app deployment semantics (i.e. looking for the app creation transaction note). * - * @param typedCallParams An object containing the method signature, args, and any other relevant parameters - * @param returnValueFormatter An optional delegate which when provided will be used to map non-undefined return values to the target type - * @returns The result of the smart contract call + * Automatically populates appName, defaultSender and source maps from the factory + * if not specified in the params. + * @param params The parameters to create the app client + * @returns The `AppClient` */ - public async call(typedCallParams: TypedCallParams, returnValueFormatter?: (value: any) => MethodReturn) { - return this.mapReturnValue>(await this.appClient.call(typedCallParams), returnValueFormatter) + public async getAppClientByCreatorAndName( + params: AppFactoryResolveAppClientByCreatorAndNameParams, + ) { + return new HelloWorldClient(await this.appFactory.getAppClientByCreatorAndName(params)) } /** @@ -308,92 +237,299 @@ export class HelloWorldClient { * @param params The arguments for the contract calls and any additional parameters for the call * @returns The deployment result */ - public deploy(params: HelloWorldDeployArgs & AppClientDeployCoreParams & IncludeSchema = {}): ReturnType { - const createArgs = params.createCall?.(HelloWorldCallFactory.create) - return this.appClient.deploy({ + public async deploy(params: HelloWorldDeployParams = {}) { + const result = await this.appFactory.deploy({ ...params, - createArgs, - createOnCompleteAction: createArgs?.onCompleteAction, }) + return { result: result.result, appClient: new HelloWorldClient(result.appClient) } } /** - * Gets available create methods + * Get parameters to create transactions (create and deploy related calls) for the current app. A good mental model for this is that these parameters represent a deferred transaction creation. */ - public get create() { - const $this = this - return { + readonly params = { + /** + * Gets available create methods + */ + create: { /** * Creates a new instance of the HelloWorld smart contract using a bare call. * - * @param args The arguments for the bare call + * @param params The params for the bare (raw) call + * @returns The params for a create call + */ + bare: (params?: Expand) => { + return this.appFactory.params.bare.create(params) + }, + }, + + } + + /** + * Create transactions for the current app + */ + readonly createTransaction = { + /** + * Gets available create methods + */ + create: { + /** + * Creates a new instance of the HelloWorld smart contract using a bare call. + * + * @param params The params for the bare (raw) call + * @returns The params for a create call + */ + bare: (params?: Expand) => { + return this.appFactory.params.bare.create(params) + }, + }, + + } + + /** + * Send calls to the current app + */ + readonly send = { + /** + * Gets available create methods + */ + create: { + /** + * Creates a new instance of the HelloWorld smart contract using a bare call. + * + * @param params The params for the bare (raw) call * @returns The create result */ - async bare(args: BareCallArgs & AppClientCallCoreParams & AppClientCompilationParams & IncludeSchema & CoreAppCallArgs & (OnCompleteNoOp) = {}) { - return $this.mapReturnValue(await $this.appClient.create(args)) + bare: async (params?: Expand) => { + const result = await this.appFactory.send.bare.create(params) + return { result: result.result, appClient: new HelloWorldClient(result.appClient) } }, - } + }, + } +} +/** + * A client to make calls to the HelloWorld smart contract + */ +export class HelloWorldClient { + /** + * The underlying `AppClient` for when you want to have more flexibility + */ + public readonly appClient: AppClient + /** - * Makes a clear_state call to an existing instance of the HelloWorld smart contract. + * Creates a new instance of `HelloWorldClient` * - * @param args The arguments for the bare call - * @returns The clear_state result + * @param appClient An `AppClient` instance which has been created with the HelloWorld app spec + */ + constructor(appClient: AppClient) + /** + * Creates a new instance of `HelloWorldClient` + * + * @param params The parameters to initialise the app client with + */ + constructor(params: Omit) + constructor(appClientOrParams: AppClient | Omit) { + this.appClient = appClientOrParams instanceof AppClient ? appClientOrParams : new AppClient({ + ...appClientOrParams, + appSpec: APP_SPEC, + }) + } + + /** + * Checks for decode errors on the given return value and maps the return value to the return type for the given method + * @returns The typed return value or undefined if there was no value + */ + decodeReturnValue(method: TSignature, returnValue: ABIReturn | undefined) { + return returnValue !== undefined ? getArc56ReturnValue>(returnValue, this.appClient.getABIMethod(method), APP_SPEC.structs) : undefined + } + + /** + * Returns a new `HelloWorldClient` client, resolving the app by creator address and name + * using AlgoKit app deployment semantics (i.e. looking for the app creation transaction note). + * @param params The parameters to create the app client + */ + public static async fromCreatorAndName(params: Omit): Promise { + return new HelloWorldClient(await AppClient.fromCreatorAndName({...params, appSpec: APP_SPEC})) + } + + /** + * Returns an `HelloWorldClient` instance for the current network based on + * pre-determined network-specific app IDs specified in the ARC-56 app spec. + * + * If no IDs are in the app spec or the network isn't recognised, an error is thrown. + * @param params The parameters to create the app client */ - public clearState(args: BareCallArgs & AppClientCallCoreParams & CoreAppCallArgs = {}) { - return this.appClient.clearState(args) + static async fromNetwork( + params: Omit + ): Promise { + return new HelloWorldClient(await AppClient.fromNetwork({...params, appSpec: APP_SPEC})) + } + + /** The ID of the app instance this client is linked to. */ + public get appId() { + return this.appClient.appId + } + + /** The app address of the app instance this client is linked to. */ + public get appAddress() { + return this.appClient.appAddress + } + + /** The name of the app. */ + public get appName() { + return this.appClient.appName + } + + /** The ARC-56 app spec being used */ + public get appSpec() { + return this.appClient.appSpec + } + + /** A reference to the underlying `AlgorandClient` this app client is using. */ + public get algorand(): AlgorandClientInterface { + return this.appClient.algorand } /** - * Calls the hello(string)string ABI method. + * Get parameters to create transactions for the current app. A good mental model for this is that these parameters represent a deferred transaction creation. + */ + readonly params = { + /** + * Makes a clear_state call to an existing instance of the HelloWorld smart contract. + * + * @param params The params for the bare (raw) call + * @returns The clearState result + */ + clearState: (params?: Expand) => { + return this.appClient.params.bare.clearState(params) + }, + + /** + * Makes a call to the HelloWorld smart contract using the `hello(string)string` ABI method. + * + * @param params The params for the smart contract call + * @returns The call params + */ + hello: (params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + return this.appClient.params.call(HelloWorldParamsFactory.hello(params)) + }, + + } + + /** + * Create transactions for the current app + */ + readonly createTransaction = { + /** + * Makes a clear_state call to an existing instance of the HelloWorld smart contract. + * + * @param params The params for the bare (raw) call + * @returns The clearState result + */ + clearState: (params?: Expand) => { + return this.appClient.createTransaction.bare.clearState(params) + }, + + /** + * Makes a call to the HelloWorld smart contract using the `hello(string)string` ABI method. + * + * @param params The params for the smart contract call + * @returns The call transaction + */ + hello: (params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + return this.appClient.createTransaction.call(HelloWorldParamsFactory.hello(params)) + }, + + } + + /** + * Send calls to the current app + */ + readonly send = { + /** + * Makes a clear_state call to an existing instance of the HelloWorld smart contract. + * + * @param params The params for the bare (raw) call + * @returns The clearState result + */ + clearState: (params?: Expand) => { + return this.appClient.send.bare.clearState(params) + }, + + /** + * Makes a call to the HelloWorld smart contract using the `hello(string)string` ABI method. + * + * @param params The params for the smart contract call + * @returns The call result + */ + hello: async (params: CallParams & SendParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + const result = await this.appClient.send.call(HelloWorldParamsFactory.hello(params)) + return {...result, return: result.return as undefined | HelloWorldReturns['hello(string)string']} + }, + + } + + /** + * Clone this app client with different params * - * @param args The arguments for the contract call - * @param params Any additional parameters for the call - * @returns The result of the call + * @param params The params to use for the the cloned app client. Omit a param to keep the original value. Set a param to override the original value. Setting to undefined will clear the original value. + * @returns A new app client with the altered params + */ + public clone(params: CloneAppClientParams) { + return new HelloWorldClient(this.appClient.clone(params)) + } + + /** + * Methods to access state for the current HelloWorld app */ - public hello(args: MethodArgs<'hello(string)string'>, params: AppClientCallCoreParams & CoreAppCallArgs = {}) { - return this.call(HelloWorldCallFactory.hello(args, params)) + state = { } - public compose(): HelloWorldComposer { + public newGroup(): HelloWorldComposer { const client = this - const atc = new AtomicTransactionComposer() + const composer = this.algorand.newGroup() let promiseChain:Promise = Promise.resolve() - const resultMappers: Array any)> = [] + const resultMappers: Array any)> = [] return { - hello(args: MethodArgs<'hello(string)string'>, params?: AppClientComposeCallCoreParams & CoreAppCallArgs) { - promiseChain = promiseChain.then(() => client.hello(args, {...params, sendParams: {...params?.sendParams, skipSending: true, atc}})) - resultMappers.push(undefined) + /** + * Add a hello(string)string method call against the HelloWorld contract + */ + hello(params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) { + promiseChain = promiseChain.then(async () => composer.addAppCallMethodCall(await client.params.hello(params))) + resultMappers.push((v) => client.decodeReturnValue('hello(string)string', v)) return this }, - clearState(args?: BareCallArgs & AppClientComposeCallCoreParams & CoreAppCallArgs) { - promiseChain = promiseChain.then(() => client.clearState({...args, sendParams: {...args?.sendParams, skipSending: true, atc}})) - resultMappers.push(undefined) + /** + * Add a clear state call to the HelloWorld contract + */ + clearState(params: AppClientBareCallParams) { + promiseChain = promiseChain.then(() => composer.addAppCall(client.params.clearState(params))) return this }, - addTransaction(txn: TransactionWithSigner | TransactionToSign | Transaction | Promise, defaultSender?: SendTransactionFrom) { - promiseChain = promiseChain.then(async () => atc.addTransaction(await algokit.getTransactionWithSigner(txn, defaultSender ?? client.sender))) + addTransaction(txn: Transaction, signer?: TransactionSigner) { + promiseChain = promiseChain.then(() => composer.addTransaction(txn, signer)) return this }, - async atc() { + async composer() { await promiseChain - return atc + return composer }, async simulate(options?: SimulateOptions) { await promiseChain - const result = await atc.simulate(client.algod, new modelsv2.SimulateRequest({ txnGroups: [], ...options })) + const result = await composer.simulate(options) return { ...result, - returns: result.methodResults?.map((val, i) => resultMappers[i] !== undefined ? resultMappers[i]!(val.returnValue) : val.returnValue) + returns: result.returns?.map((val, i) => resultMappers[i] !== undefined ? resultMappers[i]!(val) : val.returnValue) } }, - async execute(sendParams?: AppClientComposeExecuteParams) { + async send(params?: SendParams) { await promiseChain - const result = await algokit.sendAtomicTransactionComposer({ atc, sendParams }, client.algod) + const result = await composer.send(params) return { ...result, - returns: result.returns?.map((val, i) => resultMappers[i] !== undefined ? resultMappers[i]!(val.returnValue) : val.returnValue) + returns: result.returns?.map((val, i) => resultMappers[i] !== undefined ? resultMappers[i]!(val) : val.returnValue) } } } as unknown as HelloWorldComposer @@ -407,7 +543,7 @@ export type HelloWorldComposer = { * @param params Any additional parameters for the call * @returns The typed transaction composer so you can fluently chain multiple calls or call execute to execute all queued up transactions */ - hello(args: MethodArgs<'hello(string)string'>, params?: AppClientComposeCallCoreParams & CoreAppCallArgs): HelloWorldComposer<[...TReturns, MethodReturn<'hello(string)string'>]> + hello(params?: CallParams): HelloWorldComposer<[...TReturns, HelloWorldReturns['hello(string)string'] | undefined]> /** * Makes a clear_state call to an existing instance of the HelloWorld smart contract. @@ -415,37 +551,29 @@ export type HelloWorldComposer = { * @param args The arguments for the bare call * @returns The typed transaction composer so you can fluently chain multiple calls or call execute to execute all queued up transactions */ - clearState(args?: BareCallArgs & AppClientComposeCallCoreParams & CoreAppCallArgs): HelloWorldComposer<[...TReturns, undefined]> + clearState(params?: AppClientBareCallParams): HelloWorldComposer<[...TReturns, undefined]> /** * Adds a transaction to the composer * - * @param txn One of: A TransactionWithSigner object (returned as is), a TransactionToSign object (signer is obtained from the signer property), a Transaction object (signer is extracted from the defaultSender parameter), an async SendTransactionResult returned by one of algokit utils helpers (signer is obtained from the defaultSender parameter) - * @param defaultSender The default sender to be used to obtain a signer where the object provided to the transaction parameter does not include a signer. + * @param txn A transaction to add to the transaction group + * @param signer The optional signer to use when signing this transaction. */ - addTransaction(txn: TransactionWithSigner | TransactionToSign | Transaction | Promise, defaultSender?: SendTransactionFrom): HelloWorldComposer + addTransaction(txn: Transaction, signer?: TransactionSigner): HelloWorldComposer /** * Returns the underlying AtomicTransactionComposer instance */ - atc(): Promise + composer(): TransactionComposer /** * Simulates the transaction group and returns the result */ - simulate(options?: SimulateOptions): Promise> + simulate(options?: SimulateOptions): Promise & { simulateResponse: SimulateResponse }> /** - * Executes the transaction group and returns the results + * Sends the transaction group to the network and returns the results */ - execute(sendParams?: AppClientComposeExecuteParams): Promise> -} -export type SimulateOptions = Omit[0], 'txnGroups'> -export type HelloWorldComposerSimulateResult = { - returns: TReturns - methodResults: ABIResult[] - simulateResponse: modelsv2.SimulateResponse + send(params?: SendParams): Promise> } -export type HelloWorldComposerResults = { +export type HelloWorldComposerResults = Expand + diff --git a/examples/production_python_react/projects/production_python_react-frontend/tests/example.spec.ts b/examples/production_python_react/projects/production_python_react-frontend/tests/example.spec.ts index df83322..d6909a3 100644 --- a/examples/production_python_react/projects/production_python_react-frontend/tests/example.spec.ts +++ b/examples/production_python_react/projects/production_python_react-frontend/tests/example.spec.ts @@ -1,7 +1,10 @@ -import { randomAccount } from '@algorandfoundation/algokit-utils' +import { algorandFixture } from '@algorandfoundation/algokit-utils/testing' import { expect, test } from '@playwright/test' +const localnet = algorandFixture() + test.beforeEach(async ({ page }) => { + await localnet.beforeEach() await page.goto('http://localhost:5173/') }) @@ -27,8 +30,7 @@ test('authentication and dummy payment transaction', async ({ page }) => { // 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('receiver-address').fill(localnet.context.testAccount.addr) await page.getByTestId('send-algo').click() // 3. Must be able to see a notification that the transaction was sent diff --git a/examples/production_python_react/projects/production_python_react-frontend/vite.config.ts b/examples/production_python_react/projects/production_python_react-frontend/vite.config.ts index 36f7f4e..e42dc7c 100644 --- a/examples/production_python_react/projects/production_python_react-frontend/vite.config.ts +++ b/examples/production_python_react/projects/production_python_react-frontend/vite.config.ts @@ -1,7 +1,15 @@ import react from '@vitejs/plugin-react' import { defineConfig } from 'vite' +import { nodePolyfills } from 'vite-plugin-node-polyfills' // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react()], + plugins: [ + react(), + nodePolyfills({ + globals: { + Buffer: true, + }, + }), + ], }) diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/contracts/clients/CalculatorClient.ts b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/contracts/clients/CalculatorClient.ts index cc57180..94efde1 100644 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/contracts/clients/CalculatorClient.ts +++ b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/contracts/clients/CalculatorClient.ts @@ -73,7 +73,7 @@ export const APP_SPEC: AppSpec = { } }, "source": { - "approval": "I3ByYWdtYSB2ZXJzaW9uIDEwCmJ5dGVjYmxvY2sgMHgxNTFmN2M3NQoKLy8gVGhpcyBURUFMIHdhcyBnZW5lcmF0ZWQgYnkgVEVBTFNjcmlwdCB2MC4xMDQuMQovLyBodHRwczovL2dpdGh1Yi5jb20vYWxnb3JhbmRmb3VuZGF0aW9uL1RFQUxTY3JpcHQKCi8vIFRoaXMgY29udHJhY3QgaXMgY29tcGxpYW50IHdpdGggYW5kL29yIGltcGxlbWVudHMgdGhlIGZvbGxvd2luZyBBUkNzOiBbIEFSQzQgXQoKLy8gVGhlIGZvbGxvd2luZyB0ZW4gbGluZXMgb2YgVEVBTCBoYW5kbGUgaW5pdGlhbCBwcm9ncmFtIGZsb3cKLy8gVGhpcyBwYXR0ZXJuIGlzIHVzZWQgdG8gbWFrZSBpdCBlYXN5IGZvciBhbnlvbmUgdG8gcGFyc2UgdGhlIHN0YXJ0IG9mIHRoZSBwcm9ncmFtIGFuZCBkZXRlcm1pbmUgaWYgYSBzcGVjaWZpYyBhY3Rpb24gaXMgYWxsb3dlZAovLyBIZXJlLCBhY3Rpb24gcmVmZXJzIHRvIHRoZSBPbkNvbXBsZXRlIGluIGNvbWJpbmF0aW9uIHdpdGggd2hldGhlciB0aGUgYXBwIGlzIGJlaW5nIGNyZWF0ZWQgb3IgY2FsbGVkCi8vIEV2ZXJ5IHBvc3NpYmxlIGFjdGlvbiBmb3IgdGhpcyBjb250cmFjdCBpcyByZXByZXNlbnRlZCBpbiB0aGUgc3dpdGNoIHN0YXRlbWVudAovLyBJZiB0aGUgYWN0aW9uIGlzIG5vdCBpbXBsZW1lbnRlZCBpbiB0aGUgY29udHJhY3QsIGl0cyByZXNwZWN0aXZlIGJyYW5jaCB3aWxsIGJlICIqTk9UX0lNUExFTUVOVEVEIiB3aGljaCBqdXN0IGNvbnRhaW5zICJlcnIiCnR4biBBcHBsaWNhdGlvbklECiEKcHVzaGludCA2CioKdHhuIE9uQ29tcGxldGlvbgorCnN3aXRjaCAqY2FsbF9Ob09wICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqY3JlYXRlX05vT3AgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVECgoqTk9UX0lNUExFTUVOVEVEOgoJLy8gVGhlIHJlcXVlc3RlZCBhY3Rpb24gaXMgbm90IGltcGxlbWVudGVkIGluIHRoaXMgY29udHJhY3QuIEFyZSB5b3UgdXNpbmcgdGhlIGNvcnJlY3QgT25Db21wbGV0ZT8gRGlkIHlvdSBzZXQgeW91ciBhcHAgSUQ/CgllcnIKCi8vIGdldFN1bShhOiB1aW50NjQsIGI6IHVpbnQ2NCk6IHVpbnQ2NAovLwovLyBDYWxjdWxhdGVzIHRoZSBzdW0gb2YgdHdvIG51bWJlcnMKLy8KLy8gQHBhcmFtIGEKLy8gQHBhcmFtIGIKLy8gQHJldHVybnMgVGhlIHN1bSBvZiBhIGFuZCBiCmdldFN1bToKCXByb3RvIDIgMQoKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6MTIKCS8vIHJldHVybiBhICsgYjsKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCSsKCXJldHN1YgoKLy8gZ2V0RGlmZmVyZW5jZShhOiB1aW50NjQsIGI6IHVpbnQ2NCk6IHVpbnQ2NAovLwovLyBDYWxjdWxhdGVzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdHdvIG51bWJlcnMKLy8KLy8gQHBhcmFtIGEKLy8gQHBhcmFtIGIKLy8gQHJldHVybnMgVGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhIGFuZCBiLgpnZXREaWZmZXJlbmNlOgoJcHJvdG8gMiAxCgoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czoyMwoJLy8gcmV0dXJuIGEgPj0gYiA/IGEgLSBiIDogYiAtIGE7CglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0Cgk+PQoJYnogKnRlcm5hcnkwX2ZhbHNlCglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0CgktCgliICp0ZXJuYXJ5MF9lbmQKCip0ZXJuYXJ5MF9mYWxzZToKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCS0KCip0ZXJuYXJ5MF9lbmQ6CglyZXRzdWIKCi8vIGRvTWF0aCh1aW50NjQsdWludDY0LHN0cmluZyl1aW50NjQKKmFiaV9yb3V0ZV9kb01hdGg6CgkvLyBUaGUgQUJJIHJldHVybiBwcmVmaXgKCWJ5dGUgMHgxNTFmN2M3NQoKCS8vIG9wZXJhdGlvbjogc3RyaW5nCgl0eG5hIEFwcGxpY2F0aW9uQXJncyAzCglleHRyYWN0IDIgMAoKCS8vIGI6IHVpbnQ2NAoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgoJYnRvaQoKCS8vIGE6IHVpbnQ2NAoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQoJYnRvaQoKCS8vIGV4ZWN1dGUgZG9NYXRoKHVpbnQ2NCx1aW50NjQsc3RyaW5nKXVpbnQ2NAoJY2FsbHN1YiBkb01hdGgKCWl0b2IKCWNvbmNhdAoJbG9nCglwdXNoaW50IDEKCXJldHVybgoKLy8gZG9NYXRoKGE6IHVpbnQ2NCwgYjogdWludDY0LCBvcGVyYXRpb246IHN0cmluZyk6IHVpbnQ2NAovLwovLyBBIG1ldGhvZCB0aGF0IHRha2VzIHR3byBudW1iZXJzIGFuZCBkb2VzIGVpdGhlciBhZGRpdGlvbiBvciBzdWJ0cmFjdGlvbgovLwovLyBAcGFyYW0gYSBUaGUgZmlyc3QgdWludDY0Ci8vIEBwYXJhbSBiIFRoZSBzZWNvbmQgdWludDY0Ci8vIEBwYXJhbSBvcGVyYXRpb24gVGhlIG9wZXJhdGlvbiB0byBwZXJmb3JtLiBDYW4gYmUgZWl0aGVyICdzdW0nIG9yICdkaWZmZXJlbmNlJwovLwovLyBAcmV0dXJucyBUaGUgcmVzdWx0IG9mIHRoZSBvcGVyYXRpb24KZG9NYXRoOgoJcHJvdG8gMyAxCgoJLy8gUHVzaCBlbXB0eSBieXRlcyBhZnRlciB0aGUgZnJhbWUgcG9pbnRlciB0byByZXNlcnZlIHNwYWNlIGZvciBsb2NhbCB2YXJpYWJsZXMKCXB1c2hieXRlcyAweAoKCS8vICppZjBfY29uZGl0aW9uCgkvLyBjb250cmFjdHMvQ2FsY3VsYXRvci5hbGdvLnRzOjM4CgkvLyBvcGVyYXRpb24gPT09ICdzdW0nCglmcmFtZV9kaWcgLTMgLy8gb3BlcmF0aW9uOiBzdHJpbmcKCXB1c2hieXRlcyAweDczNzU2ZCAvLyAic3VtIgoJPT0KCWJ6ICppZjBfZWxzZWlmMV9jb25kaXRpb24KCgkvLyAqaWYwX2NvbnNlcXVlbnQKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6MzkKCS8vIHJlc3VsdCA9IHRoaXMuZ2V0U3VtKGEsIGIpCglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0CglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CgljYWxsc3ViIGdldFN1bQoJZnJhbWVfYnVyeSAwIC8vIHJlc3VsdDogdWludDY0CgliICppZjBfZW5kCgoqaWYwX2Vsc2VpZjFfY29uZGl0aW9uOgoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czo0MAoJLy8gb3BlcmF0aW9uID09PSAnZGlmZmVyZW5jZScKCWZyYW1lX2RpZyAtMyAvLyBvcGVyYXRpb246IHN0cmluZwoJcHVzaGJ5dGVzIDB4NjQ2OTY2NjY2NTcyNjU2ZTYzNjUgLy8gImRpZmZlcmVuY2UiCgk9PQoJYnogKmlmMF9lbHNlCgoJLy8gKmlmMF9lbHNlaWYxX2NvbnNlcXVlbnQKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6NDEKCS8vIHJlc3VsdCA9IHRoaXMuZ2V0RGlmZmVyZW5jZShhLCBiKQoJZnJhbWVfZGlnIC0yIC8vIGI6IHVpbnQ2NAoJZnJhbWVfZGlnIC0xIC8vIGE6IHVpbnQ2NAoJY2FsbHN1YiBnZXREaWZmZXJlbmNlCglmcmFtZV9idXJ5IDAgLy8gcmVzdWx0OiB1aW50NjQKCWIgKmlmMF9lbmQKCippZjBfZWxzZToKCS8vIEludmFsaWQgb3BlcmF0aW9uCgllcnIKCippZjBfZW5kOgoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czo0NAoJLy8gcmV0dXJuIHJlc3VsdDsKCWZyYW1lX2RpZyAwIC8vIHJlc3VsdDogdWludDY0CgoJLy8gc2V0IHRoZSBzdWJyb3V0aW5lIHJldHVybiB2YWx1ZQoJZnJhbWVfYnVyeSAwCglyZXRzdWIKCi8vIGhlbGxvKHN0cmluZylzdHJpbmcKKmFiaV9yb3V0ZV9oZWxsbzoKCS8vIFRoZSBBQkkgcmV0dXJuIHByZWZpeAoJYnl0ZSAweDE1MWY3Yzc1CgoJLy8gbmFtZTogc3RyaW5nCgl0eG5hIEFwcGxpY2F0aW9uQXJncyAxCglleHRyYWN0IDIgMAoKCS8vIGV4ZWN1dGUgaGVsbG8oc3RyaW5nKXN0cmluZwoJY2FsbHN1YiBoZWxsbwoJZHVwCglsZW4KCWl0b2IKCWV4dHJhY3QgNiAyCglzd2FwCgljb25jYXQKCWNvbmNhdAoJbG9nCglwdXNoaW50IDEKCXJldHVybgoKLy8gaGVsbG8obmFtZTogc3RyaW5nKTogc3RyaW5nCi8vCi8vIEEgZGVtb25zdHJhdGlvbiBtZXRob2QgdXNlZCBpbiB0aGUgQWxnb0tpdCBmdWxsc3RhY2sgdGVtcGxhdGUuCi8vIEdyZWV0cyB0aGUgdXNlciBieSBuYW1lLgovLwovLyBAcGFyYW0gbmFtZSBUaGUgbmFtZSBvZiB0aGUgdXNlciB0byBncmVldC4KLy8gQHJldHVybnMgQSBncmVldGluZyBtZXNzYWdlIHRvIHRoZSB1c2VyLgpoZWxsbzoKCXByb3RvIDEgMQoKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6NTUKCS8vIHJldHVybiAnSGVsbG8sICcgKyBuYW1lOwoJcHVzaGJ5dGVzIDB4NDg2NTZjNmM2ZjJjMjAgLy8gIkhlbGxvLCAiCglmcmFtZV9kaWcgLTEgLy8gbmFtZTogc3RyaW5nCgljb25jYXQKCXJldHN1YgoKKmFiaV9yb3V0ZV9jcmVhdGVBcHBsaWNhdGlvbjoKCXB1c2hpbnQgMQoJcmV0dXJuCgoqY3JlYXRlX05vT3A6CglwdXNoYnl0ZXMgMHhiODQ0N2IzNiAvLyBtZXRob2QgImNyZWF0ZUFwcGxpY2F0aW9uKCl2b2lkIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9jcmVhdGVBcHBsaWNhdGlvbgoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjcmVhdGUgTm9PcAoJZXJyCgoqY2FsbF9Ob09wOgoJcHVzaGJ5dGVzIDB4NzZhN2VmMzMgLy8gbWV0aG9kICJkb01hdGgodWludDY0LHVpbnQ2NCxzdHJpbmcpdWludDY0IgoJcHVzaGJ5dGVzIDB4MDJiZWNlMTEgLy8gbWV0aG9kICJoZWxsbyhzdHJpbmcpc3RyaW5nIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9kb01hdGggKmFiaV9yb3V0ZV9oZWxsbwoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjYWxsIE5vT3AKCWVycg==", + "approval": "I3ByYWdtYSB2ZXJzaW9uIDEwCmludGNibG9jayAxCmJ5dGVjYmxvY2sgMHgxNTFmN2M3NQoKLy8gVGhpcyBURUFMIHdhcyBnZW5lcmF0ZWQgYnkgVEVBTFNjcmlwdCB2MC4xMDUuNQovLyBodHRwczovL2dpdGh1Yi5jb20vYWxnb3JhbmRmb3VuZGF0aW9uL1RFQUxTY3JpcHQKCi8vIFRoaXMgY29udHJhY3QgaXMgY29tcGxpYW50IHdpdGggYW5kL29yIGltcGxlbWVudHMgdGhlIGZvbGxvd2luZyBBUkNzOiBbIEFSQzQgXQoKLy8gVGhlIGZvbGxvd2luZyB0ZW4gbGluZXMgb2YgVEVBTCBoYW5kbGUgaW5pdGlhbCBwcm9ncmFtIGZsb3cKLy8gVGhpcyBwYXR0ZXJuIGlzIHVzZWQgdG8gbWFrZSBpdCBlYXN5IGZvciBhbnlvbmUgdG8gcGFyc2UgdGhlIHN0YXJ0IG9mIHRoZSBwcm9ncmFtIGFuZCBkZXRlcm1pbmUgaWYgYSBzcGVjaWZpYyBhY3Rpb24gaXMgYWxsb3dlZAovLyBIZXJlLCBhY3Rpb24gcmVmZXJzIHRvIHRoZSBPbkNvbXBsZXRlIGluIGNvbWJpbmF0aW9uIHdpdGggd2hldGhlciB0aGUgYXBwIGlzIGJlaW5nIGNyZWF0ZWQgb3IgY2FsbGVkCi8vIEV2ZXJ5IHBvc3NpYmxlIGFjdGlvbiBmb3IgdGhpcyBjb250cmFjdCBpcyByZXByZXNlbnRlZCBpbiB0aGUgc3dpdGNoIHN0YXRlbWVudAovLyBJZiB0aGUgYWN0aW9uIGlzIG5vdCBpbXBsZW1lbnRlZCBpbiB0aGUgY29udHJhY3QsIGl0cyByZXNwZWN0aXZlIGJyYW5jaCB3aWxsIGJlICIqTk9UX0lNUExFTUVOVEVEIiB3aGljaCBqdXN0IGNvbnRhaW5zICJlcnIiCnR4biBBcHBsaWNhdGlvbklECiEKcHVzaGludCA2CioKdHhuIE9uQ29tcGxldGlvbgorCnN3aXRjaCAqY2FsbF9Ob09wICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqY3JlYXRlX05vT3AgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVECgoqTk9UX0lNUExFTUVOVEVEOgoJLy8gVGhlIHJlcXVlc3RlZCBhY3Rpb24gaXMgbm90IGltcGxlbWVudGVkIGluIHRoaXMgY29udHJhY3QuIEFyZSB5b3UgdXNpbmcgdGhlIGNvcnJlY3QgT25Db21wbGV0ZT8gRGlkIHlvdSBzZXQgeW91ciBhcHAgSUQ/CgllcnIKCi8vIGdldFN1bShhOiB1aW50NjQsIGI6IHVpbnQ2NCk6IHVpbnQ2NAovLwovLyBDYWxjdWxhdGVzIHRoZSBzdW0gb2YgdHdvIG51bWJlcnMKLy8KLy8gQHBhcmFtIGEKLy8gQHBhcmFtIGIKLy8gQHJldHVybnMgVGhlIHN1bSBvZiBhIGFuZCBiCmdldFN1bToKCXByb3RvIDIgMQoKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6MTIKCS8vIHJldHVybiBhICsgYjsKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCSsKCXJldHN1YgoKLy8gZ2V0RGlmZmVyZW5jZShhOiB1aW50NjQsIGI6IHVpbnQ2NCk6IHVpbnQ2NAovLwovLyBDYWxjdWxhdGVzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdHdvIG51bWJlcnMKLy8KLy8gQHBhcmFtIGEKLy8gQHBhcmFtIGIKLy8gQHJldHVybnMgVGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhIGFuZCBiLgpnZXREaWZmZXJlbmNlOgoJcHJvdG8gMiAxCgoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czoyMwoJLy8gcmV0dXJuIGEgPj0gYiA/IGEgLSBiIDogYiAtIGE7CglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0Cgk+PQoJYnogKnRlcm5hcnkwX2ZhbHNlCglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0CgktCgliICp0ZXJuYXJ5MF9lbmQKCip0ZXJuYXJ5MF9mYWxzZToKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCS0KCip0ZXJuYXJ5MF9lbmQ6CglyZXRzdWIKCi8vIGRvTWF0aCh1aW50NjQsdWludDY0LHN0cmluZyl1aW50NjQKKmFiaV9yb3V0ZV9kb01hdGg6CgkvLyBUaGUgQUJJIHJldHVybiBwcmVmaXgKCWJ5dGVjIDAgLy8gMHgxNTFmN2M3NQoKCS8vIG9wZXJhdGlvbjogc3RyaW5nCgl0eG5hIEFwcGxpY2F0aW9uQXJncyAzCglleHRyYWN0IDIgMAoKCS8vIGI6IHVpbnQ2NAoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgoJYnRvaQoKCS8vIGE6IHVpbnQ2NAoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQoJYnRvaQoKCS8vIGV4ZWN1dGUgZG9NYXRoKHVpbnQ2NCx1aW50NjQsc3RyaW5nKXVpbnQ2NAoJY2FsbHN1YiBkb01hdGgKCWl0b2IKCWNvbmNhdAoJbG9nCglpbnRjIDAgLy8gMQoJcmV0dXJuCgovLyBkb01hdGgoYTogdWludDY0LCBiOiB1aW50NjQsIG9wZXJhdGlvbjogc3RyaW5nKTogdWludDY0Ci8vCi8vIEEgbWV0aG9kIHRoYXQgdGFrZXMgdHdvIG51bWJlcnMgYW5kIGRvZXMgZWl0aGVyIGFkZGl0aW9uIG9yIHN1YnRyYWN0aW9uCi8vCi8vIEBwYXJhbSBhIFRoZSBmaXJzdCB1aW50NjQKLy8gQHBhcmFtIGIgVGhlIHNlY29uZCB1aW50NjQKLy8gQHBhcmFtIG9wZXJhdGlvbiBUaGUgb3BlcmF0aW9uIHRvIHBlcmZvcm0uIENhbiBiZSBlaXRoZXIgJ3N1bScgb3IgJ2RpZmZlcmVuY2UnCi8vCi8vIEByZXR1cm5zIFRoZSByZXN1bHQgb2YgdGhlIG9wZXJhdGlvbgpkb01hdGg6Cglwcm90byAzIDEKCgkvLyBQdXNoIGVtcHR5IGJ5dGVzIGFmdGVyIHRoZSBmcmFtZSBwb2ludGVyIHRvIHJlc2VydmUgc3BhY2UgZm9yIGxvY2FsIHZhcmlhYmxlcwoJcHVzaGJ5dGVzIDB4CgoJLy8gKmlmMF9jb25kaXRpb24KCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6MzgKCS8vIG9wZXJhdGlvbiA9PT0gJ3N1bScKCWZyYW1lX2RpZyAtMyAvLyBvcGVyYXRpb246IHN0cmluZwoJcHVzaGJ5dGVzIDB4NzM3NTZkIC8vICJzdW0iCgk9PQoJYnogKmlmMF9lbHNlaWYxX2NvbmRpdGlvbgoKCS8vICppZjBfY29uc2VxdWVudAoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czozOQoJLy8gcmVzdWx0ID0gdGhpcy5nZXRTdW0oYSwgYikKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCWNhbGxzdWIgZ2V0U3VtCglmcmFtZV9idXJ5IDAgLy8gcmVzdWx0OiB1aW50NjQKCWIgKmlmMF9lbmQKCippZjBfZWxzZWlmMV9jb25kaXRpb246CgkvLyBjb250cmFjdHMvQ2FsY3VsYXRvci5hbGdvLnRzOjQwCgkvLyBvcGVyYXRpb24gPT09ICdkaWZmZXJlbmNlJwoJZnJhbWVfZGlnIC0zIC8vIG9wZXJhdGlvbjogc3RyaW5nCglwdXNoYnl0ZXMgMHg2NDY5NjY2NjY1NzI2NTZlNjM2NSAvLyAiZGlmZmVyZW5jZSIKCT09CglieiAqaWYwX2Vsc2UKCgkvLyAqaWYwX2Vsc2VpZjFfY29uc2VxdWVudAoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czo0MQoJLy8gcmVzdWx0ID0gdGhpcy5nZXREaWZmZXJlbmNlKGEsIGIpCglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0CglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CgljYWxsc3ViIGdldERpZmZlcmVuY2UKCWZyYW1lX2J1cnkgMCAvLyByZXN1bHQ6IHVpbnQ2NAoJYiAqaWYwX2VuZAoKKmlmMF9lbHNlOgoJLy8gSW52YWxpZCBvcGVyYXRpb24KCWVycgoKKmlmMF9lbmQ6CgkvLyBjb250cmFjdHMvQ2FsY3VsYXRvci5hbGdvLnRzOjQ0CgkvLyByZXR1cm4gcmVzdWx0OwoJZnJhbWVfZGlnIDAgLy8gcmVzdWx0OiB1aW50NjQKCgkvLyBzZXQgdGhlIHN1YnJvdXRpbmUgcmV0dXJuIHZhbHVlCglmcmFtZV9idXJ5IDAKCXJldHN1YgoKLy8gaGVsbG8oc3RyaW5nKXN0cmluZwoqYWJpX3JvdXRlX2hlbGxvOgoJLy8gVGhlIEFCSSByZXR1cm4gcHJlZml4CglieXRlYyAwIC8vIDB4MTUxZjdjNzUKCgkvLyBuYW1lOiBzdHJpbmcKCXR4bmEgQXBwbGljYXRpb25BcmdzIDEKCWV4dHJhY3QgMiAwCgoJLy8gZXhlY3V0ZSBoZWxsbyhzdHJpbmcpc3RyaW5nCgljYWxsc3ViIGhlbGxvCglkdXAKCWxlbgoJaXRvYgoJZXh0cmFjdCA2IDIKCXN3YXAKCWNvbmNhdAoJY29uY2F0Cglsb2cKCWludGMgMCAvLyAxCglyZXR1cm4KCi8vIGhlbGxvKG5hbWU6IHN0cmluZyk6IHN0cmluZwovLwovLyBBIGRlbW9uc3RyYXRpb24gbWV0aG9kIHVzZWQgaW4gdGhlIEFsZ29LaXQgZnVsbHN0YWNrIHRlbXBsYXRlLgovLyBHcmVldHMgdGhlIHVzZXIgYnkgbmFtZS4KLy8KLy8gQHBhcmFtIG5hbWUgVGhlIG5hbWUgb2YgdGhlIHVzZXIgdG8gZ3JlZXQuCi8vIEByZXR1cm5zIEEgZ3JlZXRpbmcgbWVzc2FnZSB0byB0aGUgdXNlci4KaGVsbG86Cglwcm90byAxIDEKCgkvLyBjb250cmFjdHMvQ2FsY3VsYXRvci5hbGdvLnRzOjU1CgkvLyByZXR1cm4gJ0hlbGxvLCAnICsgbmFtZTsKCXB1c2hieXRlcyAweDQ4NjU2YzZjNmYyYzIwIC8vICJIZWxsbywgIgoJZnJhbWVfZGlnIC0xIC8vIG5hbWU6IHN0cmluZwoJY29uY2F0CglyZXRzdWIKCiphYmlfcm91dGVfY3JlYXRlQXBwbGljYXRpb246CglpbnRjIDAgLy8gMQoJcmV0dXJuCgoqY3JlYXRlX05vT3A6CglwdXNoYnl0ZXMgMHhiODQ0N2IzNiAvLyBtZXRob2QgImNyZWF0ZUFwcGxpY2F0aW9uKCl2b2lkIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9jcmVhdGVBcHBsaWNhdGlvbgoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjcmVhdGUgTm9PcAoJZXJyCgoqY2FsbF9Ob09wOgoJcHVzaGJ5dGVzIDB4NzZhN2VmMzMgLy8gbWV0aG9kICJkb01hdGgodWludDY0LHVpbnQ2NCxzdHJpbmcpdWludDY0IgoJcHVzaGJ5dGVzIDB4MDJiZWNlMTEgLy8gbWV0aG9kICJoZWxsbyhzdHJpbmcpc3RyaW5nIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9kb01hdGggKmFiaV9yb3V0ZV9oZWxsbwoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjYWxsIE5vT3AKCWVycg==", "clear": "I3ByYWdtYSB2ZXJzaW9uIDEw" }, "contract": { diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-frontend/.algokit/.copier-answers.yml b/examples/production_tealscript_react/projects/production_tealscript_react-frontend/.algokit/.copier-answers.yml index a8a071e..23e7c30 100644 --- a/examples/production_tealscript_react/projects/production_tealscript_react-frontend/.algokit/.copier-answers.yml +++ b/examples/production_tealscript_react/projects/production_tealscript_react-frontend/.algokit/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier; NEVER EDIT MANUALLY -_commit: 1.0.8 +_commit: 1.0.9 _src_path: gh:algorandfoundation/algokit-react-frontend-template author_email: None author_name: None diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-frontend/index.html b/examples/production_tealscript_react/projects/production_tealscript_react-frontend/index.html index a85566a..5a2ef4f 100644 --- a/examples/production_tealscript_react/projects/production_tealscript_react-frontend/index.html +++ b/examples/production_tealscript_react/projects/production_tealscript_react-frontend/index.html @@ -7,9 +7,6 @@
- diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-frontend/package.json b/examples/production_tealscript_react/projects/production_tealscript_react-frontend/package.json index 1cf2054..50fff7c 100644 --- a/examples/production_tealscript_react/projects/production_tealscript_react-frontend/package.json +++ b/examples/production_tealscript_react/projects/production_tealscript_react-frontend/package.json @@ -8,21 +8,21 @@ "private": true, "type": "module", "engines": { - "node": ">=18.0", + "node": ">=20.0", "npm": ">=9.0" }, "devDependencies": { - "@algorandfoundation/algokit-client-generator": "^3.0.3", + "@algorandfoundation/algokit-client-generator": "^4.0.0", "@types/node": "^18.17.14", "@types/react": "^18.2.11", "@types/react-dom": "^18.2.4", "@vitejs/plugin-react": "^4.2.1", "autoprefixer": "^10.4.14", - "eslint": "^8.42.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-prettier": "^5.0.0", - "@typescript-eslint/eslint-plugin": "^6.5.0", - "@typescript-eslint/parser": "^6.5.0", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "@typescript-eslint/eslint-plugin": "^7.0.2", + "@typescript-eslint/parser": "^7.0.2", "postcss": "^8.4.24", "tailwindcss": "3.3.2", "ts-jest": "^29.1.1", @@ -31,16 +31,16 @@ "typescript": "^5.1.6", "@playwright/test": "^1.35.0", "playwright": "^1.35.0", - "vite": "^5.0.0" + "vite": "^5.0.0", + "vite-plugin-node-polyfills": "^0.22.0" }, "dependencies": { - "@walletconnect/modal-sign-html": "^2.6.1", - "@algorandfoundation/algokit-utils": "^6.0.2", + "@algorandfoundation/algokit-utils": "^7.0.0", "@blockshake/defly-connect": "^1.1.6", "@daffiwallet/connect": "^1.0.3", - "@perawallet/connect": "^1.3.1", - "@txnlab/use-wallet": "^2.4.0", - "algosdk": "^2.7.0", + "@perawallet/connect": "^1.3.4", + "@txnlab/use-wallet": "^2.8.2", + "algosdk": ">=2.9.0 <3.0", "daisyui": "^4.0.0", "notistack": "^3.0.1", "react": "^18.2.0", @@ -74,5 +74,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "overrides": { + "ws@>7.0.0 <7.5.9": "7.5.10" } } diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-frontend/src/components/AppCalls.tsx b/examples/production_tealscript_react/projects/production_tealscript_react-frontend/src/components/AppCalls.tsx index 7f8f8b5..ba5f5e5 100644 --- a/examples/production_tealscript_react/projects/production_tealscript_react-frontend/src/components/AppCalls.tsx +++ b/examples/production_tealscript_react/projects/production_tealscript_react-frontend/src/components/AppCalls.tsx @@ -1,10 +1,9 @@ -import * as algokit from '@algorandfoundation/algokit-utils' -import { TransactionSignerAccount } from '@algorandfoundation/algokit-utils/types/account' import { useWallet } from '@txnlab/use-wallet' import { useSnackbar } from 'notistack' import { useState } from 'react' -import { CalculatorClient } from '../contracts/Calculator' -import { getAlgodConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' +import { CalculatorFactory } from '../contracts/Calculator' +import { getAlgodConfigFromViteEnvironment, getIndexerConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' +import { AlgorandClient } from '@algorandfoundation/algokit-utils' interface AppCallsInterface { openModal: boolean @@ -14,16 +13,16 @@ interface AppCallsInterface { const AppCalls = ({ openModal, setModalState }: AppCallsInterface) => { const [loading, setLoading] = useState(false) const [contractInput, setContractInput] = useState('') + const { enqueueSnackbar } = useSnackbar() + const { signer, activeAddress } = useWallet() const algodConfig = getAlgodConfigFromViteEnvironment() - const algodClient = algokit.getAlgoClient({ - server: algodConfig.server, - port: algodConfig.port, - token: algodConfig.token, + const indexerConfig = getIndexerConfigFromViteEnvironment() + const algorand = AlgorandClient.fromConfig({ + algodConfig, + indexerConfig, }) - - const { enqueueSnackbar } = useSnackbar() - const { signer, activeAddress } = useWallet() + algorand.setDefaultSigner(signer) const sendAppCall = async () => { setLoading(true) @@ -33,27 +32,33 @@ const AppCalls = ({ openModal, setModalState }: AppCallsInterface) => { // Instead, you would deploy your contract on your backend and reference it by id. // Given the simplicity of the starter contract, we are deploying it on the frontend // for demonstration purposes. - const appClient = new CalculatorClient( - { - sender: { signer, addr: activeAddress } as TransactionSignerAccount, - resolveBy: 'id', - id: 0, - }, - algodClient, - ) - await appClient.create.createApplication({}).catch((e: Error) => { + const factory = new CalculatorFactory({ + defaultSender: activeAddress, + algorand, + }) + const deployResult = await factory.send.create.createApplication().catch((e: Error) => { enqueueSnackbar(`Error deploying the contract: ${e.message}`, { variant: 'error' }) setLoading(false) - return + return undefined }) - const response = await appClient.hello({ name: contractInput }).catch((e: Error) => { + if (!deployResult) { + return + } + + const { appClient } = deployResult + + const response = await appClient.send.hello({ args: { name: contractInput } }).catch((e: Error) => { enqueueSnackbar(`Error calling the contract: ${e.message}`, { variant: 'error' }) setLoading(false) - return + return undefined }) - enqueueSnackbar(`Response from the contract: ${response?.return}`, { variant: 'success' }) + if (!response) { + return + } + + enqueueSnackbar(`Response from the contract: ${response.return}`, { variant: 'success' }) setLoading(false) } diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-frontend/src/components/Transact.tsx b/examples/production_tealscript_react/projects/production_tealscript_react-frontend/src/components/Transact.tsx index 16bd932..9e32a9e 100644 --- a/examples/production_tealscript_react/projects/production_tealscript_react-frontend/src/components/Transact.tsx +++ b/examples/production_tealscript_react/projects/production_tealscript_react-frontend/src/components/Transact.tsx @@ -1,6 +1,5 @@ -import * as algokit from '@algorandfoundation/algokit-utils' +import { algo, AlgorandClient } 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' @@ -15,15 +14,11 @@ const Transact = ({ openModal, setModalState }: TransactInterface) => { const [receiverAddress, setReceiverAddress] = useState('') const algodConfig = getAlgodConfigFromViteEnvironment() - const algodClient = algokit.getAlgoClient({ - server: algodConfig.server, - port: algodConfig.port, - token: algodConfig.token, - }) + const algorand = AlgorandClient.fromConfig({ algodConfig }) const { enqueueSnackbar } = useSnackbar() - const { signer, activeAddress, signTransactions, sendTransactions } = useWallet() + const { signer, activeAddress } = useWallet() const handleSubmitAlgo = async () => { setLoading(true) @@ -33,25 +28,15 @@ const Transact = ({ openModal, setModalState }: TransactInterface) => { 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' }) + const result = await algorand.send.payment({ + signer, + sender: activeAddress, + receiver: receiverAddress, + amount: algo(1), + }) + enqueueSnackbar(`Transaction sent: ${result.txIds[0]}`, { variant: 'success' }) setReceiverAddress('') } catch (e) { enqueueSnackbar('Failed to send transaction', { variant: 'error' }) diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-frontend/src/contracts/Calculator.ts b/examples/production_tealscript_react/projects/production_tealscript_react-frontend/src/contracts/Calculator.ts index 11f0fc2..7a29148 100644 --- a/examples/production_tealscript_react/projects/production_tealscript_react-frontend/src/contracts/Calculator.ts +++ b/examples/production_tealscript_react/projects/production_tealscript_react-frontend/src/contracts/Calculator.ts @@ -1,315 +1,222 @@ /* eslint-disable */ -// @ts-nocheck /** * This file was automatically generated by @algorandfoundation/algokit-client-generator. * DO NOT MODIFY IT BY HAND. - * requires: @algorandfoundation/algokit-utils: ^2 + * requires: @algorandfoundation/algokit-utils: ^7 */ -import * as algokit from '@algorandfoundation/algokit-utils' -import type { - ABIAppCallArg, - AppCallTransactionResult, - AppCallTransactionResultOfType, - AppCompilationResult, - AppReference, - AppState, - AppStorageSchema, - CoreAppCallArgs, - RawAppCallArgs, - TealTemplateParams, -} from '@algorandfoundation/algokit-utils/types/app' -import type { - AppClientCallCoreParams, +import { AlgorandClientInterface } from '@algorandfoundation/algokit-utils/types/algorand-client-interface' +import { ABIReturn, AppReturn, SendAppTransactionResult } from '@algorandfoundation/algokit-utils/types/app' +import { Arc56Contract, getArc56ReturnValue, getABIStructFromABITuple } from '@algorandfoundation/algokit-utils/types/app-arc56' +import { + AppClient, + AppClientMethodCallParams, + AppClientParams, + AppClientBareCallParams, + CallOnComplete, AppClientCompilationParams, - AppClientDeployCoreParams, - AppDetails, - ApplicationClient, + ResolveAppClientByCreatorAndName, + ResolveAppClientByNetwork, + CloneAppClientParams, } from '@algorandfoundation/algokit-utils/types/app-client' -import type { AppSpec } from '@algorandfoundation/algokit-utils/types/app-spec' -import type { SendTransactionResult, TransactionToSign, SendTransactionFrom, SendTransactionParams } from '@algorandfoundation/algokit-utils/types/transaction' -import type { ABIResult, TransactionWithSigner } from 'algosdk' -import { Algodv2, OnApplicationComplete, Transaction, AtomicTransactionComposer, modelsv2 } from 'algosdk' -export const APP_SPEC: AppSpec = { - "hints": { - "doMath(uint64,uint64,string)uint64": { - "call_config": { - "no_op": "CALL" - } - }, - "hello(string)string": { - "call_config": { - "no_op": "CALL" - } - }, - "createApplication()void": { - "call_config": { - "no_op": "CREATE" - } - } - }, - "bare_call_config": { - "no_op": "NEVER", - "opt_in": "NEVER", - "close_out": "NEVER", - "update_application": "NEVER", - "delete_application": "NEVER" - }, - "schema": { - "local": { - "declared": {}, - "reserved": {} - }, - "global": { - "declared": {}, - "reserved": {} - } - }, - "state": { - "global": { - "num_byte_slices": 0, - "num_uints": 0 - }, - "local": { - "num_byte_slices": 0, - "num_uints": 0 - } - }, - "source": { - "approval": "I3ByYWdtYSB2ZXJzaW9uIDEwCmJ5dGVjYmxvY2sgMHgxNTFmN2M3NQoKLy8gVGhpcyBURUFMIHdhcyBnZW5lcmF0ZWQgYnkgVEVBTFNjcmlwdCB2MC4xMDQuMQovLyBodHRwczovL2dpdGh1Yi5jb20vYWxnb3JhbmRmb3VuZGF0aW9uL1RFQUxTY3JpcHQKCi8vIFRoaXMgY29udHJhY3QgaXMgY29tcGxpYW50IHdpdGggYW5kL29yIGltcGxlbWVudHMgdGhlIGZvbGxvd2luZyBBUkNzOiBbIEFSQzQgXQoKLy8gVGhlIGZvbGxvd2luZyB0ZW4gbGluZXMgb2YgVEVBTCBoYW5kbGUgaW5pdGlhbCBwcm9ncmFtIGZsb3cKLy8gVGhpcyBwYXR0ZXJuIGlzIHVzZWQgdG8gbWFrZSBpdCBlYXN5IGZvciBhbnlvbmUgdG8gcGFyc2UgdGhlIHN0YXJ0IG9mIHRoZSBwcm9ncmFtIGFuZCBkZXRlcm1pbmUgaWYgYSBzcGVjaWZpYyBhY3Rpb24gaXMgYWxsb3dlZAovLyBIZXJlLCBhY3Rpb24gcmVmZXJzIHRvIHRoZSBPbkNvbXBsZXRlIGluIGNvbWJpbmF0aW9uIHdpdGggd2hldGhlciB0aGUgYXBwIGlzIGJlaW5nIGNyZWF0ZWQgb3IgY2FsbGVkCi8vIEV2ZXJ5IHBvc3NpYmxlIGFjdGlvbiBmb3IgdGhpcyBjb250cmFjdCBpcyByZXByZXNlbnRlZCBpbiB0aGUgc3dpdGNoIHN0YXRlbWVudAovLyBJZiB0aGUgYWN0aW9uIGlzIG5vdCBpbXBsZW1lbnRlZCBpbiB0aGUgY29udHJhY3QsIGl0cyByZXNwZWN0aXZlIGJyYW5jaCB3aWxsIGJlICIqTk9UX0lNUExFTUVOVEVEIiB3aGljaCBqdXN0IGNvbnRhaW5zICJlcnIiCnR4biBBcHBsaWNhdGlvbklECiEKcHVzaGludCA2CioKdHhuIE9uQ29tcGxldGlvbgorCnN3aXRjaCAqY2FsbF9Ob09wICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqY3JlYXRlX05vT3AgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVECgoqTk9UX0lNUExFTUVOVEVEOgoJLy8gVGhlIHJlcXVlc3RlZCBhY3Rpb24gaXMgbm90IGltcGxlbWVudGVkIGluIHRoaXMgY29udHJhY3QuIEFyZSB5b3UgdXNpbmcgdGhlIGNvcnJlY3QgT25Db21wbGV0ZT8gRGlkIHlvdSBzZXQgeW91ciBhcHAgSUQ/CgllcnIKCi8vIGdldFN1bShhOiB1aW50NjQsIGI6IHVpbnQ2NCk6IHVpbnQ2NAovLwovLyBDYWxjdWxhdGVzIHRoZSBzdW0gb2YgdHdvIG51bWJlcnMKLy8KLy8gQHBhcmFtIGEKLy8gQHBhcmFtIGIKLy8gQHJldHVybnMgVGhlIHN1bSBvZiBhIGFuZCBiCmdldFN1bToKCXByb3RvIDIgMQoKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6MTIKCS8vIHJldHVybiBhICsgYjsKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCSsKCXJldHN1YgoKLy8gZ2V0RGlmZmVyZW5jZShhOiB1aW50NjQsIGI6IHVpbnQ2NCk6IHVpbnQ2NAovLwovLyBDYWxjdWxhdGVzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdHdvIG51bWJlcnMKLy8KLy8gQHBhcmFtIGEKLy8gQHBhcmFtIGIKLy8gQHJldHVybnMgVGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhIGFuZCBiLgpnZXREaWZmZXJlbmNlOgoJcHJvdG8gMiAxCgoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czoyMwoJLy8gcmV0dXJuIGEgPj0gYiA/IGEgLSBiIDogYiAtIGE7CglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0Cgk+PQoJYnogKnRlcm5hcnkwX2ZhbHNlCglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0CgktCgliICp0ZXJuYXJ5MF9lbmQKCip0ZXJuYXJ5MF9mYWxzZToKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCS0KCip0ZXJuYXJ5MF9lbmQ6CglyZXRzdWIKCi8vIGRvTWF0aCh1aW50NjQsdWludDY0LHN0cmluZyl1aW50NjQKKmFiaV9yb3V0ZV9kb01hdGg6CgkvLyBUaGUgQUJJIHJldHVybiBwcmVmaXgKCWJ5dGUgMHgxNTFmN2M3NQoKCS8vIG9wZXJhdGlvbjogc3RyaW5nCgl0eG5hIEFwcGxpY2F0aW9uQXJncyAzCglleHRyYWN0IDIgMAoKCS8vIGI6IHVpbnQ2NAoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgoJYnRvaQoKCS8vIGE6IHVpbnQ2NAoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQoJYnRvaQoKCS8vIGV4ZWN1dGUgZG9NYXRoKHVpbnQ2NCx1aW50NjQsc3RyaW5nKXVpbnQ2NAoJY2FsbHN1YiBkb01hdGgKCWl0b2IKCWNvbmNhdAoJbG9nCglwdXNoaW50IDEKCXJldHVybgoKLy8gZG9NYXRoKGE6IHVpbnQ2NCwgYjogdWludDY0LCBvcGVyYXRpb246IHN0cmluZyk6IHVpbnQ2NAovLwovLyBBIG1ldGhvZCB0aGF0IHRha2VzIHR3byBudW1iZXJzIGFuZCBkb2VzIGVpdGhlciBhZGRpdGlvbiBvciBzdWJ0cmFjdGlvbgovLwovLyBAcGFyYW0gYSBUaGUgZmlyc3QgdWludDY0Ci8vIEBwYXJhbSBiIFRoZSBzZWNvbmQgdWludDY0Ci8vIEBwYXJhbSBvcGVyYXRpb24gVGhlIG9wZXJhdGlvbiB0byBwZXJmb3JtLiBDYW4gYmUgZWl0aGVyICdzdW0nIG9yICdkaWZmZXJlbmNlJwovLwovLyBAcmV0dXJucyBUaGUgcmVzdWx0IG9mIHRoZSBvcGVyYXRpb24KZG9NYXRoOgoJcHJvdG8gMyAxCgoJLy8gUHVzaCBlbXB0eSBieXRlcyBhZnRlciB0aGUgZnJhbWUgcG9pbnRlciB0byByZXNlcnZlIHNwYWNlIGZvciBsb2NhbCB2YXJpYWJsZXMKCXB1c2hieXRlcyAweAoKCS8vICppZjBfY29uZGl0aW9uCgkvLyBjb250cmFjdHMvQ2FsY3VsYXRvci5hbGdvLnRzOjM4CgkvLyBvcGVyYXRpb24gPT09ICdzdW0nCglmcmFtZV9kaWcgLTMgLy8gb3BlcmF0aW9uOiBzdHJpbmcKCXB1c2hieXRlcyAweDczNzU2ZCAvLyAic3VtIgoJPT0KCWJ6ICppZjBfZWxzZWlmMV9jb25kaXRpb24KCgkvLyAqaWYwX2NvbnNlcXVlbnQKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6MzkKCS8vIHJlc3VsdCA9IHRoaXMuZ2V0U3VtKGEsIGIpCglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0CglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CgljYWxsc3ViIGdldFN1bQoJZnJhbWVfYnVyeSAwIC8vIHJlc3VsdDogdWludDY0CgliICppZjBfZW5kCgoqaWYwX2Vsc2VpZjFfY29uZGl0aW9uOgoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czo0MAoJLy8gb3BlcmF0aW9uID09PSAnZGlmZmVyZW5jZScKCWZyYW1lX2RpZyAtMyAvLyBvcGVyYXRpb246IHN0cmluZwoJcHVzaGJ5dGVzIDB4NjQ2OTY2NjY2NTcyNjU2ZTYzNjUgLy8gImRpZmZlcmVuY2UiCgk9PQoJYnogKmlmMF9lbHNlCgoJLy8gKmlmMF9lbHNlaWYxX2NvbnNlcXVlbnQKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6NDEKCS8vIHJlc3VsdCA9IHRoaXMuZ2V0RGlmZmVyZW5jZShhLCBiKQoJZnJhbWVfZGlnIC0yIC8vIGI6IHVpbnQ2NAoJZnJhbWVfZGlnIC0xIC8vIGE6IHVpbnQ2NAoJY2FsbHN1YiBnZXREaWZmZXJlbmNlCglmcmFtZV9idXJ5IDAgLy8gcmVzdWx0OiB1aW50NjQKCWIgKmlmMF9lbmQKCippZjBfZWxzZToKCS8vIEludmFsaWQgb3BlcmF0aW9uCgllcnIKCippZjBfZW5kOgoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czo0NAoJLy8gcmV0dXJuIHJlc3VsdDsKCWZyYW1lX2RpZyAwIC8vIHJlc3VsdDogdWludDY0CgoJLy8gc2V0IHRoZSBzdWJyb3V0aW5lIHJldHVybiB2YWx1ZQoJZnJhbWVfYnVyeSAwCglyZXRzdWIKCi8vIGhlbGxvKHN0cmluZylzdHJpbmcKKmFiaV9yb3V0ZV9oZWxsbzoKCS8vIFRoZSBBQkkgcmV0dXJuIHByZWZpeAoJYnl0ZSAweDE1MWY3Yzc1CgoJLy8gbmFtZTogc3RyaW5nCgl0eG5hIEFwcGxpY2F0aW9uQXJncyAxCglleHRyYWN0IDIgMAoKCS8vIGV4ZWN1dGUgaGVsbG8oc3RyaW5nKXN0cmluZwoJY2FsbHN1YiBoZWxsbwoJZHVwCglsZW4KCWl0b2IKCWV4dHJhY3QgNiAyCglzd2FwCgljb25jYXQKCWNvbmNhdAoJbG9nCglwdXNoaW50IDEKCXJldHVybgoKLy8gaGVsbG8obmFtZTogc3RyaW5nKTogc3RyaW5nCi8vCi8vIEEgZGVtb25zdHJhdGlvbiBtZXRob2QgdXNlZCBpbiB0aGUgQWxnb0tpdCBmdWxsc3RhY2sgdGVtcGxhdGUuCi8vIEdyZWV0cyB0aGUgdXNlciBieSBuYW1lLgovLwovLyBAcGFyYW0gbmFtZSBUaGUgbmFtZSBvZiB0aGUgdXNlciB0byBncmVldC4KLy8gQHJldHVybnMgQSBncmVldGluZyBtZXNzYWdlIHRvIHRoZSB1c2VyLgpoZWxsbzoKCXByb3RvIDEgMQoKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6NTUKCS8vIHJldHVybiAnSGVsbG8sICcgKyBuYW1lOwoJcHVzaGJ5dGVzIDB4NDg2NTZjNmM2ZjJjMjAgLy8gIkhlbGxvLCAiCglmcmFtZV9kaWcgLTEgLy8gbmFtZTogc3RyaW5nCgljb25jYXQKCXJldHN1YgoKKmFiaV9yb3V0ZV9jcmVhdGVBcHBsaWNhdGlvbjoKCXB1c2hpbnQgMQoJcmV0dXJuCgoqY3JlYXRlX05vT3A6CglwdXNoYnl0ZXMgMHhiODQ0N2IzNiAvLyBtZXRob2QgImNyZWF0ZUFwcGxpY2F0aW9uKCl2b2lkIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9jcmVhdGVBcHBsaWNhdGlvbgoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjcmVhdGUgTm9PcAoJZXJyCgoqY2FsbF9Ob09wOgoJcHVzaGJ5dGVzIDB4NzZhN2VmMzMgLy8gbWV0aG9kICJkb01hdGgodWludDY0LHVpbnQ2NCxzdHJpbmcpdWludDY0IgoJcHVzaGJ5dGVzIDB4MDJiZWNlMTEgLy8gbWV0aG9kICJoZWxsbyhzdHJpbmcpc3RyaW5nIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9kb01hdGggKmFiaV9yb3V0ZV9oZWxsbwoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjYWxsIE5vT3AKCWVycg==", - "clear": "I3ByYWdtYSB2ZXJzaW9uIDEw" - }, - "contract": { - "name": "Calculator", - "desc": "", - "methods": [ - { - "name": "doMath", - "desc": "A method that takes two numbers and does either addition or subtraction", - "args": [ - { - "name": "a", - "type": "uint64", - "desc": "The first uint64" - }, - { - "name": "b", - "type": "uint64", - "desc": "The second uint64" - }, - { - "name": "operation", - "type": "string", - "desc": "The operation to perform. Can be either 'sum' or 'difference'" - } - ], - "returns": { - "type": "uint64", - "desc": "The result of the operation" - } - }, - { - "name": "hello", - "desc": "A demonstration method used in the AlgoKit fullstack template.\nGreets the user by name.", - "args": [ - { - "name": "name", - "type": "string", - "desc": "The name of the user to greet." - } - ], - "returns": { - "type": "string", - "desc": "A greeting message to the user." - } - }, - { - "name": "createApplication", - "args": [], - "returns": { - "type": "void" - } - } - ] - } -} +import { AppFactory, AppFactoryAppClientParams, AppFactoryResolveAppClientByCreatorAndNameParams, AppFactoryDeployParams, AppFactoryParams, CreateSchema } from '@algorandfoundation/algokit-utils/types/app-factory' +import { TransactionComposer, AppCallMethodCall, AppMethodCallTransactionArgument, SimulateOptions } from '@algorandfoundation/algokit-utils/types/composer' +import { SendParams, SendSingleTransactionResult, SendAtomicTransactionComposerResults } from '@algorandfoundation/algokit-utils/types/transaction' +import { Address, encodeAddress, modelsv2, OnApplicationComplete, Transaction, TransactionSigner } from 'algosdk' +import SimulateResponse = modelsv2.SimulateResponse + +export const APP_SPEC: Arc56Contract = {"arcs":[],"name":"Calculator","desc":"","structs":{},"methods":[{"name":"doMath","desc":"A method that takes two numbers and does either addition or subtraction","args":[{"name":"a","type":"uint64","desc":"The first uint64"},{"name":"b","type":"uint64","desc":"The second uint64"},{"name":"operation","type":"string","desc":"The operation to perform. Can be either 'sum' or 'difference'"}],"returns":{"type":"uint64","desc":"The result of the operation"},"events":[],"actions":{"create":[],"call":["NoOp"]}},{"name":"hello","desc":"A demonstration method used in the AlgoKit fullstack template.\nGreets the user by name.","args":[{"name":"name","type":"string","desc":"The name of the user to greet."}],"returns":{"type":"string","desc":"A greeting message to the user."},"events":[],"actions":{"create":[],"call":["NoOp"]}},{"name":"createApplication","args":[],"returns":{"type":"void"},"events":[],"actions":{"create":["NoOp"],"call":[]}}],"state":{"schema":{"global":{"ints":0,"bytes":0},"local":{"ints":0,"bytes":0}},"keys":{"global":{},"local":{},"box":{}},"maps":{"global":{},"local":{},"box":{}}},"source":{"approval":"I3ByYWdtYSB2ZXJzaW9uIDEwCmludGNibG9jayAxCmJ5dGVjYmxvY2sgMHgxNTFmN2M3NQoKLy8gVGhpcyBURUFMIHdhcyBnZW5lcmF0ZWQgYnkgVEVBTFNjcmlwdCB2MC4xMDUuNQovLyBodHRwczovL2dpdGh1Yi5jb20vYWxnb3JhbmRmb3VuZGF0aW9uL1RFQUxTY3JpcHQKCi8vIFRoaXMgY29udHJhY3QgaXMgY29tcGxpYW50IHdpdGggYW5kL29yIGltcGxlbWVudHMgdGhlIGZvbGxvd2luZyBBUkNzOiBbIEFSQzQgXQoKLy8gVGhlIGZvbGxvd2luZyB0ZW4gbGluZXMgb2YgVEVBTCBoYW5kbGUgaW5pdGlhbCBwcm9ncmFtIGZsb3cKLy8gVGhpcyBwYXR0ZXJuIGlzIHVzZWQgdG8gbWFrZSBpdCBlYXN5IGZvciBhbnlvbmUgdG8gcGFyc2UgdGhlIHN0YXJ0IG9mIHRoZSBwcm9ncmFtIGFuZCBkZXRlcm1pbmUgaWYgYSBzcGVjaWZpYyBhY3Rpb24gaXMgYWxsb3dlZAovLyBIZXJlLCBhY3Rpb24gcmVmZXJzIHRvIHRoZSBPbkNvbXBsZXRlIGluIGNvbWJpbmF0aW9uIHdpdGggd2hldGhlciB0aGUgYXBwIGlzIGJlaW5nIGNyZWF0ZWQgb3IgY2FsbGVkCi8vIEV2ZXJ5IHBvc3NpYmxlIGFjdGlvbiBmb3IgdGhpcyBjb250cmFjdCBpcyByZXByZXNlbnRlZCBpbiB0aGUgc3dpdGNoIHN0YXRlbWVudAovLyBJZiB0aGUgYWN0aW9uIGlzIG5vdCBpbXBsZW1lbnRlZCBpbiB0aGUgY29udHJhY3QsIGl0cyByZXNwZWN0aXZlIGJyYW5jaCB3aWxsIGJlICIqTk9UX0lNUExFTUVOVEVEIiB3aGljaCBqdXN0IGNvbnRhaW5zICJlcnIiCnR4biBBcHBsaWNhdGlvbklECiEKcHVzaGludCA2CioKdHhuIE9uQ29tcGxldGlvbgorCnN3aXRjaCAqY2FsbF9Ob09wICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqY3JlYXRlX05vT3AgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVECgoqTk9UX0lNUExFTUVOVEVEOgoJLy8gVGhlIHJlcXVlc3RlZCBhY3Rpb24gaXMgbm90IGltcGxlbWVudGVkIGluIHRoaXMgY29udHJhY3QuIEFyZSB5b3UgdXNpbmcgdGhlIGNvcnJlY3QgT25Db21wbGV0ZT8gRGlkIHlvdSBzZXQgeW91ciBhcHAgSUQ/CgllcnIKCi8vIGdldFN1bShhOiB1aW50NjQsIGI6IHVpbnQ2NCk6IHVpbnQ2NAovLwovLyBDYWxjdWxhdGVzIHRoZSBzdW0gb2YgdHdvIG51bWJlcnMKLy8KLy8gQHBhcmFtIGEKLy8gQHBhcmFtIGIKLy8gQHJldHVybnMgVGhlIHN1bSBvZiBhIGFuZCBiCmdldFN1bToKCXByb3RvIDIgMQoKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6MTIKCS8vIHJldHVybiBhICsgYjsKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCSsKCXJldHN1YgoKLy8gZ2V0RGlmZmVyZW5jZShhOiB1aW50NjQsIGI6IHVpbnQ2NCk6IHVpbnQ2NAovLwovLyBDYWxjdWxhdGVzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdHdvIG51bWJlcnMKLy8KLy8gQHBhcmFtIGEKLy8gQHBhcmFtIGIKLy8gQHJldHVybnMgVGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhIGFuZCBiLgpnZXREaWZmZXJlbmNlOgoJcHJvdG8gMiAxCgoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czoyMwoJLy8gcmV0dXJuIGEgPj0gYiA/IGEgLSBiIDogYiAtIGE7CglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0Cgk+PQoJYnogKnRlcm5hcnkwX2ZhbHNlCglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0CgktCgliICp0ZXJuYXJ5MF9lbmQKCip0ZXJuYXJ5MF9mYWxzZToKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCS0KCip0ZXJuYXJ5MF9lbmQ6CglyZXRzdWIKCi8vIGRvTWF0aCh1aW50NjQsdWludDY0LHN0cmluZyl1aW50NjQKKmFiaV9yb3V0ZV9kb01hdGg6CgkvLyBUaGUgQUJJIHJldHVybiBwcmVmaXgKCWJ5dGVjIDAgLy8gMHgxNTFmN2M3NQoKCS8vIG9wZXJhdGlvbjogc3RyaW5nCgl0eG5hIEFwcGxpY2F0aW9uQXJncyAzCglleHRyYWN0IDIgMAoKCS8vIGI6IHVpbnQ2NAoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgoJYnRvaQoKCS8vIGE6IHVpbnQ2NAoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQoJYnRvaQoKCS8vIGV4ZWN1dGUgZG9NYXRoKHVpbnQ2NCx1aW50NjQsc3RyaW5nKXVpbnQ2NAoJY2FsbHN1YiBkb01hdGgKCWl0b2IKCWNvbmNhdAoJbG9nCglpbnRjIDAgLy8gMQoJcmV0dXJuCgovLyBkb01hdGgoYTogdWludDY0LCBiOiB1aW50NjQsIG9wZXJhdGlvbjogc3RyaW5nKTogdWludDY0Ci8vCi8vIEEgbWV0aG9kIHRoYXQgdGFrZXMgdHdvIG51bWJlcnMgYW5kIGRvZXMgZWl0aGVyIGFkZGl0aW9uIG9yIHN1YnRyYWN0aW9uCi8vCi8vIEBwYXJhbSBhIFRoZSBmaXJzdCB1aW50NjQKLy8gQHBhcmFtIGIgVGhlIHNlY29uZCB1aW50NjQKLy8gQHBhcmFtIG9wZXJhdGlvbiBUaGUgb3BlcmF0aW9uIHRvIHBlcmZvcm0uIENhbiBiZSBlaXRoZXIgJ3N1bScgb3IgJ2RpZmZlcmVuY2UnCi8vCi8vIEByZXR1cm5zIFRoZSByZXN1bHQgb2YgdGhlIG9wZXJhdGlvbgpkb01hdGg6Cglwcm90byAzIDEKCgkvLyBQdXNoIGVtcHR5IGJ5dGVzIGFmdGVyIHRoZSBmcmFtZSBwb2ludGVyIHRvIHJlc2VydmUgc3BhY2UgZm9yIGxvY2FsIHZhcmlhYmxlcwoJcHVzaGJ5dGVzIDB4CgoJLy8gKmlmMF9jb25kaXRpb24KCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6MzgKCS8vIG9wZXJhdGlvbiA9PT0gJ3N1bScKCWZyYW1lX2RpZyAtMyAvLyBvcGVyYXRpb246IHN0cmluZwoJcHVzaGJ5dGVzIDB4NzM3NTZkIC8vICJzdW0iCgk9PQoJYnogKmlmMF9lbHNlaWYxX2NvbmRpdGlvbgoKCS8vICppZjBfY29uc2VxdWVudAoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czozOQoJLy8gcmVzdWx0ID0gdGhpcy5nZXRTdW0oYSwgYikKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCWNhbGxzdWIgZ2V0U3VtCglmcmFtZV9idXJ5IDAgLy8gcmVzdWx0OiB1aW50NjQKCWIgKmlmMF9lbmQKCippZjBfZWxzZWlmMV9jb25kaXRpb246CgkvLyBjb250cmFjdHMvQ2FsY3VsYXRvci5hbGdvLnRzOjQwCgkvLyBvcGVyYXRpb24gPT09ICdkaWZmZXJlbmNlJwoJZnJhbWVfZGlnIC0zIC8vIG9wZXJhdGlvbjogc3RyaW5nCglwdXNoYnl0ZXMgMHg2NDY5NjY2NjY1NzI2NTZlNjM2NSAvLyAiZGlmZmVyZW5jZSIKCT09CglieiAqaWYwX2Vsc2UKCgkvLyAqaWYwX2Vsc2VpZjFfY29uc2VxdWVudAoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czo0MQoJLy8gcmVzdWx0ID0gdGhpcy5nZXREaWZmZXJlbmNlKGEsIGIpCglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0CglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CgljYWxsc3ViIGdldERpZmZlcmVuY2UKCWZyYW1lX2J1cnkgMCAvLyByZXN1bHQ6IHVpbnQ2NAoJYiAqaWYwX2VuZAoKKmlmMF9lbHNlOgoJLy8gSW52YWxpZCBvcGVyYXRpb24KCWVycgoKKmlmMF9lbmQ6CgkvLyBjb250cmFjdHMvQ2FsY3VsYXRvci5hbGdvLnRzOjQ0CgkvLyByZXR1cm4gcmVzdWx0OwoJZnJhbWVfZGlnIDAgLy8gcmVzdWx0OiB1aW50NjQKCgkvLyBzZXQgdGhlIHN1YnJvdXRpbmUgcmV0dXJuIHZhbHVlCglmcmFtZV9idXJ5IDAKCXJldHN1YgoKLy8gaGVsbG8oc3RyaW5nKXN0cmluZwoqYWJpX3JvdXRlX2hlbGxvOgoJLy8gVGhlIEFCSSByZXR1cm4gcHJlZml4CglieXRlYyAwIC8vIDB4MTUxZjdjNzUKCgkvLyBuYW1lOiBzdHJpbmcKCXR4bmEgQXBwbGljYXRpb25BcmdzIDEKCWV4dHJhY3QgMiAwCgoJLy8gZXhlY3V0ZSBoZWxsbyhzdHJpbmcpc3RyaW5nCgljYWxsc3ViIGhlbGxvCglkdXAKCWxlbgoJaXRvYgoJZXh0cmFjdCA2IDIKCXN3YXAKCWNvbmNhdAoJY29uY2F0Cglsb2cKCWludGMgMCAvLyAxCglyZXR1cm4KCi8vIGhlbGxvKG5hbWU6IHN0cmluZyk6IHN0cmluZwovLwovLyBBIGRlbW9uc3RyYXRpb24gbWV0aG9kIHVzZWQgaW4gdGhlIEFsZ29LaXQgZnVsbHN0YWNrIHRlbXBsYXRlLgovLyBHcmVldHMgdGhlIHVzZXIgYnkgbmFtZS4KLy8KLy8gQHBhcmFtIG5hbWUgVGhlIG5hbWUgb2YgdGhlIHVzZXIgdG8gZ3JlZXQuCi8vIEByZXR1cm5zIEEgZ3JlZXRpbmcgbWVzc2FnZSB0byB0aGUgdXNlci4KaGVsbG86Cglwcm90byAxIDEKCgkvLyBjb250cmFjdHMvQ2FsY3VsYXRvci5hbGdvLnRzOjU1CgkvLyByZXR1cm4gJ0hlbGxvLCAnICsgbmFtZTsKCXB1c2hieXRlcyAweDQ4NjU2YzZjNmYyYzIwIC8vICJIZWxsbywgIgoJZnJhbWVfZGlnIC0xIC8vIG5hbWU6IHN0cmluZwoJY29uY2F0CglyZXRzdWIKCiphYmlfcm91dGVfY3JlYXRlQXBwbGljYXRpb246CglpbnRjIDAgLy8gMQoJcmV0dXJuCgoqY3JlYXRlX05vT3A6CglwdXNoYnl0ZXMgMHhiODQ0N2IzNiAvLyBtZXRob2QgImNyZWF0ZUFwcGxpY2F0aW9uKCl2b2lkIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9jcmVhdGVBcHBsaWNhdGlvbgoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjcmVhdGUgTm9PcAoJZXJyCgoqY2FsbF9Ob09wOgoJcHVzaGJ5dGVzIDB4NzZhN2VmMzMgLy8gbWV0aG9kICJkb01hdGgodWludDY0LHVpbnQ2NCxzdHJpbmcpdWludDY0IgoJcHVzaGJ5dGVzIDB4MDJiZWNlMTEgLy8gbWV0aG9kICJoZWxsbyhzdHJpbmcpc3RyaW5nIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9kb01hdGggKmFiaV9yb3V0ZV9oZWxsbwoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjYWxsIE5vT3AKCWVycg==","clear":"I3ByYWdtYSB2ZXJzaW9uIDEw"},"bareActions":{"create":[],"call":[]}} as unknown as Arc56Contract -/** - * Defines an onCompletionAction of 'no_op' - */ -export type OnCompleteNoOp = { onCompleteAction?: 'no_op' | OnApplicationComplete.NoOpOC } -/** - * Defines an onCompletionAction of 'opt_in' - */ -export type OnCompleteOptIn = { onCompleteAction: 'opt_in' | OnApplicationComplete.OptInOC } -/** - * Defines an onCompletionAction of 'close_out' - */ -export type OnCompleteCloseOut = { onCompleteAction: 'close_out' | OnApplicationComplete.CloseOutOC } -/** - * Defines an onCompletionAction of 'delete_application' - */ -export type OnCompleteDelApp = { onCompleteAction: 'delete_application' | OnApplicationComplete.DeleteApplicationOC } -/** - * Defines an onCompletionAction of 'update_application' - */ -export type OnCompleteUpdApp = { onCompleteAction: 'update_application' | OnApplicationComplete.UpdateApplicationOC } -/** - * A state record containing a single unsigned integer - */ -export type IntegerState = { - /** - * Gets the state value as a BigInt. - */ - asBigInt(): bigint - /** - * Gets the state value as a number. - */ - asNumber(): number -} /** * A state record containing binary data */ -export type BinaryState = { +export interface BinaryState { /** * Gets the state value as a Uint8Array */ - asByteArray(): Uint8Array + asByteArray(): Uint8Array | undefined /** * Gets the state value as a string */ - asString(): string + asString(): string | undefined } -export type AppCreateCallTransactionResult = AppCallTransactionResult & Partial & AppReference -export type AppUpdateCallTransactionResult = AppCallTransactionResult & Partial +class BinaryStateValue implements BinaryState { + constructor(private value: Uint8Array | undefined) {} -export type AppClientComposeCallCoreParams = Omit & { - sendParams?: Omit + asByteArray(): Uint8Array | undefined { + return this.value + } + + asString(): string | undefined { + return this.value !== undefined ? Buffer.from(this.value).toString('utf-8') : undefined + } } -export type AppClientComposeExecuteParams = Pick -export type IncludeSchema = { +/** + * Expands types for IntelliSense so they are more human readable + * See https://stackoverflow.com/a/69288824 + */ +export type Expand = T extends (...args: infer A) => infer R + ? (...args: Expand
) => Expand + : T extends infer O + ? { [K in keyof O]: O[K] } + : never + + +/** + * The argument types for the Calculator contract + */ +export type CalculatorArgs = { /** - * Any overrides for the storage schema to request for the created app; by default the schema indicated by the app spec is used. + * The object representation of the arguments for each method */ - schema?: Partial + obj: { + 'doMath(uint64,uint64,string)uint64': { + /** + * The first uint64 + */ + a: bigint | number + /** + * The second uint64 + */ + b: bigint | number + /** + * The operation to perform. Can be either 'sum' or 'difference' + */ + operation: string + } + 'hello(string)string': { + /** + * The name of the user to greet. + */ + name: string + } + 'createApplication()void': Record + } + /** + * The tuple representation of the arguments for each method + */ + tuple: { + 'doMath(uint64,uint64,string)uint64': [a: bigint | number, b: bigint | number, operation: string] + 'hello(string)string': [name: string] + 'createApplication()void': [] + } +} + +/** + * The return type for each method + */ +export type CalculatorReturns = { + 'doMath(uint64,uint64,string)uint64': bigint + 'hello(string)string': string + 'createApplication()void': void } /** * Defines the types of available calls and state of the Calculator smart contract. */ -export type Calculator = { +export type CalculatorTypes = { /** * Maps method signatures / names to their argument and return types. */ methods: & Record<'doMath(uint64,uint64,string)uint64' | 'doMath', { - argsObj: { - /** - * The first uint64 - */ - a: bigint | number - /** - * The second uint64 - */ - b: bigint | number - /** - * The operation to perform. Can be either 'sum' or 'difference' - */ - operation: string - } - argsTuple: [a: bigint | number, b: bigint | number, operation: string] + argsObj: CalculatorArgs['obj']['doMath(uint64,uint64,string)uint64'] + argsTuple: CalculatorArgs['tuple']['doMath(uint64,uint64,string)uint64'] /** * The result of the operation */ - returns: bigint + returns: CalculatorReturns['doMath(uint64,uint64,string)uint64'] }> & Record<'hello(string)string' | 'hello', { - argsObj: { - /** - * The name of the user to greet. - */ - name: string - } - argsTuple: [name: string] + argsObj: CalculatorArgs['obj']['hello(string)string'] + argsTuple: CalculatorArgs['tuple']['hello(string)string'] /** * A greeting message to the user. */ - returns: string + returns: CalculatorReturns['hello(string)string'] }> & Record<'createApplication()void' | 'createApplication', { - argsObj: { - } - argsTuple: [] - returns: void + argsObj: CalculatorArgs['obj']['createApplication()void'] + argsTuple: CalculatorArgs['tuple']['createApplication()void'] + returns: CalculatorReturns['createApplication()void'] }> } + /** - * Defines the possible abi call signatures + * Defines the possible abi call signatures. */ -export type CalculatorSig = keyof Calculator['methods'] +export type CalculatorSignatures = keyof CalculatorTypes['methods'] /** - * Defines an object containing all relevant parameters for a single call to the contract. Where TSignature is undefined, a bare call is made + * Defines the possible abi call signatures for methods that return a non-void value. */ -export type TypedCallParams = { - method: TSignature - methodArgs: TSignature extends undefined ? undefined : Array -} & AppClientCallCoreParams & CoreAppCallArgs +export type CalculatorNonVoidMethodSignatures = keyof CalculatorTypes['methods'] extends infer T ? T extends keyof CalculatorTypes['methods'] ? MethodReturn extends void ? never : T : never : never /** - * Defines the arguments required for a bare call + * Defines an object containing all relevant parameters for a single call to the contract. */ -export type BareCallArgs = Omit +export type CallParams = Expand< + Omit & + { + /** The args for the ABI method call, either as an ordered array or an object */ + args: Expand + } +> /** - * Maps a method signature from the Calculator smart contract to the method's arguments in either tuple of struct form + * Maps a method signature from the Calculator smart contract to the method's arguments in either tuple or struct form */ -export type MethodArgs = Calculator['methods'][TSignature]['argsObj' | 'argsTuple'] +export type MethodArgs = CalculatorTypes['methods'][TSignature]['argsObj' | 'argsTuple'] /** * Maps a method signature from the Calculator smart contract to the method's return type */ -export type MethodReturn = Calculator['methods'][TSignature]['returns'] +export type MethodReturn = CalculatorTypes['methods'][TSignature]['returns'] + /** - * A factory for available 'create' calls - */ -export type CalculatorCreateCalls = (typeof CalculatorCallFactory)['create'] -/** - * Defines supported create methods for this smart contract + * Defines supported create method params for this smart contract */ export type CalculatorCreateCallParams = - | (TypedCallParams<'createApplication()void'> & (OnCompleteNoOp)) + | Expand & {method: 'createApplication'} & {onComplete?: OnApplicationComplete.NoOpOC} & CreateSchema> + | Expand & {method: 'createApplication()void'} & {onComplete?: OnApplicationComplete.NoOpOC} & CreateSchema> /** * Defines arguments required for the deploy method. */ -export type CalculatorDeployArgs = { - deployTimeParams?: TealTemplateParams +export type CalculatorDeployParams = Expand & { /** - * A delegate which takes a create call factory and returns the create call params for this smart contract + * Create transaction parameters to use if a create needs to be issued as part of deployment; use `method` to define ABI call (if available) or leave out for a bare call (if available) */ - createCall?: (callFactory: CalculatorCreateCalls) => CalculatorCreateCallParams -} + createParams?: CalculatorCreateCallParams +}> /** - * Exposes methods for constructing all available smart contract calls + * Exposes methods for constructing `AppClient` params objects for ABI calls to the Calculator smart contract */ -export abstract class CalculatorCallFactory { +export abstract class CalculatorParamsFactory { /** - * Gets available create call factories + * Gets available create ABI call param factories */ static get create() { return { + _resolveByMethod(params: TParams) { + switch(params.method) { + case 'createApplication': + case 'createApplication()void': + return CalculatorParamsFactory.create.createApplication(params) + } + throw new Error(`Unknown ' + verb + ' method`) + }, + /** - * Constructs a create call for the Calculator smart contract using the createApplication()void ABI method + * Constructs create ABI call params for the Calculator smart contract using the createApplication()void ABI method * - * @param args Any args for the contract call - * @param params Any additional parameters for the call - * @returns A TypedCallParams object for the call + * @param params Parameters for the call + * @returns An `AppClientMethodCallParams` object for the call */ - createApplication(args: MethodArgs<'createApplication()void'>, params: AppClientCallCoreParams & CoreAppCallArgs & AppClientCompilationParams & (OnCompleteNoOp) = {}) { + createApplication(params: CallParams & AppClientCompilationParams & {onComplete?: OnApplicationComplete.NoOpOC}): AppClientMethodCallParams & AppClientCompilationParams & {onComplete?: OnApplicationComplete.NoOpOC} { return { - method: 'createApplication()void' as const, - methodArgs: Array.isArray(args) ? args : [], ...params, + method: 'createApplication()void' as const, + args: Array.isArray(params.args) ? params.args : [], } }, } @@ -320,87 +227,96 @@ export abstract class CalculatorCallFactory { * * A method that takes two numbers and does either addition or subtraction * - * @param args Any args for the contract call - * @param params Any additional parameters for the call - * @returns A TypedCallParams object for the call + * @param params Parameters for the call + * @returns An `AppClientMethodCallParams` object for the call */ - static doMath(args: MethodArgs<'doMath(uint64,uint64,string)uint64'>, params: AppClientCallCoreParams & CoreAppCallArgs) { + static doMath(params: CallParams & CallOnComplete): AppClientMethodCallParams & CallOnComplete { return { - method: 'doMath(uint64,uint64,string)uint64' as const, - methodArgs: Array.isArray(args) ? args : [args.a, args.b, args.operation], ...params, + method: 'doMath(uint64,uint64,string)uint64' as const, + args: Array.isArray(params.args) ? params.args : [params.args.a, params.args.b, params.args.operation], } } /** * Constructs a no op call for the hello(string)string ABI method * - * A demonstration method used in the AlgoKit fullstack template. -Greets the user by name. + * A demonstration method used in the AlgoKit fullstack template. + Greets the user by name. + * - * @param args Any args for the contract call - * @param params Any additional parameters for the call - * @returns A TypedCallParams object for the call + * @param params Parameters for the call + * @returns An `AppClientMethodCallParams` object for the call */ - static hello(args: MethodArgs<'hello(string)string'>, params: AppClientCallCoreParams & CoreAppCallArgs) { + static hello(params: CallParams & CallOnComplete): AppClientMethodCallParams & CallOnComplete { return { - method: 'hello(string)string' as const, - methodArgs: Array.isArray(args) ? args : [args.name], ...params, + method: 'hello(string)string' as const, + args: Array.isArray(params.args) ? params.args : [params.args.name], } } } /** - * A client to make calls to the Calculator smart contract + * A factory to create and deploy one or more instance of the Calculator smart contract and to create one or more app clients to interact with those (or other) app instances */ -export class CalculatorClient { +export class CalculatorFactory { /** - * The underlying `ApplicationClient` for when you want to have more flexibility + * The underlying `AppFactory` for when you want to have more flexibility */ - public readonly appClient: ApplicationClient - - private readonly sender: SendTransactionFrom | undefined + public readonly appFactory: AppFactory /** - * Creates a new instance of `CalculatorClient` + * Creates a new instance of `CalculatorFactory` * - * @param appDetails appDetails The details to identify the app to deploy - * @param algod An algod client instance + * @param params The parameters to initialise the app factory with */ - constructor(appDetails: AppDetails, private algod: Algodv2) { - this.sender = appDetails.sender - this.appClient = algokit.getAppClient({ - ...appDetails, - app: APP_SPEC - }, algod) + constructor(params: Omit) { + this.appFactory = new AppFactory({ + ...params, + appSpec: APP_SPEC, + }) } - + + /** The name of the app (from the ARC-32 / ARC-56 app spec or override). */ + public get appName() { + return this.appFactory.appName + } + + /** The ARC-56 app spec being used */ + get appSpec() { + return APP_SPEC + } + + /** A reference to the underlying `AlgorandClient` this app factory is using. */ + public get algorand(): AlgorandClientInterface { + return this.appFactory.algorand + } + /** - * Checks for decode errors on the AppCallTransactionResult and maps the return value to the specified generic type + * Returns a new `AppClient` client for an app instance of the given ID. * - * @param result The AppCallTransactionResult to be mapped - * @param returnValueFormatter An optional delegate to format the return value if required - * @returns The smart contract response with an updated return value + * Automatically populates appName, defaultSender and source maps from the factory + * if not specified in the params. + * @param params The parameters to create the app client + * @returns The `AppClient` */ - protected mapReturnValue(result: AppCallTransactionResult, returnValueFormatter?: (value: any) => TReturn): AppCallTransactionResultOfType & TResult { - if(result.return?.decodeError) { - throw result.return.decodeError - } - const returnValue = result.return?.returnValue !== undefined && returnValueFormatter !== undefined - ? returnValueFormatter(result.return.returnValue) - : result.return?.returnValue as TReturn | undefined - return { ...result, return: returnValue } as AppCallTransactionResultOfType & TResult + public getAppClientById(params: AppFactoryAppClientParams) { + return new CalculatorClient(this.appFactory.getAppClientById(params)) } - + /** - * Calls the ABI method with the matching signature using an onCompletion code of NO_OP + * Returns a new `AppClient` client, resolving the app by creator address and name + * using AlgoKit app deployment semantics (i.e. looking for the app creation transaction note). * - * @param typedCallParams An object containing the method signature, args, and any other relevant parameters - * @param returnValueFormatter An optional delegate which when provided will be used to map non-undefined return values to the target type - * @returns The result of the smart contract call + * Automatically populates appName, defaultSender and source maps from the factory + * if not specified in the params. + * @param params The parameters to create the app client + * @returns The `AppClient` */ - public async call(typedCallParams: TypedCallParams, returnValueFormatter?: (value: any) => MethodReturn) { - return this.mapReturnValue>(await this.appClient.call(typedCallParams), returnValueFormatter) + public async getAppClientByCreatorAndName( + params: AppFactoryResolveAppClientByCreatorAndNameParams, + ) { + return new CalculatorClient(await this.appFactory.getAppClientByCreatorAndName(params)) } /** @@ -409,114 +325,357 @@ export class CalculatorClient { * @param params The arguments for the contract calls and any additional parameters for the call * @returns The deployment result */ - public deploy(params: CalculatorDeployArgs & AppClientDeployCoreParams & IncludeSchema = {}): ReturnType { - const createArgs = params.createCall?.(CalculatorCallFactory.create) - return this.appClient.deploy({ + public async deploy(params: CalculatorDeployParams = {}) { + const result = await this.appFactory.deploy({ ...params, - createArgs, - createOnCompleteAction: createArgs?.onCompleteAction, + createParams: params.createParams?.method ? CalculatorParamsFactory.create._resolveByMethod(params.createParams) : params.createParams, }) + return { result: result.result, appClient: new CalculatorClient(result.appClient) } } /** - * Gets available create methods + * Get parameters to create transactions (create and deploy related calls) for the current app. A good mental model for this is that these parameters represent a deferred transaction creation. */ - public get create() { - const $this = this - return { + readonly params = { + /** + * Gets available create methods + */ + create: { /** * Creates a new instance of the Calculator smart contract using the createApplication()void ABI method. * - * @param args The arguments for the smart contract call - * @param params Any additional parameters for the call - * @returns The create result + * @param params The params for the smart contract call + * @returns The create params */ - async createApplication(args: MethodArgs<'createApplication()void'>, params: AppClientCallCoreParams & AppClientCompilationParams & IncludeSchema & CoreAppCallArgs & (OnCompleteNoOp) = {}) { - return $this.mapReturnValue, AppCreateCallTransactionResult>(await $this.appClient.create(CalculatorCallFactory.create.createApplication(args, params))) + createApplication: (params: CallParams & AppClientCompilationParams & CreateSchema & {onComplete?: OnApplicationComplete.NoOpOC} = {args: []}) => { + return this.appFactory.params.create(CalculatorParamsFactory.create.createApplication(params)) }, - } + }, + } /** - * Makes a clear_state call to an existing instance of the Calculator smart contract. - * - * @param args The arguments for the bare call - * @returns The clear_state result + * Create transactions for the current app */ - public clearState(args: BareCallArgs & AppClientCallCoreParams & CoreAppCallArgs = {}) { - return this.appClient.clearState(args) + readonly createTransaction = { + /** + * Gets available create methods + */ + create: { + /** + * Creates a new instance of the Calculator smart contract using the createApplication()void ABI method. + * + * @param params The params for the smart contract call + * @returns The create params + */ + createApplication: (params: CallParams & AppClientCompilationParams & CreateSchema & {onComplete?: OnApplicationComplete.NoOpOC} = {args: []}) => { + return this.appFactory.params.create(CalculatorParamsFactory.create.createApplication(params)) + }, + }, + } /** - * Calls the doMath(uint64,uint64,string)uint64 ABI method. + * Send calls to the current app + */ + readonly send = { + /** + * Gets available create methods + */ + create: { + /** + * Creates a new instance of the Calculator smart contract using an ABI method call using the createApplication()void ABI method. + * + * @param params The params for the smart contract call + * @returns The create result + */ + createApplication: async (params: CallParams & AppClientCompilationParams & CreateSchema & SendParams & {onComplete?: OnApplicationComplete.NoOpOC} = {args: []}) => { + const result = await this.appFactory.send.create(CalculatorParamsFactory.create.createApplication(params)) + return { result: { ...result.result, return: result.result.return as undefined | CalculatorReturns['createApplication()void'] }, appClient: new CalculatorClient(result.appClient) } + }, + }, + + } + +} +/** + * A client to make calls to the Calculator smart contract + */ +export class CalculatorClient { + /** + * The underlying `AppClient` for when you want to have more flexibility + */ + public readonly appClient: AppClient + + /** + * Creates a new instance of `CalculatorClient` * - * A method that takes two numbers and does either addition or subtraction + * @param appClient An `AppClient` instance which has been created with the Calculator app spec + */ + constructor(appClient: AppClient) + /** + * Creates a new instance of `CalculatorClient` * - * @param args The arguments for the contract call - * @param params Any additional parameters for the call - * @returns The result of the call: The result of the operation + * @param params The parameters to initialise the app client with */ - public doMath(args: MethodArgs<'doMath(uint64,uint64,string)uint64'>, params: AppClientCallCoreParams & CoreAppCallArgs = {}) { - return this.call(CalculatorCallFactory.doMath(args, params)) + constructor(params: Omit) + constructor(appClientOrParams: AppClient | Omit) { + this.appClient = appClientOrParams instanceof AppClient ? appClientOrParams : new AppClient({ + ...appClientOrParams, + appSpec: APP_SPEC, + }) } - + /** - * Calls the hello(string)string ABI method. + * Checks for decode errors on the given return value and maps the return value to the return type for the given method + * @returns The typed return value or undefined if there was no value + */ + decodeReturnValue(method: TSignature, returnValue: ABIReturn | undefined) { + return returnValue !== undefined ? getArc56ReturnValue>(returnValue, this.appClient.getABIMethod(method), APP_SPEC.structs) : undefined + } + + /** + * Returns a new `CalculatorClient` client, resolving the app by creator address and name + * using AlgoKit app deployment semantics (i.e. looking for the app creation transaction note). + * @param params The parameters to create the app client + */ + public static async fromCreatorAndName(params: Omit): Promise { + return new CalculatorClient(await AppClient.fromCreatorAndName({...params, appSpec: APP_SPEC})) + } + + /** + * Returns an `CalculatorClient` instance for the current network based on + * pre-determined network-specific app IDs specified in the ARC-56 app spec. * - * A demonstration method used in the AlgoKit fullstack template. -Greets the user by name. + * If no IDs are in the app spec or the network isn't recognised, an error is thrown. + * @param params The parameters to create the app client + */ + static async fromNetwork( + params: Omit + ): Promise { + return new CalculatorClient(await AppClient.fromNetwork({...params, appSpec: APP_SPEC})) + } + + /** The ID of the app instance this client is linked to. */ + public get appId() { + return this.appClient.appId + } + + /** The app address of the app instance this client is linked to. */ + public get appAddress() { + return this.appClient.appAddress + } + + /** The name of the app. */ + public get appName() { + return this.appClient.appName + } + + /** The ARC-56 app spec being used */ + public get appSpec() { + return this.appClient.appSpec + } + + /** A reference to the underlying `AlgorandClient` this app client is using. */ + public get algorand(): AlgorandClientInterface { + return this.appClient.algorand + } + + /** + * Get parameters to create transactions for the current app. A good mental model for this is that these parameters represent a deferred transaction creation. + */ + readonly params = { + /** + * Makes a clear_state call to an existing instance of the Calculator smart contract. + * + * @param params The params for the bare (raw) call + * @returns The clearState result + */ + clearState: (params?: Expand) => { + return this.appClient.params.bare.clearState(params) + }, + + /** + * Makes a call to the Calculator smart contract using the `doMath(uint64,uint64,string)uint64` ABI method. + * + * A method that takes two numbers and does either addition or subtraction + * + * @param params The params for the smart contract call + * @returns The call params: The result of the operation + */ + doMath: (params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + return this.appClient.params.call(CalculatorParamsFactory.doMath(params)) + }, + + /** + * Makes a call to the Calculator smart contract using the `hello(string)string` ABI method. + * + * A demonstration method used in the AlgoKit fullstack template. + Greets the user by name. + + * + * @param params The params for the smart contract call + * @returns The call params: A greeting message to the user. + */ + hello: (params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + return this.appClient.params.call(CalculatorParamsFactory.hello(params)) + }, + + } + + /** + * Create transactions for the current app + */ + readonly createTransaction = { + /** + * Makes a clear_state call to an existing instance of the Calculator smart contract. + * + * @param params The params for the bare (raw) call + * @returns The clearState result + */ + clearState: (params?: Expand) => { + return this.appClient.createTransaction.bare.clearState(params) + }, + + /** + * Makes a call to the Calculator smart contract using the `doMath(uint64,uint64,string)uint64` ABI method. + * + * A method that takes two numbers and does either addition or subtraction + * + * @param params The params for the smart contract call + * @returns The call transaction: The result of the operation + */ + doMath: (params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + return this.appClient.createTransaction.call(CalculatorParamsFactory.doMath(params)) + }, + + /** + * Makes a call to the Calculator smart contract using the `hello(string)string` ABI method. + * + * A demonstration method used in the AlgoKit fullstack template. + Greets the user by name. + + * + * @param params The params for the smart contract call + * @returns The call transaction: A greeting message to the user. + */ + hello: (params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + return this.appClient.createTransaction.call(CalculatorParamsFactory.hello(params)) + }, + + } + + /** + * Send calls to the current app + */ + readonly send = { + /** + * Makes a clear_state call to an existing instance of the Calculator smart contract. + * + * @param params The params for the bare (raw) call + * @returns The clearState result + */ + clearState: (params?: Expand) => { + return this.appClient.send.bare.clearState(params) + }, + + /** + * Makes a call to the Calculator smart contract using the `doMath(uint64,uint64,string)uint64` ABI method. + * + * A method that takes two numbers and does either addition or subtraction + * + * @param params The params for the smart contract call + * @returns The call result: The result of the operation + */ + doMath: async (params: CallParams & SendParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + const result = await this.appClient.send.call(CalculatorParamsFactory.doMath(params)) + return {...result, return: result.return as undefined | CalculatorReturns['doMath(uint64,uint64,string)uint64']} + }, + + /** + * Makes a call to the Calculator smart contract using the `hello(string)string` ABI method. + * + * A demonstration method used in the AlgoKit fullstack template. + Greets the user by name. + + * + * @param params The params for the smart contract call + * @returns The call result: A greeting message to the user. + */ + hello: async (params: CallParams & SendParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + const result = await this.appClient.send.call(CalculatorParamsFactory.hello(params)) + return {...result, return: result.return as undefined | CalculatorReturns['hello(string)string']} + }, + + } + + /** + * Clone this app client with different params * - * @param args The arguments for the contract call - * @param params Any additional parameters for the call - * @returns The result of the call: A greeting message to the user. + * @param params The params to use for the the cloned app client. Omit a param to keep the original value. Set a param to override the original value. Setting to undefined will clear the original value. + * @returns A new app client with the altered params */ - public hello(args: MethodArgs<'hello(string)string'>, params: AppClientCallCoreParams & CoreAppCallArgs = {}) { - return this.call(CalculatorCallFactory.hello(args, params)) + public clone(params: CloneAppClientParams) { + return new CalculatorClient(this.appClient.clone(params)) } - public compose(): CalculatorComposer { + /** + * Methods to access state for the current Calculator app + */ + state = { + } + + public newGroup(): CalculatorComposer { const client = this - const atc = new AtomicTransactionComposer() + const composer = this.algorand.newGroup() let promiseChain:Promise = Promise.resolve() - const resultMappers: Array any)> = [] + const resultMappers: Array any)> = [] return { - doMath(args: MethodArgs<'doMath(uint64,uint64,string)uint64'>, params?: AppClientComposeCallCoreParams & CoreAppCallArgs) { - promiseChain = promiseChain.then(() => client.doMath(args, {...params, sendParams: {...params?.sendParams, skipSending: true, atc}})) - resultMappers.push(undefined) + /** + * Add a doMath(uint64,uint64,string)uint64 method call against the Calculator contract + */ + doMath(params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) { + promiseChain = promiseChain.then(async () => composer.addAppCallMethodCall(await client.params.doMath(params))) + resultMappers.push((v) => client.decodeReturnValue('doMath(uint64,uint64,string)uint64', v)) return this }, - hello(args: MethodArgs<'hello(string)string'>, params?: AppClientComposeCallCoreParams & CoreAppCallArgs) { - promiseChain = promiseChain.then(() => client.hello(args, {...params, sendParams: {...params?.sendParams, skipSending: true, atc}})) - resultMappers.push(undefined) + /** + * Add a hello(string)string method call against the Calculator contract + */ + hello(params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) { + promiseChain = promiseChain.then(async () => composer.addAppCallMethodCall(await client.params.hello(params))) + resultMappers.push((v) => client.decodeReturnValue('hello(string)string', v)) return this }, - clearState(args?: BareCallArgs & AppClientComposeCallCoreParams & CoreAppCallArgs) { - promiseChain = promiseChain.then(() => client.clearState({...args, sendParams: {...args?.sendParams, skipSending: true, atc}})) - resultMappers.push(undefined) + /** + * Add a clear state call to the Calculator contract + */ + clearState(params: AppClientBareCallParams) { + promiseChain = promiseChain.then(() => composer.addAppCall(client.params.clearState(params))) return this }, - addTransaction(txn: TransactionWithSigner | TransactionToSign | Transaction | Promise, defaultSender?: SendTransactionFrom) { - promiseChain = promiseChain.then(async () => atc.addTransaction(await algokit.getTransactionWithSigner(txn, defaultSender ?? client.sender))) + addTransaction(txn: Transaction, signer?: TransactionSigner) { + promiseChain = promiseChain.then(() => composer.addTransaction(txn, signer)) return this }, - async atc() { + async composer() { await promiseChain - return atc + return composer }, async simulate(options?: SimulateOptions) { await promiseChain - const result = await atc.simulate(client.algod, new modelsv2.SimulateRequest({ txnGroups: [], ...options })) + const result = await composer.simulate(options) return { ...result, - returns: result.methodResults?.map((val, i) => resultMappers[i] !== undefined ? resultMappers[i]!(val.returnValue) : val.returnValue) + returns: result.returns?.map((val, i) => resultMappers[i] !== undefined ? resultMappers[i]!(val) : val.returnValue) } }, - async execute(sendParams?: AppClientComposeExecuteParams) { + async send(params?: SendParams) { await promiseChain - const result = await algokit.sendAtomicTransactionComposer({ atc, sendParams }, client.algod) + const result = await composer.send(params) return { ...result, - returns: result.returns?.map((val, i) => resultMappers[i] !== undefined ? resultMappers[i]!(val.returnValue) : val.returnValue) + returns: result.returns?.map((val, i) => resultMappers[i] !== undefined ? resultMappers[i]!(val) : val.returnValue) } } } as unknown as CalculatorComposer @@ -532,19 +691,20 @@ export type CalculatorComposer = { * @param params Any additional parameters for the call * @returns The typed transaction composer so you can fluently chain multiple calls or call execute to execute all queued up transactions */ - doMath(args: MethodArgs<'doMath(uint64,uint64,string)uint64'>, params?: AppClientComposeCallCoreParams & CoreAppCallArgs): CalculatorComposer<[...TReturns, MethodReturn<'doMath(uint64,uint64,string)uint64'>]> + doMath(params?: CallParams): CalculatorComposer<[...TReturns, CalculatorReturns['doMath(uint64,uint64,string)uint64'] | undefined]> /** * Calls the hello(string)string ABI method. * - * A demonstration method used in the AlgoKit fullstack template. -Greets the user by name. + * A demonstration method used in the AlgoKit fullstack template. + Greets the user by name. + * * @param args The arguments for the contract call * @param params Any additional parameters for the call * @returns The typed transaction composer so you can fluently chain multiple calls or call execute to execute all queued up transactions */ - hello(args: MethodArgs<'hello(string)string'>, params?: AppClientComposeCallCoreParams & CoreAppCallArgs): CalculatorComposer<[...TReturns, MethodReturn<'hello(string)string'>]> + hello(params?: CallParams): CalculatorComposer<[...TReturns, CalculatorReturns['hello(string)string'] | undefined]> /** * Makes a clear_state call to an existing instance of the Calculator smart contract. @@ -552,37 +712,29 @@ Greets the user by name. * @param args The arguments for the bare call * @returns The typed transaction composer so you can fluently chain multiple calls or call execute to execute all queued up transactions */ - clearState(args?: BareCallArgs & AppClientComposeCallCoreParams & CoreAppCallArgs): CalculatorComposer<[...TReturns, undefined]> + clearState(params?: AppClientBareCallParams): CalculatorComposer<[...TReturns, undefined]> /** * Adds a transaction to the composer * - * @param txn One of: A TransactionWithSigner object (returned as is), a TransactionToSign object (signer is obtained from the signer property), a Transaction object (signer is extracted from the defaultSender parameter), an async SendTransactionResult returned by one of algokit utils helpers (signer is obtained from the defaultSender parameter) - * @param defaultSender The default sender to be used to obtain a signer where the object provided to the transaction parameter does not include a signer. + * @param txn A transaction to add to the transaction group + * @param signer The optional signer to use when signing this transaction. */ - addTransaction(txn: TransactionWithSigner | TransactionToSign | Transaction | Promise, defaultSender?: SendTransactionFrom): CalculatorComposer + addTransaction(txn: Transaction, signer?: TransactionSigner): CalculatorComposer /** * Returns the underlying AtomicTransactionComposer instance */ - atc(): Promise + composer(): TransactionComposer /** * Simulates the transaction group and returns the result */ - simulate(options?: SimulateOptions): Promise> + simulate(options?: SimulateOptions): Promise & { simulateResponse: SimulateResponse }> /** - * Executes the transaction group and returns the results + * Sends the transaction group to the network and returns the results */ - execute(sendParams?: AppClientComposeExecuteParams): Promise> -} -export type SimulateOptions = Omit[0], 'txnGroups'> -export type CalculatorComposerSimulateResult = { - returns: TReturns - methodResults: ABIResult[] - simulateResponse: modelsv2.SimulateResponse + send(params?: SendParams): Promise> } -export type CalculatorComposerResults = { +export type CalculatorComposerResults = Expand + diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-frontend/tests/example.spec.ts b/examples/production_tealscript_react/projects/production_tealscript_react-frontend/tests/example.spec.ts index df83322..d6909a3 100644 --- a/examples/production_tealscript_react/projects/production_tealscript_react-frontend/tests/example.spec.ts +++ b/examples/production_tealscript_react/projects/production_tealscript_react-frontend/tests/example.spec.ts @@ -1,7 +1,10 @@ -import { randomAccount } from '@algorandfoundation/algokit-utils' +import { algorandFixture } from '@algorandfoundation/algokit-utils/testing' import { expect, test } from '@playwright/test' +const localnet = algorandFixture() + test.beforeEach(async ({ page }) => { + await localnet.beforeEach() await page.goto('http://localhost:5173/') }) @@ -27,8 +30,7 @@ test('authentication and dummy payment transaction', async ({ page }) => { // 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('receiver-address').fill(localnet.context.testAccount.addr) await page.getByTestId('send-algo').click() // 3. Must be able to see a notification that the transaction was sent diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-frontend/vite.config.ts b/examples/production_tealscript_react/projects/production_tealscript_react-frontend/vite.config.ts index 36f7f4e..e42dc7c 100644 --- a/examples/production_tealscript_react/projects/production_tealscript_react-frontend/vite.config.ts +++ b/examples/production_tealscript_react/projects/production_tealscript_react-frontend/vite.config.ts @@ -1,7 +1,15 @@ import react from '@vitejs/plugin-react' import { defineConfig } from 'vite' +import { nodePolyfills } from 'vite-plugin-node-polyfills' // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react()], + plugins: [ + react(), + nodePolyfills({ + globals: { + Buffer: true, + }, + }), + ], }) diff --git a/examples/starter_python_react/projects/starter_python_react-contracts/.algokit/.copier-answers.yml b/examples/starter_python_react/projects/starter_python_react-contracts/.algokit/.copier-answers.yml index 6612abe..8555fbe 100644 --- a/examples/starter_python_react/projects/starter_python_react-contracts/.algokit/.copier-answers.yml +++ b/examples/starter_python_react/projects/starter_python_react-contracts/.algokit/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier; NEVER EDIT MANUALLY -_commit: 1.3.3 +_commit: 1.4.1 _src_path: gh:algorandfoundation/algokit-python-template author_email: None author_name: None diff --git a/examples/starter_python_react/projects/starter_python_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 b/examples/starter_python_react/projects/starter_python_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 index ac114cc..bd3ad2d 100644 --- a/examples/starter_python_react/projects/starter_python_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 +++ b/examples/starter_python_react/projects/starter_python_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 @@ -1,5 +1,5 @@ import * as algokit from '@algorandfoundation/algokit-utils' -import { {{ contract_name.split('_')|map('capitalize')|join }}Client } from '../artifacts/{{ contract_name }}/{{ contract_name.split('_')|map('capitalize')|join }}Client' +import { {{ contract_name.split('_')|map('capitalize')|join }}Factory } from '../artifacts/{{ contract_name }}/{{ contract_name.split('_')|map('capitalize')|join }}Client' // Below is a showcase of various deployment options you can use in TypeScript Client export async function deploy() { @@ -7,27 +7,27 @@ export async function deploy() { const algorand = algokit.AlgorandClient.fromEnvironment() const deployer = await algorand.account.fromEnvironment('DEPLOYER') - - const appClient = algorand.client.getTypedAppClientByCreatorAndName({{ contract_name.split('_')|map('capitalize')|join }}Client, { - sender: deployer, - creatorAddress: deployer.addr, - }) - const app = await appClient.deploy({ - onSchemaBreak: 'append', - onUpdate: 'append', + const factory = algorand.client.getTypedAppFactory({{ contract_name.split('_')|map('capitalize')|join }}Factory, { + defaultSender: deployer.addr, }) + const { appClient, result } = await factory.deploy({ onUpdate: 'append', onSchemaBreak: 'append' }) + // If app was just created fund the app account - if (['create', 'replace'].includes(app.operationPerformed)) { + if (['create', 'replace'].includes(result.operationPerformed)) { await algorand.send.payment({ - amount: algokit.algos(1), + amount: (1).algo(), sender: deployer.addr, receiver: app.appAddress, }) } - const method = 'hello' - const response = await appClient.hello({ name: 'world' }) - console.log(`Called ${method} on ${app.name} (${app.appId}) with name = world, received: ${response.return}`) + const method = 'hello' + const response = await appClient.send.hello({ + args: { name: 'world' }, + }) + console.log( + `Called ${method} on ${appClient.appClient.appName} (${appClient.appClient.appId}) with name = world, received: ${response.return}`, + ) } diff --git a/examples/starter_python_react/projects/starter_python_react-contracts/README.md b/examples/starter_python_react/projects/starter_python_react-contracts/README.md index 3b29bca..574709e 100644 --- a/examples/starter_python_react/projects/starter_python_react-contracts/README.md +++ b/examples/starter_python_react/projects/starter_python_react-contracts/README.md @@ -80,6 +80,15 @@ By default the template instance does not contain any env files. Using [`algokit To generate a new `.env` or `.env.{target_network}` file, run `algokit generate env-file` +### Debugging Smart Contracts + +This project is optimized to work with AlgoKit AVM Debugger extension. To activate it: +Refer to the commented header in the `index.ts` file in the `smart_contracts` folder. + +If you have opted in to include VSCode launch configurations in your project, you can also use the `Debug TEAL via AlgoKit AVM Debugger` launch configuration to interactively select an available trace file and launch the debug session for your smart contract. + +For information on using and setting up the `AlgoKit AVM Debugger` VSCode extension refer [here](https://github.com/algorandfoundation/algokit-avm-vscode-debugger). To install the extension from the VSCode Marketplace, use the following link: [AlgoKit AVM Debugger extension](https://marketplace.visualstudio.com/items?itemName=algorandfoundation.algokit-avm-vscode-debugger). + # Tools This project makes use of Algorand Python to build Algorand smart contracts. The following tools are in use: diff --git a/examples/starter_python_react/projects/starter_python_react-contracts/package.json b/examples/starter_python_react/projects/starter_python_react-contracts/package.json index e329b0d..5b1fef2 100644 --- a/examples/starter_python_react/projects/starter_python_react-contracts/package.json +++ b/examples/starter_python_react/projects/starter_python_react-contracts/package.json @@ -9,15 +9,16 @@ "format": "prettier --write ." }, "engines": { - "node": ">=18.0", + "node": ">=20.0", "npm": ">=9.0" }, "dependencies": { - "@algorandfoundation/algokit-utils": "^6.0.2", - "algosdk": "^2.7.0" + "@algorandfoundation/algokit-utils": "^7.0.0", + "@algorandfoundation/algokit-utils-debug": "^1.0.2", + "algosdk": ">=2.9.0 <3.0" }, "devDependencies": { - "@algorandfoundation/algokit-client-generator": "^3.0.3", + "@algorandfoundation/algokit-client-generator": "^4.0.0", "dotenv": "^16.0.3", "prettier": "^2.8.4", "ts-node-dev": "^2.0.0", diff --git a/examples/starter_python_react/projects/starter_python_react-contracts/poetry.toml b/examples/starter_python_react/projects/starter_python_react-contracts/poetry.toml index ab1033b..5fcef8c 100644 --- a/examples/starter_python_react/projects/starter_python_react-contracts/poetry.toml +++ b/examples/starter_python_react/projects/starter_python_react-contracts/poetry.toml @@ -1,2 +1,3 @@ [virtualenvs] in-project = true +prefer-active-python = true diff --git a/examples/starter_python_react/projects/starter_python_react-contracts/smart_contracts/_helpers/build.py b/examples/starter_python_react/projects/starter_python_react-contracts/smart_contracts/_helpers/build.py index 2694825..37ceb1f 100644 --- a/examples/starter_python_react/projects/starter_python_react-contracts/smart_contracts/_helpers/build.py +++ b/examples/starter_python_react/projects/starter_python_react-contracts/smart_contracts/_helpers/build.py @@ -31,7 +31,7 @@ def build(output_dir: Path, contract_path: Path) -> Path: contract_path.absolute(), f"--out-dir={output_dir}", "--output-arc32", - "--debug-level=0", + "--output-source-map", ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, diff --git a/examples/starter_python_react/projects/starter_python_react-contracts/smart_contracts/hello_world/deploy-config.ts b/examples/starter_python_react/projects/starter_python_react-contracts/smart_contracts/hello_world/deploy-config.ts index 5df7a31..e0608c9 100644 --- a/examples/starter_python_react/projects/starter_python_react-contracts/smart_contracts/hello_world/deploy-config.ts +++ b/examples/starter_python_react/projects/starter_python_react-contracts/smart_contracts/hello_world/deploy-config.ts @@ -1,5 +1,5 @@ import * as algokit from '@algorandfoundation/algokit-utils' -import { HelloWorldClient } from '../artifacts/hello_world/HelloWorldClient' +import { HelloWorldFactory } from '../artifacts/hello_world/HelloWorldClient' // Below is a showcase of various deployment options you can use in TypeScript Client export async function deploy() { @@ -8,26 +8,26 @@ export async function deploy() { const algorand = algokit.AlgorandClient.fromEnvironment() const deployer = await algorand.account.fromEnvironment('DEPLOYER') - const appClient = algorand.client.getTypedAppClientByCreatorAndName(HelloWorldClient, { - sender: deployer, - creatorAddress: deployer.addr, + const factory = algorand.client.getTypedAppFactory(HelloWorldFactory, { + defaultSender: deployer.addr, }) - const app = await appClient.deploy({ - onSchemaBreak: 'append', - onUpdate: 'append', - }) + const { appClient, result } = await factory.deploy({ onUpdate: 'append', onSchemaBreak: 'append' }) // If app was just created fund the app account - if (['create', 'replace'].includes(app.operationPerformed)) { + if (['create', 'replace'].includes(result.operationPerformed)) { await algorand.send.payment({ - amount: algokit.algos(1), + amount: (1).algo(), sender: deployer.addr, - receiver: app.appAddress, + receiver: appClient.appAddress, }) } - const method = 'hello' - const response = await appClient.hello({ name: 'world' }) - console.log(`Called ${method} on ${app.name} (${app.appId}) with name = world, received: ${response.return}`) + const method = 'hello' + const response = await appClient.send.hello({ + args: { name: 'world' }, + }) + console.log( + `Called ${method} on ${appClient.appClient.appName} (${appClient.appClient.appId}) with name = world, received: ${response.return}`, + ) } diff --git a/examples/starter_python_react/projects/starter_python_react-contracts/smart_contracts/index.ts b/examples/starter_python_react/projects/starter_python_react-contracts/smart_contracts/index.ts index 6955e33..623ca2b 100644 --- a/examples/starter_python_react/projects/starter_python_react-contracts/smart_contracts/index.ts +++ b/examples/starter_python_react/projects/starter_python_react-contracts/smart_contracts/index.ts @@ -2,6 +2,7 @@ import * as fs from 'fs' import * as path from 'path' import { consoleLogger } from '@algorandfoundation/algokit-utils/types/logging' import * as algokit from '@algorandfoundation/algokit-utils' +// import { registerDebugEventHandlers } from '@algorandfoundation/algokit-utils-debug' // Uncomment to enable persisting artifacts required by AlgoKit AVM Debugger // Uncomment the debug and traceAll options to enable auto generation of AVM Debugger compliant sourceMap and simulation trace file. // Learn more about using AlgoKit AVM Debugger to debug your TEAL source codes and inspect various kinds of Algorand transactions in atomic groups -> https://github.com/algorandfoundation/algokit-avm-vscode-Debugger @@ -11,6 +12,7 @@ algokit.Config.configure({ // debug: true, // traceAll: true, }) +// registerDebugEventHandlers() // Uncomment to enable persisting artifacts required by AlgoKit AVM Debugger // base directory const baseDir = path.resolve(__dirname) diff --git a/examples/starter_python_react/projects/starter_python_react-frontend/.algokit/.copier-answers.yml b/examples/starter_python_react/projects/starter_python_react-frontend/.algokit/.copier-answers.yml index 14d3650..7cbdb13 100644 --- a/examples/starter_python_react/projects/starter_python_react-frontend/.algokit/.copier-answers.yml +++ b/examples/starter_python_react/projects/starter_python_react-frontend/.algokit/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier; NEVER EDIT MANUALLY -_commit: 1.0.8 +_commit: 1.0.9 _src_path: gh:algorandfoundation/algokit-react-frontend-template author_email: None author_name: None diff --git a/examples/starter_python_react/projects/starter_python_react-frontend/index.html b/examples/starter_python_react/projects/starter_python_react-frontend/index.html index a85566a..5a2ef4f 100644 --- a/examples/starter_python_react/projects/starter_python_react-frontend/index.html +++ b/examples/starter_python_react/projects/starter_python_react-frontend/index.html @@ -7,9 +7,6 @@
- diff --git a/examples/starter_python_react/projects/starter_python_react-frontend/package.json b/examples/starter_python_react/projects/starter_python_react-frontend/package.json index e96a2ba..6e7ee6b 100644 --- a/examples/starter_python_react/projects/starter_python_react-frontend/package.json +++ b/examples/starter_python_react/projects/starter_python_react-frontend/package.json @@ -8,11 +8,11 @@ "private": true, "type": "module", "engines": { - "node": ">=18.0", + "node": ">=20.0", "npm": ">=9.0" }, "devDependencies": { - "@algorandfoundation/algokit-client-generator": "^3.0.3", + "@algorandfoundation/algokit-client-generator": "^4.0.0", "@types/node": "^18.17.14", "@types/react": "^18.2.11", "@types/react-dom": "^18.2.4", @@ -20,16 +20,16 @@ "autoprefixer": "^10.4.14", "ts-node": "^10.9.1", "typescript": "^5.1.6", - "vite": "^5.0.0" + "vite": "^5.0.0", + "vite-plugin-node-polyfills": "^0.22.0" }, "dependencies": { - "@walletconnect/modal-sign-html": "^2.6.1", - "@algorandfoundation/algokit-utils": "^6.0.2", + "@algorandfoundation/algokit-utils": "^7.0.0", "@blockshake/defly-connect": "^1.1.6", "@daffiwallet/connect": "^1.0.3", - "@perawallet/connect": "^1.3.1", - "@txnlab/use-wallet": "^2.4.0", - "algosdk": "^2.7.0", + "@perawallet/connect": "^1.3.4", + "@txnlab/use-wallet": "^2.8.2", + "algosdk": ">=2.9.0 <3.0", "notistack": "^3.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -57,5 +57,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "overrides": { + "ws@>7.0.0 <7.5.9": "7.5.10" } } diff --git a/examples/starter_python_react/projects/starter_python_react-frontend/src/components/AppCalls.tsx b/examples/starter_python_react/projects/starter_python_react-frontend/src/components/AppCalls.tsx index 897b80c..1667fbc 100644 --- a/examples/starter_python_react/projects/starter_python_react-frontend/src/components/AppCalls.tsx +++ b/examples/starter_python_react/projects/starter_python_react-frontend/src/components/AppCalls.tsx @@ -1,12 +1,10 @@ -import * as algokit from '@algorandfoundation/algokit-utils' -import { TransactionSignerAccount } from '@algorandfoundation/algokit-utils/types/account' import { useWallet } from '@txnlab/use-wallet' import { useSnackbar } from 'notistack' import { useState } from 'react' -import { AppDetails } from '@algorandfoundation/algokit-utils/types/app-client' -import { HelloWorldClient } from '../contracts/HelloWorld' +import { HelloWorldFactory } from '../contracts/HelloWorld' import { OnSchemaBreak, OnUpdate } from '@algorandfoundation/algokit-utils/types/app' import { getAlgodConfigFromViteEnvironment, getIndexerConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' +import { AlgorandClient } from '@algorandfoundation/algokit-utils' interface AppCallsInterface { openModal: boolean @@ -16,22 +14,16 @@ interface AppCallsInterface { const AppCalls = ({ openModal, setModalState }: AppCallsInterface) => { const [loading, setLoading] = useState(false) const [contractInput, setContractInput] = useState('') + const { enqueueSnackbar } = useSnackbar() + const { signer, activeAddress } = useWallet() const algodConfig = getAlgodConfigFromViteEnvironment() - const algodClient = algokit.getAlgoClient({ - server: algodConfig.server, - port: algodConfig.port, - token: algodConfig.token, - }) const indexerConfig = getIndexerConfigFromViteEnvironment() - const indexer = algokit.getAlgoIndexerClient({ - server: indexerConfig.server, - port: indexerConfig.port, - token: indexerConfig.token, + const algorand = AlgorandClient.fromConfig({ + algodConfig, + indexerConfig, }) - - const { enqueueSnackbar } = useSnackbar() - const { signer, activeAddress } = useWallet() + algorand.setDefaultSigner(signer) const sendAppCall = async () => { setLoading(true) @@ -41,31 +33,38 @@ const AppCalls = ({ openModal, setModalState }: AppCallsInterface) => { // Instead, you would deploy your contract on your backend and reference it by id. // Given the simplicity of the starter contract, we are deploying it on the frontend // for demonstration purposes. - const appDetails = { - resolveBy: 'creatorAndName', - sender: { signer, addr: activeAddress } as TransactionSignerAccount, - creatorAddress: activeAddress, - findExistingUsing: indexer, - } as AppDetails + const factory = new HelloWorldFactory({ + defaultSender: activeAddress, + algorand, + }) + const deployResult = await factory + .deploy({ + onSchemaBreak: OnSchemaBreak.AppendApp, + onUpdate: OnUpdate.AppendApp, + }) + .catch((e: Error) => { + enqueueSnackbar(`Error deploying the contract: ${e.message}`, { variant: 'error' }) + setLoading(false) + return undefined + }) - const appClient = new HelloWorldClient(appDetails, algodClient) - const deployParams = { - onSchemaBreak: OnSchemaBreak.AppendApp, - onUpdate: OnUpdate.AppendApp, - } - await appClient.deploy(deployParams).catch((e: Error) => { - enqueueSnackbar(`Error deploying the contract: ${e.message}`, { variant: 'error' }) - setLoading(false) + if (!deployResult) { return - }) + } + + const { appClient } = deployResult - const response = await appClient.hello({ name: contractInput }).catch((e: Error) => { + const response = await appClient.send.hello({ args: { name: contractInput } }).catch((e: Error) => { enqueueSnackbar(`Error calling the contract: ${e.message}`, { variant: 'error' }) setLoading(false) - return + return undefined }) - enqueueSnackbar(`Response from the contract: ${response?.return}`, { variant: 'success' }) + if (!response) { + return + } + + enqueueSnackbar(`Response from the contract: ${response.return}`, { variant: 'success' }) setLoading(false) } diff --git a/examples/starter_python_react/projects/starter_python_react-frontend/src/components/Transact.tsx b/examples/starter_python_react/projects/starter_python_react-frontend/src/components/Transact.tsx index becfadc..760040b 100644 --- a/examples/starter_python_react/projects/starter_python_react-frontend/src/components/Transact.tsx +++ b/examples/starter_python_react/projects/starter_python_react-frontend/src/components/Transact.tsx @@ -1,6 +1,5 @@ -import * as algokit from '@algorandfoundation/algokit-utils' +import { algo, AlgorandClient } 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' @@ -15,15 +14,11 @@ const Transact = ({ openModal, setModalState }: TransactInterface) => { const [receiverAddress, setReceiverAddress] = useState('') const algodConfig = getAlgodConfigFromViteEnvironment() - const algodClient = algokit.getAlgoClient({ - server: algodConfig.server, - port: algodConfig.port, - token: algodConfig.token, - }) + const algorand = AlgorandClient.fromConfig({ algodConfig }) const { enqueueSnackbar } = useSnackbar() - const { signer, activeAddress, signTransactions, sendTransactions } = useWallet() + const { signer, activeAddress } = useWallet() const handleSubmitAlgo = async () => { setLoading(true) @@ -33,25 +28,15 @@ const Transact = ({ openModal, setModalState }: TransactInterface) => { 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' }) + const result = await algorand.send.payment({ + signer, + sender: activeAddress, + receiver: receiverAddress, + amount: algo(1), + }) + enqueueSnackbar(`Transaction sent: ${result.txIds[0]}`, { variant: 'success' }) setReceiverAddress('') } catch (e) { enqueueSnackbar('Failed to send transaction', { variant: 'error' }) diff --git a/examples/starter_python_react/projects/starter_python_react-frontend/src/contracts/HelloWorld.ts b/examples/starter_python_react/projects/starter_python_react-frontend/src/contracts/HelloWorld.ts index 48df604..58fcdab 100644 --- a/examples/starter_python_react/projects/starter_python_react-frontend/src/contracts/HelloWorld.ts +++ b/examples/starter_python_react/projects/starter_python_react-frontend/src/contracts/HelloWorld.ts @@ -1,305 +1,234 @@ /* eslint-disable */ -// @ts-nocheck /** * This file was automatically generated by @algorandfoundation/algokit-client-generator. * DO NOT MODIFY IT BY HAND. - * requires: @algorandfoundation/algokit-utils: ^2 + * requires: @algorandfoundation/algokit-utils: ^7 */ -import * as algokit from '@algorandfoundation/algokit-utils' -import type { - ABIAppCallArg, - AppCallTransactionResult, - AppCallTransactionResultOfType, - AppCompilationResult, - AppReference, - AppState, - AppStorageSchema, - CoreAppCallArgs, - RawAppCallArgs, - TealTemplateParams, -} from '@algorandfoundation/algokit-utils/types/app' -import type { - AppClientCallCoreParams, +import { AlgorandClientInterface } from '@algorandfoundation/algokit-utils/types/algorand-client-interface' +import { ABIReturn, AppReturn, SendAppTransactionResult } from '@algorandfoundation/algokit-utils/types/app' +import { Arc56Contract, getArc56ReturnValue, getABIStructFromABITuple } from '@algorandfoundation/algokit-utils/types/app-arc56' +import { + AppClient, + AppClientMethodCallParams, + AppClientParams, + AppClientBareCallParams, + CallOnComplete, AppClientCompilationParams, - AppClientDeployCoreParams, - AppDetails, - ApplicationClient, + ResolveAppClientByCreatorAndName, + ResolveAppClientByNetwork, + CloneAppClientParams, } from '@algorandfoundation/algokit-utils/types/app-client' -import type { AppSpec } from '@algorandfoundation/algokit-utils/types/app-spec' -import type { SendTransactionResult, TransactionToSign, SendTransactionFrom, SendTransactionParams } from '@algorandfoundation/algokit-utils/types/transaction' -import type { ABIResult, TransactionWithSigner } from 'algosdk' -import { Algodv2, OnApplicationComplete, Transaction, AtomicTransactionComposer, modelsv2 } from 'algosdk' -export const APP_SPEC: AppSpec = { - "hints": { - "hello(string)string": { - "call_config": { - "no_op": "CALL" - } - } - }, - "source": { - "approval": "I3ByYWdtYSB2ZXJzaW9uIDEwCgpzbWFydF9jb250cmFjdHMuaGVsbG9fd29ybGQuY29udHJhY3QuSGVsbG9Xb3JsZC5hcHByb3ZhbF9wcm9ncmFtOgogICAgY2FsbHN1YiBfX3B1eWFfYXJjNF9yb3V0ZXJfXwogICAgcmV0dXJuCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuX19wdXlhX2FyYzRfcm91dGVyX18oKSAtPiB1aW50NjQ6Cl9fcHV5YV9hcmM0X3JvdXRlcl9fOgogICAgcHJvdG8gMCAxCiAgICB0eG4gTnVtQXBwQXJncwogICAgYnogX19wdXlhX2FyYzRfcm91dGVyX19fYmFyZV9yb3V0aW5nQDUKICAgIG1ldGhvZCAiaGVsbG8oc3RyaW5nKXN0cmluZyIKICAgIHR4bmEgQXBwbGljYXRpb25BcmdzIDAKICAgIG1hdGNoIF9fcHV5YV9hcmM0X3JvdXRlcl9fX2hlbGxvX3JvdXRlQDIKICAgIGludCAwCiAgICByZXRzdWIKCl9fcHV5YV9hcmM0X3JvdXRlcl9fX2hlbGxvX3JvdXRlQDI6CiAgICB0eG4gT25Db21wbGV0aW9uCiAgICAhCiAgICBhc3NlcnQgLy8gT25Db21wbGV0aW9uIGlzIE5vT3AKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICBhc3NlcnQgLy8gaXMgbm90IGNyZWF0aW5nCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAxCiAgICBleHRyYWN0IDIgMAogICAgY2FsbHN1YiBoZWxsbwogICAgZHVwCiAgICBsZW4KICAgIGl0b2IKICAgIGV4dHJhY3QgNiAyCiAgICBzd2FwCiAgICBjb25jYXQKICAgIGJ5dGUgMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludCAxCiAgICByZXRzdWIKCl9fcHV5YV9hcmM0X3JvdXRlcl9fX2JhcmVfcm91dGluZ0A1OgogICAgdHhuIE9uQ29tcGxldGlvbgogICAgYm56IF9fcHV5YV9hcmM0X3JvdXRlcl9fX2FmdGVyX2lmX2Vsc2VAOQogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgICEKICAgIGFzc2VydCAvLyBpcyBjcmVhdGluZwogICAgaW50IDEKICAgIHJldHN1YgoKX19wdXlhX2FyYzRfcm91dGVyX19fYWZ0ZXJfaWZfZWxzZUA5OgogICAgaW50IDAKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvKG5hbWU6IGJ5dGVzKSAtPiBieXRlczoKaGVsbG86CiAgICBwcm90byAxIDEKICAgIGJ5dGUgIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCg==", - "clear": "I3ByYWdtYSB2ZXJzaW9uIDEwCgpzbWFydF9jb250cmFjdHMuaGVsbG9fd29ybGQuY29udHJhY3QuSGVsbG9Xb3JsZC5jbGVhcl9zdGF0ZV9wcm9ncmFtOgogICAgaW50IDEKICAgIHJldHVybgo=" - }, - "state": { - "global": { - "num_byte_slices": 0, - "num_uints": 0 - }, - "local": { - "num_byte_slices": 0, - "num_uints": 0 - } - }, - "schema": { - "global": { - "declared": {}, - "reserved": {} - }, - "local": { - "declared": {}, - "reserved": {} - } - }, - "contract": { - "name": "HelloWorld", - "methods": [ - { - "name": "hello", - "args": [ - { - "type": "string", - "name": "name" - } - ], - "readonly": false, - "returns": { - "type": "string" - } - } - ], - "networks": {} - }, - "bare_call_config": { - "no_op": "CREATE" - } -} +import { AppFactory, AppFactoryAppClientParams, AppFactoryResolveAppClientByCreatorAndNameParams, AppFactoryDeployParams, AppFactoryParams, CreateSchema } from '@algorandfoundation/algokit-utils/types/app-factory' +import { TransactionComposer, AppCallMethodCall, AppMethodCallTransactionArgument, SimulateOptions } from '@algorandfoundation/algokit-utils/types/composer' +import { SendParams, SendSingleTransactionResult, SendAtomicTransactionComposerResults } from '@algorandfoundation/algokit-utils/types/transaction' +import { Address, encodeAddress, modelsv2, OnApplicationComplete, Transaction, TransactionSigner } from 'algosdk' +import SimulateResponse = modelsv2.SimulateResponse + +export const APP_SPEC: Arc56Contract = {"arcs":[],"name":"HelloWorld","structs":{},"methods":[{"name":"hello","args":[{"name":"name","type":"string"}],"returns":{"type":"string"},"events":[],"actions":{"create":[],"call":["NoOp"]}}],"state":{"schema":{"global":{"ints":0,"bytes":0},"local":{"ints":0,"bytes":0}},"keys":{"global":{},"local":{},"box":{}},"maps":{"global":{},"local":{},"box":{}}},"source":{"approval":"I3ByYWdtYSB2ZXJzaW9uIDEwCgpzbWFydF9jb250cmFjdHMuaGVsbG9fd29ybGQuY29udHJhY3QuSGVsbG9Xb3JsZC5hcHByb3ZhbF9wcm9ncmFtOgogICAgaW50Y2Jsb2NrIDAgMQogICAgY2FsbHN1YiBfX3B1eWFfYXJjNF9yb3V0ZXJfXwogICAgcmV0dXJuCgoKLy8gc21hcnRfY29udHJhY3RzLmhlbGxvX3dvcmxkLmNvbnRyYWN0LkhlbGxvV29ybGQuX19wdXlhX2FyYzRfcm91dGVyX18oKSAtPiB1aW50NjQ6Cl9fcHV5YV9hcmM0X3JvdXRlcl9fOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjUKICAgIC8vIGNsYXNzIEhlbGxvV29ybGQoQVJDNENvbnRyYWN0KToKICAgIHByb3RvIDAgMQogICAgdHhuIE51bUFwcEFyZ3MKICAgIGJ6IF9fcHV5YV9hcmM0X3JvdXRlcl9fX2JhcmVfcm91dGluZ0A1CiAgICBwdXNoYnl0ZXMgMHgwMmJlY2UxMSAvLyBtZXRob2QgImhlbGxvKHN0cmluZylzdHJpbmciCiAgICB0eG5hIEFwcGxpY2F0aW9uQXJncyAwCiAgICBtYXRjaCBfX3B1eWFfYXJjNF9yb3V0ZXJfX19oZWxsb19yb3V0ZUAyCiAgICBpbnRjXzAgLy8gMAogICAgcmV0c3ViCgpfX3B1eWFfYXJjNF9yb3V0ZXJfX19oZWxsb19yb3V0ZUAyOgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjYKICAgIC8vIEBhYmltZXRob2QoKQogICAgdHhuIE9uQ29tcGxldGlvbgogICAgIQogICAgYXNzZXJ0IC8vIE9uQ29tcGxldGlvbiBpcyBub3QgTm9PcAogICAgdHhuIEFwcGxpY2F0aW9uSUQKICAgIGFzc2VydCAvLyBjYW4gb25seSBjYWxsIHdoZW4gbm90IGNyZWF0aW5nCiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQogICAgZXh0cmFjdCAyIDAKICAgIC8vIHNtYXJ0X2NvbnRyYWN0cy9oZWxsb193b3JsZC9jb250cmFjdC5weTo2CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIGNhbGxzdWIgaGVsbG8KICAgIGR1cAogICAgbGVuCiAgICBpdG9iCiAgICBleHRyYWN0IDYgMgogICAgc3dhcAogICAgY29uY2F0CiAgICBwdXNoYnl0ZXMgMHgxNTFmN2M3NQogICAgc3dhcAogICAgY29uY2F0CiAgICBsb2cKICAgIGludGNfMSAvLyAxCiAgICByZXRzdWIKCl9fcHV5YV9hcmM0X3JvdXRlcl9fX2JhcmVfcm91dGluZ0A1OgogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjUKICAgIC8vIGNsYXNzIEhlbGxvV29ybGQoQVJDNENvbnRyYWN0KToKICAgIHR4biBPbkNvbXBsZXRpb24KICAgIGJueiBfX3B1eWFfYXJjNF9yb3V0ZXJfX19hZnRlcl9pZl9lbHNlQDkKICAgIHR4biBBcHBsaWNhdGlvbklECiAgICAhCiAgICBhc3NlcnQgLy8gY2FuIG9ubHkgY2FsbCB3aGVuIGNyZWF0aW5nCiAgICBpbnRjXzEgLy8gMQogICAgcmV0c3ViCgpfX3B1eWFfYXJjNF9yb3V0ZXJfX19hZnRlcl9pZl9lbHNlQDk6CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6NQogICAgLy8gY2xhc3MgSGVsbG9Xb3JsZChBUkM0Q29udHJhY3QpOgogICAgaW50Y18wIC8vIDAKICAgIHJldHN1YgoKCi8vIHNtYXJ0X2NvbnRyYWN0cy5oZWxsb193b3JsZC5jb250cmFjdC5IZWxsb1dvcmxkLmhlbGxvKG5hbWU6IGJ5dGVzKSAtPiBieXRlczoKaGVsbG86CiAgICAvLyBzbWFydF9jb250cmFjdHMvaGVsbG9fd29ybGQvY29udHJhY3QucHk6Ni03CiAgICAvLyBAYWJpbWV0aG9kKCkKICAgIC8vIGRlZiBoZWxsbyhzZWxmLCBuYW1lOiBTdHJpbmcpIC0+IFN0cmluZzoKICAgIHByb3RvIDEgMQogICAgLy8gc21hcnRfY29udHJhY3RzL2hlbGxvX3dvcmxkL2NvbnRyYWN0LnB5OjgKICAgIC8vIHJldHVybiAiSGVsbG8sICIgKyBuYW1lCiAgICBwdXNoYnl0ZXMgIkhlbGxvLCAiCiAgICBmcmFtZV9kaWcgLTEKICAgIGNvbmNhdAogICAgcmV0c3ViCg==","clear":"I3ByYWdtYSB2ZXJzaW9uIDEwCgpzbWFydF9jb250cmFjdHMuaGVsbG9fd29ybGQuY29udHJhY3QuSGVsbG9Xb3JsZC5jbGVhcl9zdGF0ZV9wcm9ncmFtOgogICAgcHVzaGludCAxIC8vIDEKICAgIHJldHVybgo="},"bareActions":{"create":["NoOp"],"call":[]}} as unknown as Arc56Contract -/** - * Defines an onCompletionAction of 'no_op' - */ -export type OnCompleteNoOp = { onCompleteAction?: 'no_op' | OnApplicationComplete.NoOpOC } -/** - * Defines an onCompletionAction of 'opt_in' - */ -export type OnCompleteOptIn = { onCompleteAction: 'opt_in' | OnApplicationComplete.OptInOC } -/** - * Defines an onCompletionAction of 'close_out' - */ -export type OnCompleteCloseOut = { onCompleteAction: 'close_out' | OnApplicationComplete.CloseOutOC } -/** - * Defines an onCompletionAction of 'delete_application' - */ -export type OnCompleteDelApp = { onCompleteAction: 'delete_application' | OnApplicationComplete.DeleteApplicationOC } -/** - * Defines an onCompletionAction of 'update_application' - */ -export type OnCompleteUpdApp = { onCompleteAction: 'update_application' | OnApplicationComplete.UpdateApplicationOC } -/** - * A state record containing a single unsigned integer - */ -export type IntegerState = { - /** - * Gets the state value as a BigInt. - */ - asBigInt(): bigint - /** - * Gets the state value as a number. - */ - asNumber(): number -} /** * A state record containing binary data */ -export type BinaryState = { +export interface BinaryState { /** * Gets the state value as a Uint8Array */ - asByteArray(): Uint8Array + asByteArray(): Uint8Array | undefined /** * Gets the state value as a string */ - asString(): string + asString(): string | undefined } -export type AppCreateCallTransactionResult = AppCallTransactionResult & Partial & AppReference -export type AppUpdateCallTransactionResult = AppCallTransactionResult & Partial +class BinaryStateValue implements BinaryState { + constructor(private value: Uint8Array | undefined) {} -export type AppClientComposeCallCoreParams = Omit & { - sendParams?: Omit + asByteArray(): Uint8Array | undefined { + return this.value + } + + asString(): string | undefined { + return this.value !== undefined ? Buffer.from(this.value).toString('utf-8') : undefined + } } -export type AppClientComposeExecuteParams = Pick -export type IncludeSchema = { +/** + * Expands types for IntelliSense so they are more human readable + * See https://stackoverflow.com/a/69288824 + */ +export type Expand = T extends (...args: infer A) => infer R + ? (...args: Expand
) => Expand + : T extends infer O + ? { [K in keyof O]: O[K] } + : never + + +/** + * The argument types for the HelloWorld contract + */ +export type HelloWorldArgs = { /** - * Any overrides for the storage schema to request for the created app; by default the schema indicated by the app spec is used. + * The object representation of the arguments for each method */ - schema?: Partial + obj: { + 'hello(string)string': { + name: string + } + } + /** + * The tuple representation of the arguments for each method + */ + tuple: { + 'hello(string)string': [name: string] + } +} + +/** + * The return type for each method + */ +export type HelloWorldReturns = { + 'hello(string)string': string } /** * Defines the types of available calls and state of the HelloWorld smart contract. */ -export type HelloWorld = { +export type HelloWorldTypes = { /** * Maps method signatures / names to their argument and return types. */ methods: & Record<'hello(string)string' | 'hello', { - argsObj: { - name: string - } - argsTuple: [name: string] - returns: string + argsObj: HelloWorldArgs['obj']['hello(string)string'] + argsTuple: HelloWorldArgs['tuple']['hello(string)string'] + returns: HelloWorldReturns['hello(string)string'] }> } + /** - * Defines the possible abi call signatures + * Defines the possible abi call signatures. */ -export type HelloWorldSig = keyof HelloWorld['methods'] +export type HelloWorldSignatures = keyof HelloWorldTypes['methods'] /** - * Defines an object containing all relevant parameters for a single call to the contract. Where TSignature is undefined, a bare call is made + * Defines the possible abi call signatures for methods that return a non-void value. */ -export type TypedCallParams = { - method: TSignature - methodArgs: TSignature extends undefined ? undefined : Array -} & AppClientCallCoreParams & CoreAppCallArgs +export type HelloWorldNonVoidMethodSignatures = keyof HelloWorldTypes['methods'] extends infer T ? T extends keyof HelloWorldTypes['methods'] ? MethodReturn extends void ? never : T : never : never /** - * Defines the arguments required for a bare call + * Defines an object containing all relevant parameters for a single call to the contract. */ -export type BareCallArgs = Omit +export type CallParams = Expand< + Omit & + { + /** The args for the ABI method call, either as an ordered array or an object */ + args: Expand + } +> /** - * Maps a method signature from the HelloWorld smart contract to the method's arguments in either tuple of struct form + * Maps a method signature from the HelloWorld smart contract to the method's arguments in either tuple or struct form */ -export type MethodArgs = HelloWorld['methods'][TSignature]['argsObj' | 'argsTuple'] +export type MethodArgs = HelloWorldTypes['methods'][TSignature]['argsObj' | 'argsTuple'] /** * Maps a method signature from the HelloWorld smart contract to the method's return type */ -export type MethodReturn = HelloWorld['methods'][TSignature]['returns'] +export type MethodReturn = HelloWorldTypes['methods'][TSignature]['returns'] + /** - * A factory for available 'create' calls - */ -export type HelloWorldCreateCalls = (typeof HelloWorldCallFactory)['create'] -/** - * Defines supported create methods for this smart contract + * Defines supported create method params for this smart contract */ export type HelloWorldCreateCallParams = - | (TypedCallParams & (OnCompleteNoOp)) + | Expand /** * Defines arguments required for the deploy method. */ -export type HelloWorldDeployArgs = { - deployTimeParams?: TealTemplateParams +export type HelloWorldDeployParams = Expand & { /** - * A delegate which takes a create call factory and returns the create call params for this smart contract + * Create transaction parameters to use if a create needs to be issued as part of deployment; use `method` to define ABI call (if available) or leave out for a bare call (if available) */ - createCall?: (callFactory: HelloWorldCreateCalls) => HelloWorldCreateCallParams -} + createParams?: HelloWorldCreateCallParams +}> /** - * Exposes methods for constructing all available smart contract calls + * Exposes methods for constructing `AppClient` params objects for ABI calls to the HelloWorld smart contract */ -export abstract class HelloWorldCallFactory { - /** - * Gets available create call factories - */ - static get create() { - return { - /** - * Constructs a create call for the HelloWorld smart contract using a bare call - * - * @param params Any parameters for the call - * @returns A TypedCallParams object for the call - */ - bare(params: BareCallArgs & AppClientCallCoreParams & CoreAppCallArgs & AppClientCompilationParams & (OnCompleteNoOp) = {}) { - return { - method: undefined, - methodArgs: undefined, - ...params, - } - }, - } - } - +export abstract class HelloWorldParamsFactory { /** * Constructs a no op call for the hello(string)string ABI method * - * @param args Any args for the contract call - * @param params Any additional parameters for the call - * @returns A TypedCallParams object for the call + * @param params Parameters for the call + * @returns An `AppClientMethodCallParams` object for the call */ - static hello(args: MethodArgs<'hello(string)string'>, params: AppClientCallCoreParams & CoreAppCallArgs) { + static hello(params: CallParams & CallOnComplete): AppClientMethodCallParams & CallOnComplete { return { - method: 'hello(string)string' as const, - methodArgs: Array.isArray(args) ? args : [args.name], ...params, + method: 'hello(string)string' as const, + args: Array.isArray(params.args) ? params.args : [params.args.name], } } } /** - * A client to make calls to the HelloWorld smart contract + * A factory to create and deploy one or more instance of the HelloWorld smart contract and to create one or more app clients to interact with those (or other) app instances */ -export class HelloWorldClient { +export class HelloWorldFactory { /** - * The underlying `ApplicationClient` for when you want to have more flexibility + * The underlying `AppFactory` for when you want to have more flexibility */ - public readonly appClient: ApplicationClient - - private readonly sender: SendTransactionFrom | undefined + public readonly appFactory: AppFactory /** - * Creates a new instance of `HelloWorldClient` + * Creates a new instance of `HelloWorldFactory` * - * @param appDetails appDetails The details to identify the app to deploy - * @param algod An algod client instance - */ - constructor(appDetails: AppDetails, private algod: Algodv2) { - this.sender = appDetails.sender - this.appClient = algokit.getAppClient({ - ...appDetails, - app: APP_SPEC - }, algod) + * @param params The parameters to initialise the app factory with + */ + constructor(params: Omit) { + this.appFactory = new AppFactory({ + ...params, + appSpec: APP_SPEC, + }) } - + + /** The name of the app (from the ARC-32 / ARC-56 app spec or override). */ + public get appName() { + return this.appFactory.appName + } + + /** The ARC-56 app spec being used */ + get appSpec() { + return APP_SPEC + } + + /** A reference to the underlying `AlgorandClient` this app factory is using. */ + public get algorand(): AlgorandClientInterface { + return this.appFactory.algorand + } + /** - * Checks for decode errors on the AppCallTransactionResult and maps the return value to the specified generic type + * Returns a new `AppClient` client for an app instance of the given ID. * - * @param result The AppCallTransactionResult to be mapped - * @param returnValueFormatter An optional delegate to format the return value if required - * @returns The smart contract response with an updated return value + * Automatically populates appName, defaultSender and source maps from the factory + * if not specified in the params. + * @param params The parameters to create the app client + * @returns The `AppClient` */ - protected mapReturnValue(result: AppCallTransactionResult, returnValueFormatter?: (value: any) => TReturn): AppCallTransactionResultOfType & TResult { - if(result.return?.decodeError) { - throw result.return.decodeError - } - const returnValue = result.return?.returnValue !== undefined && returnValueFormatter !== undefined - ? returnValueFormatter(result.return.returnValue) - : result.return?.returnValue as TReturn | undefined - return { ...result, return: returnValue } as AppCallTransactionResultOfType & TResult + public getAppClientById(params: AppFactoryAppClientParams) { + return new HelloWorldClient(this.appFactory.getAppClientById(params)) } - + /** - * Calls the ABI method with the matching signature using an onCompletion code of NO_OP + * Returns a new `AppClient` client, resolving the app by creator address and name + * using AlgoKit app deployment semantics (i.e. looking for the app creation transaction note). * - * @param typedCallParams An object containing the method signature, args, and any other relevant parameters - * @param returnValueFormatter An optional delegate which when provided will be used to map non-undefined return values to the target type - * @returns The result of the smart contract call + * Automatically populates appName, defaultSender and source maps from the factory + * if not specified in the params. + * @param params The parameters to create the app client + * @returns The `AppClient` */ - public async call(typedCallParams: TypedCallParams, returnValueFormatter?: (value: any) => MethodReturn) { - return this.mapReturnValue>(await this.appClient.call(typedCallParams), returnValueFormatter) + public async getAppClientByCreatorAndName( + params: AppFactoryResolveAppClientByCreatorAndNameParams, + ) { + return new HelloWorldClient(await this.appFactory.getAppClientByCreatorAndName(params)) } /** @@ -308,92 +237,299 @@ export class HelloWorldClient { * @param params The arguments for the contract calls and any additional parameters for the call * @returns The deployment result */ - public deploy(params: HelloWorldDeployArgs & AppClientDeployCoreParams & IncludeSchema = {}): ReturnType { - const createArgs = params.createCall?.(HelloWorldCallFactory.create) - return this.appClient.deploy({ + public async deploy(params: HelloWorldDeployParams = {}) { + const result = await this.appFactory.deploy({ ...params, - createArgs, - createOnCompleteAction: createArgs?.onCompleteAction, }) + return { result: result.result, appClient: new HelloWorldClient(result.appClient) } } /** - * Gets available create methods + * Get parameters to create transactions (create and deploy related calls) for the current app. A good mental model for this is that these parameters represent a deferred transaction creation. */ - public get create() { - const $this = this - return { + readonly params = { + /** + * Gets available create methods + */ + create: { /** * Creates a new instance of the HelloWorld smart contract using a bare call. * - * @param args The arguments for the bare call + * @param params The params for the bare (raw) call + * @returns The params for a create call + */ + bare: (params?: Expand) => { + return this.appFactory.params.bare.create(params) + }, + }, + + } + + /** + * Create transactions for the current app + */ + readonly createTransaction = { + /** + * Gets available create methods + */ + create: { + /** + * Creates a new instance of the HelloWorld smart contract using a bare call. + * + * @param params The params for the bare (raw) call + * @returns The params for a create call + */ + bare: (params?: Expand) => { + return this.appFactory.params.bare.create(params) + }, + }, + + } + + /** + * Send calls to the current app + */ + readonly send = { + /** + * Gets available create methods + */ + create: { + /** + * Creates a new instance of the HelloWorld smart contract using a bare call. + * + * @param params The params for the bare (raw) call * @returns The create result */ - async bare(args: BareCallArgs & AppClientCallCoreParams & AppClientCompilationParams & IncludeSchema & CoreAppCallArgs & (OnCompleteNoOp) = {}) { - return $this.mapReturnValue(await $this.appClient.create(args)) + bare: async (params?: Expand) => { + const result = await this.appFactory.send.bare.create(params) + return { result: result.result, appClient: new HelloWorldClient(result.appClient) } }, - } + }, + } +} +/** + * A client to make calls to the HelloWorld smart contract + */ +export class HelloWorldClient { + /** + * The underlying `AppClient` for when you want to have more flexibility + */ + public readonly appClient: AppClient + /** - * Makes a clear_state call to an existing instance of the HelloWorld smart contract. + * Creates a new instance of `HelloWorldClient` * - * @param args The arguments for the bare call - * @returns The clear_state result + * @param appClient An `AppClient` instance which has been created with the HelloWorld app spec + */ + constructor(appClient: AppClient) + /** + * Creates a new instance of `HelloWorldClient` + * + * @param params The parameters to initialise the app client with + */ + constructor(params: Omit) + constructor(appClientOrParams: AppClient | Omit) { + this.appClient = appClientOrParams instanceof AppClient ? appClientOrParams : new AppClient({ + ...appClientOrParams, + appSpec: APP_SPEC, + }) + } + + /** + * Checks for decode errors on the given return value and maps the return value to the return type for the given method + * @returns The typed return value or undefined if there was no value + */ + decodeReturnValue(method: TSignature, returnValue: ABIReturn | undefined) { + return returnValue !== undefined ? getArc56ReturnValue>(returnValue, this.appClient.getABIMethod(method), APP_SPEC.structs) : undefined + } + + /** + * Returns a new `HelloWorldClient` client, resolving the app by creator address and name + * using AlgoKit app deployment semantics (i.e. looking for the app creation transaction note). + * @param params The parameters to create the app client + */ + public static async fromCreatorAndName(params: Omit): Promise { + return new HelloWorldClient(await AppClient.fromCreatorAndName({...params, appSpec: APP_SPEC})) + } + + /** + * Returns an `HelloWorldClient` instance for the current network based on + * pre-determined network-specific app IDs specified in the ARC-56 app spec. + * + * If no IDs are in the app spec or the network isn't recognised, an error is thrown. + * @param params The parameters to create the app client */ - public clearState(args: BareCallArgs & AppClientCallCoreParams & CoreAppCallArgs = {}) { - return this.appClient.clearState(args) + static async fromNetwork( + params: Omit + ): Promise { + return new HelloWorldClient(await AppClient.fromNetwork({...params, appSpec: APP_SPEC})) + } + + /** The ID of the app instance this client is linked to. */ + public get appId() { + return this.appClient.appId + } + + /** The app address of the app instance this client is linked to. */ + public get appAddress() { + return this.appClient.appAddress + } + + /** The name of the app. */ + public get appName() { + return this.appClient.appName + } + + /** The ARC-56 app spec being used */ + public get appSpec() { + return this.appClient.appSpec + } + + /** A reference to the underlying `AlgorandClient` this app client is using. */ + public get algorand(): AlgorandClientInterface { + return this.appClient.algorand } /** - * Calls the hello(string)string ABI method. + * Get parameters to create transactions for the current app. A good mental model for this is that these parameters represent a deferred transaction creation. + */ + readonly params = { + /** + * Makes a clear_state call to an existing instance of the HelloWorld smart contract. + * + * @param params The params for the bare (raw) call + * @returns The clearState result + */ + clearState: (params?: Expand) => { + return this.appClient.params.bare.clearState(params) + }, + + /** + * Makes a call to the HelloWorld smart contract using the `hello(string)string` ABI method. + * + * @param params The params for the smart contract call + * @returns The call params + */ + hello: (params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + return this.appClient.params.call(HelloWorldParamsFactory.hello(params)) + }, + + } + + /** + * Create transactions for the current app + */ + readonly createTransaction = { + /** + * Makes a clear_state call to an existing instance of the HelloWorld smart contract. + * + * @param params The params for the bare (raw) call + * @returns The clearState result + */ + clearState: (params?: Expand) => { + return this.appClient.createTransaction.bare.clearState(params) + }, + + /** + * Makes a call to the HelloWorld smart contract using the `hello(string)string` ABI method. + * + * @param params The params for the smart contract call + * @returns The call transaction + */ + hello: (params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + return this.appClient.createTransaction.call(HelloWorldParamsFactory.hello(params)) + }, + + } + + /** + * Send calls to the current app + */ + readonly send = { + /** + * Makes a clear_state call to an existing instance of the HelloWorld smart contract. + * + * @param params The params for the bare (raw) call + * @returns The clearState result + */ + clearState: (params?: Expand) => { + return this.appClient.send.bare.clearState(params) + }, + + /** + * Makes a call to the HelloWorld smart contract using the `hello(string)string` ABI method. + * + * @param params The params for the smart contract call + * @returns The call result + */ + hello: async (params: CallParams & SendParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + const result = await this.appClient.send.call(HelloWorldParamsFactory.hello(params)) + return {...result, return: result.return as undefined | HelloWorldReturns['hello(string)string']} + }, + + } + + /** + * Clone this app client with different params * - * @param args The arguments for the contract call - * @param params Any additional parameters for the call - * @returns The result of the call + * @param params The params to use for the the cloned app client. Omit a param to keep the original value. Set a param to override the original value. Setting to undefined will clear the original value. + * @returns A new app client with the altered params + */ + public clone(params: CloneAppClientParams) { + return new HelloWorldClient(this.appClient.clone(params)) + } + + /** + * Methods to access state for the current HelloWorld app */ - public hello(args: MethodArgs<'hello(string)string'>, params: AppClientCallCoreParams & CoreAppCallArgs = {}) { - return this.call(HelloWorldCallFactory.hello(args, params)) + state = { } - public compose(): HelloWorldComposer { + public newGroup(): HelloWorldComposer { const client = this - const atc = new AtomicTransactionComposer() + const composer = this.algorand.newGroup() let promiseChain:Promise = Promise.resolve() - const resultMappers: Array any)> = [] + const resultMappers: Array any)> = [] return { - hello(args: MethodArgs<'hello(string)string'>, params?: AppClientComposeCallCoreParams & CoreAppCallArgs) { - promiseChain = promiseChain.then(() => client.hello(args, {...params, sendParams: {...params?.sendParams, skipSending: true, atc}})) - resultMappers.push(undefined) + /** + * Add a hello(string)string method call against the HelloWorld contract + */ + hello(params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) { + promiseChain = promiseChain.then(async () => composer.addAppCallMethodCall(await client.params.hello(params))) + resultMappers.push((v) => client.decodeReturnValue('hello(string)string', v)) return this }, - clearState(args?: BareCallArgs & AppClientComposeCallCoreParams & CoreAppCallArgs) { - promiseChain = promiseChain.then(() => client.clearState({...args, sendParams: {...args?.sendParams, skipSending: true, atc}})) - resultMappers.push(undefined) + /** + * Add a clear state call to the HelloWorld contract + */ + clearState(params: AppClientBareCallParams) { + promiseChain = promiseChain.then(() => composer.addAppCall(client.params.clearState(params))) return this }, - addTransaction(txn: TransactionWithSigner | TransactionToSign | Transaction | Promise, defaultSender?: SendTransactionFrom) { - promiseChain = promiseChain.then(async () => atc.addTransaction(await algokit.getTransactionWithSigner(txn, defaultSender ?? client.sender))) + addTransaction(txn: Transaction, signer?: TransactionSigner) { + promiseChain = promiseChain.then(() => composer.addTransaction(txn, signer)) return this }, - async atc() { + async composer() { await promiseChain - return atc + return composer }, async simulate(options?: SimulateOptions) { await promiseChain - const result = await atc.simulate(client.algod, new modelsv2.SimulateRequest({ txnGroups: [], ...options })) + const result = await composer.simulate(options) return { ...result, - returns: result.methodResults?.map((val, i) => resultMappers[i] !== undefined ? resultMappers[i]!(val.returnValue) : val.returnValue) + returns: result.returns?.map((val, i) => resultMappers[i] !== undefined ? resultMappers[i]!(val) : val.returnValue) } }, - async execute(sendParams?: AppClientComposeExecuteParams) { + async send(params?: SendParams) { await promiseChain - const result = await algokit.sendAtomicTransactionComposer({ atc, sendParams }, client.algod) + const result = await composer.send(params) return { ...result, - returns: result.returns?.map((val, i) => resultMappers[i] !== undefined ? resultMappers[i]!(val.returnValue) : val.returnValue) + returns: result.returns?.map((val, i) => resultMappers[i] !== undefined ? resultMappers[i]!(val) : val.returnValue) } } } as unknown as HelloWorldComposer @@ -407,7 +543,7 @@ export type HelloWorldComposer = { * @param params Any additional parameters for the call * @returns The typed transaction composer so you can fluently chain multiple calls or call execute to execute all queued up transactions */ - hello(args: MethodArgs<'hello(string)string'>, params?: AppClientComposeCallCoreParams & CoreAppCallArgs): HelloWorldComposer<[...TReturns, MethodReturn<'hello(string)string'>]> + hello(params?: CallParams): HelloWorldComposer<[...TReturns, HelloWorldReturns['hello(string)string'] | undefined]> /** * Makes a clear_state call to an existing instance of the HelloWorld smart contract. @@ -415,37 +551,29 @@ export type HelloWorldComposer = { * @param args The arguments for the bare call * @returns The typed transaction composer so you can fluently chain multiple calls or call execute to execute all queued up transactions */ - clearState(args?: BareCallArgs & AppClientComposeCallCoreParams & CoreAppCallArgs): HelloWorldComposer<[...TReturns, undefined]> + clearState(params?: AppClientBareCallParams): HelloWorldComposer<[...TReturns, undefined]> /** * Adds a transaction to the composer * - * @param txn One of: A TransactionWithSigner object (returned as is), a TransactionToSign object (signer is obtained from the signer property), a Transaction object (signer is extracted from the defaultSender parameter), an async SendTransactionResult returned by one of algokit utils helpers (signer is obtained from the defaultSender parameter) - * @param defaultSender The default sender to be used to obtain a signer where the object provided to the transaction parameter does not include a signer. + * @param txn A transaction to add to the transaction group + * @param signer The optional signer to use when signing this transaction. */ - addTransaction(txn: TransactionWithSigner | TransactionToSign | Transaction | Promise, defaultSender?: SendTransactionFrom): HelloWorldComposer + addTransaction(txn: Transaction, signer?: TransactionSigner): HelloWorldComposer /** * Returns the underlying AtomicTransactionComposer instance */ - atc(): Promise + composer(): TransactionComposer /** * Simulates the transaction group and returns the result */ - simulate(options?: SimulateOptions): Promise> + simulate(options?: SimulateOptions): Promise & { simulateResponse: SimulateResponse }> /** - * Executes the transaction group and returns the results + * Sends the transaction group to the network and returns the results */ - execute(sendParams?: AppClientComposeExecuteParams): Promise> -} -export type SimulateOptions = Omit[0], 'txnGroups'> -export type HelloWorldComposerSimulateResult = { - returns: TReturns - methodResults: ABIResult[] - simulateResponse: modelsv2.SimulateResponse + send(params?: SendParams): Promise> } -export type HelloWorldComposerResults = { +export type HelloWorldComposerResults = Expand + diff --git a/examples/starter_python_react/projects/starter_python_react-frontend/vite.config.ts b/examples/starter_python_react/projects/starter_python_react-frontend/vite.config.ts index 36f7f4e..e42dc7c 100644 --- a/examples/starter_python_react/projects/starter_python_react-frontend/vite.config.ts +++ b/examples/starter_python_react/projects/starter_python_react-frontend/vite.config.ts @@ -1,7 +1,15 @@ import react from '@vitejs/plugin-react' import { defineConfig } from 'vite' +import { nodePolyfills } from 'vite-plugin-node-polyfills' // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react()], + plugins: [ + react(), + nodePolyfills({ + globals: { + Buffer: true, + }, + }), + ], }) diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/contracts/clients/CalculatorClient.ts b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/contracts/clients/CalculatorClient.ts index cc57180..94efde1 100644 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/contracts/clients/CalculatorClient.ts +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/contracts/clients/CalculatorClient.ts @@ -73,7 +73,7 @@ export const APP_SPEC: AppSpec = { } }, "source": { - "approval": "I3ByYWdtYSB2ZXJzaW9uIDEwCmJ5dGVjYmxvY2sgMHgxNTFmN2M3NQoKLy8gVGhpcyBURUFMIHdhcyBnZW5lcmF0ZWQgYnkgVEVBTFNjcmlwdCB2MC4xMDQuMQovLyBodHRwczovL2dpdGh1Yi5jb20vYWxnb3JhbmRmb3VuZGF0aW9uL1RFQUxTY3JpcHQKCi8vIFRoaXMgY29udHJhY3QgaXMgY29tcGxpYW50IHdpdGggYW5kL29yIGltcGxlbWVudHMgdGhlIGZvbGxvd2luZyBBUkNzOiBbIEFSQzQgXQoKLy8gVGhlIGZvbGxvd2luZyB0ZW4gbGluZXMgb2YgVEVBTCBoYW5kbGUgaW5pdGlhbCBwcm9ncmFtIGZsb3cKLy8gVGhpcyBwYXR0ZXJuIGlzIHVzZWQgdG8gbWFrZSBpdCBlYXN5IGZvciBhbnlvbmUgdG8gcGFyc2UgdGhlIHN0YXJ0IG9mIHRoZSBwcm9ncmFtIGFuZCBkZXRlcm1pbmUgaWYgYSBzcGVjaWZpYyBhY3Rpb24gaXMgYWxsb3dlZAovLyBIZXJlLCBhY3Rpb24gcmVmZXJzIHRvIHRoZSBPbkNvbXBsZXRlIGluIGNvbWJpbmF0aW9uIHdpdGggd2hldGhlciB0aGUgYXBwIGlzIGJlaW5nIGNyZWF0ZWQgb3IgY2FsbGVkCi8vIEV2ZXJ5IHBvc3NpYmxlIGFjdGlvbiBmb3IgdGhpcyBjb250cmFjdCBpcyByZXByZXNlbnRlZCBpbiB0aGUgc3dpdGNoIHN0YXRlbWVudAovLyBJZiB0aGUgYWN0aW9uIGlzIG5vdCBpbXBsZW1lbnRlZCBpbiB0aGUgY29udHJhY3QsIGl0cyByZXNwZWN0aXZlIGJyYW5jaCB3aWxsIGJlICIqTk9UX0lNUExFTUVOVEVEIiB3aGljaCBqdXN0IGNvbnRhaW5zICJlcnIiCnR4biBBcHBsaWNhdGlvbklECiEKcHVzaGludCA2CioKdHhuIE9uQ29tcGxldGlvbgorCnN3aXRjaCAqY2FsbF9Ob09wICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqY3JlYXRlX05vT3AgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVECgoqTk9UX0lNUExFTUVOVEVEOgoJLy8gVGhlIHJlcXVlc3RlZCBhY3Rpb24gaXMgbm90IGltcGxlbWVudGVkIGluIHRoaXMgY29udHJhY3QuIEFyZSB5b3UgdXNpbmcgdGhlIGNvcnJlY3QgT25Db21wbGV0ZT8gRGlkIHlvdSBzZXQgeW91ciBhcHAgSUQ/CgllcnIKCi8vIGdldFN1bShhOiB1aW50NjQsIGI6IHVpbnQ2NCk6IHVpbnQ2NAovLwovLyBDYWxjdWxhdGVzIHRoZSBzdW0gb2YgdHdvIG51bWJlcnMKLy8KLy8gQHBhcmFtIGEKLy8gQHBhcmFtIGIKLy8gQHJldHVybnMgVGhlIHN1bSBvZiBhIGFuZCBiCmdldFN1bToKCXByb3RvIDIgMQoKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6MTIKCS8vIHJldHVybiBhICsgYjsKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCSsKCXJldHN1YgoKLy8gZ2V0RGlmZmVyZW5jZShhOiB1aW50NjQsIGI6IHVpbnQ2NCk6IHVpbnQ2NAovLwovLyBDYWxjdWxhdGVzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdHdvIG51bWJlcnMKLy8KLy8gQHBhcmFtIGEKLy8gQHBhcmFtIGIKLy8gQHJldHVybnMgVGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhIGFuZCBiLgpnZXREaWZmZXJlbmNlOgoJcHJvdG8gMiAxCgoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czoyMwoJLy8gcmV0dXJuIGEgPj0gYiA/IGEgLSBiIDogYiAtIGE7CglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0Cgk+PQoJYnogKnRlcm5hcnkwX2ZhbHNlCglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0CgktCgliICp0ZXJuYXJ5MF9lbmQKCip0ZXJuYXJ5MF9mYWxzZToKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCS0KCip0ZXJuYXJ5MF9lbmQ6CglyZXRzdWIKCi8vIGRvTWF0aCh1aW50NjQsdWludDY0LHN0cmluZyl1aW50NjQKKmFiaV9yb3V0ZV9kb01hdGg6CgkvLyBUaGUgQUJJIHJldHVybiBwcmVmaXgKCWJ5dGUgMHgxNTFmN2M3NQoKCS8vIG9wZXJhdGlvbjogc3RyaW5nCgl0eG5hIEFwcGxpY2F0aW9uQXJncyAzCglleHRyYWN0IDIgMAoKCS8vIGI6IHVpbnQ2NAoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgoJYnRvaQoKCS8vIGE6IHVpbnQ2NAoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQoJYnRvaQoKCS8vIGV4ZWN1dGUgZG9NYXRoKHVpbnQ2NCx1aW50NjQsc3RyaW5nKXVpbnQ2NAoJY2FsbHN1YiBkb01hdGgKCWl0b2IKCWNvbmNhdAoJbG9nCglwdXNoaW50IDEKCXJldHVybgoKLy8gZG9NYXRoKGE6IHVpbnQ2NCwgYjogdWludDY0LCBvcGVyYXRpb246IHN0cmluZyk6IHVpbnQ2NAovLwovLyBBIG1ldGhvZCB0aGF0IHRha2VzIHR3byBudW1iZXJzIGFuZCBkb2VzIGVpdGhlciBhZGRpdGlvbiBvciBzdWJ0cmFjdGlvbgovLwovLyBAcGFyYW0gYSBUaGUgZmlyc3QgdWludDY0Ci8vIEBwYXJhbSBiIFRoZSBzZWNvbmQgdWludDY0Ci8vIEBwYXJhbSBvcGVyYXRpb24gVGhlIG9wZXJhdGlvbiB0byBwZXJmb3JtLiBDYW4gYmUgZWl0aGVyICdzdW0nIG9yICdkaWZmZXJlbmNlJwovLwovLyBAcmV0dXJucyBUaGUgcmVzdWx0IG9mIHRoZSBvcGVyYXRpb24KZG9NYXRoOgoJcHJvdG8gMyAxCgoJLy8gUHVzaCBlbXB0eSBieXRlcyBhZnRlciB0aGUgZnJhbWUgcG9pbnRlciB0byByZXNlcnZlIHNwYWNlIGZvciBsb2NhbCB2YXJpYWJsZXMKCXB1c2hieXRlcyAweAoKCS8vICppZjBfY29uZGl0aW9uCgkvLyBjb250cmFjdHMvQ2FsY3VsYXRvci5hbGdvLnRzOjM4CgkvLyBvcGVyYXRpb24gPT09ICdzdW0nCglmcmFtZV9kaWcgLTMgLy8gb3BlcmF0aW9uOiBzdHJpbmcKCXB1c2hieXRlcyAweDczNzU2ZCAvLyAic3VtIgoJPT0KCWJ6ICppZjBfZWxzZWlmMV9jb25kaXRpb24KCgkvLyAqaWYwX2NvbnNlcXVlbnQKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6MzkKCS8vIHJlc3VsdCA9IHRoaXMuZ2V0U3VtKGEsIGIpCglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0CglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CgljYWxsc3ViIGdldFN1bQoJZnJhbWVfYnVyeSAwIC8vIHJlc3VsdDogdWludDY0CgliICppZjBfZW5kCgoqaWYwX2Vsc2VpZjFfY29uZGl0aW9uOgoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czo0MAoJLy8gb3BlcmF0aW9uID09PSAnZGlmZmVyZW5jZScKCWZyYW1lX2RpZyAtMyAvLyBvcGVyYXRpb246IHN0cmluZwoJcHVzaGJ5dGVzIDB4NjQ2OTY2NjY2NTcyNjU2ZTYzNjUgLy8gImRpZmZlcmVuY2UiCgk9PQoJYnogKmlmMF9lbHNlCgoJLy8gKmlmMF9lbHNlaWYxX2NvbnNlcXVlbnQKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6NDEKCS8vIHJlc3VsdCA9IHRoaXMuZ2V0RGlmZmVyZW5jZShhLCBiKQoJZnJhbWVfZGlnIC0yIC8vIGI6IHVpbnQ2NAoJZnJhbWVfZGlnIC0xIC8vIGE6IHVpbnQ2NAoJY2FsbHN1YiBnZXREaWZmZXJlbmNlCglmcmFtZV9idXJ5IDAgLy8gcmVzdWx0OiB1aW50NjQKCWIgKmlmMF9lbmQKCippZjBfZWxzZToKCS8vIEludmFsaWQgb3BlcmF0aW9uCgllcnIKCippZjBfZW5kOgoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czo0NAoJLy8gcmV0dXJuIHJlc3VsdDsKCWZyYW1lX2RpZyAwIC8vIHJlc3VsdDogdWludDY0CgoJLy8gc2V0IHRoZSBzdWJyb3V0aW5lIHJldHVybiB2YWx1ZQoJZnJhbWVfYnVyeSAwCglyZXRzdWIKCi8vIGhlbGxvKHN0cmluZylzdHJpbmcKKmFiaV9yb3V0ZV9oZWxsbzoKCS8vIFRoZSBBQkkgcmV0dXJuIHByZWZpeAoJYnl0ZSAweDE1MWY3Yzc1CgoJLy8gbmFtZTogc3RyaW5nCgl0eG5hIEFwcGxpY2F0aW9uQXJncyAxCglleHRyYWN0IDIgMAoKCS8vIGV4ZWN1dGUgaGVsbG8oc3RyaW5nKXN0cmluZwoJY2FsbHN1YiBoZWxsbwoJZHVwCglsZW4KCWl0b2IKCWV4dHJhY3QgNiAyCglzd2FwCgljb25jYXQKCWNvbmNhdAoJbG9nCglwdXNoaW50IDEKCXJldHVybgoKLy8gaGVsbG8obmFtZTogc3RyaW5nKTogc3RyaW5nCi8vCi8vIEEgZGVtb25zdHJhdGlvbiBtZXRob2QgdXNlZCBpbiB0aGUgQWxnb0tpdCBmdWxsc3RhY2sgdGVtcGxhdGUuCi8vIEdyZWV0cyB0aGUgdXNlciBieSBuYW1lLgovLwovLyBAcGFyYW0gbmFtZSBUaGUgbmFtZSBvZiB0aGUgdXNlciB0byBncmVldC4KLy8gQHJldHVybnMgQSBncmVldGluZyBtZXNzYWdlIHRvIHRoZSB1c2VyLgpoZWxsbzoKCXByb3RvIDEgMQoKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6NTUKCS8vIHJldHVybiAnSGVsbG8sICcgKyBuYW1lOwoJcHVzaGJ5dGVzIDB4NDg2NTZjNmM2ZjJjMjAgLy8gIkhlbGxvLCAiCglmcmFtZV9kaWcgLTEgLy8gbmFtZTogc3RyaW5nCgljb25jYXQKCXJldHN1YgoKKmFiaV9yb3V0ZV9jcmVhdGVBcHBsaWNhdGlvbjoKCXB1c2hpbnQgMQoJcmV0dXJuCgoqY3JlYXRlX05vT3A6CglwdXNoYnl0ZXMgMHhiODQ0N2IzNiAvLyBtZXRob2QgImNyZWF0ZUFwcGxpY2F0aW9uKCl2b2lkIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9jcmVhdGVBcHBsaWNhdGlvbgoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjcmVhdGUgTm9PcAoJZXJyCgoqY2FsbF9Ob09wOgoJcHVzaGJ5dGVzIDB4NzZhN2VmMzMgLy8gbWV0aG9kICJkb01hdGgodWludDY0LHVpbnQ2NCxzdHJpbmcpdWludDY0IgoJcHVzaGJ5dGVzIDB4MDJiZWNlMTEgLy8gbWV0aG9kICJoZWxsbyhzdHJpbmcpc3RyaW5nIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9kb01hdGggKmFiaV9yb3V0ZV9oZWxsbwoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjYWxsIE5vT3AKCWVycg==", + "approval": "I3ByYWdtYSB2ZXJzaW9uIDEwCmludGNibG9jayAxCmJ5dGVjYmxvY2sgMHgxNTFmN2M3NQoKLy8gVGhpcyBURUFMIHdhcyBnZW5lcmF0ZWQgYnkgVEVBTFNjcmlwdCB2MC4xMDUuNQovLyBodHRwczovL2dpdGh1Yi5jb20vYWxnb3JhbmRmb3VuZGF0aW9uL1RFQUxTY3JpcHQKCi8vIFRoaXMgY29udHJhY3QgaXMgY29tcGxpYW50IHdpdGggYW5kL29yIGltcGxlbWVudHMgdGhlIGZvbGxvd2luZyBBUkNzOiBbIEFSQzQgXQoKLy8gVGhlIGZvbGxvd2luZyB0ZW4gbGluZXMgb2YgVEVBTCBoYW5kbGUgaW5pdGlhbCBwcm9ncmFtIGZsb3cKLy8gVGhpcyBwYXR0ZXJuIGlzIHVzZWQgdG8gbWFrZSBpdCBlYXN5IGZvciBhbnlvbmUgdG8gcGFyc2UgdGhlIHN0YXJ0IG9mIHRoZSBwcm9ncmFtIGFuZCBkZXRlcm1pbmUgaWYgYSBzcGVjaWZpYyBhY3Rpb24gaXMgYWxsb3dlZAovLyBIZXJlLCBhY3Rpb24gcmVmZXJzIHRvIHRoZSBPbkNvbXBsZXRlIGluIGNvbWJpbmF0aW9uIHdpdGggd2hldGhlciB0aGUgYXBwIGlzIGJlaW5nIGNyZWF0ZWQgb3IgY2FsbGVkCi8vIEV2ZXJ5IHBvc3NpYmxlIGFjdGlvbiBmb3IgdGhpcyBjb250cmFjdCBpcyByZXByZXNlbnRlZCBpbiB0aGUgc3dpdGNoIHN0YXRlbWVudAovLyBJZiB0aGUgYWN0aW9uIGlzIG5vdCBpbXBsZW1lbnRlZCBpbiB0aGUgY29udHJhY3QsIGl0cyByZXNwZWN0aXZlIGJyYW5jaCB3aWxsIGJlICIqTk9UX0lNUExFTUVOVEVEIiB3aGljaCBqdXN0IGNvbnRhaW5zICJlcnIiCnR4biBBcHBsaWNhdGlvbklECiEKcHVzaGludCA2CioKdHhuIE9uQ29tcGxldGlvbgorCnN3aXRjaCAqY2FsbF9Ob09wICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqY3JlYXRlX05vT3AgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVECgoqTk9UX0lNUExFTUVOVEVEOgoJLy8gVGhlIHJlcXVlc3RlZCBhY3Rpb24gaXMgbm90IGltcGxlbWVudGVkIGluIHRoaXMgY29udHJhY3QuIEFyZSB5b3UgdXNpbmcgdGhlIGNvcnJlY3QgT25Db21wbGV0ZT8gRGlkIHlvdSBzZXQgeW91ciBhcHAgSUQ/CgllcnIKCi8vIGdldFN1bShhOiB1aW50NjQsIGI6IHVpbnQ2NCk6IHVpbnQ2NAovLwovLyBDYWxjdWxhdGVzIHRoZSBzdW0gb2YgdHdvIG51bWJlcnMKLy8KLy8gQHBhcmFtIGEKLy8gQHBhcmFtIGIKLy8gQHJldHVybnMgVGhlIHN1bSBvZiBhIGFuZCBiCmdldFN1bToKCXByb3RvIDIgMQoKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6MTIKCS8vIHJldHVybiBhICsgYjsKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCSsKCXJldHN1YgoKLy8gZ2V0RGlmZmVyZW5jZShhOiB1aW50NjQsIGI6IHVpbnQ2NCk6IHVpbnQ2NAovLwovLyBDYWxjdWxhdGVzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdHdvIG51bWJlcnMKLy8KLy8gQHBhcmFtIGEKLy8gQHBhcmFtIGIKLy8gQHJldHVybnMgVGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhIGFuZCBiLgpnZXREaWZmZXJlbmNlOgoJcHJvdG8gMiAxCgoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czoyMwoJLy8gcmV0dXJuIGEgPj0gYiA/IGEgLSBiIDogYiAtIGE7CglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0Cgk+PQoJYnogKnRlcm5hcnkwX2ZhbHNlCglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0CgktCgliICp0ZXJuYXJ5MF9lbmQKCip0ZXJuYXJ5MF9mYWxzZToKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCS0KCip0ZXJuYXJ5MF9lbmQ6CglyZXRzdWIKCi8vIGRvTWF0aCh1aW50NjQsdWludDY0LHN0cmluZyl1aW50NjQKKmFiaV9yb3V0ZV9kb01hdGg6CgkvLyBUaGUgQUJJIHJldHVybiBwcmVmaXgKCWJ5dGVjIDAgLy8gMHgxNTFmN2M3NQoKCS8vIG9wZXJhdGlvbjogc3RyaW5nCgl0eG5hIEFwcGxpY2F0aW9uQXJncyAzCglleHRyYWN0IDIgMAoKCS8vIGI6IHVpbnQ2NAoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgoJYnRvaQoKCS8vIGE6IHVpbnQ2NAoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQoJYnRvaQoKCS8vIGV4ZWN1dGUgZG9NYXRoKHVpbnQ2NCx1aW50NjQsc3RyaW5nKXVpbnQ2NAoJY2FsbHN1YiBkb01hdGgKCWl0b2IKCWNvbmNhdAoJbG9nCglpbnRjIDAgLy8gMQoJcmV0dXJuCgovLyBkb01hdGgoYTogdWludDY0LCBiOiB1aW50NjQsIG9wZXJhdGlvbjogc3RyaW5nKTogdWludDY0Ci8vCi8vIEEgbWV0aG9kIHRoYXQgdGFrZXMgdHdvIG51bWJlcnMgYW5kIGRvZXMgZWl0aGVyIGFkZGl0aW9uIG9yIHN1YnRyYWN0aW9uCi8vCi8vIEBwYXJhbSBhIFRoZSBmaXJzdCB1aW50NjQKLy8gQHBhcmFtIGIgVGhlIHNlY29uZCB1aW50NjQKLy8gQHBhcmFtIG9wZXJhdGlvbiBUaGUgb3BlcmF0aW9uIHRvIHBlcmZvcm0uIENhbiBiZSBlaXRoZXIgJ3N1bScgb3IgJ2RpZmZlcmVuY2UnCi8vCi8vIEByZXR1cm5zIFRoZSByZXN1bHQgb2YgdGhlIG9wZXJhdGlvbgpkb01hdGg6Cglwcm90byAzIDEKCgkvLyBQdXNoIGVtcHR5IGJ5dGVzIGFmdGVyIHRoZSBmcmFtZSBwb2ludGVyIHRvIHJlc2VydmUgc3BhY2UgZm9yIGxvY2FsIHZhcmlhYmxlcwoJcHVzaGJ5dGVzIDB4CgoJLy8gKmlmMF9jb25kaXRpb24KCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6MzgKCS8vIG9wZXJhdGlvbiA9PT0gJ3N1bScKCWZyYW1lX2RpZyAtMyAvLyBvcGVyYXRpb246IHN0cmluZwoJcHVzaGJ5dGVzIDB4NzM3NTZkIC8vICJzdW0iCgk9PQoJYnogKmlmMF9lbHNlaWYxX2NvbmRpdGlvbgoKCS8vICppZjBfY29uc2VxdWVudAoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czozOQoJLy8gcmVzdWx0ID0gdGhpcy5nZXRTdW0oYSwgYikKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCWNhbGxzdWIgZ2V0U3VtCglmcmFtZV9idXJ5IDAgLy8gcmVzdWx0OiB1aW50NjQKCWIgKmlmMF9lbmQKCippZjBfZWxzZWlmMV9jb25kaXRpb246CgkvLyBjb250cmFjdHMvQ2FsY3VsYXRvci5hbGdvLnRzOjQwCgkvLyBvcGVyYXRpb24gPT09ICdkaWZmZXJlbmNlJwoJZnJhbWVfZGlnIC0zIC8vIG9wZXJhdGlvbjogc3RyaW5nCglwdXNoYnl0ZXMgMHg2NDY5NjY2NjY1NzI2NTZlNjM2NSAvLyAiZGlmZmVyZW5jZSIKCT09CglieiAqaWYwX2Vsc2UKCgkvLyAqaWYwX2Vsc2VpZjFfY29uc2VxdWVudAoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czo0MQoJLy8gcmVzdWx0ID0gdGhpcy5nZXREaWZmZXJlbmNlKGEsIGIpCglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0CglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CgljYWxsc3ViIGdldERpZmZlcmVuY2UKCWZyYW1lX2J1cnkgMCAvLyByZXN1bHQ6IHVpbnQ2NAoJYiAqaWYwX2VuZAoKKmlmMF9lbHNlOgoJLy8gSW52YWxpZCBvcGVyYXRpb24KCWVycgoKKmlmMF9lbmQ6CgkvLyBjb250cmFjdHMvQ2FsY3VsYXRvci5hbGdvLnRzOjQ0CgkvLyByZXR1cm4gcmVzdWx0OwoJZnJhbWVfZGlnIDAgLy8gcmVzdWx0OiB1aW50NjQKCgkvLyBzZXQgdGhlIHN1YnJvdXRpbmUgcmV0dXJuIHZhbHVlCglmcmFtZV9idXJ5IDAKCXJldHN1YgoKLy8gaGVsbG8oc3RyaW5nKXN0cmluZwoqYWJpX3JvdXRlX2hlbGxvOgoJLy8gVGhlIEFCSSByZXR1cm4gcHJlZml4CglieXRlYyAwIC8vIDB4MTUxZjdjNzUKCgkvLyBuYW1lOiBzdHJpbmcKCXR4bmEgQXBwbGljYXRpb25BcmdzIDEKCWV4dHJhY3QgMiAwCgoJLy8gZXhlY3V0ZSBoZWxsbyhzdHJpbmcpc3RyaW5nCgljYWxsc3ViIGhlbGxvCglkdXAKCWxlbgoJaXRvYgoJZXh0cmFjdCA2IDIKCXN3YXAKCWNvbmNhdAoJY29uY2F0Cglsb2cKCWludGMgMCAvLyAxCglyZXR1cm4KCi8vIGhlbGxvKG5hbWU6IHN0cmluZyk6IHN0cmluZwovLwovLyBBIGRlbW9uc3RyYXRpb24gbWV0aG9kIHVzZWQgaW4gdGhlIEFsZ29LaXQgZnVsbHN0YWNrIHRlbXBsYXRlLgovLyBHcmVldHMgdGhlIHVzZXIgYnkgbmFtZS4KLy8KLy8gQHBhcmFtIG5hbWUgVGhlIG5hbWUgb2YgdGhlIHVzZXIgdG8gZ3JlZXQuCi8vIEByZXR1cm5zIEEgZ3JlZXRpbmcgbWVzc2FnZSB0byB0aGUgdXNlci4KaGVsbG86Cglwcm90byAxIDEKCgkvLyBjb250cmFjdHMvQ2FsY3VsYXRvci5hbGdvLnRzOjU1CgkvLyByZXR1cm4gJ0hlbGxvLCAnICsgbmFtZTsKCXB1c2hieXRlcyAweDQ4NjU2YzZjNmYyYzIwIC8vICJIZWxsbywgIgoJZnJhbWVfZGlnIC0xIC8vIG5hbWU6IHN0cmluZwoJY29uY2F0CglyZXRzdWIKCiphYmlfcm91dGVfY3JlYXRlQXBwbGljYXRpb246CglpbnRjIDAgLy8gMQoJcmV0dXJuCgoqY3JlYXRlX05vT3A6CglwdXNoYnl0ZXMgMHhiODQ0N2IzNiAvLyBtZXRob2QgImNyZWF0ZUFwcGxpY2F0aW9uKCl2b2lkIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9jcmVhdGVBcHBsaWNhdGlvbgoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjcmVhdGUgTm9PcAoJZXJyCgoqY2FsbF9Ob09wOgoJcHVzaGJ5dGVzIDB4NzZhN2VmMzMgLy8gbWV0aG9kICJkb01hdGgodWludDY0LHVpbnQ2NCxzdHJpbmcpdWludDY0IgoJcHVzaGJ5dGVzIDB4MDJiZWNlMTEgLy8gbWV0aG9kICJoZWxsbyhzdHJpbmcpc3RyaW5nIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9kb01hdGggKmFiaV9yb3V0ZV9oZWxsbwoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjYWxsIE5vT3AKCWVycg==", "clear": "I3ByYWdtYSB2ZXJzaW9uIDEw" }, "contract": { diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/.algokit/.copier-answers.yml b/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/.algokit/.copier-answers.yml index 02fee69..3a07b00 100644 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/.algokit/.copier-answers.yml +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/.algokit/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier; NEVER EDIT MANUALLY -_commit: 1.0.8 +_commit: 1.0.9 _src_path: gh:algorandfoundation/algokit-react-frontend-template author_email: None author_name: None diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/index.html b/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/index.html index a85566a..5a2ef4f 100644 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/index.html +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/index.html @@ -7,9 +7,6 @@
- diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/package.json b/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/package.json index f946807..529fcbd 100644 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/package.json +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/package.json @@ -8,11 +8,11 @@ "private": true, "type": "module", "engines": { - "node": ">=18.0", + "node": ">=20.0", "npm": ">=9.0" }, "devDependencies": { - "@algorandfoundation/algokit-client-generator": "^3.0.3", + "@algorandfoundation/algokit-client-generator": "^4.0.0", "@types/node": "^18.17.14", "@types/react": "^18.2.11", "@types/react-dom": "^18.2.4", @@ -20,16 +20,16 @@ "autoprefixer": "^10.4.14", "ts-node": "^10.9.1", "typescript": "^5.1.6", - "vite": "^5.0.0" + "vite": "^5.0.0", + "vite-plugin-node-polyfills": "^0.22.0" }, "dependencies": { - "@walletconnect/modal-sign-html": "^2.6.1", - "@algorandfoundation/algokit-utils": "^6.0.2", + "@algorandfoundation/algokit-utils": "^7.0.0", "@blockshake/defly-connect": "^1.1.6", "@daffiwallet/connect": "^1.0.3", - "@perawallet/connect": "^1.3.1", - "@txnlab/use-wallet": "^2.4.0", - "algosdk": "^2.7.0", + "@perawallet/connect": "^1.3.4", + "@txnlab/use-wallet": "^2.8.2", + "algosdk": ">=2.9.0 <3.0", "notistack": "^3.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -57,5 +57,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "overrides": { + "ws@>7.0.0 <7.5.9": "7.5.10" } } diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/src/components/AppCalls.tsx b/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/src/components/AppCalls.tsx index 7f8f8b5..ba5f5e5 100644 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/src/components/AppCalls.tsx +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/src/components/AppCalls.tsx @@ -1,10 +1,9 @@ -import * as algokit from '@algorandfoundation/algokit-utils' -import { TransactionSignerAccount } from '@algorandfoundation/algokit-utils/types/account' import { useWallet } from '@txnlab/use-wallet' import { useSnackbar } from 'notistack' import { useState } from 'react' -import { CalculatorClient } from '../contracts/Calculator' -import { getAlgodConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' +import { CalculatorFactory } from '../contracts/Calculator' +import { getAlgodConfigFromViteEnvironment, getIndexerConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' +import { AlgorandClient } from '@algorandfoundation/algokit-utils' interface AppCallsInterface { openModal: boolean @@ -14,16 +13,16 @@ interface AppCallsInterface { const AppCalls = ({ openModal, setModalState }: AppCallsInterface) => { const [loading, setLoading] = useState(false) const [contractInput, setContractInput] = useState('') + const { enqueueSnackbar } = useSnackbar() + const { signer, activeAddress } = useWallet() const algodConfig = getAlgodConfigFromViteEnvironment() - const algodClient = algokit.getAlgoClient({ - server: algodConfig.server, - port: algodConfig.port, - token: algodConfig.token, + const indexerConfig = getIndexerConfigFromViteEnvironment() + const algorand = AlgorandClient.fromConfig({ + algodConfig, + indexerConfig, }) - - const { enqueueSnackbar } = useSnackbar() - const { signer, activeAddress } = useWallet() + algorand.setDefaultSigner(signer) const sendAppCall = async () => { setLoading(true) @@ -33,27 +32,33 @@ const AppCalls = ({ openModal, setModalState }: AppCallsInterface) => { // Instead, you would deploy your contract on your backend and reference it by id. // Given the simplicity of the starter contract, we are deploying it on the frontend // for demonstration purposes. - const appClient = new CalculatorClient( - { - sender: { signer, addr: activeAddress } as TransactionSignerAccount, - resolveBy: 'id', - id: 0, - }, - algodClient, - ) - await appClient.create.createApplication({}).catch((e: Error) => { + const factory = new CalculatorFactory({ + defaultSender: activeAddress, + algorand, + }) + const deployResult = await factory.send.create.createApplication().catch((e: Error) => { enqueueSnackbar(`Error deploying the contract: ${e.message}`, { variant: 'error' }) setLoading(false) - return + return undefined }) - const response = await appClient.hello({ name: contractInput }).catch((e: Error) => { + if (!deployResult) { + return + } + + const { appClient } = deployResult + + const response = await appClient.send.hello({ args: { name: contractInput } }).catch((e: Error) => { enqueueSnackbar(`Error calling the contract: ${e.message}`, { variant: 'error' }) setLoading(false) - return + return undefined }) - enqueueSnackbar(`Response from the contract: ${response?.return}`, { variant: 'success' }) + if (!response) { + return + } + + enqueueSnackbar(`Response from the contract: ${response.return}`, { variant: 'success' }) setLoading(false) } diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/src/components/Transact.tsx b/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/src/components/Transact.tsx index becfadc..760040b 100644 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/src/components/Transact.tsx +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/src/components/Transact.tsx @@ -1,6 +1,5 @@ -import * as algokit from '@algorandfoundation/algokit-utils' +import { algo, AlgorandClient } 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' @@ -15,15 +14,11 @@ const Transact = ({ openModal, setModalState }: TransactInterface) => { const [receiverAddress, setReceiverAddress] = useState('') const algodConfig = getAlgodConfigFromViteEnvironment() - const algodClient = algokit.getAlgoClient({ - server: algodConfig.server, - port: algodConfig.port, - token: algodConfig.token, - }) + const algorand = AlgorandClient.fromConfig({ algodConfig }) const { enqueueSnackbar } = useSnackbar() - const { signer, activeAddress, signTransactions, sendTransactions } = useWallet() + const { signer, activeAddress } = useWallet() const handleSubmitAlgo = async () => { setLoading(true) @@ -33,25 +28,15 @@ const Transact = ({ openModal, setModalState }: TransactInterface) => { 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' }) + const result = await algorand.send.payment({ + signer, + sender: activeAddress, + receiver: receiverAddress, + amount: algo(1), + }) + enqueueSnackbar(`Transaction sent: ${result.txIds[0]}`, { variant: 'success' }) setReceiverAddress('') } catch (e) { enqueueSnackbar('Failed to send transaction', { variant: 'error' }) diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/src/contracts/Calculator.ts b/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/src/contracts/Calculator.ts index 11f0fc2..7a29148 100644 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/src/contracts/Calculator.ts +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/src/contracts/Calculator.ts @@ -1,315 +1,222 @@ /* eslint-disable */ -// @ts-nocheck /** * This file was automatically generated by @algorandfoundation/algokit-client-generator. * DO NOT MODIFY IT BY HAND. - * requires: @algorandfoundation/algokit-utils: ^2 + * requires: @algorandfoundation/algokit-utils: ^7 */ -import * as algokit from '@algorandfoundation/algokit-utils' -import type { - ABIAppCallArg, - AppCallTransactionResult, - AppCallTransactionResultOfType, - AppCompilationResult, - AppReference, - AppState, - AppStorageSchema, - CoreAppCallArgs, - RawAppCallArgs, - TealTemplateParams, -} from '@algorandfoundation/algokit-utils/types/app' -import type { - AppClientCallCoreParams, +import { AlgorandClientInterface } from '@algorandfoundation/algokit-utils/types/algorand-client-interface' +import { ABIReturn, AppReturn, SendAppTransactionResult } from '@algorandfoundation/algokit-utils/types/app' +import { Arc56Contract, getArc56ReturnValue, getABIStructFromABITuple } from '@algorandfoundation/algokit-utils/types/app-arc56' +import { + AppClient, + AppClientMethodCallParams, + AppClientParams, + AppClientBareCallParams, + CallOnComplete, AppClientCompilationParams, - AppClientDeployCoreParams, - AppDetails, - ApplicationClient, + ResolveAppClientByCreatorAndName, + ResolveAppClientByNetwork, + CloneAppClientParams, } from '@algorandfoundation/algokit-utils/types/app-client' -import type { AppSpec } from '@algorandfoundation/algokit-utils/types/app-spec' -import type { SendTransactionResult, TransactionToSign, SendTransactionFrom, SendTransactionParams } from '@algorandfoundation/algokit-utils/types/transaction' -import type { ABIResult, TransactionWithSigner } from 'algosdk' -import { Algodv2, OnApplicationComplete, Transaction, AtomicTransactionComposer, modelsv2 } from 'algosdk' -export const APP_SPEC: AppSpec = { - "hints": { - "doMath(uint64,uint64,string)uint64": { - "call_config": { - "no_op": "CALL" - } - }, - "hello(string)string": { - "call_config": { - "no_op": "CALL" - } - }, - "createApplication()void": { - "call_config": { - "no_op": "CREATE" - } - } - }, - "bare_call_config": { - "no_op": "NEVER", - "opt_in": "NEVER", - "close_out": "NEVER", - "update_application": "NEVER", - "delete_application": "NEVER" - }, - "schema": { - "local": { - "declared": {}, - "reserved": {} - }, - "global": { - "declared": {}, - "reserved": {} - } - }, - "state": { - "global": { - "num_byte_slices": 0, - "num_uints": 0 - }, - "local": { - "num_byte_slices": 0, - "num_uints": 0 - } - }, - "source": { - "approval": "I3ByYWdtYSB2ZXJzaW9uIDEwCmJ5dGVjYmxvY2sgMHgxNTFmN2M3NQoKLy8gVGhpcyBURUFMIHdhcyBnZW5lcmF0ZWQgYnkgVEVBTFNjcmlwdCB2MC4xMDQuMQovLyBodHRwczovL2dpdGh1Yi5jb20vYWxnb3JhbmRmb3VuZGF0aW9uL1RFQUxTY3JpcHQKCi8vIFRoaXMgY29udHJhY3QgaXMgY29tcGxpYW50IHdpdGggYW5kL29yIGltcGxlbWVudHMgdGhlIGZvbGxvd2luZyBBUkNzOiBbIEFSQzQgXQoKLy8gVGhlIGZvbGxvd2luZyB0ZW4gbGluZXMgb2YgVEVBTCBoYW5kbGUgaW5pdGlhbCBwcm9ncmFtIGZsb3cKLy8gVGhpcyBwYXR0ZXJuIGlzIHVzZWQgdG8gbWFrZSBpdCBlYXN5IGZvciBhbnlvbmUgdG8gcGFyc2UgdGhlIHN0YXJ0IG9mIHRoZSBwcm9ncmFtIGFuZCBkZXRlcm1pbmUgaWYgYSBzcGVjaWZpYyBhY3Rpb24gaXMgYWxsb3dlZAovLyBIZXJlLCBhY3Rpb24gcmVmZXJzIHRvIHRoZSBPbkNvbXBsZXRlIGluIGNvbWJpbmF0aW9uIHdpdGggd2hldGhlciB0aGUgYXBwIGlzIGJlaW5nIGNyZWF0ZWQgb3IgY2FsbGVkCi8vIEV2ZXJ5IHBvc3NpYmxlIGFjdGlvbiBmb3IgdGhpcyBjb250cmFjdCBpcyByZXByZXNlbnRlZCBpbiB0aGUgc3dpdGNoIHN0YXRlbWVudAovLyBJZiB0aGUgYWN0aW9uIGlzIG5vdCBpbXBsZW1lbnRlZCBpbiB0aGUgY29udHJhY3QsIGl0cyByZXNwZWN0aXZlIGJyYW5jaCB3aWxsIGJlICIqTk9UX0lNUExFTUVOVEVEIiB3aGljaCBqdXN0IGNvbnRhaW5zICJlcnIiCnR4biBBcHBsaWNhdGlvbklECiEKcHVzaGludCA2CioKdHhuIE9uQ29tcGxldGlvbgorCnN3aXRjaCAqY2FsbF9Ob09wICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqY3JlYXRlX05vT3AgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVECgoqTk9UX0lNUExFTUVOVEVEOgoJLy8gVGhlIHJlcXVlc3RlZCBhY3Rpb24gaXMgbm90IGltcGxlbWVudGVkIGluIHRoaXMgY29udHJhY3QuIEFyZSB5b3UgdXNpbmcgdGhlIGNvcnJlY3QgT25Db21wbGV0ZT8gRGlkIHlvdSBzZXQgeW91ciBhcHAgSUQ/CgllcnIKCi8vIGdldFN1bShhOiB1aW50NjQsIGI6IHVpbnQ2NCk6IHVpbnQ2NAovLwovLyBDYWxjdWxhdGVzIHRoZSBzdW0gb2YgdHdvIG51bWJlcnMKLy8KLy8gQHBhcmFtIGEKLy8gQHBhcmFtIGIKLy8gQHJldHVybnMgVGhlIHN1bSBvZiBhIGFuZCBiCmdldFN1bToKCXByb3RvIDIgMQoKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6MTIKCS8vIHJldHVybiBhICsgYjsKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCSsKCXJldHN1YgoKLy8gZ2V0RGlmZmVyZW5jZShhOiB1aW50NjQsIGI6IHVpbnQ2NCk6IHVpbnQ2NAovLwovLyBDYWxjdWxhdGVzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdHdvIG51bWJlcnMKLy8KLy8gQHBhcmFtIGEKLy8gQHBhcmFtIGIKLy8gQHJldHVybnMgVGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhIGFuZCBiLgpnZXREaWZmZXJlbmNlOgoJcHJvdG8gMiAxCgoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czoyMwoJLy8gcmV0dXJuIGEgPj0gYiA/IGEgLSBiIDogYiAtIGE7CglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0Cgk+PQoJYnogKnRlcm5hcnkwX2ZhbHNlCglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0CgktCgliICp0ZXJuYXJ5MF9lbmQKCip0ZXJuYXJ5MF9mYWxzZToKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCS0KCip0ZXJuYXJ5MF9lbmQ6CglyZXRzdWIKCi8vIGRvTWF0aCh1aW50NjQsdWludDY0LHN0cmluZyl1aW50NjQKKmFiaV9yb3V0ZV9kb01hdGg6CgkvLyBUaGUgQUJJIHJldHVybiBwcmVmaXgKCWJ5dGUgMHgxNTFmN2M3NQoKCS8vIG9wZXJhdGlvbjogc3RyaW5nCgl0eG5hIEFwcGxpY2F0aW9uQXJncyAzCglleHRyYWN0IDIgMAoKCS8vIGI6IHVpbnQ2NAoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgoJYnRvaQoKCS8vIGE6IHVpbnQ2NAoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQoJYnRvaQoKCS8vIGV4ZWN1dGUgZG9NYXRoKHVpbnQ2NCx1aW50NjQsc3RyaW5nKXVpbnQ2NAoJY2FsbHN1YiBkb01hdGgKCWl0b2IKCWNvbmNhdAoJbG9nCglwdXNoaW50IDEKCXJldHVybgoKLy8gZG9NYXRoKGE6IHVpbnQ2NCwgYjogdWludDY0LCBvcGVyYXRpb246IHN0cmluZyk6IHVpbnQ2NAovLwovLyBBIG1ldGhvZCB0aGF0IHRha2VzIHR3byBudW1iZXJzIGFuZCBkb2VzIGVpdGhlciBhZGRpdGlvbiBvciBzdWJ0cmFjdGlvbgovLwovLyBAcGFyYW0gYSBUaGUgZmlyc3QgdWludDY0Ci8vIEBwYXJhbSBiIFRoZSBzZWNvbmQgdWludDY0Ci8vIEBwYXJhbSBvcGVyYXRpb24gVGhlIG9wZXJhdGlvbiB0byBwZXJmb3JtLiBDYW4gYmUgZWl0aGVyICdzdW0nIG9yICdkaWZmZXJlbmNlJwovLwovLyBAcmV0dXJucyBUaGUgcmVzdWx0IG9mIHRoZSBvcGVyYXRpb24KZG9NYXRoOgoJcHJvdG8gMyAxCgoJLy8gUHVzaCBlbXB0eSBieXRlcyBhZnRlciB0aGUgZnJhbWUgcG9pbnRlciB0byByZXNlcnZlIHNwYWNlIGZvciBsb2NhbCB2YXJpYWJsZXMKCXB1c2hieXRlcyAweAoKCS8vICppZjBfY29uZGl0aW9uCgkvLyBjb250cmFjdHMvQ2FsY3VsYXRvci5hbGdvLnRzOjM4CgkvLyBvcGVyYXRpb24gPT09ICdzdW0nCglmcmFtZV9kaWcgLTMgLy8gb3BlcmF0aW9uOiBzdHJpbmcKCXB1c2hieXRlcyAweDczNzU2ZCAvLyAic3VtIgoJPT0KCWJ6ICppZjBfZWxzZWlmMV9jb25kaXRpb24KCgkvLyAqaWYwX2NvbnNlcXVlbnQKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6MzkKCS8vIHJlc3VsdCA9IHRoaXMuZ2V0U3VtKGEsIGIpCglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0CglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CgljYWxsc3ViIGdldFN1bQoJZnJhbWVfYnVyeSAwIC8vIHJlc3VsdDogdWludDY0CgliICppZjBfZW5kCgoqaWYwX2Vsc2VpZjFfY29uZGl0aW9uOgoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czo0MAoJLy8gb3BlcmF0aW9uID09PSAnZGlmZmVyZW5jZScKCWZyYW1lX2RpZyAtMyAvLyBvcGVyYXRpb246IHN0cmluZwoJcHVzaGJ5dGVzIDB4NjQ2OTY2NjY2NTcyNjU2ZTYzNjUgLy8gImRpZmZlcmVuY2UiCgk9PQoJYnogKmlmMF9lbHNlCgoJLy8gKmlmMF9lbHNlaWYxX2NvbnNlcXVlbnQKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6NDEKCS8vIHJlc3VsdCA9IHRoaXMuZ2V0RGlmZmVyZW5jZShhLCBiKQoJZnJhbWVfZGlnIC0yIC8vIGI6IHVpbnQ2NAoJZnJhbWVfZGlnIC0xIC8vIGE6IHVpbnQ2NAoJY2FsbHN1YiBnZXREaWZmZXJlbmNlCglmcmFtZV9idXJ5IDAgLy8gcmVzdWx0OiB1aW50NjQKCWIgKmlmMF9lbmQKCippZjBfZWxzZToKCS8vIEludmFsaWQgb3BlcmF0aW9uCgllcnIKCippZjBfZW5kOgoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czo0NAoJLy8gcmV0dXJuIHJlc3VsdDsKCWZyYW1lX2RpZyAwIC8vIHJlc3VsdDogdWludDY0CgoJLy8gc2V0IHRoZSBzdWJyb3V0aW5lIHJldHVybiB2YWx1ZQoJZnJhbWVfYnVyeSAwCglyZXRzdWIKCi8vIGhlbGxvKHN0cmluZylzdHJpbmcKKmFiaV9yb3V0ZV9oZWxsbzoKCS8vIFRoZSBBQkkgcmV0dXJuIHByZWZpeAoJYnl0ZSAweDE1MWY3Yzc1CgoJLy8gbmFtZTogc3RyaW5nCgl0eG5hIEFwcGxpY2F0aW9uQXJncyAxCglleHRyYWN0IDIgMAoKCS8vIGV4ZWN1dGUgaGVsbG8oc3RyaW5nKXN0cmluZwoJY2FsbHN1YiBoZWxsbwoJZHVwCglsZW4KCWl0b2IKCWV4dHJhY3QgNiAyCglzd2FwCgljb25jYXQKCWNvbmNhdAoJbG9nCglwdXNoaW50IDEKCXJldHVybgoKLy8gaGVsbG8obmFtZTogc3RyaW5nKTogc3RyaW5nCi8vCi8vIEEgZGVtb25zdHJhdGlvbiBtZXRob2QgdXNlZCBpbiB0aGUgQWxnb0tpdCBmdWxsc3RhY2sgdGVtcGxhdGUuCi8vIEdyZWV0cyB0aGUgdXNlciBieSBuYW1lLgovLwovLyBAcGFyYW0gbmFtZSBUaGUgbmFtZSBvZiB0aGUgdXNlciB0byBncmVldC4KLy8gQHJldHVybnMgQSBncmVldGluZyBtZXNzYWdlIHRvIHRoZSB1c2VyLgpoZWxsbzoKCXByb3RvIDEgMQoKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6NTUKCS8vIHJldHVybiAnSGVsbG8sICcgKyBuYW1lOwoJcHVzaGJ5dGVzIDB4NDg2NTZjNmM2ZjJjMjAgLy8gIkhlbGxvLCAiCglmcmFtZV9kaWcgLTEgLy8gbmFtZTogc3RyaW5nCgljb25jYXQKCXJldHN1YgoKKmFiaV9yb3V0ZV9jcmVhdGVBcHBsaWNhdGlvbjoKCXB1c2hpbnQgMQoJcmV0dXJuCgoqY3JlYXRlX05vT3A6CglwdXNoYnl0ZXMgMHhiODQ0N2IzNiAvLyBtZXRob2QgImNyZWF0ZUFwcGxpY2F0aW9uKCl2b2lkIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9jcmVhdGVBcHBsaWNhdGlvbgoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjcmVhdGUgTm9PcAoJZXJyCgoqY2FsbF9Ob09wOgoJcHVzaGJ5dGVzIDB4NzZhN2VmMzMgLy8gbWV0aG9kICJkb01hdGgodWludDY0LHVpbnQ2NCxzdHJpbmcpdWludDY0IgoJcHVzaGJ5dGVzIDB4MDJiZWNlMTEgLy8gbWV0aG9kICJoZWxsbyhzdHJpbmcpc3RyaW5nIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9kb01hdGggKmFiaV9yb3V0ZV9oZWxsbwoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjYWxsIE5vT3AKCWVycg==", - "clear": "I3ByYWdtYSB2ZXJzaW9uIDEw" - }, - "contract": { - "name": "Calculator", - "desc": "", - "methods": [ - { - "name": "doMath", - "desc": "A method that takes two numbers and does either addition or subtraction", - "args": [ - { - "name": "a", - "type": "uint64", - "desc": "The first uint64" - }, - { - "name": "b", - "type": "uint64", - "desc": "The second uint64" - }, - { - "name": "operation", - "type": "string", - "desc": "The operation to perform. Can be either 'sum' or 'difference'" - } - ], - "returns": { - "type": "uint64", - "desc": "The result of the operation" - } - }, - { - "name": "hello", - "desc": "A demonstration method used in the AlgoKit fullstack template.\nGreets the user by name.", - "args": [ - { - "name": "name", - "type": "string", - "desc": "The name of the user to greet." - } - ], - "returns": { - "type": "string", - "desc": "A greeting message to the user." - } - }, - { - "name": "createApplication", - "args": [], - "returns": { - "type": "void" - } - } - ] - } -} +import { AppFactory, AppFactoryAppClientParams, AppFactoryResolveAppClientByCreatorAndNameParams, AppFactoryDeployParams, AppFactoryParams, CreateSchema } from '@algorandfoundation/algokit-utils/types/app-factory' +import { TransactionComposer, AppCallMethodCall, AppMethodCallTransactionArgument, SimulateOptions } from '@algorandfoundation/algokit-utils/types/composer' +import { SendParams, SendSingleTransactionResult, SendAtomicTransactionComposerResults } from '@algorandfoundation/algokit-utils/types/transaction' +import { Address, encodeAddress, modelsv2, OnApplicationComplete, Transaction, TransactionSigner } from 'algosdk' +import SimulateResponse = modelsv2.SimulateResponse + +export const APP_SPEC: Arc56Contract = {"arcs":[],"name":"Calculator","desc":"","structs":{},"methods":[{"name":"doMath","desc":"A method that takes two numbers and does either addition or subtraction","args":[{"name":"a","type":"uint64","desc":"The first uint64"},{"name":"b","type":"uint64","desc":"The second uint64"},{"name":"operation","type":"string","desc":"The operation to perform. Can be either 'sum' or 'difference'"}],"returns":{"type":"uint64","desc":"The result of the operation"},"events":[],"actions":{"create":[],"call":["NoOp"]}},{"name":"hello","desc":"A demonstration method used in the AlgoKit fullstack template.\nGreets the user by name.","args":[{"name":"name","type":"string","desc":"The name of the user to greet."}],"returns":{"type":"string","desc":"A greeting message to the user."},"events":[],"actions":{"create":[],"call":["NoOp"]}},{"name":"createApplication","args":[],"returns":{"type":"void"},"events":[],"actions":{"create":["NoOp"],"call":[]}}],"state":{"schema":{"global":{"ints":0,"bytes":0},"local":{"ints":0,"bytes":0}},"keys":{"global":{},"local":{},"box":{}},"maps":{"global":{},"local":{},"box":{}}},"source":{"approval":"I3ByYWdtYSB2ZXJzaW9uIDEwCmludGNibG9jayAxCmJ5dGVjYmxvY2sgMHgxNTFmN2M3NQoKLy8gVGhpcyBURUFMIHdhcyBnZW5lcmF0ZWQgYnkgVEVBTFNjcmlwdCB2MC4xMDUuNQovLyBodHRwczovL2dpdGh1Yi5jb20vYWxnb3JhbmRmb3VuZGF0aW9uL1RFQUxTY3JpcHQKCi8vIFRoaXMgY29udHJhY3QgaXMgY29tcGxpYW50IHdpdGggYW5kL29yIGltcGxlbWVudHMgdGhlIGZvbGxvd2luZyBBUkNzOiBbIEFSQzQgXQoKLy8gVGhlIGZvbGxvd2luZyB0ZW4gbGluZXMgb2YgVEVBTCBoYW5kbGUgaW5pdGlhbCBwcm9ncmFtIGZsb3cKLy8gVGhpcyBwYXR0ZXJuIGlzIHVzZWQgdG8gbWFrZSBpdCBlYXN5IGZvciBhbnlvbmUgdG8gcGFyc2UgdGhlIHN0YXJ0IG9mIHRoZSBwcm9ncmFtIGFuZCBkZXRlcm1pbmUgaWYgYSBzcGVjaWZpYyBhY3Rpb24gaXMgYWxsb3dlZAovLyBIZXJlLCBhY3Rpb24gcmVmZXJzIHRvIHRoZSBPbkNvbXBsZXRlIGluIGNvbWJpbmF0aW9uIHdpdGggd2hldGhlciB0aGUgYXBwIGlzIGJlaW5nIGNyZWF0ZWQgb3IgY2FsbGVkCi8vIEV2ZXJ5IHBvc3NpYmxlIGFjdGlvbiBmb3IgdGhpcyBjb250cmFjdCBpcyByZXByZXNlbnRlZCBpbiB0aGUgc3dpdGNoIHN0YXRlbWVudAovLyBJZiB0aGUgYWN0aW9uIGlzIG5vdCBpbXBsZW1lbnRlZCBpbiB0aGUgY29udHJhY3QsIGl0cyByZXNwZWN0aXZlIGJyYW5jaCB3aWxsIGJlICIqTk9UX0lNUExFTUVOVEVEIiB3aGljaCBqdXN0IGNvbnRhaW5zICJlcnIiCnR4biBBcHBsaWNhdGlvbklECiEKcHVzaGludCA2CioKdHhuIE9uQ29tcGxldGlvbgorCnN3aXRjaCAqY2FsbF9Ob09wICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqY3JlYXRlX05vT3AgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVEICpOT1RfSU1QTEVNRU5URUQgKk5PVF9JTVBMRU1FTlRFRCAqTk9UX0lNUExFTUVOVEVECgoqTk9UX0lNUExFTUVOVEVEOgoJLy8gVGhlIHJlcXVlc3RlZCBhY3Rpb24gaXMgbm90IGltcGxlbWVudGVkIGluIHRoaXMgY29udHJhY3QuIEFyZSB5b3UgdXNpbmcgdGhlIGNvcnJlY3QgT25Db21wbGV0ZT8gRGlkIHlvdSBzZXQgeW91ciBhcHAgSUQ/CgllcnIKCi8vIGdldFN1bShhOiB1aW50NjQsIGI6IHVpbnQ2NCk6IHVpbnQ2NAovLwovLyBDYWxjdWxhdGVzIHRoZSBzdW0gb2YgdHdvIG51bWJlcnMKLy8KLy8gQHBhcmFtIGEKLy8gQHBhcmFtIGIKLy8gQHJldHVybnMgVGhlIHN1bSBvZiBhIGFuZCBiCmdldFN1bToKCXByb3RvIDIgMQoKCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6MTIKCS8vIHJldHVybiBhICsgYjsKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCSsKCXJldHN1YgoKLy8gZ2V0RGlmZmVyZW5jZShhOiB1aW50NjQsIGI6IHVpbnQ2NCk6IHVpbnQ2NAovLwovLyBDYWxjdWxhdGVzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdHdvIG51bWJlcnMKLy8KLy8gQHBhcmFtIGEKLy8gQHBhcmFtIGIKLy8gQHJldHVybnMgVGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhIGFuZCBiLgpnZXREaWZmZXJlbmNlOgoJcHJvdG8gMiAxCgoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czoyMwoJLy8gcmV0dXJuIGEgPj0gYiA/IGEgLSBiIDogYiAtIGE7CglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0Cgk+PQoJYnogKnRlcm5hcnkwX2ZhbHNlCglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0CgktCgliICp0ZXJuYXJ5MF9lbmQKCip0ZXJuYXJ5MF9mYWxzZToKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCS0KCip0ZXJuYXJ5MF9lbmQ6CglyZXRzdWIKCi8vIGRvTWF0aCh1aW50NjQsdWludDY0LHN0cmluZyl1aW50NjQKKmFiaV9yb3V0ZV9kb01hdGg6CgkvLyBUaGUgQUJJIHJldHVybiBwcmVmaXgKCWJ5dGVjIDAgLy8gMHgxNTFmN2M3NQoKCS8vIG9wZXJhdGlvbjogc3RyaW5nCgl0eG5hIEFwcGxpY2F0aW9uQXJncyAzCglleHRyYWN0IDIgMAoKCS8vIGI6IHVpbnQ2NAoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgoJYnRvaQoKCS8vIGE6IHVpbnQ2NAoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQoJYnRvaQoKCS8vIGV4ZWN1dGUgZG9NYXRoKHVpbnQ2NCx1aW50NjQsc3RyaW5nKXVpbnQ2NAoJY2FsbHN1YiBkb01hdGgKCWl0b2IKCWNvbmNhdAoJbG9nCglpbnRjIDAgLy8gMQoJcmV0dXJuCgovLyBkb01hdGgoYTogdWludDY0LCBiOiB1aW50NjQsIG9wZXJhdGlvbjogc3RyaW5nKTogdWludDY0Ci8vCi8vIEEgbWV0aG9kIHRoYXQgdGFrZXMgdHdvIG51bWJlcnMgYW5kIGRvZXMgZWl0aGVyIGFkZGl0aW9uIG9yIHN1YnRyYWN0aW9uCi8vCi8vIEBwYXJhbSBhIFRoZSBmaXJzdCB1aW50NjQKLy8gQHBhcmFtIGIgVGhlIHNlY29uZCB1aW50NjQKLy8gQHBhcmFtIG9wZXJhdGlvbiBUaGUgb3BlcmF0aW9uIHRvIHBlcmZvcm0uIENhbiBiZSBlaXRoZXIgJ3N1bScgb3IgJ2RpZmZlcmVuY2UnCi8vCi8vIEByZXR1cm5zIFRoZSByZXN1bHQgb2YgdGhlIG9wZXJhdGlvbgpkb01hdGg6Cglwcm90byAzIDEKCgkvLyBQdXNoIGVtcHR5IGJ5dGVzIGFmdGVyIHRoZSBmcmFtZSBwb2ludGVyIHRvIHJlc2VydmUgc3BhY2UgZm9yIGxvY2FsIHZhcmlhYmxlcwoJcHVzaGJ5dGVzIDB4CgoJLy8gKmlmMF9jb25kaXRpb24KCS8vIGNvbnRyYWN0cy9DYWxjdWxhdG9yLmFsZ28udHM6MzgKCS8vIG9wZXJhdGlvbiA9PT0gJ3N1bScKCWZyYW1lX2RpZyAtMyAvLyBvcGVyYXRpb246IHN0cmluZwoJcHVzaGJ5dGVzIDB4NzM3NTZkIC8vICJzdW0iCgk9PQoJYnogKmlmMF9lbHNlaWYxX2NvbmRpdGlvbgoKCS8vICppZjBfY29uc2VxdWVudAoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czozOQoJLy8gcmVzdWx0ID0gdGhpcy5nZXRTdW0oYSwgYikKCWZyYW1lX2RpZyAtMiAvLyBiOiB1aW50NjQKCWZyYW1lX2RpZyAtMSAvLyBhOiB1aW50NjQKCWNhbGxzdWIgZ2V0U3VtCglmcmFtZV9idXJ5IDAgLy8gcmVzdWx0OiB1aW50NjQKCWIgKmlmMF9lbmQKCippZjBfZWxzZWlmMV9jb25kaXRpb246CgkvLyBjb250cmFjdHMvQ2FsY3VsYXRvci5hbGdvLnRzOjQwCgkvLyBvcGVyYXRpb24gPT09ICdkaWZmZXJlbmNlJwoJZnJhbWVfZGlnIC0zIC8vIG9wZXJhdGlvbjogc3RyaW5nCglwdXNoYnl0ZXMgMHg2NDY5NjY2NjY1NzI2NTZlNjM2NSAvLyAiZGlmZmVyZW5jZSIKCT09CglieiAqaWYwX2Vsc2UKCgkvLyAqaWYwX2Vsc2VpZjFfY29uc2VxdWVudAoJLy8gY29udHJhY3RzL0NhbGN1bGF0b3IuYWxnby50czo0MQoJLy8gcmVzdWx0ID0gdGhpcy5nZXREaWZmZXJlbmNlKGEsIGIpCglmcmFtZV9kaWcgLTIgLy8gYjogdWludDY0CglmcmFtZV9kaWcgLTEgLy8gYTogdWludDY0CgljYWxsc3ViIGdldERpZmZlcmVuY2UKCWZyYW1lX2J1cnkgMCAvLyByZXN1bHQ6IHVpbnQ2NAoJYiAqaWYwX2VuZAoKKmlmMF9lbHNlOgoJLy8gSW52YWxpZCBvcGVyYXRpb24KCWVycgoKKmlmMF9lbmQ6CgkvLyBjb250cmFjdHMvQ2FsY3VsYXRvci5hbGdvLnRzOjQ0CgkvLyByZXR1cm4gcmVzdWx0OwoJZnJhbWVfZGlnIDAgLy8gcmVzdWx0OiB1aW50NjQKCgkvLyBzZXQgdGhlIHN1YnJvdXRpbmUgcmV0dXJuIHZhbHVlCglmcmFtZV9idXJ5IDAKCXJldHN1YgoKLy8gaGVsbG8oc3RyaW5nKXN0cmluZwoqYWJpX3JvdXRlX2hlbGxvOgoJLy8gVGhlIEFCSSByZXR1cm4gcHJlZml4CglieXRlYyAwIC8vIDB4MTUxZjdjNzUKCgkvLyBuYW1lOiBzdHJpbmcKCXR4bmEgQXBwbGljYXRpb25BcmdzIDEKCWV4dHJhY3QgMiAwCgoJLy8gZXhlY3V0ZSBoZWxsbyhzdHJpbmcpc3RyaW5nCgljYWxsc3ViIGhlbGxvCglkdXAKCWxlbgoJaXRvYgoJZXh0cmFjdCA2IDIKCXN3YXAKCWNvbmNhdAoJY29uY2F0Cglsb2cKCWludGMgMCAvLyAxCglyZXR1cm4KCi8vIGhlbGxvKG5hbWU6IHN0cmluZyk6IHN0cmluZwovLwovLyBBIGRlbW9uc3RyYXRpb24gbWV0aG9kIHVzZWQgaW4gdGhlIEFsZ29LaXQgZnVsbHN0YWNrIHRlbXBsYXRlLgovLyBHcmVldHMgdGhlIHVzZXIgYnkgbmFtZS4KLy8KLy8gQHBhcmFtIG5hbWUgVGhlIG5hbWUgb2YgdGhlIHVzZXIgdG8gZ3JlZXQuCi8vIEByZXR1cm5zIEEgZ3JlZXRpbmcgbWVzc2FnZSB0byB0aGUgdXNlci4KaGVsbG86Cglwcm90byAxIDEKCgkvLyBjb250cmFjdHMvQ2FsY3VsYXRvci5hbGdvLnRzOjU1CgkvLyByZXR1cm4gJ0hlbGxvLCAnICsgbmFtZTsKCXB1c2hieXRlcyAweDQ4NjU2YzZjNmYyYzIwIC8vICJIZWxsbywgIgoJZnJhbWVfZGlnIC0xIC8vIG5hbWU6IHN0cmluZwoJY29uY2F0CglyZXRzdWIKCiphYmlfcm91dGVfY3JlYXRlQXBwbGljYXRpb246CglpbnRjIDAgLy8gMQoJcmV0dXJuCgoqY3JlYXRlX05vT3A6CglwdXNoYnl0ZXMgMHhiODQ0N2IzNiAvLyBtZXRob2QgImNyZWF0ZUFwcGxpY2F0aW9uKCl2b2lkIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9jcmVhdGVBcHBsaWNhdGlvbgoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjcmVhdGUgTm9PcAoJZXJyCgoqY2FsbF9Ob09wOgoJcHVzaGJ5dGVzIDB4NzZhN2VmMzMgLy8gbWV0aG9kICJkb01hdGgodWludDY0LHVpbnQ2NCxzdHJpbmcpdWludDY0IgoJcHVzaGJ5dGVzIDB4MDJiZWNlMTEgLy8gbWV0aG9kICJoZWxsbyhzdHJpbmcpc3RyaW5nIgoJdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMAoJbWF0Y2ggKmFiaV9yb3V0ZV9kb01hdGggKmFiaV9yb3V0ZV9oZWxsbwoKCS8vIHRoaXMgY29udHJhY3QgZG9lcyBub3QgaW1wbGVtZW50IHRoZSBnaXZlbiBBQkkgbWV0aG9kIGZvciBjYWxsIE5vT3AKCWVycg==","clear":"I3ByYWdtYSB2ZXJzaW9uIDEw"},"bareActions":{"create":[],"call":[]}} as unknown as Arc56Contract -/** - * Defines an onCompletionAction of 'no_op' - */ -export type OnCompleteNoOp = { onCompleteAction?: 'no_op' | OnApplicationComplete.NoOpOC } -/** - * Defines an onCompletionAction of 'opt_in' - */ -export type OnCompleteOptIn = { onCompleteAction: 'opt_in' | OnApplicationComplete.OptInOC } -/** - * Defines an onCompletionAction of 'close_out' - */ -export type OnCompleteCloseOut = { onCompleteAction: 'close_out' | OnApplicationComplete.CloseOutOC } -/** - * Defines an onCompletionAction of 'delete_application' - */ -export type OnCompleteDelApp = { onCompleteAction: 'delete_application' | OnApplicationComplete.DeleteApplicationOC } -/** - * Defines an onCompletionAction of 'update_application' - */ -export type OnCompleteUpdApp = { onCompleteAction: 'update_application' | OnApplicationComplete.UpdateApplicationOC } -/** - * A state record containing a single unsigned integer - */ -export type IntegerState = { - /** - * Gets the state value as a BigInt. - */ - asBigInt(): bigint - /** - * Gets the state value as a number. - */ - asNumber(): number -} /** * A state record containing binary data */ -export type BinaryState = { +export interface BinaryState { /** * Gets the state value as a Uint8Array */ - asByteArray(): Uint8Array + asByteArray(): Uint8Array | undefined /** * Gets the state value as a string */ - asString(): string + asString(): string | undefined } -export type AppCreateCallTransactionResult = AppCallTransactionResult & Partial & AppReference -export type AppUpdateCallTransactionResult = AppCallTransactionResult & Partial +class BinaryStateValue implements BinaryState { + constructor(private value: Uint8Array | undefined) {} -export type AppClientComposeCallCoreParams = Omit & { - sendParams?: Omit + asByteArray(): Uint8Array | undefined { + return this.value + } + + asString(): string | undefined { + return this.value !== undefined ? Buffer.from(this.value).toString('utf-8') : undefined + } } -export type AppClientComposeExecuteParams = Pick -export type IncludeSchema = { +/** + * Expands types for IntelliSense so they are more human readable + * See https://stackoverflow.com/a/69288824 + */ +export type Expand = T extends (...args: infer A) => infer R + ? (...args: Expand
) => Expand + : T extends infer O + ? { [K in keyof O]: O[K] } + : never + + +/** + * The argument types for the Calculator contract + */ +export type CalculatorArgs = { /** - * Any overrides for the storage schema to request for the created app; by default the schema indicated by the app spec is used. + * The object representation of the arguments for each method */ - schema?: Partial + obj: { + 'doMath(uint64,uint64,string)uint64': { + /** + * The first uint64 + */ + a: bigint | number + /** + * The second uint64 + */ + b: bigint | number + /** + * The operation to perform. Can be either 'sum' or 'difference' + */ + operation: string + } + 'hello(string)string': { + /** + * The name of the user to greet. + */ + name: string + } + 'createApplication()void': Record + } + /** + * The tuple representation of the arguments for each method + */ + tuple: { + 'doMath(uint64,uint64,string)uint64': [a: bigint | number, b: bigint | number, operation: string] + 'hello(string)string': [name: string] + 'createApplication()void': [] + } +} + +/** + * The return type for each method + */ +export type CalculatorReturns = { + 'doMath(uint64,uint64,string)uint64': bigint + 'hello(string)string': string + 'createApplication()void': void } /** * Defines the types of available calls and state of the Calculator smart contract. */ -export type Calculator = { +export type CalculatorTypes = { /** * Maps method signatures / names to their argument and return types. */ methods: & Record<'doMath(uint64,uint64,string)uint64' | 'doMath', { - argsObj: { - /** - * The first uint64 - */ - a: bigint | number - /** - * The second uint64 - */ - b: bigint | number - /** - * The operation to perform. Can be either 'sum' or 'difference' - */ - operation: string - } - argsTuple: [a: bigint | number, b: bigint | number, operation: string] + argsObj: CalculatorArgs['obj']['doMath(uint64,uint64,string)uint64'] + argsTuple: CalculatorArgs['tuple']['doMath(uint64,uint64,string)uint64'] /** * The result of the operation */ - returns: bigint + returns: CalculatorReturns['doMath(uint64,uint64,string)uint64'] }> & Record<'hello(string)string' | 'hello', { - argsObj: { - /** - * The name of the user to greet. - */ - name: string - } - argsTuple: [name: string] + argsObj: CalculatorArgs['obj']['hello(string)string'] + argsTuple: CalculatorArgs['tuple']['hello(string)string'] /** * A greeting message to the user. */ - returns: string + returns: CalculatorReturns['hello(string)string'] }> & Record<'createApplication()void' | 'createApplication', { - argsObj: { - } - argsTuple: [] - returns: void + argsObj: CalculatorArgs['obj']['createApplication()void'] + argsTuple: CalculatorArgs['tuple']['createApplication()void'] + returns: CalculatorReturns['createApplication()void'] }> } + /** - * Defines the possible abi call signatures + * Defines the possible abi call signatures. */ -export type CalculatorSig = keyof Calculator['methods'] +export type CalculatorSignatures = keyof CalculatorTypes['methods'] /** - * Defines an object containing all relevant parameters for a single call to the contract. Where TSignature is undefined, a bare call is made + * Defines the possible abi call signatures for methods that return a non-void value. */ -export type TypedCallParams = { - method: TSignature - methodArgs: TSignature extends undefined ? undefined : Array -} & AppClientCallCoreParams & CoreAppCallArgs +export type CalculatorNonVoidMethodSignatures = keyof CalculatorTypes['methods'] extends infer T ? T extends keyof CalculatorTypes['methods'] ? MethodReturn extends void ? never : T : never : never /** - * Defines the arguments required for a bare call + * Defines an object containing all relevant parameters for a single call to the contract. */ -export type BareCallArgs = Omit +export type CallParams = Expand< + Omit & + { + /** The args for the ABI method call, either as an ordered array or an object */ + args: Expand + } +> /** - * Maps a method signature from the Calculator smart contract to the method's arguments in either tuple of struct form + * Maps a method signature from the Calculator smart contract to the method's arguments in either tuple or struct form */ -export type MethodArgs = Calculator['methods'][TSignature]['argsObj' | 'argsTuple'] +export type MethodArgs = CalculatorTypes['methods'][TSignature]['argsObj' | 'argsTuple'] /** * Maps a method signature from the Calculator smart contract to the method's return type */ -export type MethodReturn = Calculator['methods'][TSignature]['returns'] +export type MethodReturn = CalculatorTypes['methods'][TSignature]['returns'] + /** - * A factory for available 'create' calls - */ -export type CalculatorCreateCalls = (typeof CalculatorCallFactory)['create'] -/** - * Defines supported create methods for this smart contract + * Defines supported create method params for this smart contract */ export type CalculatorCreateCallParams = - | (TypedCallParams<'createApplication()void'> & (OnCompleteNoOp)) + | Expand & {method: 'createApplication'} & {onComplete?: OnApplicationComplete.NoOpOC} & CreateSchema> + | Expand & {method: 'createApplication()void'} & {onComplete?: OnApplicationComplete.NoOpOC} & CreateSchema> /** * Defines arguments required for the deploy method. */ -export type CalculatorDeployArgs = { - deployTimeParams?: TealTemplateParams +export type CalculatorDeployParams = Expand & { /** - * A delegate which takes a create call factory and returns the create call params for this smart contract + * Create transaction parameters to use if a create needs to be issued as part of deployment; use `method` to define ABI call (if available) or leave out for a bare call (if available) */ - createCall?: (callFactory: CalculatorCreateCalls) => CalculatorCreateCallParams -} + createParams?: CalculatorCreateCallParams +}> /** - * Exposes methods for constructing all available smart contract calls + * Exposes methods for constructing `AppClient` params objects for ABI calls to the Calculator smart contract */ -export abstract class CalculatorCallFactory { +export abstract class CalculatorParamsFactory { /** - * Gets available create call factories + * Gets available create ABI call param factories */ static get create() { return { + _resolveByMethod(params: TParams) { + switch(params.method) { + case 'createApplication': + case 'createApplication()void': + return CalculatorParamsFactory.create.createApplication(params) + } + throw new Error(`Unknown ' + verb + ' method`) + }, + /** - * Constructs a create call for the Calculator smart contract using the createApplication()void ABI method + * Constructs create ABI call params for the Calculator smart contract using the createApplication()void ABI method * - * @param args Any args for the contract call - * @param params Any additional parameters for the call - * @returns A TypedCallParams object for the call + * @param params Parameters for the call + * @returns An `AppClientMethodCallParams` object for the call */ - createApplication(args: MethodArgs<'createApplication()void'>, params: AppClientCallCoreParams & CoreAppCallArgs & AppClientCompilationParams & (OnCompleteNoOp) = {}) { + createApplication(params: CallParams & AppClientCompilationParams & {onComplete?: OnApplicationComplete.NoOpOC}): AppClientMethodCallParams & AppClientCompilationParams & {onComplete?: OnApplicationComplete.NoOpOC} { return { - method: 'createApplication()void' as const, - methodArgs: Array.isArray(args) ? args : [], ...params, + method: 'createApplication()void' as const, + args: Array.isArray(params.args) ? params.args : [], } }, } @@ -320,87 +227,96 @@ export abstract class CalculatorCallFactory { * * A method that takes two numbers and does either addition or subtraction * - * @param args Any args for the contract call - * @param params Any additional parameters for the call - * @returns A TypedCallParams object for the call + * @param params Parameters for the call + * @returns An `AppClientMethodCallParams` object for the call */ - static doMath(args: MethodArgs<'doMath(uint64,uint64,string)uint64'>, params: AppClientCallCoreParams & CoreAppCallArgs) { + static doMath(params: CallParams & CallOnComplete): AppClientMethodCallParams & CallOnComplete { return { - method: 'doMath(uint64,uint64,string)uint64' as const, - methodArgs: Array.isArray(args) ? args : [args.a, args.b, args.operation], ...params, + method: 'doMath(uint64,uint64,string)uint64' as const, + args: Array.isArray(params.args) ? params.args : [params.args.a, params.args.b, params.args.operation], } } /** * Constructs a no op call for the hello(string)string ABI method * - * A demonstration method used in the AlgoKit fullstack template. -Greets the user by name. + * A demonstration method used in the AlgoKit fullstack template. + Greets the user by name. + * - * @param args Any args for the contract call - * @param params Any additional parameters for the call - * @returns A TypedCallParams object for the call + * @param params Parameters for the call + * @returns An `AppClientMethodCallParams` object for the call */ - static hello(args: MethodArgs<'hello(string)string'>, params: AppClientCallCoreParams & CoreAppCallArgs) { + static hello(params: CallParams & CallOnComplete): AppClientMethodCallParams & CallOnComplete { return { - method: 'hello(string)string' as const, - methodArgs: Array.isArray(args) ? args : [args.name], ...params, + method: 'hello(string)string' as const, + args: Array.isArray(params.args) ? params.args : [params.args.name], } } } /** - * A client to make calls to the Calculator smart contract + * A factory to create and deploy one or more instance of the Calculator smart contract and to create one or more app clients to interact with those (or other) app instances */ -export class CalculatorClient { +export class CalculatorFactory { /** - * The underlying `ApplicationClient` for when you want to have more flexibility + * The underlying `AppFactory` for when you want to have more flexibility */ - public readonly appClient: ApplicationClient - - private readonly sender: SendTransactionFrom | undefined + public readonly appFactory: AppFactory /** - * Creates a new instance of `CalculatorClient` + * Creates a new instance of `CalculatorFactory` * - * @param appDetails appDetails The details to identify the app to deploy - * @param algod An algod client instance + * @param params The parameters to initialise the app factory with */ - constructor(appDetails: AppDetails, private algod: Algodv2) { - this.sender = appDetails.sender - this.appClient = algokit.getAppClient({ - ...appDetails, - app: APP_SPEC - }, algod) + constructor(params: Omit) { + this.appFactory = new AppFactory({ + ...params, + appSpec: APP_SPEC, + }) } - + + /** The name of the app (from the ARC-32 / ARC-56 app spec or override). */ + public get appName() { + return this.appFactory.appName + } + + /** The ARC-56 app spec being used */ + get appSpec() { + return APP_SPEC + } + + /** A reference to the underlying `AlgorandClient` this app factory is using. */ + public get algorand(): AlgorandClientInterface { + return this.appFactory.algorand + } + /** - * Checks for decode errors on the AppCallTransactionResult and maps the return value to the specified generic type + * Returns a new `AppClient` client for an app instance of the given ID. * - * @param result The AppCallTransactionResult to be mapped - * @param returnValueFormatter An optional delegate to format the return value if required - * @returns The smart contract response with an updated return value + * Automatically populates appName, defaultSender and source maps from the factory + * if not specified in the params. + * @param params The parameters to create the app client + * @returns The `AppClient` */ - protected mapReturnValue(result: AppCallTransactionResult, returnValueFormatter?: (value: any) => TReturn): AppCallTransactionResultOfType & TResult { - if(result.return?.decodeError) { - throw result.return.decodeError - } - const returnValue = result.return?.returnValue !== undefined && returnValueFormatter !== undefined - ? returnValueFormatter(result.return.returnValue) - : result.return?.returnValue as TReturn | undefined - return { ...result, return: returnValue } as AppCallTransactionResultOfType & TResult + public getAppClientById(params: AppFactoryAppClientParams) { + return new CalculatorClient(this.appFactory.getAppClientById(params)) } - + /** - * Calls the ABI method with the matching signature using an onCompletion code of NO_OP + * Returns a new `AppClient` client, resolving the app by creator address and name + * using AlgoKit app deployment semantics (i.e. looking for the app creation transaction note). * - * @param typedCallParams An object containing the method signature, args, and any other relevant parameters - * @param returnValueFormatter An optional delegate which when provided will be used to map non-undefined return values to the target type - * @returns The result of the smart contract call + * Automatically populates appName, defaultSender and source maps from the factory + * if not specified in the params. + * @param params The parameters to create the app client + * @returns The `AppClient` */ - public async call(typedCallParams: TypedCallParams, returnValueFormatter?: (value: any) => MethodReturn) { - return this.mapReturnValue>(await this.appClient.call(typedCallParams), returnValueFormatter) + public async getAppClientByCreatorAndName( + params: AppFactoryResolveAppClientByCreatorAndNameParams, + ) { + return new CalculatorClient(await this.appFactory.getAppClientByCreatorAndName(params)) } /** @@ -409,114 +325,357 @@ export class CalculatorClient { * @param params The arguments for the contract calls and any additional parameters for the call * @returns The deployment result */ - public deploy(params: CalculatorDeployArgs & AppClientDeployCoreParams & IncludeSchema = {}): ReturnType { - const createArgs = params.createCall?.(CalculatorCallFactory.create) - return this.appClient.deploy({ + public async deploy(params: CalculatorDeployParams = {}) { + const result = await this.appFactory.deploy({ ...params, - createArgs, - createOnCompleteAction: createArgs?.onCompleteAction, + createParams: params.createParams?.method ? CalculatorParamsFactory.create._resolveByMethod(params.createParams) : params.createParams, }) + return { result: result.result, appClient: new CalculatorClient(result.appClient) } } /** - * Gets available create methods + * Get parameters to create transactions (create and deploy related calls) for the current app. A good mental model for this is that these parameters represent a deferred transaction creation. */ - public get create() { - const $this = this - return { + readonly params = { + /** + * Gets available create methods + */ + create: { /** * Creates a new instance of the Calculator smart contract using the createApplication()void ABI method. * - * @param args The arguments for the smart contract call - * @param params Any additional parameters for the call - * @returns The create result + * @param params The params for the smart contract call + * @returns The create params */ - async createApplication(args: MethodArgs<'createApplication()void'>, params: AppClientCallCoreParams & AppClientCompilationParams & IncludeSchema & CoreAppCallArgs & (OnCompleteNoOp) = {}) { - return $this.mapReturnValue, AppCreateCallTransactionResult>(await $this.appClient.create(CalculatorCallFactory.create.createApplication(args, params))) + createApplication: (params: CallParams & AppClientCompilationParams & CreateSchema & {onComplete?: OnApplicationComplete.NoOpOC} = {args: []}) => { + return this.appFactory.params.create(CalculatorParamsFactory.create.createApplication(params)) }, - } + }, + } /** - * Makes a clear_state call to an existing instance of the Calculator smart contract. - * - * @param args The arguments for the bare call - * @returns The clear_state result + * Create transactions for the current app */ - public clearState(args: BareCallArgs & AppClientCallCoreParams & CoreAppCallArgs = {}) { - return this.appClient.clearState(args) + readonly createTransaction = { + /** + * Gets available create methods + */ + create: { + /** + * Creates a new instance of the Calculator smart contract using the createApplication()void ABI method. + * + * @param params The params for the smart contract call + * @returns The create params + */ + createApplication: (params: CallParams & AppClientCompilationParams & CreateSchema & {onComplete?: OnApplicationComplete.NoOpOC} = {args: []}) => { + return this.appFactory.params.create(CalculatorParamsFactory.create.createApplication(params)) + }, + }, + } /** - * Calls the doMath(uint64,uint64,string)uint64 ABI method. + * Send calls to the current app + */ + readonly send = { + /** + * Gets available create methods + */ + create: { + /** + * Creates a new instance of the Calculator smart contract using an ABI method call using the createApplication()void ABI method. + * + * @param params The params for the smart contract call + * @returns The create result + */ + createApplication: async (params: CallParams & AppClientCompilationParams & CreateSchema & SendParams & {onComplete?: OnApplicationComplete.NoOpOC} = {args: []}) => { + const result = await this.appFactory.send.create(CalculatorParamsFactory.create.createApplication(params)) + return { result: { ...result.result, return: result.result.return as undefined | CalculatorReturns['createApplication()void'] }, appClient: new CalculatorClient(result.appClient) } + }, + }, + + } + +} +/** + * A client to make calls to the Calculator smart contract + */ +export class CalculatorClient { + /** + * The underlying `AppClient` for when you want to have more flexibility + */ + public readonly appClient: AppClient + + /** + * Creates a new instance of `CalculatorClient` * - * A method that takes two numbers and does either addition or subtraction + * @param appClient An `AppClient` instance which has been created with the Calculator app spec + */ + constructor(appClient: AppClient) + /** + * Creates a new instance of `CalculatorClient` * - * @param args The arguments for the contract call - * @param params Any additional parameters for the call - * @returns The result of the call: The result of the operation + * @param params The parameters to initialise the app client with */ - public doMath(args: MethodArgs<'doMath(uint64,uint64,string)uint64'>, params: AppClientCallCoreParams & CoreAppCallArgs = {}) { - return this.call(CalculatorCallFactory.doMath(args, params)) + constructor(params: Omit) + constructor(appClientOrParams: AppClient | Omit) { + this.appClient = appClientOrParams instanceof AppClient ? appClientOrParams : new AppClient({ + ...appClientOrParams, + appSpec: APP_SPEC, + }) } - + /** - * Calls the hello(string)string ABI method. + * Checks for decode errors on the given return value and maps the return value to the return type for the given method + * @returns The typed return value or undefined if there was no value + */ + decodeReturnValue(method: TSignature, returnValue: ABIReturn | undefined) { + return returnValue !== undefined ? getArc56ReturnValue>(returnValue, this.appClient.getABIMethod(method), APP_SPEC.structs) : undefined + } + + /** + * Returns a new `CalculatorClient` client, resolving the app by creator address and name + * using AlgoKit app deployment semantics (i.e. looking for the app creation transaction note). + * @param params The parameters to create the app client + */ + public static async fromCreatorAndName(params: Omit): Promise { + return new CalculatorClient(await AppClient.fromCreatorAndName({...params, appSpec: APP_SPEC})) + } + + /** + * Returns an `CalculatorClient` instance for the current network based on + * pre-determined network-specific app IDs specified in the ARC-56 app spec. * - * A demonstration method used in the AlgoKit fullstack template. -Greets the user by name. + * If no IDs are in the app spec or the network isn't recognised, an error is thrown. + * @param params The parameters to create the app client + */ + static async fromNetwork( + params: Omit + ): Promise { + return new CalculatorClient(await AppClient.fromNetwork({...params, appSpec: APP_SPEC})) + } + + /** The ID of the app instance this client is linked to. */ + public get appId() { + return this.appClient.appId + } + + /** The app address of the app instance this client is linked to. */ + public get appAddress() { + return this.appClient.appAddress + } + + /** The name of the app. */ + public get appName() { + return this.appClient.appName + } + + /** The ARC-56 app spec being used */ + public get appSpec() { + return this.appClient.appSpec + } + + /** A reference to the underlying `AlgorandClient` this app client is using. */ + public get algorand(): AlgorandClientInterface { + return this.appClient.algorand + } + + /** + * Get parameters to create transactions for the current app. A good mental model for this is that these parameters represent a deferred transaction creation. + */ + readonly params = { + /** + * Makes a clear_state call to an existing instance of the Calculator smart contract. + * + * @param params The params for the bare (raw) call + * @returns The clearState result + */ + clearState: (params?: Expand) => { + return this.appClient.params.bare.clearState(params) + }, + + /** + * Makes a call to the Calculator smart contract using the `doMath(uint64,uint64,string)uint64` ABI method. + * + * A method that takes two numbers and does either addition or subtraction + * + * @param params The params for the smart contract call + * @returns The call params: The result of the operation + */ + doMath: (params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + return this.appClient.params.call(CalculatorParamsFactory.doMath(params)) + }, + + /** + * Makes a call to the Calculator smart contract using the `hello(string)string` ABI method. + * + * A demonstration method used in the AlgoKit fullstack template. + Greets the user by name. + + * + * @param params The params for the smart contract call + * @returns The call params: A greeting message to the user. + */ + hello: (params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + return this.appClient.params.call(CalculatorParamsFactory.hello(params)) + }, + + } + + /** + * Create transactions for the current app + */ + readonly createTransaction = { + /** + * Makes a clear_state call to an existing instance of the Calculator smart contract. + * + * @param params The params for the bare (raw) call + * @returns The clearState result + */ + clearState: (params?: Expand) => { + return this.appClient.createTransaction.bare.clearState(params) + }, + + /** + * Makes a call to the Calculator smart contract using the `doMath(uint64,uint64,string)uint64` ABI method. + * + * A method that takes two numbers and does either addition or subtraction + * + * @param params The params for the smart contract call + * @returns The call transaction: The result of the operation + */ + doMath: (params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + return this.appClient.createTransaction.call(CalculatorParamsFactory.doMath(params)) + }, + + /** + * Makes a call to the Calculator smart contract using the `hello(string)string` ABI method. + * + * A demonstration method used in the AlgoKit fullstack template. + Greets the user by name. + + * + * @param params The params for the smart contract call + * @returns The call transaction: A greeting message to the user. + */ + hello: (params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + return this.appClient.createTransaction.call(CalculatorParamsFactory.hello(params)) + }, + + } + + /** + * Send calls to the current app + */ + readonly send = { + /** + * Makes a clear_state call to an existing instance of the Calculator smart contract. + * + * @param params The params for the bare (raw) call + * @returns The clearState result + */ + clearState: (params?: Expand) => { + return this.appClient.send.bare.clearState(params) + }, + + /** + * Makes a call to the Calculator smart contract using the `doMath(uint64,uint64,string)uint64` ABI method. + * + * A method that takes two numbers and does either addition or subtraction + * + * @param params The params for the smart contract call + * @returns The call result: The result of the operation + */ + doMath: async (params: CallParams & SendParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + const result = await this.appClient.send.call(CalculatorParamsFactory.doMath(params)) + return {...result, return: result.return as undefined | CalculatorReturns['doMath(uint64,uint64,string)uint64']} + }, + + /** + * Makes a call to the Calculator smart contract using the `hello(string)string` ABI method. + * + * A demonstration method used in the AlgoKit fullstack template. + Greets the user by name. + + * + * @param params The params for the smart contract call + * @returns The call result: A greeting message to the user. + */ + hello: async (params: CallParams & SendParams & {onComplete?: OnApplicationComplete.NoOpOC}) => { + const result = await this.appClient.send.call(CalculatorParamsFactory.hello(params)) + return {...result, return: result.return as undefined | CalculatorReturns['hello(string)string']} + }, + + } + + /** + * Clone this app client with different params * - * @param args The arguments for the contract call - * @param params Any additional parameters for the call - * @returns The result of the call: A greeting message to the user. + * @param params The params to use for the the cloned app client. Omit a param to keep the original value. Set a param to override the original value. Setting to undefined will clear the original value. + * @returns A new app client with the altered params */ - public hello(args: MethodArgs<'hello(string)string'>, params: AppClientCallCoreParams & CoreAppCallArgs = {}) { - return this.call(CalculatorCallFactory.hello(args, params)) + public clone(params: CloneAppClientParams) { + return new CalculatorClient(this.appClient.clone(params)) } - public compose(): CalculatorComposer { + /** + * Methods to access state for the current Calculator app + */ + state = { + } + + public newGroup(): CalculatorComposer { const client = this - const atc = new AtomicTransactionComposer() + const composer = this.algorand.newGroup() let promiseChain:Promise = Promise.resolve() - const resultMappers: Array any)> = [] + const resultMappers: Array any)> = [] return { - doMath(args: MethodArgs<'doMath(uint64,uint64,string)uint64'>, params?: AppClientComposeCallCoreParams & CoreAppCallArgs) { - promiseChain = promiseChain.then(() => client.doMath(args, {...params, sendParams: {...params?.sendParams, skipSending: true, atc}})) - resultMappers.push(undefined) + /** + * Add a doMath(uint64,uint64,string)uint64 method call against the Calculator contract + */ + doMath(params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) { + promiseChain = promiseChain.then(async () => composer.addAppCallMethodCall(await client.params.doMath(params))) + resultMappers.push((v) => client.decodeReturnValue('doMath(uint64,uint64,string)uint64', v)) return this }, - hello(args: MethodArgs<'hello(string)string'>, params?: AppClientComposeCallCoreParams & CoreAppCallArgs) { - promiseChain = promiseChain.then(() => client.hello(args, {...params, sendParams: {...params?.sendParams, skipSending: true, atc}})) - resultMappers.push(undefined) + /** + * Add a hello(string)string method call against the Calculator contract + */ + hello(params: CallParams & {onComplete?: OnApplicationComplete.NoOpOC}) { + promiseChain = promiseChain.then(async () => composer.addAppCallMethodCall(await client.params.hello(params))) + resultMappers.push((v) => client.decodeReturnValue('hello(string)string', v)) return this }, - clearState(args?: BareCallArgs & AppClientComposeCallCoreParams & CoreAppCallArgs) { - promiseChain = promiseChain.then(() => client.clearState({...args, sendParams: {...args?.sendParams, skipSending: true, atc}})) - resultMappers.push(undefined) + /** + * Add a clear state call to the Calculator contract + */ + clearState(params: AppClientBareCallParams) { + promiseChain = promiseChain.then(() => composer.addAppCall(client.params.clearState(params))) return this }, - addTransaction(txn: TransactionWithSigner | TransactionToSign | Transaction | Promise, defaultSender?: SendTransactionFrom) { - promiseChain = promiseChain.then(async () => atc.addTransaction(await algokit.getTransactionWithSigner(txn, defaultSender ?? client.sender))) + addTransaction(txn: Transaction, signer?: TransactionSigner) { + promiseChain = promiseChain.then(() => composer.addTransaction(txn, signer)) return this }, - async atc() { + async composer() { await promiseChain - return atc + return composer }, async simulate(options?: SimulateOptions) { await promiseChain - const result = await atc.simulate(client.algod, new modelsv2.SimulateRequest({ txnGroups: [], ...options })) + const result = await composer.simulate(options) return { ...result, - returns: result.methodResults?.map((val, i) => resultMappers[i] !== undefined ? resultMappers[i]!(val.returnValue) : val.returnValue) + returns: result.returns?.map((val, i) => resultMappers[i] !== undefined ? resultMappers[i]!(val) : val.returnValue) } }, - async execute(sendParams?: AppClientComposeExecuteParams) { + async send(params?: SendParams) { await promiseChain - const result = await algokit.sendAtomicTransactionComposer({ atc, sendParams }, client.algod) + const result = await composer.send(params) return { ...result, - returns: result.returns?.map((val, i) => resultMappers[i] !== undefined ? resultMappers[i]!(val.returnValue) : val.returnValue) + returns: result.returns?.map((val, i) => resultMappers[i] !== undefined ? resultMappers[i]!(val) : val.returnValue) } } } as unknown as CalculatorComposer @@ -532,19 +691,20 @@ export type CalculatorComposer = { * @param params Any additional parameters for the call * @returns The typed transaction composer so you can fluently chain multiple calls or call execute to execute all queued up transactions */ - doMath(args: MethodArgs<'doMath(uint64,uint64,string)uint64'>, params?: AppClientComposeCallCoreParams & CoreAppCallArgs): CalculatorComposer<[...TReturns, MethodReturn<'doMath(uint64,uint64,string)uint64'>]> + doMath(params?: CallParams): CalculatorComposer<[...TReturns, CalculatorReturns['doMath(uint64,uint64,string)uint64'] | undefined]> /** * Calls the hello(string)string ABI method. * - * A demonstration method used in the AlgoKit fullstack template. -Greets the user by name. + * A demonstration method used in the AlgoKit fullstack template. + Greets the user by name. + * * @param args The arguments for the contract call * @param params Any additional parameters for the call * @returns The typed transaction composer so you can fluently chain multiple calls or call execute to execute all queued up transactions */ - hello(args: MethodArgs<'hello(string)string'>, params?: AppClientComposeCallCoreParams & CoreAppCallArgs): CalculatorComposer<[...TReturns, MethodReturn<'hello(string)string'>]> + hello(params?: CallParams): CalculatorComposer<[...TReturns, CalculatorReturns['hello(string)string'] | undefined]> /** * Makes a clear_state call to an existing instance of the Calculator smart contract. @@ -552,37 +712,29 @@ Greets the user by name. * @param args The arguments for the bare call * @returns The typed transaction composer so you can fluently chain multiple calls or call execute to execute all queued up transactions */ - clearState(args?: BareCallArgs & AppClientComposeCallCoreParams & CoreAppCallArgs): CalculatorComposer<[...TReturns, undefined]> + clearState(params?: AppClientBareCallParams): CalculatorComposer<[...TReturns, undefined]> /** * Adds a transaction to the composer * - * @param txn One of: A TransactionWithSigner object (returned as is), a TransactionToSign object (signer is obtained from the signer property), a Transaction object (signer is extracted from the defaultSender parameter), an async SendTransactionResult returned by one of algokit utils helpers (signer is obtained from the defaultSender parameter) - * @param defaultSender The default sender to be used to obtain a signer where the object provided to the transaction parameter does not include a signer. + * @param txn A transaction to add to the transaction group + * @param signer The optional signer to use when signing this transaction. */ - addTransaction(txn: TransactionWithSigner | TransactionToSign | Transaction | Promise, defaultSender?: SendTransactionFrom): CalculatorComposer + addTransaction(txn: Transaction, signer?: TransactionSigner): CalculatorComposer /** * Returns the underlying AtomicTransactionComposer instance */ - atc(): Promise + composer(): TransactionComposer /** * Simulates the transaction group and returns the result */ - simulate(options?: SimulateOptions): Promise> + simulate(options?: SimulateOptions): Promise & { simulateResponse: SimulateResponse }> /** - * Executes the transaction group and returns the results + * Sends the transaction group to the network and returns the results */ - execute(sendParams?: AppClientComposeExecuteParams): Promise> -} -export type SimulateOptions = Omit[0], 'txnGroups'> -export type CalculatorComposerSimulateResult = { - returns: TReturns - methodResults: ABIResult[] - simulateResponse: modelsv2.SimulateResponse + send(params?: SendParams): Promise> } -export type CalculatorComposerResults = { +export type CalculatorComposerResults = Expand + diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/vite.config.ts b/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/vite.config.ts index 36f7f4e..e42dc7c 100644 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/vite.config.ts +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-frontend/vite.config.ts @@ -1,7 +1,15 @@ import react from '@vitejs/plugin-react' import { defineConfig } from 'vite' +import { nodePolyfills } from 'vite-plugin-node-polyfills' // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react()], + plugins: [ + react(), + nodePolyfills({ + globals: { + Buffer: true, + }, + }), + ], }) diff --git a/template_content/inject_content/AppCalls.tsx.jinja b/template_content/inject_content/AppCalls.tsx.jinja index 75dfa3d..b2a3f43 100644 --- a/template_content/inject_content/AppCalls.tsx.jinja +++ b/template_content/inject_content/AppCalls.tsx.jinja @@ -1,22 +1,17 @@ -import * as algokit from '@algorandfoundation/algokit-utils' -import { TransactionSignerAccount } from '@algorandfoundation/algokit-utils/types/account' import { useWallet } from '@txnlab/use-wallet' import { useSnackbar } from 'notistack' import { useState } from 'react' {%- if contract_template == 'beaker' %} -import { AppDetails } from '@algorandfoundation/algokit-utils/types/app-client' -import { {{ contract_name.split('_')|map('capitalize')|join }}Client } from '../contracts/{{ contract_name }}' +import { {{ contract_name.split('_')|map('capitalize')|join }}Factory } from '../contracts/{{ contract_name }}' import { OnSchemaBreak, OnUpdate } from '@algorandfoundation/algokit-utils/types/app' -import { getAlgodConfigFromViteEnvironment, getIndexerConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' {%- elif contract_template == 'tealscript' %} -import { {{ contract_name }}Client } from '../contracts/{{ contract_name }}' -import { getAlgodConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' +import { {{ contract_name }}Factory } from '../contracts/{{ contract_name }}' {%- else %} -import { AppDetails } from '@algorandfoundation/algokit-utils/types/app-client' -import { {{ contract_name.split('_')|map('capitalize')|join }}Client } from '../contracts/{{ contract_name.split('_')|map('capitalize')|join }}' +import { {{ contract_name.split('_')|map('capitalize')|join }}Factory } from '../contracts/{{ contract_name.split('_')|map('capitalize')|join }}' import { OnSchemaBreak, OnUpdate } from '@algorandfoundation/algokit-utils/types/app' -import { getAlgodConfigFromViteEnvironment, getIndexerConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' {%- endif %} +import { getAlgodConfigFromViteEnvironment, getIndexerConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' +import { AlgorandClient } from '@algorandfoundation/algokit-utils' interface AppCallsInterface { openModal: boolean @@ -26,25 +21,16 @@ interface AppCallsInterface { const AppCalls = ({ openModal, setModalState }: AppCallsInterface) => { const [loading, setLoading] = useState(false) const [contractInput, setContractInput] = useState('') + const { enqueueSnackbar } = useSnackbar() + const { signer, activeAddress } = useWallet() const algodConfig = getAlgodConfigFromViteEnvironment() - const algodClient = algokit.getAlgoClient({ - server: algodConfig.server, - port: algodConfig.port, - token: algodConfig.token, - }) - - {%- if contract_template != 'tealscript' %} const indexerConfig = getIndexerConfigFromViteEnvironment() - const indexer = algokit.getAlgoIndexerClient({ - server: indexerConfig.server, - port: indexerConfig.port, - token: indexerConfig.token, + const algorand = AlgorandClient.fromConfig({ + algodConfig, + indexerConfig, }) - {%- endif %} - - const { enqueueSnackbar } = useSnackbar() - const { signer, activeAddress } = useWallet() + algorand.setDefaultSigner(signer) const sendAppCall = async () => { setLoading(true) @@ -55,67 +41,67 @@ const AppCalls = ({ openModal, setModalState }: AppCallsInterface) => { // Given the simplicity of the starter contract, we are deploying it on the frontend // for demonstration purposes. {%- if contract_template == 'tealscript'%} - const appClient = new {{ contract_name }}Client( - { - sender: { signer, addr: activeAddress } as TransactionSignerAccount, - resolveBy: 'id', - id: 0, - }, - algodClient, - ) - await appClient.create.createApplication({}).catch((e: Error) => { + const factory = new {{ contract_name }}Factory({ + defaultSender: activeAddress, + algorand, + }) + const deployResult = await factory.send.create.createApplication().catch((e: Error) => { enqueueSnackbar(`Error deploying the contract: ${e.message}`, { variant: 'error' }) setLoading(false) - return + return undefined }) {%- elif preset_name == 'starter' or contract_template != 'beaker' %} - const appDetails = { - resolveBy: 'creatorAndName', - sender: { signer, addr: activeAddress } as TransactionSignerAccount, - creatorAddress: activeAddress, - findExistingUsing: indexer, - } as AppDetails - - const appClient = new {{ contract_name.split('_')|map('capitalize')|join }}Client(appDetails, algodClient) - const deployParams = { - onSchemaBreak: OnSchemaBreak.AppendApp, - onUpdate: OnUpdate.AppendApp, - } - await appClient.deploy(deployParams).catch((e: Error) => { - enqueueSnackbar(`Error deploying the contract: ${e.message}`, { variant: 'error' }) - setLoading(false) - return + const factory = new {{ contract_name.split('_')|map('capitalize')|join }}Factory({ + defaultSender: activeAddress, + algorand, }) + const deployResult = await factory + .deploy({ + onSchemaBreak: OnSchemaBreak.AppendApp, + onUpdate: OnUpdate.AppendApp, + }) + .catch((e: Error) => { + enqueueSnackbar(`Error deploying the contract: ${e.message}`, { variant: 'error' }) + setLoading(false) + return undefined + }) {%- elif preset_name == 'production' and contract_template == 'beaker' %} - const appDetails = { - resolveBy: 'creatorAndName', - sender: { signer, addr: activeAddress } as TransactionSignerAccount, - creatorAddress: activeAddress, - findExistingUsing: indexer, - } as AppDetails - - const appClient = new {{ contract_name.split('_')|map('capitalize')|join }}Client(appDetails, algodClient) const isLocal = await algokit.isLocalNet(algodClient) - const deployParams: Parameters[0] = { - allowDelete: isLocal, - allowUpdate: isLocal, - onSchemaBreak: isLocal ? OnSchemaBreak.ReplaceApp : OnSchemaBreak.Fail, - onUpdate: isLocal ? OnUpdate.UpdateApp : OnUpdate.Fail, - } - await appClient.deploy(deployParams).catch((e: Error) => { - enqueueSnackbar(`Error deploying the contract: ${e.message}`, { variant: 'error' }) - setLoading(false) - return + const factory = new {{ contract_name.split('_')|map('capitalize')|join }}Factory({ + defaultSender: activeAddress, + algorand, }) + const deployResult = await factory + .deploy({ + deletable: isLocal, + updatable: isLocal, + onSchemaBreak: isLocal ? OnSchemaBreak.ReplaceApp : OnSchemaBreak.Fail, + onUpdate: isLocal ? OnUpdate.UpdateApp : OnUpdate.Fail, + }) + .catch((e: Error) => { + enqueueSnackbar(`Error deploying the contract: ${e.message}`, { variant: 'error' }) + setLoading(false) + return undefined + }) {%- endif %} - const response = await appClient.hello({ name: contractInput }).catch((e: Error) => { + if (!deployResult) { + return + } + + const { appClient } = deployResult + + const response = await appClient.send.hello({ args: { name: contractInput } }).catch((e: Error) => { enqueueSnackbar(`Error calling the contract: ${e.message}`, { variant: 'error' }) setLoading(false) - return + return undefined }) - enqueueSnackbar(`Response from the contract: ${response?.return}`, { variant: 'success' }) + if (!response) { + return + } + + enqueueSnackbar(`Response from the contract: ${response.return}`, { variant: 'success' }) setLoading(false) }