From ae3e804b27a38221c25e73ed77d264f539bb2a83 Mon Sep 17 00:00:00 2001 From: Altynbek Orumbayev Date: Wed, 21 Feb 2024 22:59:11 +0100 Subject: [PATCH] chore: fix tests --- .../src/components/AppCalls.tsx | 11 +- .../.algokit.toml | 7 +- .../{{ contract_name }}/contract.py.j2 | 33 +++- .../{{ contract_name }}/deploy_config.py.j2 | 25 ++- .../.copier-answers.yml | 4 +- .../Build_Beaker_application.xml | 35 ++++ .../Build_Beaker_application____LocalNet.xml | 37 ++++ .../Build___Deploy_Beaker_application.xml | 38 ++++ .../Deploy_Built_Beaker_application.xml | 36 ++++ .../Reset_AlgoKit_LocalNet.xml | 17 ++ .../Start_AlgoKit_LocalNet.xml | 17 ++ .../Stop_AlgoKit_LocalNet.xml | 17 ++ ...ing-started-with-your-algokit-project.tour | 56 ++++++ .../.vscode/extensions.json | 1 + .../.vscode/launch.json | 2 +- .../README.md | 25 +-- .../pyproject.toml | 7 +- .../smart_contracts/__main__.py | 14 +- .../smart_contracts/config.py | 37 ++-- .../smart_contracts/hello_world/contract.py | 29 ++- .../hello_world/deploy_config.py | 15 +- .../smart_contracts/helpers/build.py | 37 ++-- .../tests/hello_world_test.py | 6 +- .../.copier-answers.yml | 1 - ...production_tealscript_react.code-workspace | 45 +---- .../src/components/AppCalls.tsx | 2 +- .../.algokit.toml | 19 -- .../generators/create_contract/copier.yaml | 10 - .../{{ contract_name }}/contract.py.j2 | 7 - .../{{ contract_name }}/deploy_config.py.j2 | 35 ---- .../.copier-answers.yml | 10 - .../.devcontainer.json | 16 ++ .../.editorconfig | 10 - .../.env.localnet.template | 7 - .../.env.template | 1 - .../.env.testnet.template | 3 - .../.eslintrc.js | 56 ++++++ .../.gitattributes | 1 - .../.gitignore | 181 +----------------- .../.prettierrc.toml | 6 + .../.vscode/extensions.json | 14 +- .../.vscode/launch.json | 38 ---- .../.vscode/settings.json | 48 +---- .../.vscode/tasks.json | 79 -------- .../README.md | 67 ++----- ...duction_tealscript_react-contracts.test.ts | 42 ++++ .../artifacts/components/.gitkeep} | 0 .../clients/.gitkeep} | 0 ...duction_tealscript_react-contracts.algo.ts | 47 +++++ .../jest.config.js | 6 + .../package.json | 36 ++++ .../poetry.toml | 2 - .../pyproject.toml | 49 ----- .../smart_contracts/README.md | 9 - .../smart_contracts/__main__.py | 51 ----- .../smart_contracts/config.py | 61 ------ .../smart_contracts/hello_world/contract.py | 7 - .../hello_world/deploy_config.py | 36 ---- .../smart_contracts/helpers/build.py | 56 ------ .../smart_contracts/helpers/deploy.py | 50 ----- .../tests/conftest.py | 32 ---- .../tests/hello_world_test.py | 48 ----- .../tsconfig.json | 103 ++++++++++ .../starter_beaker_react/.copier-answers.yml | 2 +- .../src/components/AppCalls.tsx | 2 +- .../.algokit.toml | 8 +- .../{{ contract_name }}/contract.py.j2 | 33 +++- .../{{ contract_name }}/deploy-config.ts.j2 | 59 ++++++ .../{{ contract_name }}/deploy_config.py.j2 | 35 ---- .../.copier-answers.yml | 6 +- .../.prettierignore | 12 ++ .../.prettierrc.js | 10 + ...ing-started-with-your-algokit-project.tour | 56 ++++++ .../.vscode/extensions.json | 5 +- .../.vscode/launch.json | 23 ++- .../.vscode/settings.json | 23 +-- .../starter_beaker_react-contracts/README.md | 29 +-- .../package.json | 24 +++ .../pyproject.toml | 30 +-- .../smart_contracts/README.md | 3 +- .../smart_contracts/__main__.py | 25 +-- .../smart_contracts/config.py | 37 ++-- .../smart_contracts/hello_world/contract.py | 13 +- .../hello_world/deploy-config.ts | 49 +++++ .../hello_world/deploy_config.py | 36 ---- .../smart_contracts/helpers/build.py | 39 ++-- .../smart_contracts/helpers/deploy.py | 50 ----- .../smart_contracts/index.ts | 47 +++++ .../tsconfig.json | 24 +++ .../starter_puya_react/.copier-answers.yml | 2 +- .../.algokit.toml | 2 +- .../{{ contract_name }}/deploy-config.ts.j2 | 50 +++++ .../{{ contract_name }}/deploy_config.py.j2 | 35 ---- .../.copier-answers.yml | 2 +- .../.prettierignore | 12 ++ .../.prettierrc.js | 10 + .../.vscode/extensions.json | 1 + .../.vscode/launch.json | 21 +- .../.vscode/settings.json | 3 + .../starter_puya_react-contracts/README.md | 7 +- .../starter_puya_react-contracts/package.json | 24 +++ .../smart_contracts/README.md | 3 +- .../smart_contracts/__main__.py | 21 +- .../hello_world/deploy-config.ts | 50 +++++ .../hello_world/deploy_config.py | 36 ---- .../smart_contracts/helpers/build.py | 2 +- .../smart_contracts/helpers/deploy.py | 50 ----- .../smart_contracts/index.ts | 47 +++++ .../tsconfig.json | 24 +++ .../.copier-answers.yml | 1 - .../src/components/AppCalls.tsx | 2 +- .../.algokit.toml | 19 -- .../generators/create_contract/copier.yaml | 10 - .../{{ contract_name }}/contract.py.j2 | 7 - .../{{ contract_name }}/deploy_config.py.j2 | 35 ---- .../.copier-answers.yml | 10 - .../.devcontainer.json | 16 ++ .../.editorconfig | 10 - .../.env.localnet.template | 7 - .../.env.template | 1 - .../.env.testnet.template | 3 - .../.eslintrc.js | 56 ++++++ .../.gitattributes | 1 - .../.gitignore | 181 +----------------- .../.prettierrc.toml | 6 + .../.vscode/extensions.json | 14 +- .../.vscode/launch.json | 38 ---- .../.vscode/settings.json | 47 +---- .../.vscode/tasks.json | 79 -------- .../README.md | 66 ++----- ...starter_tealscript_react-contracts.test.ts | 42 ++++ .../contracts/artifacts/components/.gitkeep} | 0 .../clients/.gitkeep} | 0 ...starter_tealscript_react-contracts.algo.ts | 47 +++++ .../jest.config.js | 6 + .../package.json | 36 ++++ .../poetry.toml | 2 - .../pyproject.toml | 44 ----- .../smart_contracts/README.md | 9 - .../smart_contracts/__main__.py | 51 ----- .../smart_contracts/config.py | 61 ------ .../smart_contracts/hello_world/contract.py | 7 - .../hello_world/deploy_config.py | 36 ---- .../smart_contracts/helpers/__init__.py | 0 .../smart_contracts/helpers/build.py | 56 ------ .../smart_contracts/helpers/deploy.py | 50 ----- .../tsconfig.json | 103 ++++++++++ .../starter_tealscript_react.code-workspace | 45 +---- tests/test_templates.py | 23 +-- 149 files changed, 1741 insertions(+), 2365 deletions(-) create mode 100644 examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Build_Beaker_application.xml create mode 100644 examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Build_Beaker_application____LocalNet.xml create mode 100644 examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Build___Deploy_Beaker_application.xml create mode 100644 examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Deploy_Built_Beaker_application.xml create mode 100644 examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Reset_AlgoKit_LocalNet.xml create mode 100644 examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml create mode 100644 examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Stop_AlgoKit_LocalNet.xml create mode 100644 examples/production_beaker_react/projects/production_beaker_react-contracts/.tours/getting-started-with-your-algokit-project.tour delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/.algokit.toml delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/.algokit/generators/create_contract/copier.yaml delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/.copier-answers.yml create mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/.devcontainer.json delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/.editorconfig delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/.env.localnet.template delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/.env.template delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/.env.testnet.template create mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/.eslintrc.js delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/.gitattributes create mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/.prettierrc.toml delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/.vscode/launch.json delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/.vscode/tasks.json create mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/__test__/production_tealscript_react-contracts.test.ts rename examples/production_tealscript_react/projects/production_tealscript_react-contracts/{smart_contracts/__init__.py => contracts/artifacts/components/.gitkeep} (100%) rename examples/production_tealscript_react/projects/production_tealscript_react-contracts/{smart_contracts/helpers/__init__.py => contracts/clients/.gitkeep} (100%) create mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/contracts/production_tealscript_react-contracts.algo.ts create mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/jest.config.js create mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/package.json delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/poetry.toml delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/pyproject.toml delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/README.md delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/__main__.py delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/config.py delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/hello_world/contract.py delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/hello_world/deploy_config.py delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/helpers/build.py delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/helpers/deploy.py delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/tests/conftest.py delete mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/tests/hello_world_test.py create mode 100644 examples/production_tealscript_react/projects/production_tealscript_react-contracts/tsconfig.json create mode 100644 examples/starter_beaker_react/projects/starter_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 delete mode 100644 examples/starter_beaker_react/projects/starter_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 create mode 100644 examples/starter_beaker_react/projects/starter_beaker_react-contracts/.prettierignore create mode 100644 examples/starter_beaker_react/projects/starter_beaker_react-contracts/.prettierrc.js create mode 100644 examples/starter_beaker_react/projects/starter_beaker_react-contracts/.tours/getting-started-with-your-algokit-project.tour create mode 100644 examples/starter_beaker_react/projects/starter_beaker_react-contracts/package.json create mode 100644 examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/hello_world/deploy-config.ts delete mode 100644 examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/hello_world/deploy_config.py delete mode 100644 examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/helpers/deploy.py create mode 100644 examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/index.ts create mode 100644 examples/starter_beaker_react/projects/starter_beaker_react-contracts/tsconfig.json create mode 100644 examples/starter_puya_react/projects/starter_puya_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 delete mode 100644 examples/starter_puya_react/projects/starter_puya_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 create mode 100644 examples/starter_puya_react/projects/starter_puya_react-contracts/.prettierignore create mode 100644 examples/starter_puya_react/projects/starter_puya_react-contracts/.prettierrc.js create mode 100644 examples/starter_puya_react/projects/starter_puya_react-contracts/package.json create mode 100644 examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/hello_world/deploy-config.ts delete mode 100644 examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/hello_world/deploy_config.py delete mode 100644 examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/helpers/deploy.py create mode 100644 examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/index.ts create mode 100644 examples/starter_puya_react/projects/starter_puya_react-contracts/tsconfig.json delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.algokit.toml delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.algokit/generators/create_contract/copier.yaml delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.copier-answers.yml create mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.devcontainer.json delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.editorconfig delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.env.localnet.template delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.env.template delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.env.testnet.template create mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.eslintrc.js delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.gitattributes create mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.prettierrc.toml delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.vscode/launch.json delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.vscode/tasks.json create mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/__test__/starter_tealscript_react-contracts.test.ts rename examples/{production_tealscript_react/projects/production_tealscript_react-contracts/tests/__init__.py => starter_tealscript_react/projects/starter_tealscript_react-contracts/contracts/artifacts/components/.gitkeep} (100%) rename examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/{smart_contracts/__init__.py => contracts/clients/.gitkeep} (100%) create mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/contracts/starter_tealscript_react-contracts.algo.ts create mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/jest.config.js create mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/package.json delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/poetry.toml delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/pyproject.toml delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/README.md delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/__main__.py delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/config.py delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/hello_world/contract.py delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/hello_world/deploy_config.py delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/helpers/__init__.py delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/helpers/build.py delete mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/helpers/deploy.py create mode 100644 examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/tsconfig.json diff --git a/examples/production_beaker_react/projects/production_beaker_react-app/src/components/AppCalls.tsx b/examples/production_beaker_react/projects/production_beaker_react-app/src/components/AppCalls.tsx index 6a3338e..732041c 100644 --- a/examples/production_beaker_react/projects/production_beaker_react-app/src/components/AppCalls.tsx +++ b/examples/production_beaker_react/projects/production_beaker_react-app/src/components/AppCalls.tsx @@ -5,7 +5,7 @@ import { useWallet } from '@txnlab/use-wallet' import { useSnackbar } from 'notistack' import { useState } from 'react' -import { HelloWorldClient } from '../contracts/HelloWorld' +import { HelloWorldClient } from '../contracts/hello_world' import { getAlgodConfigFromViteEnvironment, getIndexerConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' @@ -52,9 +52,12 @@ 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 deployParams = { - onSchemaBreak: 'append', - onUpdate: 'append', + const isLocal = await algokit.isLocalNet(algodClient) + const deployParams: Parameters[0] = { + allowDelete: isLocal, + allowUpdate: isLocal, + onSchemaBreak: isLocal ? 'replace' : 'fail', + onUpdate: isLocal ? 'update' : 'fail', } await appClient.deploy(deployParams).catch((e: Error) => { enqueueSnackbar(`Error deploying the contract: ${e.message}`, { variant: 'error' }) diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/.algokit.toml b/examples/production_beaker_react/projects/production_beaker_react-contracts/.algokit.toml index f355836..e0a8792 100644 --- a/examples/production_beaker_react/projects/production_beaker_react-contracts/.algokit.toml +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/.algokit.toml @@ -1,10 +1,11 @@ [algokit] -min_version = "v1.8.0" +min_version = "v1.10.0" [deploy] command = "poetry run python -m smart_contracts deploy" environment_secrets = [ "DEPLOYER_MNEMONIC", + "DISPENSER_MNEMONIC", ] [deploy.localnet] @@ -13,7 +14,3 @@ environment_secrets = [] [generate.smart_contract] description = "Adds new smart contract to existing project" path = ".algokit/generators/create_contract" - -[project] -type = 'backend' -name = 'production_beaker_react-contracts' diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 b/examples/production_beaker_react/projects/production_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 index eabbaa0..01e5881 100644 --- a/examples/production_beaker_react/projects/production_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 @@ -1,7 +1,30 @@ -from puyapy import ARC4Contract, arc4 +import beaker +import pyteal as pt +{% if preset_name == 'starter' %} +app = beaker.Application("{{ contract_name }}") +{% elif preset_name == 'production' -%} +from algokit_utils import DELETABLE_TEMPLATE_NAME, UPDATABLE_TEMPLATE_NAME -class {{ contract_name.split('_')|map('capitalize')|join }}(ARC4Contract): - @arc4.abimethod() - def hello(self, name: arc4.String) -> arc4.String: - return "Hello, " + name +app = beaker.Application("{{ contract_name }}") + + +@app.update(authorize=beaker.Authorize.only_creator(), bare=True) +def update() -> pt.Expr: + return pt.Assert( + pt.Tmpl.Int(UPDATABLE_TEMPLATE_NAME), + comment="Check app is updatable", + ) + + +@app.delete(authorize=beaker.Authorize.only_creator(), bare=True) +def delete() -> pt.Expr: + return pt.Assert( + pt.Tmpl.Int(DELETABLE_TEMPLATE_NAME), + comment="Check app is deletable", + ) +{% endif %} + +@app.external +def hello(name: pt.abi.String, *, output: pt.abi.String) -> pt.Expr: + return output.set(pt.Concat(pt.Bytes("Hello, "), name.get())) diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 b/examples/production_beaker_react/projects/production_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 index 9ab7336..bb169ac 100644 --- a/examples/production_beaker_react/projects/production_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 @@ -14,19 +14,38 @@ def deploy( app_spec: algokit_utils.ApplicationSpecification, deployer: algokit_utils.Account, ) -> None: - from smart_contracts.artifacts.hello_world.client import ( - HelloWorldClient, + from smart_contracts.artifacts.{{ contract_name }}.client import ( + {{ contract_name.split('_')|map('capitalize')|join }}Client, ) - app_client = HelloWorldClient( + app_client = {{ contract_name.split('_')|map('capitalize')|join }}Client( algod_client, creator=deployer, indexer_client=indexer_client, ) + + + {%- if preset_name == 'starter' %} app_client.deploy( on_schema_break=algokit_utils.OnSchemaBreak.AppendApp, on_update=algokit_utils.OnUpdate.AppendApp, ) + {%- elif preset_name == 'production' %} + is_mainnet = algokit_utils.is_mainnet(algod_client) + app_client.deploy( + on_schema_break=( + algokit_utils.OnSchemaBreak.AppendApp + if is_mainnet + else algokit_utils.OnSchemaBreak.ReplaceApp + ), + on_update=algokit_utils.OnUpdate.AppendApp + if is_mainnet + else algokit_utils.OnUpdate.UpdateApp, + allow_delete=not is_mainnet, + allow_update=not is_mainnet, + ) + {%- endif %} + name = "world" response = app_client.hello(name=name) logger.info( diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/.copier-answers.yml b/examples/production_beaker_react/projects/production_beaker_react-contracts/.copier-answers.yml index 3f913f2..f52b9a3 100644 --- a/examples/production_beaker_react/projects/production_beaker_react-contracts/.copier-answers.yml +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/.copier-answers.yml @@ -1,6 +1,6 @@ # Changes here will be overwritten by Copier; NEVER EDIT MANUALLY -_commit: 0.4.0-20-gfaa6d61 -_src_path: gh:algorandfoundation/algokit-puya-template +_commit: 1.12.0-25-gbe89a2c +_src_path: gh:algorandfoundation/algokit-beaker-default-template author_email: None author_name: None contract_name: hello_world diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Build_Beaker_application.xml b/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Build_Beaker_application.xml new file mode 100644 index 0000000..7eece90 --- /dev/null +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Build_Beaker_application.xml @@ -0,0 +1,35 @@ + + + + + diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Build_Beaker_application____LocalNet.xml b/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Build_Beaker_application____LocalNet.xml new file mode 100644 index 0000000..ee10c72 --- /dev/null +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Build_Beaker_application____LocalNet.xml @@ -0,0 +1,37 @@ + + + + + diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Build___Deploy_Beaker_application.xml b/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Build___Deploy_Beaker_application.xml new file mode 100644 index 0000000..f3597a5 --- /dev/null +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Build___Deploy_Beaker_application.xml @@ -0,0 +1,38 @@ + + + + + diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Deploy_Built_Beaker_application.xml b/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Deploy_Built_Beaker_application.xml new file mode 100644 index 0000000..4dd1fea --- /dev/null +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Deploy_Built_Beaker_application.xml @@ -0,0 +1,36 @@ + + + + + diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Reset_AlgoKit_LocalNet.xml b/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Reset_AlgoKit_LocalNet.xml new file mode 100644 index 0000000..7f1236a --- /dev/null +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Reset_AlgoKit_LocalNet.xml @@ -0,0 +1,17 @@ + + + + diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml b/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml new file mode 100644 index 0000000..f699a7a --- /dev/null +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml @@ -0,0 +1,17 @@ + + + + diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Stop_AlgoKit_LocalNet.xml b/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Stop_AlgoKit_LocalNet.xml new file mode 100644 index 0000000..e510cbc --- /dev/null +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/.idea/runConfigurations/Stop_AlgoKit_LocalNet.xml @@ -0,0 +1,17 @@ + + + + diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/.tours/getting-started-with-your-algokit-project.tour b/examples/production_beaker_react/projects/production_beaker_react-contracts/.tours/getting-started-with-your-algokit-project.tour new file mode 100644 index 0000000..c1309a6 --- /dev/null +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/.tours/getting-started-with-your-algokit-project.tour @@ -0,0 +1,56 @@ +{ + "$schema": "https://aka.ms/codetour-schema", + "title": "Getting Started with Your AlgoKit Project", + "steps": [ + { + "file": "README.md", + "description": "Welcome to your brand new AlgoKit template-based project. In this tour, we will guide you through the main features and capabilities included in the template.", + "line": 3 + }, + { + "file": "README.md", + "description": "Start by ensuring you have followed the setup of pre-requisites.", + "line": 9 + }, + { + "file": "smart_contracts/__main__.py", + "description": "This is the main entry point for building your smart contracts. The default template includes a starter 'Hello World' contract that is deployed via the `algokit-utils` package (either `ts` or `py`, depending on your choice). To create a new smart contract, you can use the [`algokit generate`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/generate.md) command and invoke a pre-bundled generator template by running `algokit generate smart-contract`. This action will create a new folder in the `smart_contracts` directory, named after your project. Each folder contains a `contract.py` file, which is the entry point for your contract implementation, and `deploy_config.py` | `deployConfig.ts` files (depending on the language chosen for the template), that perform the deployment of the contract.", + "line": 26 + }, + { + "file": "smart_contracts/hello_world/deploy_config.py", + "description": "The default deployment scripts invoke a sample method on the starter contract that demonstrates how to interact with your deployed Algorand on-chain applications using the [`AlgoKit Typed Clients`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/generate.md#1-typed-clients) feature.", + "line": 32 + }, + { + "file": "tests/hello_world_test.py", + "description": "If you opted to include unit tests, the default tests provided demonstrate an example of mocking, setting up fixtures, and testing smart contract calls on an AlgoKit typed client.", + "line": 36 + }, + { + "file": ".env.localnet.template", + "description": "Environment files are a crucial mechanism that allows you to set up the [`algokit deploy`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/deploy.md) feature to simplify deploying your contracts in CI/CD environments (please note we still recommend careful evaluation when it comes to deployment to MainNet). Clone the file and remove the `.template` suffix to apply the changes to deployment scripts and launch configurations. The network prefix `localnet|testnet|mainnet` is primarily optimized for `algokit deploy`. The order of loading the variables is `.env.{network}` < `.env`.", + "line": 2 + }, + { + "file": ".algokit.toml", + "description": "This is the configuration file used by AlgoKit to determine version requirements, `algokit deploy` settings, and references to custom generators.", + "line": 5 + }, + { + "file": ".vscode/launch.json", + "description": "Refer to the pre-bundled Visual Studio launch configurations, offering various options on how to execute the build and deployment of your smart contracts.", + "line": 5 + }, + { + "file": ".vscode/extensions.json", + "description": "We highly recommend installing the recommended extensions to get the most out of this template starter project in your VSCode IDE.", + "line": 3 + }, + { + "file": "smart_contracts/__main__.py", + "description": "Uncomment the following lines to enable complementary utilities that will generate artifacts required for the [AlgoKit AVM Debugger](https://github.com/algorandfoundation/algokit-avm-vscode-debugger) VSCode plugin available on the [VSCode Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=algorandfoundation.algokit-avm-vscode-debugger). A new folder will be automatically created in the `.algokit` directory with source maps of all TEAL contracts in this workspace, as well as traces that will appear in a folder at the root of the workspace. You can then use the traces as entry points to trigger the debug extension. Make sure to have the `.algokit.toml` file available at the root of the workspace.", + "line": 13 + } + ] +} diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/.vscode/extensions.json b/examples/production_beaker_react/projects/production_beaker_react-contracts/.vscode/extensions.json index ee83617..1d2e7cf 100644 --- a/examples/production_beaker_react/projects/production_beaker_react-contracts/.vscode/extensions.json +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/.vscode/extensions.json @@ -6,6 +6,7 @@ "ms-python.black-formatter", "tamasfe.even-better-toml", "editorconfig.editorconfig", + "vsls-contrib.codetour", "algorandfoundation.algokit-avm-vscode-debugger" ] } diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/.vscode/launch.json b/examples/production_beaker_react/projects/production_beaker_react-contracts/.vscode/launch.json index 46c92e0..8cebbf2 100644 --- a/examples/production_beaker_react/projects/production_beaker_react-contracts/.vscode/launch.json +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/.vscode/launch.json @@ -26,7 +26,7 @@ "module": "smart_contracts", "args": ["build"], "cwd": "${workspaceFolder}" - }, + }, { "type": "avm", "request": "launch", diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/README.md b/examples/production_beaker_react/projects/production_beaker_react-contracts/README.md index e9fbd8e..3940c8b 100644 --- a/examples/production_beaker_react/projects/production_beaker_react-contracts/README.md +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/README.md @@ -4,21 +4,16 @@ This project has been generated using AlgoKit. See below for default getting sta # Setup -### Pre-requisites - -- [Python 3.12](https://www.python.org/downloads/) or later -- [Docker](https://www.docker.com/) (only required for LocalNet) - -> Please note `Puya` smart contract development language is currently in alpha / developer preview. It is not recommended for production usage yet. +For an interactive guided walkthrough of the project install [CodeTour](https://marketplace.visualstudio.com/items?itemName=vsls-contrib.codetour) in VS Code and run the `Getting Started with Your AlgoKit Project` tour in the `CodeTour` in Explorer panel in your VSCode instance. ### Initial setup 1. Clone this repository locally 2. Install pre-requisites: - Make sure to have [Docker](https://www.docker.com/) installed and running on your machine. - - Install `AlgoKit` - [Link](https://github.com/algorandfoundation/algokit-cli#install): The recommended version is `1.7.3`. Ensure you can execute `algokit --version` and get `1.7.1` or later. + - Install `AlgoKit` - [Link](https://github.com/algorandfoundation/algokit-cli#install): The minimum required version is `1.1`. Ensure you can execute `algokit --version` and get `1.1` or later. - Bootstrap your local environment; run `algokit bootstrap all` within this folder, which will: - - Install `Poetry` - [Link](https://python-poetry.org/docs/#installation): The minimum required version is `^1.7`. Ensure you can execute `poetry -V` and get `1.2`+ + - Install `Poetry` - [Link](https://python-poetry.org/docs/#installation): The minimum required version is `1.2`. Ensure you can execute `poetry -V` and get `1.2`+ - Run `poetry install` in the root directory, which will set up a `.venv` folder with a Python virtual environment and also install all Python dependencies - Copy `.env.template` to `.env` - Run `algokit localnet start` to start a local Algorand network in Docker. If you are using VS Code launch configurations provided by the template, this will be done automatically for you. @@ -54,13 +49,19 @@ This project makes use of Python to build Algorand smart contracts. The followin - [Algorand](https://www.algorand.com/) - Layer 1 Blockchain; [Developer portal](https://developer.algorand.org/), [Why Algorand?](https://developer.algorand.org/docs/get-started/basics/why_algorand/) - [AlgoKit](https://github.com/algorandfoundation/algokit-cli) - One-stop shop tool for developers building on the Algorand network; [docs](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/algokit.md), [intro tutorial](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/tutorials/intro.md) -- [Puya](https://github.com/algorand-foundation/puya) - Smart contract development framework for developing Algorand smart contracts in pure Python; [docs](https://github.com/algorandfoundation/puya), [examples](https://github.com/algorandfoundation/puya/tree/main/examples) +- [Beaker](https://github.com/algorand-devrel/beaker) - Smart contract development framework for PyTeal; [docs](https://beaker.algo.xyz), [examples](https://github.com/algorand-devrel/beaker/tree/master/examples) - [PyTEAL](https://github.com/algorand/pyteal) - Python language binding for Algorand smart contracts; [docs](https://pyteal.readthedocs.io/en/stable/) - [AlgoKit Utils](https://github.com/algorandfoundation/algokit-utils-py) - A set of core Algorand utilities that make it easier to build solutions on Algorand. -- [Poetry](https://python-poetry.org/): Python packaging and dependency management.- [Black](https://github.com/psf/black): A Python code formatter.- [Ruff](https://github.com/charliermarsh/ruff): An extremely fast Python linter. - +- [Poetry](https://python-poetry.org/): Python packaging and dependency management. +- [Black](https://github.com/psf/black): A Python code formatter. +- [Ruff](https://github.com/charliermarsh/ruff): An extremely fast Python linter. - [mypy](https://mypy-lang.org/): Static type checker. - [pytest](https://docs.pytest.org/): Automated testing. - [pip-audit](https://pypi.org/project/pip-audit/): Tool for scanning Python environments for packages with known vulnerabilities. -It has also been configured to have a productive dev experience out of the box in [VS Code](https://code.visualstudio.com/), see the [.vscode](./.vscode) folder. +It has also been configured to have a productive dev experience out of the box in [VS Code](https://code.visualstudio.com/), see the [.vscode](./.vscode) folder. +- [AlgoKit Tealer Integration](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/tasks/analyze.md): AlgoKit Tealer Integration is a feature in the CLI that allows you to run [Tealer](https://github.com/crytic/tealer) static analyzer on your TEAL +source code. The invocation of this command is included in: +- The github actions workflow file. +- A VSCode task ('Shift+CMD|CTRL+P' and search for 'Tasks: Run Task' and select 'Analyze TEAL contracts with AlgoKit Tealer integration'). +- A `pre-commit` hook (if you have enabled `pre-commit` in your project). diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/pyproject.toml b/examples/production_beaker_react/projects/production_beaker_react-contracts/pyproject.toml index 7cc4d6a..43a6da2 100644 --- a/examples/production_beaker_react/projects/production_beaker_react-contracts/pyproject.toml +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/pyproject.toml @@ -6,12 +6,13 @@ authors = ["None "] readme = "README.md" [tool.poetry.dependencies] -python = "^3.12" +python = "^3.10" +beaker-pyteal = "^1.1.1" algokit-utils = "^2.2.0" python-dotenv = "^1.0.0" -puya = "^0" [tool.poetry.group.dev.dependencies] +setuptools = "^69.0.2" # Adding explicitly to work around pyteal https://github.com/algorand/pyteal/issues/712 black = {extras = ["d"], version = "*"} ruff = "^0.1.6" mypy = "*" @@ -41,7 +42,7 @@ pythonpath = ["smart_contracts", "tests"] [tool.mypy] files = "smart_contracts/" -python_version = "3.12" +python_version = "3.10" check_untyped_defs = true warn_redundant_casts = true warn_unused_ignores = true diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/__main__.py b/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/__main__.py index 7db8a69..e4bb040 100644 --- a/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/__main__.py +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/__main__.py @@ -27,19 +27,19 @@ def main(action: str) -> None: match action: case "build": for contract in contracts: - logger.info(f"Building app at {contract.path}") - build(artifact_path / contract.name, contract.path) + logger.info(f"Building app {contract.app.name}") + build(artifact_path / contract.app.name, contract.app) case "deploy": for contract in contracts: - logger.info(f"Deploying app {contract.name}") - app_spec_path = artifact_path / contract.name / "application.json" + logger.info(f"Deploying app {contract.app.name}") + app_spec_path = artifact_path / contract.app.name / "application.json" if contract.deploy: deploy(app_spec_path, contract.deploy) case "all": for contract in contracts: - logger.info(f"Building app at {contract.path}") - app_spec_path = build(artifact_path / contract.name, contract.path) - logger.info(f"Deploying {contract.path.name}") + logger.info(f"Building app {contract.app.name}") + app_spec_path = build(artifact_path / contract.app.name, contract.app) + logger.info(f"Deploying {contract.app.name}") if contract.deploy: deploy(app_spec_path, contract.deploy) diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/config.py b/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/config.py index 7d56d82..3a1d85f 100644 --- a/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/config.py +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/config.py @@ -6,25 +6,26 @@ from algokit_utils import Account, ApplicationSpecification from algosdk.v2client.algod import AlgodClient from algosdk.v2client.indexer import IndexerClient +from beaker import Application @dataclasses.dataclass class SmartContract: - path: Path - name: str - deploy: ( - Callable[[AlgodClient, IndexerClient, ApplicationSpecification, Account], None] - | None - ) = None + app: Application + deploy: Callable[ + [AlgodClient, IndexerClient, ApplicationSpecification, Account], None + ] | None = None -def import_contract(folder: Path) -> Path: +def import_contract(folder: Path) -> Application: """Imports the contract from a folder if it exists.""" - contract_path = folder / "contract.py" - if contract_path.exists(): - return contract_path - else: - raise Exception(f"Contract not found in {folder}") + try: + contract_module = importlib.import_module( + f"{folder.parent.name}.{folder.name}.contract" + ) + return contract_module.app + except ImportError as e: + raise Exception(f"Contract not found in {folder}") from e def import_deploy_if_exists( @@ -51,11 +52,13 @@ def has_contract_file(directory: Path) -> bool: # define contracts to build and/or deploy base_dir = Path("smart_contracts") contracts = [ - SmartContract( - path=import_contract(folder), - name=folder.name, - deploy=import_deploy_if_exists(folder), - ) + SmartContract(app=import_contract(folder), deploy=import_deploy_if_exists(folder)) for folder in base_dir.iterdir() if folder.is_dir() and has_contract_file(folder) ] + +## Comment the above and uncomment the below and define contracts manually if you want to build and specify them +## manually otherwise the above code will always include all contracts under contract.py file for any subdirectory +## in the smart_contracts directory. Optionally it will grab the deploy function from deploy_config.py if it exists. + +# contracts = [] diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/hello_world/contract.py b/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/hello_world/contract.py index 589aea9..bfda2ca 100644 --- a/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/hello_world/contract.py +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/hello_world/contract.py @@ -1,7 +1,26 @@ -from puyapy import ARC4Contract, arc4 +import beaker +import pyteal as pt +from algokit_utils import DELETABLE_TEMPLATE_NAME, UPDATABLE_TEMPLATE_NAME +app = beaker.Application("hello_world") -class HelloWorld(ARC4Contract): - @arc4.abimethod() - def hello(self, name: arc4.String) -> arc4.String: - return "Hello, " + name + +@app.update(authorize=beaker.Authorize.only_creator(), bare=True) +def update() -> pt.Expr: + return pt.Assert( + pt.Tmpl.Int(UPDATABLE_TEMPLATE_NAME), + comment="Check app is updatable", + ) + + +@app.delete(authorize=beaker.Authorize.only_creator(), bare=True) +def delete() -> pt.Expr: + return pt.Assert( + pt.Tmpl.Int(DELETABLE_TEMPLATE_NAME), + comment="Check app is deletable", + ) + + +@app.external +def hello(name: pt.abi.String, *, output: pt.abi.String) -> pt.Expr: + return output.set(pt.Concat(pt.Bytes("Hello, "), name.get())) diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/hello_world/deploy_config.py b/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/hello_world/deploy_config.py index 82e67a8..7ed7ad6 100644 --- a/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/hello_world/deploy_config.py +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/hello_world/deploy_config.py @@ -23,11 +23,20 @@ def deploy( creator=deployer, indexer_client=indexer_client, ) - + is_mainnet = algokit_utils.is_mainnet(algod_client) app_client.deploy( - on_schema_break=algokit_utils.OnSchemaBreak.AppendApp, - on_update=algokit_utils.OnUpdate.AppendApp, + on_schema_break=( + algokit_utils.OnSchemaBreak.AppendApp + if is_mainnet + else algokit_utils.OnSchemaBreak.ReplaceApp + ), + on_update=algokit_utils.OnUpdate.AppendApp + if is_mainnet + else algokit_utils.OnUpdate.UpdateApp, + allow_delete=not is_mainnet, + allow_update=not is_mainnet, ) + name = "world" response = app_client.hello(name=name) logger.info( diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/helpers/build.py b/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/helpers/build.py index 0dad0c8..753727b 100644 --- a/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/helpers/build.py +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/smart_contracts/helpers/build.py @@ -3,34 +3,22 @@ from pathlib import Path from shutil import rmtree +import beaker + logger = logging.getLogger(__name__) deployment_extension = "py" -def build(output_dir: Path, contract_path: Path) -> Path: +def build(output_dir: Path, app: beaker.Application) -> Path: output_dir = output_dir.resolve() if output_dir.exists(): rmtree(output_dir) output_dir.mkdir(exist_ok=True, parents=True) - logger.info(f"Exporting {contract_path} to {output_dir}") + logger.info(f"Exporting {app.name} to {output_dir}") + specification = app.build() + specification.export(output_dir) - build_result = subprocess.run( - [ - "poetry", - "run", - "puyapy", - contract_path.absolute(), - f"--out-dir={output_dir}", - "--output-arc32", - ], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - ) - if build_result.returncode: - raise Exception(f"Could not build contract:\n{build_result.stdout}") - - generate_result = subprocess.run( + result = subprocess.run( [ "algokit", "generate", @@ -43,14 +31,13 @@ def build(output_dir: Path, contract_path: Path) -> Path: stderr=subprocess.STDOUT, text=True, ) - if generate_result.returncode: - if "No such command" in generate_result.stdout: + if result.returncode: + if "No such command" in result.stdout: raise Exception( - "Could not generate typed client, requires AlgoKit 1.1 or " + "Could not generate typed client, requires AlgoKit 1.8.0 or " "later. Please update AlgoKit" ) else: - raise Exception( - f"Could not generate typed client:\n{generate_result.stdout}" - ) + raise Exception(f"Could not generate typed client:\n{result.stdout}") + return output_dir / "application.json" diff --git a/examples/production_beaker_react/projects/production_beaker_react-contracts/tests/hello_world_test.py b/examples/production_beaker_react/projects/production_beaker_react-contracts/tests/hello_world_test.py index bb13a43..0506685 100644 --- a/examples/production_beaker_react/projects/production_beaker_react-contracts/tests/hello_world_test.py +++ b/examples/production_beaker_react/projects/production_beaker_react-contracts/tests/hello_world_test.py @@ -24,8 +24,10 @@ def hello_world_client( ) client.deploy( - on_schema_break=algokit_utils.OnSchemaBreak.AppendApp, - on_update=algokit_utils.OnUpdate.AppendApp, + on_schema_break=algokit_utils.OnSchemaBreak.ReplaceApp, + on_update=algokit_utils.OnUpdate.UpdateApp, + allow_delete=True, + allow_update=True, ) return client diff --git a/examples/production_tealscript_react/.copier-answers.yml b/examples/production_tealscript_react/.copier-answers.yml index c24ef4e..a7d8ab3 100644 --- a/examples/production_tealscript_react/.copier-answers.yml +++ b/examples/production_tealscript_react/.copier-answers.yml @@ -4,7 +4,6 @@ _src_path: author_email: None author_name: None contract_name: hello_world -deployment_language: python preset_name: production project_name: production_tealscript_react diff --git a/examples/production_tealscript_react/production_tealscript_react.code-workspace b/examples/production_tealscript_react/production_tealscript_react.code-workspace index 72ec985..7205ac6 100644 --- a/examples/production_tealscript_react/production_tealscript_react.code-workspace +++ b/examples/production_tealscript_react/production_tealscript_react.code-workspace @@ -24,50 +24,9 @@ "tasks": { "version": "2.0.0", - "tasks": [ - { - "label": "Build artifacts (+ LocalNet)", - "command": "${workspaceFolder}/projects/production_tealscript_react-contracts/.venv/bin/python", - "windows": { - "command": "${workspaceFolder}/projects/production_tealscript_react-contracts/.venv/Scripts/python.exe" - }, - "args": ["-m", "smart_contracts", "build"], - "options": { - "cwd": "${workspaceFolder}/projects/production_tealscript_react-contracts" - }, - "dependsOn": "Start AlgoKit LocalNet", - "problemMatcher": [] - }, - { - "label": "Start AlgoKit LocalNet", - "command": "algokit", - "args": ["localnet", "start"], - "type": "shell", - "options": { - "cwd": "${workspaceFolder}" - }, - "problemMatcher": [] - } - ] + "tasks": [] }, "launch": { - "configurations": [], - "compounds": [ - { - "preLaunchTask": "Build artifacts (+ LocalNet)", - "name": "Run Frontend (+ LocalNet and Smart Contract)", - "configurations": [ - { - "name": "Deploy contracts", - "folder": "production_tealscript_react-contracts" - }, - { "name": "Run dApp", "folder": "production_tealscript_react-app" } - ], - "presentation": { - "hidden": false, - "group": "0. Run workspace" - } - } - ] + "configurations": [] } } diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-app/src/components/AppCalls.tsx b/examples/production_tealscript_react/projects/production_tealscript_react-app/src/components/AppCalls.tsx index 6a3338e..c5cceba 100644 --- a/examples/production_tealscript_react/projects/production_tealscript_react-app/src/components/AppCalls.tsx +++ b/examples/production_tealscript_react/projects/production_tealscript_react-app/src/components/AppCalls.tsx @@ -5,7 +5,7 @@ import { useWallet } from '@txnlab/use-wallet' import { useSnackbar } from 'notistack' import { useState } from 'react' -import { HelloWorldClient } from '../contracts/HelloWorld' +import { HelloWorldClient } from '../contracts/ProductionTealscriptReactContracts' import { getAlgodConfigFromViteEnvironment, getIndexerConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.algokit.toml b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.algokit.toml deleted file mode 100644 index 8bf7782..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.algokit.toml +++ /dev/null @@ -1,19 +0,0 @@ -[algokit] -min_version = "v1.8.0" - -[deploy] -command = "poetry run python -m smart_contracts deploy" -environment_secrets = [ - "DEPLOYER_MNEMONIC", -] - -[deploy.localnet] -environment_secrets = [] - -[generate.smart_contract] -description = "Adds new smart contract to existing project" -path = ".algokit/generators/create_contract" - -[project] -type = 'backend' -name = 'production_tealscript_react-contracts' diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.algokit/generators/create_contract/copier.yaml b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.algokit/generators/create_contract/copier.yaml deleted file mode 100644 index 73805de..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.algokit/generators/create_contract/copier.yaml +++ /dev/null @@ -1,10 +0,0 @@ -_tasks: - - "echo '==== Successfully initialized new smart contract 🚀 ===='" - -contract_name: - type: str - help: Name of your new contract. - placeholder: "my-new-contract" - default: "my-new-contract" - -_templates_suffix: ".j2" diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 deleted file mode 100644 index eabbaa0..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 +++ /dev/null @@ -1,7 +0,0 @@ -from puyapy import ARC4Contract, arc4 - - -class {{ contract_name.split('_')|map('capitalize')|join }}(ARC4Contract): - @arc4.abimethod() - def hello(self, name: arc4.String) -> arc4.String: - return "Hello, " + name diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 deleted file mode 100644 index 9ab7336..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 +++ /dev/null @@ -1,35 +0,0 @@ -import logging - -import algokit_utils -from algosdk.v2client.algod import AlgodClient -from algosdk.v2client.indexer import IndexerClient - -logger = logging.getLogger(__name__) - - -# define deployment behaviour based on supplied app spec -def deploy( - algod_client: AlgodClient, - indexer_client: IndexerClient, - app_spec: algokit_utils.ApplicationSpecification, - deployer: algokit_utils.Account, -) -> None: - from smart_contracts.artifacts.hello_world.client import ( - HelloWorldClient, - ) - - app_client = HelloWorldClient( - algod_client, - creator=deployer, - indexer_client=indexer_client, - ) - app_client.deploy( - on_schema_break=algokit_utils.OnSchemaBreak.AppendApp, - on_update=algokit_utils.OnUpdate.AppendApp, - ) - name = "world" - response = app_client.hello(name=name) - logger.info( - f"Called hello on {app_spec.contract.name} ({app_client.app_id}) " - f"with name={name}, received: {response.return_value}" - ) diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.copier-answers.yml b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.copier-answers.yml deleted file mode 100644 index 06886c9..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.copier-answers.yml +++ /dev/null @@ -1,10 +0,0 @@ -# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY -_commit: 0.4.0-20-gfaa6d61 -_src_path: gh:algorandfoundation/algokit-puya-template -author_email: None -author_name: None -contract_name: hello_world -deployment_language: python -preset_name: production -project_name: production_tealscript_react-contracts - diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.devcontainer.json b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.devcontainer.json new file mode 100644 index 0000000..3a30b2f --- /dev/null +++ b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.devcontainer.json @@ -0,0 +1,16 @@ +{ + "forwardPorts": [4001, 4002, 8980], + "portsAttributes": { + "4001": { + "label": "algod" + }, + "4002": { + "label": "kmd" + }, + "8980": { + "label": "indexer" + } + }, + "postCreateCommand": "pipx install algokit-cli", + "postStartCommand": "algokit localnet start" +} \ No newline at end of file diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.editorconfig b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.editorconfig deleted file mode 100644 index e2fda34..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.editorconfig +++ /dev/null @@ -1,10 +0,0 @@ -root=true - -[*] -indent_style = space -indent_size = 2 -end_of_line = lf -insert_final_newline = true - -[*.py] -indent_size = 4 diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.env.localnet.template b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.env.localnet.template deleted file mode 100644 index fcbf442..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.env.localnet.template +++ /dev/null @@ -1,7 +0,0 @@ -# this file should contain environment variables specific to algokit localnet -ALGOD_TOKEN=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -ALGOD_SERVER=http://localhost -ALGOD_PORT=4001 -INDEXER_TOKEN=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -INDEXER_SERVER=http://localhost -INDEXER_PORT=8980 diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.env.template b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.env.template deleted file mode 100644 index 184b393..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.env.template +++ /dev/null @@ -1 +0,0 @@ -# this file should contain environment variables common to all environments/networks diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.env.testnet.template b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.env.testnet.template deleted file mode 100644 index eeea43d..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.env.testnet.template +++ /dev/null @@ -1,3 +0,0 @@ -# this file contains algorand network settings for interacting with testnet via algonode -ALGOD_SERVER=https://testnet-api.algonode.cloud -INDEXER_SERVER=https://testnet-idx.algonode.cloud diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.eslintrc.js b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.eslintrc.js new file mode 100644 index 0000000..0fede66 --- /dev/null +++ b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.eslintrc.js @@ -0,0 +1,56 @@ +module.exports = { + env: { + browser: true, + es2021: true, + }, + extends: [ + 'airbnb-base', + 'plugin:import/errors', + 'plugin:import/warnings', + 'plugin:import/typescript', + 'plugin:prettier/recommended', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + plugins: ['@typescript-eslint'], + rules: { + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/ban-ts-comment': 'warn', + 'import/prefer-default-export': 'off', + 'import/extensions': [ + 'error', + 'ignorePackages', + { + js: 'never', + jsx: 'never', + ts: 'never', + tsx: 'never', + }, + ], + 'import/no-extraneous-dependencies': [ + 'error', + { + devDependencies: ['**/*.test.ts'], + }, + ], + }, + overrides: [ + { + files: ['*.algo.ts'], + rules: { + 'import/no-extraneous-dependencies': 'off', + 'object-shorthand': 'off', + 'class-methods-use-this': 'off', + 'no-undef': 'off', + 'max-classes-per-file': 'off', + 'no-bitwise': 'off', + 'operator-assignment': 'off', + 'prefer-template': 'off', + 'prefer-destructuring': 'off', + }, + }, + ], +}; diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.gitattributes b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.gitattributes deleted file mode 100644 index 6313b56..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -* text=auto eol=lf diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.gitignore b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.gitignore index e5f0b9e..40b878d 100644 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.gitignore +++ b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.gitignore @@ -1,180 +1 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ -coverage/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -.env.* -!.env.*.template -!.env.template -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Ruff (linter) -.ruff_cache/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -.idea -!.idea/ -.idea/* -!.idea/runConfigurations/ - -# macOS -.DS_Store - -# Received approval test files -*.received.* - -# NPM -node_modules - -# AlgoKit -debug_traces/ - -.algokit/static-analysis/tealer/ +node_modules/ \ No newline at end of file diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.prettierrc.toml b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.prettierrc.toml new file mode 100644 index 0000000..d979519 --- /dev/null +++ b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.prettierrc.toml @@ -0,0 +1,6 @@ +# .prettierrc.toml +trailingComma = "es5" +tabWidth = 2 +semi = true +singleQuote = true +printWidth = 120 \ No newline at end of file diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.vscode/extensions.json b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.vscode/extensions.json index ee83617..c582379 100644 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.vscode/extensions.json +++ b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.vscode/extensions.json @@ -1,11 +1,5 @@ { - "recommendations": [ - "ms-python.python", - "charliermarsh.ruff", - "matangover.mypy", - "ms-python.black-formatter", - "tamasfe.even-better-toml", - "editorconfig.editorconfig", - "algorandfoundation.algokit-avm-vscode-debugger" - ] -} + "recommendations": [ + "dbaeumer.vscode-eslint", + ] + } \ No newline at end of file diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.vscode/launch.json b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.vscode/launch.json deleted file mode 100644 index 46c92e0..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.vscode/launch.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Build & Deploy contracts", - "type": "python", - "request": "launch", - "module": "smart_contracts", - "cwd": "${workspaceFolder}", - "preLaunchTask": "Start AlgoKit LocalNet", - "envFile": "${workspaceFolder}/.env.localnet" - }, - { - "name": "Deploy contracts", - "type": "python", - "request": "launch", - "module": "smart_contracts", - "args": ["deploy"], - "cwd": "${workspaceFolder}", - "envFile": "${workspaceFolder}/.env.localnet" - }, - { - "name": "Build contracts", - "type": "python", - "request": "launch", - "module": "smart_contracts", - "args": ["build"], - "cwd": "${workspaceFolder}" - }, - { - "type": "avm", - "request": "launch", - "name": "Debug TEAL via AlgoKit AVM Debugger", - "simulateTraceFile": "${workspaceFolder}/${command:PickSimulateTraceFile}", - "stopOnEntry": true - } - ] -} diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.vscode/settings.json b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.vscode/settings.json index 0c2dfec..99a5c70 100644 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.vscode/settings.json +++ b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.vscode/settings.json @@ -1,49 +1,5 @@ { - // General - see also /.editorconfig - "editor.formatOnSave": true, - "files.exclude": { - "**/.git": true, - "**/.DS_Store": true, - "**/Thumbs.db": true, - ".mypy_cache": true, - ".pytest_cache": true, - ".ruff_cache": true, - "**/__pycache__": true, - ".idea": true - }, - - // Python - "python.analysis.extraPaths": ["${workspaceFolder}/smart_contracts"], - "python.defaultInterpreterPath": "${workspaceFolder}/.venv", - "[python]": { "editor.codeActionsOnSave": { - "source.fixAll": true, - // Prevent default import sorting from running; Ruff will sort imports for us anyway - "source.organizeImports": false + "source.fixAll.eslint": "explicit" }, - "editor.defaultFormatter": "ms-python.black-formatter", - }, - "black-formatter.args": ["--config=pyproject.toml"], - "python.testing.pytestEnabled": true, - "ruff.enable": true, - "ruff.lint.run": "onSave", - "ruff.lint.args": ["--config=pyproject.toml"], - "ruff.importStrategy": "fromEnvironment", - "ruff.fixAll": true, //lint and fix all files in workspace - "ruff.organizeImports": true, //organize imports on save - "ruff.codeAction.disableRuleComment": { - "enable": true - }, - "ruff.codeAction.fixViolation": { - "enable": true - }, - "python.analysis.typeCheckingMode": "off", - "mypy.configFile": "pyproject.toml", - // set to empty array to use config from project - "mypy.targets": [], - "mypy.runUsingActiveInterpreter": true, - - // On Windows, if execution policy is set to Signed (default) then it won't be able to activate the venv - // so instead let's set it to RemoteSigned for VS Code terminal - "terminal.integrated.shellArgs.windows": ["-ExecutionPolicy", "RemoteSigned"], -} +} \ No newline at end of file diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.vscode/tasks.json b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.vscode/tasks.json deleted file mode 100644 index eb1e767..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/.vscode/tasks.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "label": "Build contracts", - "command": "${workspaceFolder}/.venv/bin/python", - "windows": { - "command": "${workspaceFolder}/.venv/Scripts/python.exe" - }, - "args": ["-m", "smart_contracts", "build"], - "options": { - "cwd": "${workspaceFolder}" - }, - "group": { - "kind": "build", - "isDefault": true - }, - "problemMatcher": [] - }, - { - "label": "Build contracts (+ LocalNet)", - "command": "${workspaceFolder}/.venv/bin/python", - "windows": { - "command": "${workspaceFolder}/.venv/Scripts/python.exe" - }, - "args": ["-m", "smart_contracts", "build"], - "options": { - "cwd": "${workspaceFolder}" - }, - "dependsOn": "Start AlgoKit LocalNet", - "problemMatcher": [] - }, - { - "label": "Start AlgoKit LocalNet", - "command": "algokit", - "args": ["localnet", "start"], - "type": "shell", - "options": { - "cwd": "${workspaceFolder}" - }, - "problemMatcher": [] - }, - { - "label": "Stop AlgoKit LocalNet", - "command": "algokit", - "args": ["localnet", "stop"], - "type": "shell", - "options": { - "cwd": "${workspaceFolder}" - }, - "problemMatcher": [] - }, - { - "label": "Reset AlgoKit LocalNet", - "command": "algokit", - "args": ["localnet", "reset"], - "type": "shell", - "options": { - "cwd": "${workspaceFolder}" - }, - "problemMatcher": [] - }, - { - "label": "Analyze TEAL contracts with AlgoKit Tealer integration", - "command": "algokit", - "args": [ - "task", - "analyze", - "${workspaceFolder}/.algokit", - "--recursive", - "--force" - ], - "options": { - "cwd": "${workspaceFolder}" - }, - "problemMatcher": [] - } - ] -} diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/README.md b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/README.md index d9b97ef..6800947 100644 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/README.md +++ b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/README.md @@ -1,66 +1,25 @@ -# production_tealscript_react-contracts +# TEALScript Project -This project has been generated using AlgoKit. See below for default getting started instructions. +## Documentation -# Setup +For TEALScript documentation, go to https://tealscript.algo.xyz -### Pre-requisites +## Usage -- [Python 3.12](https://www.python.org/downloads/) or later -- [Docker](https://www.docker.com/) (only required for LocalNet) +### Algokit -> Please note `Puya` smart contract development language is currently in alpha / developer preview. It is not recommended for production usage yet. +This template assumes you have a local network running on your machine. The easiet way to setup a local network is with [algokit](https://github.com/algorandfoundation/algokit-cli). If you don't have Algokit or its dependencies installed locally you can open this repository in a GitHub codespace via https://codespaces.new and choosing this repo. -### Initial setup +### Build Contract -1. Clone this repository locally -2. Install pre-requisites: - - Make sure to have [Docker](https://www.docker.com/) installed and running on your machine. - - Install `AlgoKit` - [Link](https://github.com/algorandfoundation/algokit-cli#install): The recommended version is `1.7.3`. Ensure you can execute `algokit --version` and get `1.7.1` or later. - - Bootstrap your local environment; run `algokit bootstrap all` within this folder, which will: - - Install `Poetry` - [Link](https://python-poetry.org/docs/#installation): The minimum required version is `^1.7`. Ensure you can execute `poetry -V` and get `1.2`+ - - Run `poetry install` in the root directory, which will set up a `.venv` folder with a Python virtual environment and also install all Python dependencies - - Copy `.env.template` to `.env` - - Run `algokit localnet start` to start a local Algorand network in Docker. If you are using VS Code launch configurations provided by the template, this will be done automatically for you. -3. Open the project and start debugging / developing via: - - VS Code - 1. Open the repository root in VS Code - 2. Install recommended extensions - 3. Hit F5 (or whatever you have debug mapped to) and it should start running with breakpoint debugging. - > **Note** - > If using Windows: Before running for the first time you will need to select the Python Interpreter. - 1. Open the command palette (Ctrl/Cmd + Shift + P) - 2. Search for `Python: Select Interpreter` - 3. Select `./.venv/Scripts/python.exe` - - JetBrains IDEs (please note, this setup is primarily optimized for PyCharm Community Edition) - 1. Open the repository root in the IDE - 2. It should automatically detect it's a Poetry project and set up a Python interpreter and virtual environment. - 3. Hit Shift+F10|Ctrl+R (or whatever you have debug mapped to) and it should start running with breakpoint debugging. Please note, JetBrains IDEs on Windows have a known bug that in some cases may prevent executing shell scripts as pre-launch tasks, for workarounds refer to [JetBrains forums](https://youtrack.jetbrains.com/issue/IDEA-277486/Shell-script-configuration-cannot-run-as-before-launch-task). - - Other - 1. Open the repository root in your text editor of choice - 2. In a terminal run `poetry shell` - 3. Run `python -m smart_contracts` through your debugger of choice +`npm run build` will compile the contract to TEAL and generate an ABI and appspec JSON in [./contracts/artifacts](./contracts/artifacts/) and a algokit TypeScript client in [./contracts/clients](./contracts/clients/). -### Subsequently +`npm run compile-contract` or `npm run generate-client` can be used to compile the contract or generate the contract seperately. -1. If you update to the latest source code and there are new dependencies you will need to run `algokit bootstrap all` again -2. Follow step 3 above +### Run Tests -> For guidance on `smart_contracts` folder and adding new contracts to the project please see [README](smart_contracts/README.md) on the respective folder. +`npm run test` will execute the tests defined in [./\_\_test\_\_](./__test__) -# Tools - -This project makes use of Python to build Algorand smart contracts. The following tools are in use: - -- [Algorand](https://www.algorand.com/) - Layer 1 Blockchain; [Developer portal](https://developer.algorand.org/), [Why Algorand?](https://developer.algorand.org/docs/get-started/basics/why_algorand/) -- [AlgoKit](https://github.com/algorandfoundation/algokit-cli) - One-stop shop tool for developers building on the Algorand network; [docs](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/algokit.md), [intro tutorial](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/tutorials/intro.md) -- [Puya](https://github.com/algorand-foundation/puya) - Smart contract development framework for developing Algorand smart contracts in pure Python; [docs](https://github.com/algorandfoundation/puya), [examples](https://github.com/algorandfoundation/puya/tree/main/examples) -- [PyTEAL](https://github.com/algorand/pyteal) - Python language binding for Algorand smart contracts; [docs](https://pyteal.readthedocs.io/en/stable/) -- [AlgoKit Utils](https://github.com/algorandfoundation/algokit-utils-py) - A set of core Algorand utilities that make it easier to build solutions on Algorand. -- [Poetry](https://python-poetry.org/): Python packaging and dependency management.- [Black](https://github.com/psf/black): A Python code formatter.- [Ruff](https://github.com/charliermarsh/ruff): An extremely fast Python linter. - -- [mypy](https://mypy-lang.org/): Static type checker. -- [pytest](https://docs.pytest.org/): Automated testing. -- [pip-audit](https://pypi.org/project/pip-audit/): Tool for scanning Python environments for packages with known vulnerabilities. -It has also been configured to have a productive dev experience out of the box in [VS Code](https://code.visualstudio.com/), see the [.vscode](./.vscode) folder. +### Lint +`npm run lint` will lint the contracts and tests with ESLint. diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/__test__/production_tealscript_react-contracts.test.ts b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/__test__/production_tealscript_react-contracts.test.ts new file mode 100644 index 0000000..cee06b7 --- /dev/null +++ b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/__test__/production_tealscript_react-contracts.test.ts @@ -0,0 +1,42 @@ +import { describe, test, expect, beforeAll, beforeEach } from '@jest/globals'; +import { algorandFixture } from '@algorandfoundation/algokit-utils/testing'; +import { ProductionTealscriptReactContractsClient } from '../contracts/clients/ProductionTealscriptReactContractsClient'; + +const fixture = algorandFixture(); +algokit.Config.configure({ populateAppCallResources: true }); + +let appClient: ProductionTealscriptReactContractsClient; + +describe('ProductionTealscriptReactContracts', () => { + beforeEach(fixture.beforeEach); + + beforeAll(async () => { + await fixture.beforeEach(); + const { algod, testAccount } = fixture.context; + + appClient = new ProductionTealscriptReactContractsClient( + { + sender: testAccount, + resolveBy: 'id', + id: 0, + }, + algod + ); + + await appClient.create.createApplication({}); + }); + + test('sum', async () => { + const a = 13; + const b = 37; + const sum = await appClient.doMath({ a, b, operation: 'sum' }); + expect(sum.return?.valueOf()).toBe(BigInt(a + b)); + }); + + test('difference', async () => { + const a = 13; + const b = 37; + const diff = await appClient.doMath({ a, b, operation: 'difference' }); + expect(diff.return?.valueOf()).toBe(BigInt(a >= b ? a - b : b - a)); + }); +}); diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/__init__.py b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/contracts/artifacts/components/.gitkeep similarity index 100% rename from examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/__init__.py rename to examples/production_tealscript_react/projects/production_tealscript_react-contracts/contracts/artifacts/components/.gitkeep diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/helpers/__init__.py b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/contracts/clients/.gitkeep similarity index 100% rename from examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/helpers/__init__.py rename to examples/production_tealscript_react/projects/production_tealscript_react-contracts/contracts/clients/.gitkeep diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/contracts/production_tealscript_react-contracts.algo.ts b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/contracts/production_tealscript_react-contracts.algo.ts new file mode 100644 index 0000000..d85368d --- /dev/null +++ b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/contracts/production_tealscript_react-contracts.algo.ts @@ -0,0 +1,47 @@ +import { Contract } from '@algorandfoundation/tealscript'; + +// eslint-disable-next-line no-unused-vars +class ProductionTealscriptReactContracts extends Contract { + /** + * Calculates the sum of two numbers + * + * @param a + * @param b + * @returns The sum of a and b + */ + private getSum(a: number, b: number): number { + return a + b; + } + + /** + * Calculates the difference between two numbers + * + * @param a + * @param b + * @returns The difference between a and b. + */ + private getDifference(a: number, b: number): number { + return a >= b ? a - b : b - a; + } + + /** + * A method that takes two numbers and does either addition or subtraction + * + * @param a The first number + * @param b The second number + * @param operation The operation to perform. Can be either 'sum' or 'difference' + * + * @returns The result of the operation + */ + doMath(a: number, b: number, operation: string): number { + let result: number; + + if (operation === 'sum') { + result = this.getSum(a, b); + } else if (operation === 'difference') { + result = this.getDifference(a, b); + } else throw Error('Invalid operation'); + + return result; + } +} diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/jest.config.js b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/jest.config.js new file mode 100644 index 0000000..6f5ef4e --- /dev/null +++ b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/jest.config.js @@ -0,0 +1,6 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testTimeout: 60000 +}; diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/package.json b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/package.json new file mode 100644 index 0000000..285b272 --- /dev/null +++ b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/package.json @@ -0,0 +1,36 @@ +{ + "name": "production_tealscript_react-contracts", + "version": "0.0.0", + "license": "MIT", + "scripts": { + "generate-client": "algokit generate client contracts/artifacts/ --language typescript --output contracts/clients/{contract_name}Client.ts", + "compile-contract": "tealscript contracts/*.algo.ts contracts/artifacts", + "generate-components": "algokit-generate-component contracts/artifacts/ProductionTealscriptReactContracts.arc32.json contracts/artifacts/components", + "build": "npm run compile-contract && npm run generate-client", + "test": "npm run build && jest", + "lint": "eslint . --ext .ts", + "fix": "eslint . --ext .ts --fix" + }, + "dependencies": { + "@algorandfoundation/algokit-utils": "^5.5.0", + "algosdk": "^2.7.0" + }, + "devDependencies": { + "@algorandfoundation/algokit-client-generator": "^2.3.1", + "@algorandfoundation/tealscript": "latest", + "@jest/globals": "^29.5.0", + "@joe-p/algokit-generate-component": "^0.2.0", + "@typescript-eslint/eslint-plugin": "^5.13.0", + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^7.32.0 || ^8.2.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-airbnb-typescript": "^17.0.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-prettier": "^5.0.1", + "jest": "^29.5.0", + "prettier": "^3.0.3", + "ts-jest": "^29.1.0", + "typescript": "5.0.2" + } +} diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/poetry.toml b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/poetry.toml deleted file mode 100644 index ab1033b..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/poetry.toml +++ /dev/null @@ -1,2 +0,0 @@ -[virtualenvs] -in-project = true diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/pyproject.toml b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/pyproject.toml deleted file mode 100644 index 932eece..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/pyproject.toml +++ /dev/null @@ -1,49 +0,0 @@ -[tool.poetry] -name = "production_tealscript_react-contracts" -version = "0.1.0" -description = "Algorand smart contracts" -authors = ["None "] -readme = "README.md" - -[tool.poetry.dependencies] -python = "^3.12" -algokit-utils = "^2.2.0" -python-dotenv = "^1.0.0" -puya = "^0" - -[tool.poetry.group.dev.dependencies] -black = {extras = ["d"], version = "*"} -ruff = "^0.1.6" -mypy = "*" -pytest = "*" -pytest-cov = "*" -pip-audit = "*" - -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" - -[tool.ruff] -line-length = 120 -select = ["E", "F", "ANN", "UP", "N", "C4", "B", "A", "YTT", "W", "FBT", "Q", "RUF", "I"] -ignore = [ - "ANN101", # no type for self - "ANN102", # no type for cls -] -unfixable = ["B", "RUF"] - -[tool.ruff.flake8-annotations] -allow-star-arg-any = true -suppress-none-returning = true - -[tool.pytest.ini_options] -pythonpath = ["smart_contracts", "tests"] - -[tool.mypy] -files = "smart_contracts/" -python_version = "3.12" -check_untyped_defs = true -warn_redundant_casts = true -warn_unused_ignores = true -allow_untyped_defs = false -strict_equality = true diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/README.md b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/README.md deleted file mode 100644 index f765c95..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/README.md +++ /dev/null @@ -1,9 +0,0 @@ -## How to add new smart contracts? - -By the default the template creates a single `HelloWorld` contract under hello_world folder in the `smart_contracts` directory. To add a new contract: - -1. From the root of the project (`../`) execute `algokit generate smart-contract`. This will create a new starter smart contract and deployment configuration file under `{your_contract_name}` subfolder under `smart_contracts` directory. -2. Each contract potentially has different creation parameters and deployment steps. Hence, you need to define your deployment logic in `deploy_config.py`file. -3. `config.py` file will automatically build all contracts under `smart_contracts` directory. If you want to build specific contracts manually, modify the default code provided by the template in `config.py` file. - -> Please note, above is just a suggested convention tailored for the base configuration and structure of this template. Default code supplied by the template in `config.py` and `index.ts` (if using ts clients) files are tailored for the suggested convention. You are free to modify the structure and naming conventions as you see fit. diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/__main__.py b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/__main__.py deleted file mode 100644 index 7db8a69..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/__main__.py +++ /dev/null @@ -1,51 +0,0 @@ -import logging -import sys -from pathlib import Path - -from dotenv import load_dotenv - -from smart_contracts.config import contracts -from smart_contracts.helpers.build import build -from smart_contracts.helpers.deploy import deploy - -# Uncomment the following lines 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 -# from algokit_utils.config import config -# config.configure(debug=True, trace_all=True) -logging.basicConfig( - level=logging.DEBUG, format="%(asctime)s %(levelname)-10s: %(message)s" -) -logger = logging.getLogger(__name__) -logger.info("Loading .env") -load_dotenv() -root_path = Path(__file__).parent - - -def main(action: str) -> None: - artifact_path = root_path / "artifacts" - match action: - case "build": - for contract in contracts: - logger.info(f"Building app at {contract.path}") - build(artifact_path / contract.name, contract.path) - case "deploy": - for contract in contracts: - logger.info(f"Deploying app {contract.name}") - app_spec_path = artifact_path / contract.name / "application.json" - if contract.deploy: - deploy(app_spec_path, contract.deploy) - case "all": - for contract in contracts: - logger.info(f"Building app at {contract.path}") - app_spec_path = build(artifact_path / contract.name, contract.path) - logger.info(f"Deploying {contract.path.name}") - if contract.deploy: - deploy(app_spec_path, contract.deploy) - - -if __name__ == "__main__": - if len(sys.argv) > 1: - main(sys.argv[1]) - else: - main("all") diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/config.py b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/config.py deleted file mode 100644 index 7d56d82..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/config.py +++ /dev/null @@ -1,61 +0,0 @@ -import dataclasses -import importlib -from collections.abc import Callable -from pathlib import Path - -from algokit_utils import Account, ApplicationSpecification -from algosdk.v2client.algod import AlgodClient -from algosdk.v2client.indexer import IndexerClient - - -@dataclasses.dataclass -class SmartContract: - path: Path - name: str - deploy: ( - Callable[[AlgodClient, IndexerClient, ApplicationSpecification, Account], None] - | None - ) = None - - -def import_contract(folder: Path) -> Path: - """Imports the contract from a folder if it exists.""" - contract_path = folder / "contract.py" - if contract_path.exists(): - return contract_path - else: - raise Exception(f"Contract not found in {folder}") - - -def import_deploy_if_exists( - folder: Path, -) -> ( - Callable[[AlgodClient, IndexerClient, ApplicationSpecification, Account], None] - | None -): - """Imports the deploy function from a folder if it exists.""" - try: - deploy_module = importlib.import_module( - f"{folder.parent.name}.{folder.name}.deploy_config" - ) - return deploy_module.deploy - except ImportError: - return None - - -def has_contract_file(directory: Path) -> bool: - """Checks whether the directory contains contract.py file.""" - return (directory / "contract.py").exists() - - -# define contracts to build and/or deploy -base_dir = Path("smart_contracts") -contracts = [ - SmartContract( - path=import_contract(folder), - name=folder.name, - deploy=import_deploy_if_exists(folder), - ) - for folder in base_dir.iterdir() - if folder.is_dir() and has_contract_file(folder) -] diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/hello_world/contract.py b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/hello_world/contract.py deleted file mode 100644 index 589aea9..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/hello_world/contract.py +++ /dev/null @@ -1,7 +0,0 @@ -from puyapy import ARC4Contract, arc4 - - -class HelloWorld(ARC4Contract): - @arc4.abimethod() - def hello(self, name: arc4.String) -> arc4.String: - return "Hello, " + name diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/hello_world/deploy_config.py b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/hello_world/deploy_config.py deleted file mode 100644 index 82e67a8..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/hello_world/deploy_config.py +++ /dev/null @@ -1,36 +0,0 @@ -import logging - -import algokit_utils -from algosdk.v2client.algod import AlgodClient -from algosdk.v2client.indexer import IndexerClient - -logger = logging.getLogger(__name__) - - -# define deployment behaviour based on supplied app spec -def deploy( - algod_client: AlgodClient, - indexer_client: IndexerClient, - app_spec: algokit_utils.ApplicationSpecification, - deployer: algokit_utils.Account, -) -> None: - from smart_contracts.artifacts.hello_world.client import ( - HelloWorldClient, - ) - - app_client = HelloWorldClient( - algod_client, - creator=deployer, - indexer_client=indexer_client, - ) - - app_client.deploy( - on_schema_break=algokit_utils.OnSchemaBreak.AppendApp, - on_update=algokit_utils.OnUpdate.AppendApp, - ) - name = "world" - response = app_client.hello(name=name) - logger.info( - f"Called hello on {app_spec.contract.name} ({app_client.app_id}) " - f"with name={name}, received: {response.return_value}" - ) diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/helpers/build.py b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/helpers/build.py deleted file mode 100644 index 0dad0c8..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/helpers/build.py +++ /dev/null @@ -1,56 +0,0 @@ -import logging -import subprocess -from pathlib import Path -from shutil import rmtree - -logger = logging.getLogger(__name__) -deployment_extension = "py" - - -def build(output_dir: Path, contract_path: Path) -> Path: - output_dir = output_dir.resolve() - if output_dir.exists(): - rmtree(output_dir) - output_dir.mkdir(exist_ok=True, parents=True) - logger.info(f"Exporting {contract_path} to {output_dir}") - - build_result = subprocess.run( - [ - "poetry", - "run", - "puyapy", - contract_path.absolute(), - f"--out-dir={output_dir}", - "--output-arc32", - ], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - ) - if build_result.returncode: - raise Exception(f"Could not build contract:\n{build_result.stdout}") - - generate_result = subprocess.run( - [ - "algokit", - "generate", - "client", - output_dir / "application.json", - "--output", - output_dir / f"client.{deployment_extension}", - ], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - ) - if generate_result.returncode: - if "No such command" in generate_result.stdout: - raise Exception( - "Could not generate typed client, requires AlgoKit 1.1 or " - "later. Please update AlgoKit" - ) - else: - raise Exception( - f"Could not generate typed client:\n{generate_result.stdout}" - ) - return output_dir / "application.json" diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/helpers/deploy.py b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/helpers/deploy.py deleted file mode 100644 index 08367a3..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/smart_contracts/helpers/deploy.py +++ /dev/null @@ -1,50 +0,0 @@ -import logging -from collections.abc import Callable -from pathlib import Path - -from algokit_utils import ( - Account, - ApplicationSpecification, - EnsureBalanceParameters, - ensure_funded, - get_account, - get_algod_client, - get_indexer_client, -) -from algosdk.util import algos_to_microalgos -from algosdk.v2client.algod import AlgodClient -from algosdk.v2client.indexer import IndexerClient - -logger = logging.getLogger(__name__) - - -def deploy( - app_spec_path: Path, - deploy_callback: Callable[ - [AlgodClient, IndexerClient, ApplicationSpecification, Account], None - ], - deployer_initial_funds: int = 2, -) -> None: - # get clients - # by default client configuration is loaded from environment variables - algod_client = get_algod_client() - indexer_client = get_indexer_client() - - # get app spec - app_spec = ApplicationSpecification.from_json(app_spec_path.read_text()) - - # get deployer account by name - deployer = get_account(algod_client, "DEPLOYER", fund_with_algos=0) - - minimum_funds_micro_algos = algos_to_microalgos(deployer_initial_funds) - ensure_funded( - algod_client, - EnsureBalanceParameters( - account_to_fund=deployer, - min_spending_balance_micro_algos=minimum_funds_micro_algos, - min_funding_increment_micro_algos=minimum_funds_micro_algos, - ), - ) - - # use provided callback to deploy the app - deploy_callback(algod_client, indexer_client, app_spec, deployer) diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/tests/conftest.py b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/tests/conftest.py deleted file mode 100644 index b0622e7..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/tests/conftest.py +++ /dev/null @@ -1,32 +0,0 @@ -from pathlib import Path - -import pytest -from algokit_utils import ( - get_algod_client, - get_indexer_client, - is_localnet, -) -from algosdk.v2client.algod import AlgodClient -from algosdk.v2client.indexer import IndexerClient -from dotenv import load_dotenv - - -@pytest.fixture(autouse=True, scope="session") -def environment_fixture() -> None: - env_path = Path(__file__).parent.parent / ".env.localnet" - load_dotenv(env_path) - - -@pytest.fixture(scope="session") -def algod_client() -> AlgodClient: - client = get_algod_client() - - # you can remove this assertion to test on other networks, - # included here to prevent accidentally running against other networks - assert is_localnet(client) - return client - - -@pytest.fixture(scope="session") -def indexer_client() -> IndexerClient: - return get_indexer_client() diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/tests/hello_world_test.py b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/tests/hello_world_test.py deleted file mode 100644 index bb13a43..0000000 --- a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/tests/hello_world_test.py +++ /dev/null @@ -1,48 +0,0 @@ -import algokit_utils -import pytest -from algokit_utils import get_localnet_default_account -from algokit_utils.config import config -from algosdk.v2client.algod import AlgodClient -from algosdk.v2client.indexer import IndexerClient - -from smart_contracts.artifacts.hello_world.client import HelloWorldClient - - -@pytest.fixture(scope="session") -def hello_world_client( - algod_client: AlgodClient, indexer_client: IndexerClient -) -> HelloWorldClient: - config.configure( - debug=True, - # trace_all=True, - ) - - client = HelloWorldClient( - algod_client, - creator=get_localnet_default_account(algod_client), - indexer_client=indexer_client, - ) - - client.deploy( - on_schema_break=algokit_utils.OnSchemaBreak.AppendApp, - on_update=algokit_utils.OnUpdate.AppendApp, - ) - return client - - -def test_says_hello(hello_world_client: HelloWorldClient) -> None: - result = hello_world_client.hello(name="World") - - assert result.return_value == "Hello, World" - - -def test_simulate_says_hello_with_correct_budget_consumed( - hello_world_client: HelloWorldClient, algod_client: AlgodClient -) -> None: - result = ( - hello_world_client.compose().hello(name="World").hello(name="Jane").simulate() - ) - - assert result.abi_results[0].return_value == "Hello, World" - assert result.abi_results[1].return_value == "Hello, Jane" - assert result.simulate_response["txn-groups"][0]["app-budget-consumed"] < 100 diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/tsconfig.json b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/tsconfig.json new file mode 100644 index 0000000..5e3106d --- /dev/null +++ b/examples/production_tealscript_react/projects/production_tealscript_react-contracts/tsconfig.json @@ -0,0 +1,103 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/examples/starter_beaker_react/.copier-answers.yml b/examples/starter_beaker_react/.copier-answers.yml index 16333bb..2f61be3 100644 --- a/examples/starter_beaker_react/.copier-answers.yml +++ b/examples/starter_beaker_react/.copier-answers.yml @@ -4,7 +4,7 @@ _src_path: author_email: None author_name: None contract_name: hello_world -deployment_language: python +deployment_language: typescript preset_name: starter project_name: starter_beaker_react diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-app/src/components/AppCalls.tsx b/examples/starter_beaker_react/projects/starter_beaker_react-app/src/components/AppCalls.tsx index 6a3338e..7751909 100644 --- a/examples/starter_beaker_react/projects/starter_beaker_react-app/src/components/AppCalls.tsx +++ b/examples/starter_beaker_react/projects/starter_beaker_react-app/src/components/AppCalls.tsx @@ -5,7 +5,7 @@ import { useWallet } from '@txnlab/use-wallet' import { useSnackbar } from 'notistack' import { useState } from 'react' -import { HelloWorldClient } from '../contracts/HelloWorld' +import { HelloWorldClient } from '../contracts/hello_world' import { getAlgodConfigFromViteEnvironment, getIndexerConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.algokit.toml b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.algokit.toml index a91e679..f4bd363 100644 --- a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.algokit.toml +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.algokit.toml @@ -1,8 +1,8 @@ [algokit] -min_version = "v1.8.0" +min_version = "v1.10.0" [deploy] -command = "poetry run python -m smart_contracts deploy" +command = "npm run deploy:ci" environment_secrets = [ "DEPLOYER_MNEMONIC", ] @@ -13,7 +13,3 @@ environment_secrets = [] [generate.smart_contract] description = "Adds new smart contract to existing project" path = ".algokit/generators/create_contract" - -[project] -type = 'backend' -name = 'starter_beaker_react-contracts' diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 index eabbaa0..01e5881 100644 --- a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 @@ -1,7 +1,30 @@ -from puyapy import ARC4Contract, arc4 +import beaker +import pyteal as pt +{% if preset_name == 'starter' %} +app = beaker.Application("{{ contract_name }}") +{% elif preset_name == 'production' -%} +from algokit_utils import DELETABLE_TEMPLATE_NAME, UPDATABLE_TEMPLATE_NAME -class {{ contract_name.split('_')|map('capitalize')|join }}(ARC4Contract): - @arc4.abimethod() - def hello(self, name: arc4.String) -> arc4.String: - return "Hello, " + name +app = beaker.Application("{{ contract_name }}") + + +@app.update(authorize=beaker.Authorize.only_creator(), bare=True) +def update() -> pt.Expr: + return pt.Assert( + pt.Tmpl.Int(UPDATABLE_TEMPLATE_NAME), + comment="Check app is updatable", + ) + + +@app.delete(authorize=beaker.Authorize.only_creator(), bare=True) +def delete() -> pt.Expr: + return pt.Assert( + pt.Tmpl.Int(DELETABLE_TEMPLATE_NAME), + comment="Check app is deletable", + ) +{% endif %} + +@app.external +def hello(name: pt.abi.String, *, output: pt.abi.String) -> pt.Expr: + return output.set(pt.Concat(pt.Bytes("Hello, "), name.get())) diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 new file mode 100644 index 0000000..325fb24 --- /dev/null +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 @@ -0,0 +1,59 @@ +import * as algokit from '@algorandfoundation/algokit-utils' +import { {{ contract_name.split('_')|map('capitalize')|join }}Client } from '../artifacts/{{ contract_name }}/client' + +// Below is a showcase of various deployment options you can use in TypeScript Client +export async function deploy() { + console.log('=== Deploying {{ contract_name.split('_')|map('capitalize')|join }} ===') + + const algod = algokit.getAlgoClient() + const indexer = algokit.getAlgoIndexerClient() + const deployer = await algokit.mnemonicAccountFromEnvironment({ name: 'DEPLOYER', fundWith: algokit.algos(3) }, algod) + await algokit.ensureFunded( + { + accountToFund: deployer, + minSpendingBalance: algokit.algos(2), + minFundingIncrement: algokit.algos(2), + }, + algod, + ) + const appClient = new {{ contract_name.split('_')|map('capitalize')|join }}Client( + { + resolveBy: 'creatorAndName', + findExistingUsing: indexer, + sender: deployer, + creatorAddress: deployer.addr, + }, + algod, + ) + + {%- if preset_name == 'starter' %} + const app = await appClient.deploy({ + onSchemaBreak: 'append', + onUpdate: 'append', + }) + {% elif preset_name == 'production' %} + const isMainNet = await algokit.isMainNet(algod) + const app = await appClient.deploy({ + allowDelete: !isMainNet, + allowUpdate: !isMainNet, + onSchemaBreak: isMainNet ? 'append' : 'replace', + onUpdate: isMainNet ? 'append' : 'update', + }) + {% endif %} + + // If app was just created fund the app account + if (['create', 'replace'].includes(app.operationPerformed)) { + algokit.transferAlgos( + { + amount: algokit.algos(1), + from: deployer, + to: app.appAddress, + }, + algod, + ) + } + + 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}`) +} diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 deleted file mode 100644 index 9ab7336..0000000 --- a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 +++ /dev/null @@ -1,35 +0,0 @@ -import logging - -import algokit_utils -from algosdk.v2client.algod import AlgodClient -from algosdk.v2client.indexer import IndexerClient - -logger = logging.getLogger(__name__) - - -# define deployment behaviour based on supplied app spec -def deploy( - algod_client: AlgodClient, - indexer_client: IndexerClient, - app_spec: algokit_utils.ApplicationSpecification, - deployer: algokit_utils.Account, -) -> None: - from smart_contracts.artifacts.hello_world.client import ( - HelloWorldClient, - ) - - app_client = HelloWorldClient( - algod_client, - creator=deployer, - indexer_client=indexer_client, - ) - app_client.deploy( - on_schema_break=algokit_utils.OnSchemaBreak.AppendApp, - on_update=algokit_utils.OnUpdate.AppendApp, - ) - name = "world" - response = app_client.hello(name=name) - logger.info( - f"Called hello on {app_spec.contract.name} ({app_client.app_id}) " - f"with name={name}, received: {response.return_value}" - ) diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.copier-answers.yml b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.copier-answers.yml index 0998eff..e9ebfe9 100644 --- a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.copier-answers.yml +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.copier-answers.yml @@ -1,10 +1,10 @@ # Changes here will be overwritten by Copier; NEVER EDIT MANUALLY -_commit: 0.4.0-20-gfaa6d61 -_src_path: gh:algorandfoundation/algokit-puya-template +_commit: 1.12.0-25-gbe89a2c +_src_path: gh:algorandfoundation/algokit-beaker-default-template author_email: None author_name: None contract_name: hello_world -deployment_language: python +deployment_language: typescript preset_name: starter project_name: starter_beaker_react-contracts diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.prettierignore b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.prettierignore new file mode 100644 index 0000000..dbda6ae --- /dev/null +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.prettierignore @@ -0,0 +1,12 @@ +# don't ever format node_modules +node_modules +# don't lint format output (make sure it's set to your correct build folder name) +dist +build +# don't format nyc coverage output +coverage +# don't format generated types +**/generated/types.d.ts +**/generated/types.ts +# don't format ide files +.idea diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.prettierrc.js b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.prettierrc.js new file mode 100644 index 0000000..c484d0e --- /dev/null +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.prettierrc.js @@ -0,0 +1,10 @@ +module.exports = { + singleQuote: true, + jsxSingleQuote: false, + semi: false, + tabWidth: 2, + trailingComma: 'all', + printWidth: 120, + endOfLine: 'lf', + arrowParens: 'always', +} diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.tours/getting-started-with-your-algokit-project.tour b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.tours/getting-started-with-your-algokit-project.tour new file mode 100644 index 0000000..30f150f --- /dev/null +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.tours/getting-started-with-your-algokit-project.tour @@ -0,0 +1,56 @@ +{ + "$schema": "https://aka.ms/codetour-schema", + "title": "Getting Started with Your AlgoKit Project", + "steps": [ + { + "file": "README.md", + "description": "Welcome to your brand new AlgoKit template-based project. In this tour, we will guide you through the main features and capabilities included in the template.", + "line": 3 + }, + { + "file": "README.md", + "description": "Start by ensuring you have followed the setup of pre-requisites.", + "line": 9 + }, + { + "file": "smart_contracts/__main__.py", + "description": "This is the main entry point for building your smart contracts. The default template includes a starter 'Hello World' contract that is deployed via the `algokit-utils` package (either `ts` or `py`, depending on your choice). To create a new smart contract, you can use the [`algokit generate`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/generate.md) command and invoke a pre-bundled generator template by running `algokit generate smart-contract`. This action will create a new folder in the `smart_contracts` directory, named after your project. Each folder contains a `contract.py` file, which is the entry point for your contract implementation, and `deploy_config.py` | `deployConfig.ts` files (depending on the language chosen for the template), that perform the deployment of the contract.", + "line": 26 + }, + { + "file": "smart_contracts/hello_world/deploy-config.ts", + "description": "The default deployment scripts invoke a sample method on the starter contract that demonstrates how to interact with your deployed Algorand on-chain applications using the [`AlgoKit Typed Clients`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/generate.md#1-typed-clients) feature.", + "line": 32 + }, + { + "file": "tests/hello-world.spec.ts", + "description": "If you opted to include unit tests, the default tests provided demonstrate an example of mocking, setting up fixtures, and testing smart contract calls on an AlgoKit typed client.", + "line": 39 + }, + { + "file": ".env.localnet.template", + "description": "Environment files are a crucial mechanism that allows you to set up the [`algokit deploy`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/deploy.md) feature to simplify deploying your contracts in CI/CD environments (please note we still recommend careful evaluation when it comes to deployment to MainNet). Clone the file and remove the `.template` suffix to apply the changes to deployment scripts and launch configurations. The network prefix `localnet|testnet|mainnet` is primarily optimized for `algokit deploy`. The order of loading the variables is `.env.{network}` < `.env`.", + "line": 2 + }, + { + "file": ".algokit.toml", + "description": "This is the configuration file used by AlgoKit to determine version requirements, `algokit deploy` settings, and references to custom generators.", + "line": 5 + }, + { + "file": ".vscode/launch.json", + "description": "Refer to the pre-bundled Visual Studio launch configurations, offering various options on how to execute the build and deployment of your smart contracts.", + "line": 5 + }, + { + "file": ".vscode/extensions.json", + "description": "We highly recommend installing the recommended extensions to get the most out of this template starter project in your VSCode IDE.", + "line": 3 + }, + { + "file": "smart_contracts/index.ts", + "description": "Uncomment the following lines to enable complementary utilities that will generate artifacts required for the [AlgoKit AVM Debugger](https://github.com/algorandfoundation/algokit-avm-vscode-debugger) VSCode plugin available on the [VSCode Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=algorandfoundation.algokit-avm-vscode-debugger). A new folder will be automatically created in the `.algokit` directory with source maps of all TEAL contracts in this workspace, as well as traces that will appear in a folder at the root of the workspace. You can then use the traces as entry points to trigger the debug extension. Make sure to have the `.algokit.toml` file available at the root of the workspace.", + "line": 13 + } + ] +} diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.vscode/extensions.json b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.vscode/extensions.json index ee83617..e5ca4ca 100644 --- a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.vscode/extensions.json +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.vscode/extensions.json @@ -1,11 +1,10 @@ { "recommendations": [ "ms-python.python", - "charliermarsh.ruff", - "matangover.mypy", - "ms-python.black-formatter", + "esbenp.prettier-vscode", "tamasfe.even-better-toml", "editorconfig.editorconfig", + "vsls-contrib.codetour", "algorandfoundation.algokit-avm-vscode-debugger" ] } diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.vscode/launch.json b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.vscode/launch.json index 46c92e0..4fa392e 100644 --- a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.vscode/launch.json +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.vscode/launch.json @@ -3,20 +3,25 @@ "configurations": [ { "name": "Build & Deploy contracts", - "type": "python", + "type": "node", "request": "launch", - "module": "smart_contracts", - "cwd": "${workspaceFolder}", - "preLaunchTask": "Start AlgoKit LocalNet", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "deploy"], + "cwd": "${workspaceFolder}/smart_contracts", + "console": "integratedTerminal", + "skipFiles": ["/**", "node_modules/**"], + "preLaunchTask": "Build contracts (+ LocalNet)", "envFile": "${workspaceFolder}/.env.localnet" }, { "name": "Deploy contracts", - "type": "python", + "type": "node", "request": "launch", - "module": "smart_contracts", - "args": ["deploy"], - "cwd": "${workspaceFolder}", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "deploy"], + "cwd": "${workspaceFolder}/smart_contracts", + "console": "integratedTerminal", + "skipFiles": ["/**", "node_modules/**"], "envFile": "${workspaceFolder}/.env.localnet" }, { @@ -26,7 +31,7 @@ "module": "smart_contracts", "args": ["build"], "cwd": "${workspaceFolder}" - }, + }, { "type": "avm", "request": "launch", diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.vscode/settings.json b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.vscode/settings.json index a7b97d0..dfae20d 100644 --- a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.vscode/settings.json +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/.vscode/settings.json @@ -12,6 +12,9 @@ ".idea": true }, + // TypeScript + "editor.defaultFormatter": "esbenp.prettier-vscode", + // Python "python.analysis.extraPaths": ["${workspaceFolder}/smart_contracts"], "python.defaultInterpreterPath": "${workspaceFolder}/.venv", @@ -21,26 +24,8 @@ // Prevent default import sorting from running; Ruff will sort imports for us anyway "source.organizeImports": false }, - "editor.defaultFormatter": "ms-python.black-formatter", + "editor.defaultFormatter": null, }, - "black-formatter.args": ["--config=pyproject.toml"], - "ruff.enable": true, - "ruff.lint.run": "onSave", - "ruff.lint.args": ["--config=pyproject.toml"], - "ruff.importStrategy": "fromEnvironment", - "ruff.fixAll": true, //lint and fix all files in workspace - "ruff.organizeImports": true, //organize imports on save - "ruff.codeAction.disableRuleComment": { - "enable": true - }, - "ruff.codeAction.fixViolation": { - "enable": true - }, - "python.analysis.typeCheckingMode": "off", - "mypy.configFile": "pyproject.toml", - // set to empty array to use config from project - "mypy.targets": [], - "mypy.runUsingActiveInterpreter": true, // On Windows, if execution policy is set to Signed (default) then it won't be able to activate the venv // so instead let's set it to RemoteSigned for VS Code terminal diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/README.md b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/README.md index 483ea48..441b324 100644 --- a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/README.md +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/README.md @@ -4,24 +4,20 @@ This project has been generated using AlgoKit. See below for default getting sta # Setup -### Pre-requisites - -- [Python 3.12](https://www.python.org/downloads/) or later -- [Docker](https://www.docker.com/) (only required for LocalNet) - -> Please note `Puya` smart contract development language is currently in alpha / developer preview. It is not recommended for production usage yet. +For an interactive guided walkthrough of the project install [CodeTour](https://marketplace.visualstudio.com/items?itemName=vsls-contrib.codetour) in VS Code and run the `Getting Started with Your AlgoKit Project` tour in the `CodeTour` in Explorer panel in your VSCode instance. ### Initial setup 1. Clone this repository locally 2. Install pre-requisites: - Make sure to have [Docker](https://www.docker.com/) installed and running on your machine. - - Install `AlgoKit` - [Link](https://github.com/algorandfoundation/algokit-cli#install): The recommended version is `1.7.3`. Ensure you can execute `algokit --version` and get `1.7.1` or later. + - Install `AlgoKit` - [Link](https://github.com/algorandfoundation/algokit-cli#install): The minimum required version is `1.1`. Ensure you can execute `algokit --version` and get `1.1` or later. - Bootstrap your local environment; run `algokit bootstrap all` within this folder, which will: - - Install `Poetry` - [Link](https://python-poetry.org/docs/#installation): The minimum required version is `^1.7`. Ensure you can execute `poetry -V` and get `1.2`+ + - Install `Poetry` - [Link](https://python-poetry.org/docs/#installation): The minimum required version is `1.2`. Ensure you can execute `poetry -V` and get `1.2`+ - Run `poetry install` in the root directory, which will set up a `.venv` folder with a Python virtual environment and also install all Python dependencies - Copy `.env.template` to `.env` - Run `algokit localnet start` to start a local Algorand network in Docker. If you are using VS Code launch configurations provided by the template, this will be done automatically for you. + - Run `npm install` to install NPM packages 3. Open the project and start debugging / developing via: - VS Code 1. Open the repository root in VS Code @@ -54,12 +50,17 @@ This project makes use of Python to build Algorand smart contracts. The followin - [Algorand](https://www.algorand.com/) - Layer 1 Blockchain; [Developer portal](https://developer.algorand.org/), [Why Algorand?](https://developer.algorand.org/docs/get-started/basics/why_algorand/) - [AlgoKit](https://github.com/algorandfoundation/algokit-cli) - One-stop shop tool for developers building on the Algorand network; [docs](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/algokit.md), [intro tutorial](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/tutorials/intro.md) -- [Puya](https://github.com/algorand-foundation/puya) - Smart contract development framework for developing Algorand smart contracts in pure Python; [docs](https://github.com/algorandfoundation/puya), [examples](https://github.com/algorandfoundation/puya/tree/main/examples) +- [Beaker](https://github.com/algorand-devrel/beaker) - Smart contract development framework for PyTeal; [docs](https://beaker.algo.xyz), [examples](https://github.com/algorand-devrel/beaker/tree/master/examples) - [PyTEAL](https://github.com/algorand/pyteal) - Python language binding for Algorand smart contracts; [docs](https://pyteal.readthedocs.io/en/stable/) -- [AlgoKit Utils](https://github.com/algorandfoundation/algokit-utils-py) - A set of core Algorand utilities that make it easier to build solutions on Algorand. -- [Poetry](https://python-poetry.org/): Python packaging and dependency management.- [Black](https://github.com/psf/black): A Python code formatter.- [Ruff](https://github.com/charliermarsh/ruff): An extremely fast Python linter. +- [AlgoKit Utils](https://github.com/algorandfoundation/algokit-utils-ts) - A set of core Algorand utilities that make it easier to build solutions on Algorand. +- [Poetry](https://python-poetry.org/): Python packaging and dependency management. +- [npm](https://www.npmjs.com/): Node.js package manager. +- [TypeScript](https://www.typescriptlang.org/): Strongly typed programming language that builds on JavaScript. +- [ts-node-dev](https://github.com/wclr/ts-node-dev): TypeScript development execution environment. -- [mypy](https://mypy-lang.org/): Static type checker. -- [pip-audit](https://pypi.org/project/pip-audit/): Tool for scanning Python environments for packages with known vulnerabilities. It has also been configured to have a productive dev experience out of the box in [VS Code](https://code.visualstudio.com/), see the [.vscode](./.vscode) folder. - +- [AlgoKit Tealer Integration](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/tasks/analyze.md): AlgoKit Tealer Integration is a feature in the CLI that allows you to run [Tealer](https://github.com/crytic/tealer) static analyzer on your TEAL +source code. The invocation of this command is included in: +- The github actions workflow file. +- A VSCode task ('Shift+CMD|CTRL+P' and search for 'Tasks: Run Task' and select 'Analyze TEAL contracts with AlgoKit Tealer integration'). +- A `pre-commit` hook (if you have enabled `pre-commit` in your project). diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/package.json b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/package.json new file mode 100644 index 0000000..4a3ecae --- /dev/null +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/package.json @@ -0,0 +1,24 @@ +{ + "name": "smart_contracts", + "version": "1.0.0", + "description": "Smart contract deployer", + "main": "index.ts", + "scripts": { + "deploy": "ts-node-dev --transpile-only --watch .env -r dotenv/config smart_contracts/index.ts", + "deploy:ci": "ts-node --transpile-only -r dotenv/config smart_contracts/index.ts", + "format": "prettier --write ." + }, + "engines": { + "node": ">=18.0" + }, + "dependencies": { + "@algorandfoundation/algokit-utils": "^5.1.0", + "algosdk": "^2.5.0" + }, + "devDependencies": { + "dotenv": "^16.0.3", + "prettier": "^2.8.4", + "ts-node-dev": "^2.0.0", + "typescript": "^4.9.5" + } +} diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/pyproject.toml b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/pyproject.toml index d0ea7f3..632b374 100644 --- a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/pyproject.toml +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/pyproject.toml @@ -6,39 +6,15 @@ authors = ["None "] readme = "README.md" [tool.poetry.dependencies] -python = "^3.12" +python = "^3.10" +beaker-pyteal = "^1.1.1" algokit-utils = "^2.2.0" python-dotenv = "^1.0.0" -puya = "^0" [tool.poetry.group.dev.dependencies] -black = {extras = ["d"], version = "*"} -ruff = "^0.1.6" -mypy = "*" -pip-audit = "*" +setuptools = "^69.0.2" # Adding explicitly to work around pyteal https://github.com/algorand/pyteal/issues/712 [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" -[tool.ruff] -line-length = 120 -select = ["E", "F", "ANN", "UP", "N", "C4", "B", "A", "YTT", "W", "FBT", "Q", "RUF", "I"] -ignore = [ - "ANN101", # no type for self - "ANN102", # no type for cls -] -unfixable = ["B", "RUF"] - -[tool.ruff.flake8-annotations] -allow-star-arg-any = true -suppress-none-returning = true - -[tool.mypy] -files = "smart_contracts/" -python_version = "3.12" -check_untyped_defs = true -warn_redundant_casts = true -warn_unused_ignores = true -allow_untyped_defs = false -strict_equality = true diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/README.md b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/README.md index f765c95..e97ba8b 100644 --- a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/README.md +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/README.md @@ -3,7 +3,8 @@ By the default the template creates a single `HelloWorld` contract under hello_world folder in the `smart_contracts` directory. To add a new contract: 1. From the root of the project (`../`) execute `algokit generate smart-contract`. This will create a new starter smart contract and deployment configuration file under `{your_contract_name}` subfolder under `smart_contracts` directory. -2. Each contract potentially has different creation parameters and deployment steps. Hence, you need to define your deployment logic in `deploy_config.py`file. +2. Each contract potentially has different creation parameters and deployment steps. Hence, you need to define your deployment logic in `deploy-config.ts`file. 3. `config.py` file will automatically build all contracts under `smart_contracts` directory. If you want to build specific contracts manually, modify the default code provided by the template in `config.py` file. +4. Since you are generating a TypeScript client, you also need to reference your contract deployment logic in `index.ts` file. However, similar to config.py, by default, `index.ts` will auto import all TypeScript deployment files under `smart_contracts` directory. If you want to manually import specific contracts, modify the default code provided by the template in `index.ts` file. > Please note, above is just a suggested convention tailored for the base configuration and structure of this template. Default code supplied by the template in `config.py` and `index.ts` (if using ts clients) files are tailored for the suggested convention. You are free to modify the structure and naming conventions as you see fit. diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/__main__.py b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/__main__.py index 7db8a69..0026e40 100644 --- a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/__main__.py +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/__main__.py @@ -6,13 +6,7 @@ from smart_contracts.config import contracts from smart_contracts.helpers.build import build -from smart_contracts.helpers.deploy import deploy -# Uncomment the following lines 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 -# from algokit_utils.config import config -# config.configure(debug=True, trace_all=True) logging.basicConfig( level=logging.DEBUG, format="%(asctime)s %(levelname)-10s: %(message)s" ) @@ -27,25 +21,12 @@ def main(action: str) -> None: match action: case "build": for contract in contracts: - logger.info(f"Building app at {contract.path}") - build(artifact_path / contract.name, contract.path) - case "deploy": - for contract in contracts: - logger.info(f"Deploying app {contract.name}") - app_spec_path = artifact_path / contract.name / "application.json" - if contract.deploy: - deploy(app_spec_path, contract.deploy) - case "all": - for contract in contracts: - logger.info(f"Building app at {contract.path}") - app_spec_path = build(artifact_path / contract.name, contract.path) - logger.info(f"Deploying {contract.path.name}") - if contract.deploy: - deploy(app_spec_path, contract.deploy) + logger.info(f"Building app {contract.app.name}") + build(artifact_path / contract.app.name, contract.app) if __name__ == "__main__": if len(sys.argv) > 1: main(sys.argv[1]) else: - main("all") + main("build") diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/config.py b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/config.py index 7d56d82..3a1d85f 100644 --- a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/config.py +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/config.py @@ -6,25 +6,26 @@ from algokit_utils import Account, ApplicationSpecification from algosdk.v2client.algod import AlgodClient from algosdk.v2client.indexer import IndexerClient +from beaker import Application @dataclasses.dataclass class SmartContract: - path: Path - name: str - deploy: ( - Callable[[AlgodClient, IndexerClient, ApplicationSpecification, Account], None] - | None - ) = None + app: Application + deploy: Callable[ + [AlgodClient, IndexerClient, ApplicationSpecification, Account], None + ] | None = None -def import_contract(folder: Path) -> Path: +def import_contract(folder: Path) -> Application: """Imports the contract from a folder if it exists.""" - contract_path = folder / "contract.py" - if contract_path.exists(): - return contract_path - else: - raise Exception(f"Contract not found in {folder}") + try: + contract_module = importlib.import_module( + f"{folder.parent.name}.{folder.name}.contract" + ) + return contract_module.app + except ImportError as e: + raise Exception(f"Contract not found in {folder}") from e def import_deploy_if_exists( @@ -51,11 +52,13 @@ def has_contract_file(directory: Path) -> bool: # define contracts to build and/or deploy base_dir = Path("smart_contracts") contracts = [ - SmartContract( - path=import_contract(folder), - name=folder.name, - deploy=import_deploy_if_exists(folder), - ) + SmartContract(app=import_contract(folder), deploy=import_deploy_if_exists(folder)) for folder in base_dir.iterdir() if folder.is_dir() and has_contract_file(folder) ] + +## Comment the above and uncomment the below and define contracts manually if you want to build and specify them +## manually otherwise the above code will always include all contracts under contract.py file for any subdirectory +## in the smart_contracts directory. Optionally it will grab the deploy function from deploy_config.py if it exists. + +# contracts = [] diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/hello_world/contract.py b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/hello_world/contract.py index 589aea9..5ed8de0 100644 --- a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/hello_world/contract.py +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/hello_world/contract.py @@ -1,7 +1,10 @@ -from puyapy import ARC4Contract, arc4 +import beaker +import pyteal as pt -class HelloWorld(ARC4Contract): - @arc4.abimethod() - def hello(self, name: arc4.String) -> arc4.String: - return "Hello, " + name +app = beaker.Application("hello_world") + + +@app.external +def hello(name: pt.abi.String, *, output: pt.abi.String) -> pt.Expr: + return output.set(pt.Concat(pt.Bytes("Hello, "), name.get())) diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/hello_world/deploy-config.ts b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/hello_world/deploy-config.ts new file mode 100644 index 0000000..527fe2c --- /dev/null +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/hello_world/deploy-config.ts @@ -0,0 +1,49 @@ +import * as algokit from '@algorandfoundation/algokit-utils' +import { HelloWorldClient } from '../artifacts/hello_world/client' + +// Below is a showcase of various deployment options you can use in TypeScript Client +export async function deploy() { + console.log('=== Deploying HelloWorld ===') + + const algod = algokit.getAlgoClient() + const indexer = algokit.getAlgoIndexerClient() + const deployer = await algokit.mnemonicAccountFromEnvironment({ name: 'DEPLOYER', fundWith: algokit.algos(3) }, algod) + await algokit.ensureFunded( + { + accountToFund: deployer, + minSpendingBalance: algokit.algos(2), + minFundingIncrement: algokit.algos(2), + }, + algod, + ) + const appClient = new HelloWorldClient( + { + resolveBy: 'creatorAndName', + findExistingUsing: indexer, + sender: deployer, + creatorAddress: deployer.addr, + }, + algod, + ) + const app = await appClient.deploy({ + onSchemaBreak: 'append', + onUpdate: 'append', + }) + + + // If app was just created fund the app account + if (['create', 'replace'].includes(app.operationPerformed)) { + algokit.transferAlgos( + { + amount: algokit.algos(1), + from: deployer, + to: app.appAddress, + }, + algod, + ) + } + + 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}`) +} diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/hello_world/deploy_config.py b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/hello_world/deploy_config.py deleted file mode 100644 index 82e67a8..0000000 --- a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/hello_world/deploy_config.py +++ /dev/null @@ -1,36 +0,0 @@ -import logging - -import algokit_utils -from algosdk.v2client.algod import AlgodClient -from algosdk.v2client.indexer import IndexerClient - -logger = logging.getLogger(__name__) - - -# define deployment behaviour based on supplied app spec -def deploy( - algod_client: AlgodClient, - indexer_client: IndexerClient, - app_spec: algokit_utils.ApplicationSpecification, - deployer: algokit_utils.Account, -) -> None: - from smart_contracts.artifacts.hello_world.client import ( - HelloWorldClient, - ) - - app_client = HelloWorldClient( - algod_client, - creator=deployer, - indexer_client=indexer_client, - ) - - app_client.deploy( - on_schema_break=algokit_utils.OnSchemaBreak.AppendApp, - on_update=algokit_utils.OnUpdate.AppendApp, - ) - name = "world" - response = app_client.hello(name=name) - logger.info( - f"Called hello on {app_spec.contract.name} ({app_client.app_id}) " - f"with name={name}, received: {response.return_value}" - ) diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/helpers/build.py b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/helpers/build.py index 0dad0c8..167ee90 100644 --- a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/helpers/build.py +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/helpers/build.py @@ -3,34 +3,22 @@ from pathlib import Path from shutil import rmtree +import beaker + logger = logging.getLogger(__name__) -deployment_extension = "py" +deployment_extension = "ts" -def build(output_dir: Path, contract_path: Path) -> Path: +def build(output_dir: Path, app: beaker.Application) -> Path: output_dir = output_dir.resolve() if output_dir.exists(): rmtree(output_dir) output_dir.mkdir(exist_ok=True, parents=True) - logger.info(f"Exporting {contract_path} to {output_dir}") + logger.info(f"Exporting {app.name} to {output_dir}") + specification = app.build() + specification.export(output_dir) - build_result = subprocess.run( - [ - "poetry", - "run", - "puyapy", - contract_path.absolute(), - f"--out-dir={output_dir}", - "--output-arc32", - ], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - ) - if build_result.returncode: - raise Exception(f"Could not build contract:\n{build_result.stdout}") - - generate_result = subprocess.run( + result = subprocess.run( [ "algokit", "generate", @@ -43,14 +31,13 @@ def build(output_dir: Path, contract_path: Path) -> Path: stderr=subprocess.STDOUT, text=True, ) - if generate_result.returncode: - if "No such command" in generate_result.stdout: + if result.returncode: + if "No such command" in result.stdout: raise Exception( - "Could not generate typed client, requires AlgoKit 1.1 or " + "Could not generate typed client, requires AlgoKit 1.8.0 or " "later. Please update AlgoKit" ) else: - raise Exception( - f"Could not generate typed client:\n{generate_result.stdout}" - ) + raise Exception(f"Could not generate typed client:\n{result.stdout}") + return output_dir / "application.json" diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/helpers/deploy.py b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/helpers/deploy.py deleted file mode 100644 index 08367a3..0000000 --- a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/helpers/deploy.py +++ /dev/null @@ -1,50 +0,0 @@ -import logging -from collections.abc import Callable -from pathlib import Path - -from algokit_utils import ( - Account, - ApplicationSpecification, - EnsureBalanceParameters, - ensure_funded, - get_account, - get_algod_client, - get_indexer_client, -) -from algosdk.util import algos_to_microalgos -from algosdk.v2client.algod import AlgodClient -from algosdk.v2client.indexer import IndexerClient - -logger = logging.getLogger(__name__) - - -def deploy( - app_spec_path: Path, - deploy_callback: Callable[ - [AlgodClient, IndexerClient, ApplicationSpecification, Account], None - ], - deployer_initial_funds: int = 2, -) -> None: - # get clients - # by default client configuration is loaded from environment variables - algod_client = get_algod_client() - indexer_client = get_indexer_client() - - # get app spec - app_spec = ApplicationSpecification.from_json(app_spec_path.read_text()) - - # get deployer account by name - deployer = get_account(algod_client, "DEPLOYER", fund_with_algos=0) - - minimum_funds_micro_algos = algos_to_microalgos(deployer_initial_funds) - ensure_funded( - algod_client, - EnsureBalanceParameters( - account_to_fund=deployer, - min_spending_balance_micro_algos=minimum_funds_micro_algos, - min_funding_increment_micro_algos=minimum_funds_micro_algos, - ), - ) - - # use provided callback to deploy the app - deploy_callback(algod_client, indexer_client, app_spec, deployer) diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/index.ts b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/index.ts new file mode 100644 index 0000000..af40ab4 --- /dev/null +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/smart_contracts/index.ts @@ -0,0 +1,47 @@ +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' + +// 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 + +algokit.Config.configure({ + logger: consoleLogger, + // debug: true, + // traceAll: true, +}) + +// base directory +const baseDir = path.resolve(__dirname) + +// function to validate and dynamically import a module +async function importDeployerIfExists(dir: string) { + const deployerPath = path.resolve(dir, 'deploy-config') + if (fs.existsSync(deployerPath + '.ts') || fs.existsSync(deployerPath + '.js')) { + const deployer = await import(deployerPath) + return deployer.deploy + } +} + +// get a list of all deployers from the subdirectories +async function getDeployers() { + const directories = fs.readdirSync(baseDir, { withFileTypes: true }) + .filter(dirent => dirent.isDirectory()) + .map(dirent => path.resolve(baseDir, dirent.name)) + + return Promise.all(directories.map(importDeployerIfExists)) +} + +// execute all the deployers +(async () => { + const contractDeployers = (await getDeployers()).filter(Boolean) + + for (const deployer of contractDeployers) { + try { + await deployer() + } catch (e) { + console.error(e) + } + } +})() diff --git a/examples/starter_beaker_react/projects/starter_beaker_react-contracts/tsconfig.json b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/tsconfig.json new file mode 100644 index 0000000..7c76204 --- /dev/null +++ b/examples/starter_beaker_react/projects/starter_beaker_react-contracts/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "strict": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "allowJs": false, + "allowSyntheticDefaultImports": true, + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist", "coverage"] +} diff --git a/examples/starter_puya_react/.copier-answers.yml b/examples/starter_puya_react/.copier-answers.yml index 1c748da..d98b155 100644 --- a/examples/starter_puya_react/.copier-answers.yml +++ b/examples/starter_puya_react/.copier-answers.yml @@ -4,7 +4,7 @@ _src_path: author_email: None author_name: None contract_name: hello_world -deployment_language: python +deployment_language: typescript preset_name: starter project_name: starter_puya_react diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/.algokit.toml b/examples/starter_puya_react/projects/starter_puya_react-contracts/.algokit.toml index 9ee1f61..ab456dc 100644 --- a/examples/starter_puya_react/projects/starter_puya_react-contracts/.algokit.toml +++ b/examples/starter_puya_react/projects/starter_puya_react-contracts/.algokit.toml @@ -2,7 +2,7 @@ min_version = "v1.8.0" [deploy] -command = "poetry run python -m smart_contracts deploy" +command = "npm run deploy:ci" environment_secrets = [ "DEPLOYER_MNEMONIC", ] diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 b/examples/starter_puya_react/projects/starter_puya_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 new file mode 100644 index 0000000..c8b69ba --- /dev/null +++ b/examples/starter_puya_react/projects/starter_puya_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 @@ -0,0 +1,50 @@ +import * as algokit from '@algorandfoundation/algokit-utils' +import { HelloWorldClient } from '../artifacts/hello_world/client' + +// Below is a showcase of various deployment options you can use in TypeScript Client +export async function deploy() { + console.log('=== Deploying HelloWorld ===') + + const algod = algokit.getAlgoClient() + const indexer = algokit.getAlgoIndexerClient() + const deployer = await algokit.mnemonicAccountFromEnvironment({ name: 'DEPLOYER', fundWith: algokit.algos(3) }, algod) + await algokit.ensureFunded( + { + accountToFund: deployer, + minSpendingBalance: algokit.algos(2), + minFundingIncrement: algokit.algos(2), + }, + algod, + ) + const appClient = new HelloWorldClient( + { + resolveBy: 'creatorAndName', + findExistingUsing: indexer, + sender: deployer, + creatorAddress: deployer.addr, + }, + algod, + ) + + const app = await appClient.deploy({ + onSchemaBreak: 'append', + onUpdate: 'append', + }) + + + // If app was just created fund the app account + if (['create', 'replace'].includes(app.operationPerformed)) { + algokit.transferAlgos( + { + amount: algokit.algos(1), + from: deployer, + to: app.appAddress, + }, + algod, + ) + } + + 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}`) +} diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 b/examples/starter_puya_react/projects/starter_puya_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 deleted file mode 100644 index 9ab7336..0000000 --- a/examples/starter_puya_react/projects/starter_puya_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 +++ /dev/null @@ -1,35 +0,0 @@ -import logging - -import algokit_utils -from algosdk.v2client.algod import AlgodClient -from algosdk.v2client.indexer import IndexerClient - -logger = logging.getLogger(__name__) - - -# define deployment behaviour based on supplied app spec -def deploy( - algod_client: AlgodClient, - indexer_client: IndexerClient, - app_spec: algokit_utils.ApplicationSpecification, - deployer: algokit_utils.Account, -) -> None: - from smart_contracts.artifacts.hello_world.client import ( - HelloWorldClient, - ) - - app_client = HelloWorldClient( - algod_client, - creator=deployer, - indexer_client=indexer_client, - ) - app_client.deploy( - on_schema_break=algokit_utils.OnSchemaBreak.AppendApp, - on_update=algokit_utils.OnUpdate.AppendApp, - ) - name = "world" - response = app_client.hello(name=name) - logger.info( - f"Called hello on {app_spec.contract.name} ({app_client.app_id}) " - f"with name={name}, received: {response.return_value}" - ) diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/.copier-answers.yml b/examples/starter_puya_react/projects/starter_puya_react-contracts/.copier-answers.yml index fef6d74..14fcc1d 100644 --- a/examples/starter_puya_react/projects/starter_puya_react-contracts/.copier-answers.yml +++ b/examples/starter_puya_react/projects/starter_puya_react-contracts/.copier-answers.yml @@ -4,7 +4,7 @@ _src_path: gh:algorandfoundation/algokit-puya-template author_email: None author_name: None contract_name: hello_world -deployment_language: python +deployment_language: typescript preset_name: starter project_name: starter_puya_react-contracts diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/.prettierignore b/examples/starter_puya_react/projects/starter_puya_react-contracts/.prettierignore new file mode 100644 index 0000000..dbda6ae --- /dev/null +++ b/examples/starter_puya_react/projects/starter_puya_react-contracts/.prettierignore @@ -0,0 +1,12 @@ +# don't ever format node_modules +node_modules +# don't lint format output (make sure it's set to your correct build folder name) +dist +build +# don't format nyc coverage output +coverage +# don't format generated types +**/generated/types.d.ts +**/generated/types.ts +# don't format ide files +.idea diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/.prettierrc.js b/examples/starter_puya_react/projects/starter_puya_react-contracts/.prettierrc.js new file mode 100644 index 0000000..c484d0e --- /dev/null +++ b/examples/starter_puya_react/projects/starter_puya_react-contracts/.prettierrc.js @@ -0,0 +1,10 @@ +module.exports = { + singleQuote: true, + jsxSingleQuote: false, + semi: false, + tabWidth: 2, + trailingComma: 'all', + printWidth: 120, + endOfLine: 'lf', + arrowParens: 'always', +} diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/.vscode/extensions.json b/examples/starter_puya_react/projects/starter_puya_react-contracts/.vscode/extensions.json index ee83617..47b2649 100644 --- a/examples/starter_puya_react/projects/starter_puya_react-contracts/.vscode/extensions.json +++ b/examples/starter_puya_react/projects/starter_puya_react-contracts/.vscode/extensions.json @@ -4,6 +4,7 @@ "charliermarsh.ruff", "matangover.mypy", "ms-python.black-formatter", + "esbenp.prettier-vscode", "tamasfe.even-better-toml", "editorconfig.editorconfig", "algorandfoundation.algokit-avm-vscode-debugger" diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/.vscode/launch.json b/examples/starter_puya_react/projects/starter_puya_react-contracts/.vscode/launch.json index 46c92e0..de9a409 100644 --- a/examples/starter_puya_react/projects/starter_puya_react-contracts/.vscode/launch.json +++ b/examples/starter_puya_react/projects/starter_puya_react-contracts/.vscode/launch.json @@ -3,20 +3,25 @@ "configurations": [ { "name": "Build & Deploy contracts", - "type": "python", + "type": "node", "request": "launch", - "module": "smart_contracts", - "cwd": "${workspaceFolder}", - "preLaunchTask": "Start AlgoKit LocalNet", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "deploy"], + "cwd": "${workspaceFolder}/smart_contracts", + "console": "integratedTerminal", + "skipFiles": ["/**", "node_modules/**"], + "preLaunchTask": "Build contracts (+ LocalNet)", "envFile": "${workspaceFolder}/.env.localnet" }, { "name": "Deploy contracts", - "type": "python", + "type": "node", "request": "launch", - "module": "smart_contracts", - "args": ["deploy"], - "cwd": "${workspaceFolder}", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "deploy"], + "cwd": "${workspaceFolder}/smart_contracts", + "console": "integratedTerminal", + "skipFiles": ["/**", "node_modules/**"], "envFile": "${workspaceFolder}/.env.localnet" }, { diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/.vscode/settings.json b/examples/starter_puya_react/projects/starter_puya_react-contracts/.vscode/settings.json index a7b97d0..af8031f 100644 --- a/examples/starter_puya_react/projects/starter_puya_react-contracts/.vscode/settings.json +++ b/examples/starter_puya_react/projects/starter_puya_react-contracts/.vscode/settings.json @@ -12,6 +12,9 @@ ".idea": true }, + // TypeScript + "editor.defaultFormatter": "esbenp.prettier-vscode", + // Python "python.analysis.extraPaths": ["${workspaceFolder}/smart_contracts"], "python.defaultInterpreterPath": "${workspaceFolder}/.venv", diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/README.md b/examples/starter_puya_react/projects/starter_puya_react-contracts/README.md index 6f55dfd..92bc1c2 100644 --- a/examples/starter_puya_react/projects/starter_puya_react-contracts/README.md +++ b/examples/starter_puya_react/projects/starter_puya_react-contracts/README.md @@ -22,6 +22,7 @@ This project has been generated using AlgoKit. See below for default getting sta - Run `poetry install` in the root directory, which will set up a `.venv` folder with a Python virtual environment and also install all Python dependencies - Copy `.env.template` to `.env` - Run `algokit localnet start` to start a local Algorand network in Docker. If you are using VS Code launch configurations provided by the template, this will be done automatically for you. + - Run `npm install` to install NPM packages 3. Open the project and start debugging / developing via: - VS Code 1. Open the repository root in VS Code @@ -56,10 +57,14 @@ This project makes use of Python to build Algorand smart contracts. The followin - [AlgoKit](https://github.com/algorandfoundation/algokit-cli) - One-stop shop tool for developers building on the Algorand network; [docs](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/algokit.md), [intro tutorial](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/tutorials/intro.md) - [Puya](https://github.com/algorand-foundation/puya) - Smart contract development framework for developing Algorand smart contracts in pure Python; [docs](https://github.com/algorandfoundation/puya), [examples](https://github.com/algorandfoundation/puya/tree/main/examples) - [PyTEAL](https://github.com/algorand/pyteal) - Python language binding for Algorand smart contracts; [docs](https://pyteal.readthedocs.io/en/stable/) -- [AlgoKit Utils](https://github.com/algorandfoundation/algokit-utils-py) - A set of core Algorand utilities that make it easier to build solutions on Algorand. +- [AlgoKit Utils](https://github.com/algorandfoundation/algokit-utils-ts) - A set of core Algorand utilities that make it easier to build solutions on Algorand. - [Poetry](https://python-poetry.org/): Python packaging and dependency management.- [Black](https://github.com/psf/black): A Python code formatter.- [Ruff](https://github.com/charliermarsh/ruff): An extremely fast Python linter. - [mypy](https://mypy-lang.org/): Static type checker. - [pip-audit](https://pypi.org/project/pip-audit/): Tool for scanning Python environments for packages with known vulnerabilities. +- [npm](https://www.npmjs.com/): Node.js package manager +- [TypeScript](https://www.typescriptlang.org/): Strongly typed programming language that builds on JavaScript +- [ts-node-dev](https://github.com/wclr/ts-node-dev): TypeScript development execution environment + It has also been configured to have a productive dev experience out of the box in [VS Code](https://code.visualstudio.com/), see the [.vscode](./.vscode) folder. diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/package.json b/examples/starter_puya_react/projects/starter_puya_react-contracts/package.json new file mode 100644 index 0000000..4a3ecae --- /dev/null +++ b/examples/starter_puya_react/projects/starter_puya_react-contracts/package.json @@ -0,0 +1,24 @@ +{ + "name": "smart_contracts", + "version": "1.0.0", + "description": "Smart contract deployer", + "main": "index.ts", + "scripts": { + "deploy": "ts-node-dev --transpile-only --watch .env -r dotenv/config smart_contracts/index.ts", + "deploy:ci": "ts-node --transpile-only -r dotenv/config smart_contracts/index.ts", + "format": "prettier --write ." + }, + "engines": { + "node": ">=18.0" + }, + "dependencies": { + "@algorandfoundation/algokit-utils": "^5.1.0", + "algosdk": "^2.5.0" + }, + "devDependencies": { + "dotenv": "^16.0.3", + "prettier": "^2.8.4", + "ts-node-dev": "^2.0.0", + "typescript": "^4.9.5" + } +} diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/README.md b/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/README.md index f765c95..e97ba8b 100644 --- a/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/README.md +++ b/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/README.md @@ -3,7 +3,8 @@ By the default the template creates a single `HelloWorld` contract under hello_world folder in the `smart_contracts` directory. To add a new contract: 1. From the root of the project (`../`) execute `algokit generate smart-contract`. This will create a new starter smart contract and deployment configuration file under `{your_contract_name}` subfolder under `smart_contracts` directory. -2. Each contract potentially has different creation parameters and deployment steps. Hence, you need to define your deployment logic in `deploy_config.py`file. +2. Each contract potentially has different creation parameters and deployment steps. Hence, you need to define your deployment logic in `deploy-config.ts`file. 3. `config.py` file will automatically build all contracts under `smart_contracts` directory. If you want to build specific contracts manually, modify the default code provided by the template in `config.py` file. +4. Since you are generating a TypeScript client, you also need to reference your contract deployment logic in `index.ts` file. However, similar to config.py, by default, `index.ts` will auto import all TypeScript deployment files under `smart_contracts` directory. If you want to manually import specific contracts, modify the default code provided by the template in `index.ts` file. > Please note, above is just a suggested convention tailored for the base configuration and structure of this template. Default code supplied by the template in `config.py` and `index.ts` (if using ts clients) files are tailored for the suggested convention. You are free to modify the structure and naming conventions as you see fit. diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/__main__.py b/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/__main__.py index 7db8a69..b91ab2e 100644 --- a/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/__main__.py +++ b/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/__main__.py @@ -6,13 +6,7 @@ from smart_contracts.config import contracts from smart_contracts.helpers.build import build -from smart_contracts.helpers.deploy import deploy -# Uncomment the following lines 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 -# from algokit_utils.config import config -# config.configure(debug=True, trace_all=True) logging.basicConfig( level=logging.DEBUG, format="%(asctime)s %(levelname)-10s: %(message)s" ) @@ -29,23 +23,10 @@ def main(action: str) -> None: for contract in contracts: logger.info(f"Building app at {contract.path}") build(artifact_path / contract.name, contract.path) - case "deploy": - for contract in contracts: - logger.info(f"Deploying app {contract.name}") - app_spec_path = artifact_path / contract.name / "application.json" - if contract.deploy: - deploy(app_spec_path, contract.deploy) - case "all": - for contract in contracts: - logger.info(f"Building app at {contract.path}") - app_spec_path = build(artifact_path / contract.name, contract.path) - logger.info(f"Deploying {contract.path.name}") - if contract.deploy: - deploy(app_spec_path, contract.deploy) if __name__ == "__main__": if len(sys.argv) > 1: main(sys.argv[1]) else: - main("all") + main("build") diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/hello_world/deploy-config.ts b/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/hello_world/deploy-config.ts new file mode 100644 index 0000000..c8b69ba --- /dev/null +++ b/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/hello_world/deploy-config.ts @@ -0,0 +1,50 @@ +import * as algokit from '@algorandfoundation/algokit-utils' +import { HelloWorldClient } from '../artifacts/hello_world/client' + +// Below is a showcase of various deployment options you can use in TypeScript Client +export async function deploy() { + console.log('=== Deploying HelloWorld ===') + + const algod = algokit.getAlgoClient() + const indexer = algokit.getAlgoIndexerClient() + const deployer = await algokit.mnemonicAccountFromEnvironment({ name: 'DEPLOYER', fundWith: algokit.algos(3) }, algod) + await algokit.ensureFunded( + { + accountToFund: deployer, + minSpendingBalance: algokit.algos(2), + minFundingIncrement: algokit.algos(2), + }, + algod, + ) + const appClient = new HelloWorldClient( + { + resolveBy: 'creatorAndName', + findExistingUsing: indexer, + sender: deployer, + creatorAddress: deployer.addr, + }, + algod, + ) + + const app = await appClient.deploy({ + onSchemaBreak: 'append', + onUpdate: 'append', + }) + + + // If app was just created fund the app account + if (['create', 'replace'].includes(app.operationPerformed)) { + algokit.transferAlgos( + { + amount: algokit.algos(1), + from: deployer, + to: app.appAddress, + }, + algod, + ) + } + + 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}`) +} diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/hello_world/deploy_config.py b/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/hello_world/deploy_config.py deleted file mode 100644 index 82e67a8..0000000 --- a/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/hello_world/deploy_config.py +++ /dev/null @@ -1,36 +0,0 @@ -import logging - -import algokit_utils -from algosdk.v2client.algod import AlgodClient -from algosdk.v2client.indexer import IndexerClient - -logger = logging.getLogger(__name__) - - -# define deployment behaviour based on supplied app spec -def deploy( - algod_client: AlgodClient, - indexer_client: IndexerClient, - app_spec: algokit_utils.ApplicationSpecification, - deployer: algokit_utils.Account, -) -> None: - from smart_contracts.artifacts.hello_world.client import ( - HelloWorldClient, - ) - - app_client = HelloWorldClient( - algod_client, - creator=deployer, - indexer_client=indexer_client, - ) - - app_client.deploy( - on_schema_break=algokit_utils.OnSchemaBreak.AppendApp, - on_update=algokit_utils.OnUpdate.AppendApp, - ) - name = "world" - response = app_client.hello(name=name) - logger.info( - f"Called hello on {app_spec.contract.name} ({app_client.app_id}) " - f"with name={name}, received: {response.return_value}" - ) diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/helpers/build.py b/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/helpers/build.py index 0dad0c8..67377d3 100644 --- a/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/helpers/build.py +++ b/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/helpers/build.py @@ -4,7 +4,7 @@ from shutil import rmtree logger = logging.getLogger(__name__) -deployment_extension = "py" +deployment_extension = "ts" def build(output_dir: Path, contract_path: Path) -> Path: diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/helpers/deploy.py b/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/helpers/deploy.py deleted file mode 100644 index 08367a3..0000000 --- a/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/helpers/deploy.py +++ /dev/null @@ -1,50 +0,0 @@ -import logging -from collections.abc import Callable -from pathlib import Path - -from algokit_utils import ( - Account, - ApplicationSpecification, - EnsureBalanceParameters, - ensure_funded, - get_account, - get_algod_client, - get_indexer_client, -) -from algosdk.util import algos_to_microalgos -from algosdk.v2client.algod import AlgodClient -from algosdk.v2client.indexer import IndexerClient - -logger = logging.getLogger(__name__) - - -def deploy( - app_spec_path: Path, - deploy_callback: Callable[ - [AlgodClient, IndexerClient, ApplicationSpecification, Account], None - ], - deployer_initial_funds: int = 2, -) -> None: - # get clients - # by default client configuration is loaded from environment variables - algod_client = get_algod_client() - indexer_client = get_indexer_client() - - # get app spec - app_spec = ApplicationSpecification.from_json(app_spec_path.read_text()) - - # get deployer account by name - deployer = get_account(algod_client, "DEPLOYER", fund_with_algos=0) - - minimum_funds_micro_algos = algos_to_microalgos(deployer_initial_funds) - ensure_funded( - algod_client, - EnsureBalanceParameters( - account_to_fund=deployer, - min_spending_balance_micro_algos=minimum_funds_micro_algos, - min_funding_increment_micro_algos=minimum_funds_micro_algos, - ), - ) - - # use provided callback to deploy the app - deploy_callback(algod_client, indexer_client, app_spec, deployer) diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/index.ts b/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/index.ts new file mode 100644 index 0000000..af40ab4 --- /dev/null +++ b/examples/starter_puya_react/projects/starter_puya_react-contracts/smart_contracts/index.ts @@ -0,0 +1,47 @@ +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' + +// 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 + +algokit.Config.configure({ + logger: consoleLogger, + // debug: true, + // traceAll: true, +}) + +// base directory +const baseDir = path.resolve(__dirname) + +// function to validate and dynamically import a module +async function importDeployerIfExists(dir: string) { + const deployerPath = path.resolve(dir, 'deploy-config') + if (fs.existsSync(deployerPath + '.ts') || fs.existsSync(deployerPath + '.js')) { + const deployer = await import(deployerPath) + return deployer.deploy + } +} + +// get a list of all deployers from the subdirectories +async function getDeployers() { + const directories = fs.readdirSync(baseDir, { withFileTypes: true }) + .filter(dirent => dirent.isDirectory()) + .map(dirent => path.resolve(baseDir, dirent.name)) + + return Promise.all(directories.map(importDeployerIfExists)) +} + +// execute all the deployers +(async () => { + const contractDeployers = (await getDeployers()).filter(Boolean) + + for (const deployer of contractDeployers) { + try { + await deployer() + } catch (e) { + console.error(e) + } + } +})() diff --git a/examples/starter_puya_react/projects/starter_puya_react-contracts/tsconfig.json b/examples/starter_puya_react/projects/starter_puya_react-contracts/tsconfig.json new file mode 100644 index 0000000..7c76204 --- /dev/null +++ b/examples/starter_puya_react/projects/starter_puya_react-contracts/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "strict": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "allowJs": false, + "allowSyntheticDefaultImports": true, + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist", "coverage"] +} diff --git a/examples/starter_tealscript_react/.copier-answers.yml b/examples/starter_tealscript_react/.copier-answers.yml index 6dc7bbf..91d247c 100644 --- a/examples/starter_tealscript_react/.copier-answers.yml +++ b/examples/starter_tealscript_react/.copier-answers.yml @@ -4,7 +4,6 @@ _src_path: author_email: None author_name: None contract_name: hello_world -deployment_language: python preset_name: starter project_name: starter_tealscript_react diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-app/src/components/AppCalls.tsx b/examples/starter_tealscript_react/projects/starter_tealscript_react-app/src/components/AppCalls.tsx index 6a3338e..fdf09d3 100644 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-app/src/components/AppCalls.tsx +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-app/src/components/AppCalls.tsx @@ -5,7 +5,7 @@ import { useWallet } from '@txnlab/use-wallet' import { useSnackbar } from 'notistack' import { useState } from 'react' -import { HelloWorldClient } from '../contracts/HelloWorld' +import { HelloWorldClient } from '../contracts/StarterTealscriptReactContracts' import { getAlgodConfigFromViteEnvironment, getIndexerConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.algokit.toml b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.algokit.toml deleted file mode 100644 index ef52e14..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.algokit.toml +++ /dev/null @@ -1,19 +0,0 @@ -[algokit] -min_version = "v1.8.0" - -[deploy] -command = "poetry run python -m smart_contracts deploy" -environment_secrets = [ - "DEPLOYER_MNEMONIC", -] - -[deploy.localnet] -environment_secrets = [] - -[generate.smart_contract] -description = "Adds new smart contract to existing project" -path = ".algokit/generators/create_contract" - -[project] -type = 'backend' -name = 'starter_tealscript_react-contracts' diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.algokit/generators/create_contract/copier.yaml b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.algokit/generators/create_contract/copier.yaml deleted file mode 100644 index 73805de..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.algokit/generators/create_contract/copier.yaml +++ /dev/null @@ -1,10 +0,0 @@ -_tasks: - - "echo '==== Successfully initialized new smart contract 🚀 ===='" - -contract_name: - type: str - help: Name of your new contract. - placeholder: "my-new-contract" - default: "my-new-contract" - -_templates_suffix: ".j2" diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 deleted file mode 100644 index eabbaa0..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 +++ /dev/null @@ -1,7 +0,0 @@ -from puyapy import ARC4Contract, arc4 - - -class {{ contract_name.split('_')|map('capitalize')|join }}(ARC4Contract): - @arc4.abimethod() - def hello(self, name: arc4.String) -> arc4.String: - return "Hello, " + name diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 deleted file mode 100644 index 9ab7336..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 +++ /dev/null @@ -1,35 +0,0 @@ -import logging - -import algokit_utils -from algosdk.v2client.algod import AlgodClient -from algosdk.v2client.indexer import IndexerClient - -logger = logging.getLogger(__name__) - - -# define deployment behaviour based on supplied app spec -def deploy( - algod_client: AlgodClient, - indexer_client: IndexerClient, - app_spec: algokit_utils.ApplicationSpecification, - deployer: algokit_utils.Account, -) -> None: - from smart_contracts.artifacts.hello_world.client import ( - HelloWorldClient, - ) - - app_client = HelloWorldClient( - algod_client, - creator=deployer, - indexer_client=indexer_client, - ) - app_client.deploy( - on_schema_break=algokit_utils.OnSchemaBreak.AppendApp, - on_update=algokit_utils.OnUpdate.AppendApp, - ) - name = "world" - response = app_client.hello(name=name) - logger.info( - f"Called hello on {app_spec.contract.name} ({app_client.app_id}) " - f"with name={name}, received: {response.return_value}" - ) diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.copier-answers.yml b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.copier-answers.yml deleted file mode 100644 index a812dcc..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.copier-answers.yml +++ /dev/null @@ -1,10 +0,0 @@ -# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY -_commit: 0.4.0-20-gfaa6d61 -_src_path: gh:algorandfoundation/algokit-puya-template -author_email: None -author_name: None -contract_name: hello_world -deployment_language: python -preset_name: starter -project_name: starter_tealscript_react-contracts - diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.devcontainer.json b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.devcontainer.json new file mode 100644 index 0000000..3a30b2f --- /dev/null +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.devcontainer.json @@ -0,0 +1,16 @@ +{ + "forwardPorts": [4001, 4002, 8980], + "portsAttributes": { + "4001": { + "label": "algod" + }, + "4002": { + "label": "kmd" + }, + "8980": { + "label": "indexer" + } + }, + "postCreateCommand": "pipx install algokit-cli", + "postStartCommand": "algokit localnet start" +} \ No newline at end of file diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.editorconfig b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.editorconfig deleted file mode 100644 index e2fda34..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.editorconfig +++ /dev/null @@ -1,10 +0,0 @@ -root=true - -[*] -indent_style = space -indent_size = 2 -end_of_line = lf -insert_final_newline = true - -[*.py] -indent_size = 4 diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.env.localnet.template b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.env.localnet.template deleted file mode 100644 index fcbf442..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.env.localnet.template +++ /dev/null @@ -1,7 +0,0 @@ -# this file should contain environment variables specific to algokit localnet -ALGOD_TOKEN=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -ALGOD_SERVER=http://localhost -ALGOD_PORT=4001 -INDEXER_TOKEN=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -INDEXER_SERVER=http://localhost -INDEXER_PORT=8980 diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.env.template b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.env.template deleted file mode 100644 index 184b393..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.env.template +++ /dev/null @@ -1 +0,0 @@ -# this file should contain environment variables common to all environments/networks diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.env.testnet.template b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.env.testnet.template deleted file mode 100644 index eeea43d..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.env.testnet.template +++ /dev/null @@ -1,3 +0,0 @@ -# this file contains algorand network settings for interacting with testnet via algonode -ALGOD_SERVER=https://testnet-api.algonode.cloud -INDEXER_SERVER=https://testnet-idx.algonode.cloud diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.eslintrc.js b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.eslintrc.js new file mode 100644 index 0000000..0fede66 --- /dev/null +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.eslintrc.js @@ -0,0 +1,56 @@ +module.exports = { + env: { + browser: true, + es2021: true, + }, + extends: [ + 'airbnb-base', + 'plugin:import/errors', + 'plugin:import/warnings', + 'plugin:import/typescript', + 'plugin:prettier/recommended', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + plugins: ['@typescript-eslint'], + rules: { + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/ban-ts-comment': 'warn', + 'import/prefer-default-export': 'off', + 'import/extensions': [ + 'error', + 'ignorePackages', + { + js: 'never', + jsx: 'never', + ts: 'never', + tsx: 'never', + }, + ], + 'import/no-extraneous-dependencies': [ + 'error', + { + devDependencies: ['**/*.test.ts'], + }, + ], + }, + overrides: [ + { + files: ['*.algo.ts'], + rules: { + 'import/no-extraneous-dependencies': 'off', + 'object-shorthand': 'off', + 'class-methods-use-this': 'off', + 'no-undef': 'off', + 'max-classes-per-file': 'off', + 'no-bitwise': 'off', + 'operator-assignment': 'off', + 'prefer-template': 'off', + 'prefer-destructuring': 'off', + }, + }, + ], +}; diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.gitattributes b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.gitattributes deleted file mode 100644 index 6313b56..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -* text=auto eol=lf diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.gitignore b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.gitignore index e5f0b9e..40b878d 100644 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.gitignore +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.gitignore @@ -1,180 +1 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ -coverage/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -.env.* -!.env.*.template -!.env.template -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Ruff (linter) -.ruff_cache/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -.idea -!.idea/ -.idea/* -!.idea/runConfigurations/ - -# macOS -.DS_Store - -# Received approval test files -*.received.* - -# NPM -node_modules - -# AlgoKit -debug_traces/ - -.algokit/static-analysis/tealer/ +node_modules/ \ No newline at end of file diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.prettierrc.toml b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.prettierrc.toml new file mode 100644 index 0000000..d979519 --- /dev/null +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.prettierrc.toml @@ -0,0 +1,6 @@ +# .prettierrc.toml +trailingComma = "es5" +tabWidth = 2 +semi = true +singleQuote = true +printWidth = 120 \ No newline at end of file diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.vscode/extensions.json b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.vscode/extensions.json index ee83617..c582379 100644 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.vscode/extensions.json +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.vscode/extensions.json @@ -1,11 +1,5 @@ { - "recommendations": [ - "ms-python.python", - "charliermarsh.ruff", - "matangover.mypy", - "ms-python.black-formatter", - "tamasfe.even-better-toml", - "editorconfig.editorconfig", - "algorandfoundation.algokit-avm-vscode-debugger" - ] -} + "recommendations": [ + "dbaeumer.vscode-eslint", + ] + } \ No newline at end of file diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.vscode/launch.json b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.vscode/launch.json deleted file mode 100644 index 46c92e0..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.vscode/launch.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Build & Deploy contracts", - "type": "python", - "request": "launch", - "module": "smart_contracts", - "cwd": "${workspaceFolder}", - "preLaunchTask": "Start AlgoKit LocalNet", - "envFile": "${workspaceFolder}/.env.localnet" - }, - { - "name": "Deploy contracts", - "type": "python", - "request": "launch", - "module": "smart_contracts", - "args": ["deploy"], - "cwd": "${workspaceFolder}", - "envFile": "${workspaceFolder}/.env.localnet" - }, - { - "name": "Build contracts", - "type": "python", - "request": "launch", - "module": "smart_contracts", - "args": ["build"], - "cwd": "${workspaceFolder}" - }, - { - "type": "avm", - "request": "launch", - "name": "Debug TEAL via AlgoKit AVM Debugger", - "simulateTraceFile": "${workspaceFolder}/${command:PickSimulateTraceFile}", - "stopOnEntry": true - } - ] -} diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.vscode/settings.json b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.vscode/settings.json index a7b97d0..99a5c70 100644 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.vscode/settings.json +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.vscode/settings.json @@ -1,48 +1,5 @@ { - // General - see also /.editorconfig - "editor.formatOnSave": true, - "files.exclude": { - "**/.git": true, - "**/.DS_Store": true, - "**/Thumbs.db": true, - ".mypy_cache": true, - ".pytest_cache": true, - ".ruff_cache": true, - "**/__pycache__": true, - ".idea": true - }, - - // Python - "python.analysis.extraPaths": ["${workspaceFolder}/smart_contracts"], - "python.defaultInterpreterPath": "${workspaceFolder}/.venv", - "[python]": { "editor.codeActionsOnSave": { - "source.fixAll": true, - // Prevent default import sorting from running; Ruff will sort imports for us anyway - "source.organizeImports": false + "source.fixAll.eslint": "explicit" }, - "editor.defaultFormatter": "ms-python.black-formatter", - }, - "black-formatter.args": ["--config=pyproject.toml"], - "ruff.enable": true, - "ruff.lint.run": "onSave", - "ruff.lint.args": ["--config=pyproject.toml"], - "ruff.importStrategy": "fromEnvironment", - "ruff.fixAll": true, //lint and fix all files in workspace - "ruff.organizeImports": true, //organize imports on save - "ruff.codeAction.disableRuleComment": { - "enable": true - }, - "ruff.codeAction.fixViolation": { - "enable": true - }, - "python.analysis.typeCheckingMode": "off", - "mypy.configFile": "pyproject.toml", - // set to empty array to use config from project - "mypy.targets": [], - "mypy.runUsingActiveInterpreter": true, - - // On Windows, if execution policy is set to Signed (default) then it won't be able to activate the venv - // so instead let's set it to RemoteSigned for VS Code terminal - "terminal.integrated.shellArgs.windows": ["-ExecutionPolicy", "RemoteSigned"], -} +} \ No newline at end of file diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.vscode/tasks.json b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.vscode/tasks.json deleted file mode 100644 index eb1e767..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/.vscode/tasks.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "label": "Build contracts", - "command": "${workspaceFolder}/.venv/bin/python", - "windows": { - "command": "${workspaceFolder}/.venv/Scripts/python.exe" - }, - "args": ["-m", "smart_contracts", "build"], - "options": { - "cwd": "${workspaceFolder}" - }, - "group": { - "kind": "build", - "isDefault": true - }, - "problemMatcher": [] - }, - { - "label": "Build contracts (+ LocalNet)", - "command": "${workspaceFolder}/.venv/bin/python", - "windows": { - "command": "${workspaceFolder}/.venv/Scripts/python.exe" - }, - "args": ["-m", "smart_contracts", "build"], - "options": { - "cwd": "${workspaceFolder}" - }, - "dependsOn": "Start AlgoKit LocalNet", - "problemMatcher": [] - }, - { - "label": "Start AlgoKit LocalNet", - "command": "algokit", - "args": ["localnet", "start"], - "type": "shell", - "options": { - "cwd": "${workspaceFolder}" - }, - "problemMatcher": [] - }, - { - "label": "Stop AlgoKit LocalNet", - "command": "algokit", - "args": ["localnet", "stop"], - "type": "shell", - "options": { - "cwd": "${workspaceFolder}" - }, - "problemMatcher": [] - }, - { - "label": "Reset AlgoKit LocalNet", - "command": "algokit", - "args": ["localnet", "reset"], - "type": "shell", - "options": { - "cwd": "${workspaceFolder}" - }, - "problemMatcher": [] - }, - { - "label": "Analyze TEAL contracts with AlgoKit Tealer integration", - "command": "algokit", - "args": [ - "task", - "analyze", - "${workspaceFolder}/.algokit", - "--recursive", - "--force" - ], - "options": { - "cwd": "${workspaceFolder}" - }, - "problemMatcher": [] - } - ] -} diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/README.md b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/README.md index 44d0c30..6800947 100644 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/README.md +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/README.md @@ -1,65 +1,25 @@ -# starter_tealscript_react-contracts +# TEALScript Project -This project has been generated using AlgoKit. See below for default getting started instructions. +## Documentation -# Setup +For TEALScript documentation, go to https://tealscript.algo.xyz -### Pre-requisites +## Usage -- [Python 3.12](https://www.python.org/downloads/) or later -- [Docker](https://www.docker.com/) (only required for LocalNet) +### Algokit -> Please note `Puya` smart contract development language is currently in alpha / developer preview. It is not recommended for production usage yet. +This template assumes you have a local network running on your machine. The easiet way to setup a local network is with [algokit](https://github.com/algorandfoundation/algokit-cli). If you don't have Algokit or its dependencies installed locally you can open this repository in a GitHub codespace via https://codespaces.new and choosing this repo. -### Initial setup +### Build Contract -1. Clone this repository locally -2. Install pre-requisites: - - Make sure to have [Docker](https://www.docker.com/) installed and running on your machine. - - Install `AlgoKit` - [Link](https://github.com/algorandfoundation/algokit-cli#install): The recommended version is `1.7.3`. Ensure you can execute `algokit --version` and get `1.7.1` or later. - - Bootstrap your local environment; run `algokit bootstrap all` within this folder, which will: - - Install `Poetry` - [Link](https://python-poetry.org/docs/#installation): The minimum required version is `^1.7`. Ensure you can execute `poetry -V` and get `1.2`+ - - Run `poetry install` in the root directory, which will set up a `.venv` folder with a Python virtual environment and also install all Python dependencies - - Copy `.env.template` to `.env` - - Run `algokit localnet start` to start a local Algorand network in Docker. If you are using VS Code launch configurations provided by the template, this will be done automatically for you. -3. Open the project and start debugging / developing via: - - VS Code - 1. Open the repository root in VS Code - 2. Install recommended extensions - 3. Hit F5 (or whatever you have debug mapped to) and it should start running with breakpoint debugging. - > **Note** - > If using Windows: Before running for the first time you will need to select the Python Interpreter. - 1. Open the command palette (Ctrl/Cmd + Shift + P) - 2. Search for `Python: Select Interpreter` - 3. Select `./.venv/Scripts/python.exe` - - JetBrains IDEs (please note, this setup is primarily optimized for PyCharm Community Edition) - 1. Open the repository root in the IDE - 2. It should automatically detect it's a Poetry project and set up a Python interpreter and virtual environment. - 3. Hit Shift+F10|Ctrl+R (or whatever you have debug mapped to) and it should start running with breakpoint debugging. Please note, JetBrains IDEs on Windows have a known bug that in some cases may prevent executing shell scripts as pre-launch tasks, for workarounds refer to [JetBrains forums](https://youtrack.jetbrains.com/issue/IDEA-277486/Shell-script-configuration-cannot-run-as-before-launch-task). - - Other - 1. Open the repository root in your text editor of choice - 2. In a terminal run `poetry shell` - 3. Run `python -m smart_contracts` through your debugger of choice +`npm run build` will compile the contract to TEAL and generate an ABI and appspec JSON in [./contracts/artifacts](./contracts/artifacts/) and a algokit TypeScript client in [./contracts/clients](./contracts/clients/). -### Subsequently +`npm run compile-contract` or `npm run generate-client` can be used to compile the contract or generate the contract seperately. -1. If you update to the latest source code and there are new dependencies you will need to run `algokit bootstrap all` again -2. Follow step 3 above +### Run Tests -> For guidance on `smart_contracts` folder and adding new contracts to the project please see [README](smart_contracts/README.md) on the respective folder. +`npm run test` will execute the tests defined in [./\_\_test\_\_](./__test__) -# Tools - -This project makes use of Python to build Algorand smart contracts. The following tools are in use: - -- [Algorand](https://www.algorand.com/) - Layer 1 Blockchain; [Developer portal](https://developer.algorand.org/), [Why Algorand?](https://developer.algorand.org/docs/get-started/basics/why_algorand/) -- [AlgoKit](https://github.com/algorandfoundation/algokit-cli) - One-stop shop tool for developers building on the Algorand network; [docs](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/algokit.md), [intro tutorial](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/tutorials/intro.md) -- [Puya](https://github.com/algorand-foundation/puya) - Smart contract development framework for developing Algorand smart contracts in pure Python; [docs](https://github.com/algorandfoundation/puya), [examples](https://github.com/algorandfoundation/puya/tree/main/examples) -- [PyTEAL](https://github.com/algorand/pyteal) - Python language binding for Algorand smart contracts; [docs](https://pyteal.readthedocs.io/en/stable/) -- [AlgoKit Utils](https://github.com/algorandfoundation/algokit-utils-py) - A set of core Algorand utilities that make it easier to build solutions on Algorand. -- [Poetry](https://python-poetry.org/): Python packaging and dependency management.- [Black](https://github.com/psf/black): A Python code formatter.- [Ruff](https://github.com/charliermarsh/ruff): An extremely fast Python linter. - -- [mypy](https://mypy-lang.org/): Static type checker. -- [pip-audit](https://pypi.org/project/pip-audit/): Tool for scanning Python environments for packages with known vulnerabilities. -It has also been configured to have a productive dev experience out of the box in [VS Code](https://code.visualstudio.com/), see the [.vscode](./.vscode) folder. +### Lint +`npm run lint` will lint the contracts and tests with ESLint. diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/__test__/starter_tealscript_react-contracts.test.ts b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/__test__/starter_tealscript_react-contracts.test.ts new file mode 100644 index 0000000..7f747e2 --- /dev/null +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/__test__/starter_tealscript_react-contracts.test.ts @@ -0,0 +1,42 @@ +import { describe, test, expect, beforeAll, beforeEach } from '@jest/globals'; +import { algorandFixture } from '@algorandfoundation/algokit-utils/testing'; +import { StarterTealscriptReactContractsClient } from '../contracts/clients/StarterTealscriptReactContractsClient'; + +const fixture = algorandFixture(); +algokit.Config.configure({ populateAppCallResources: true }); + +let appClient: StarterTealscriptReactContractsClient; + +describe('StarterTealscriptReactContracts', () => { + beforeEach(fixture.beforeEach); + + beforeAll(async () => { + await fixture.beforeEach(); + const { algod, testAccount } = fixture.context; + + appClient = new StarterTealscriptReactContractsClient( + { + sender: testAccount, + resolveBy: 'id', + id: 0, + }, + algod + ); + + await appClient.create.createApplication({}); + }); + + test('sum', async () => { + const a = 13; + const b = 37; + const sum = await appClient.doMath({ a, b, operation: 'sum' }); + expect(sum.return?.valueOf()).toBe(BigInt(a + b)); + }); + + test('difference', async () => { + const a = 13; + const b = 37; + const diff = await appClient.doMath({ a, b, operation: 'difference' }); + expect(diff.return?.valueOf()).toBe(BigInt(a >= b ? a - b : b - a)); + }); +}); diff --git a/examples/production_tealscript_react/projects/production_tealscript_react-contracts/tests/__init__.py b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/contracts/artifacts/components/.gitkeep similarity index 100% rename from examples/production_tealscript_react/projects/production_tealscript_react-contracts/tests/__init__.py rename to examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/contracts/artifacts/components/.gitkeep diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/__init__.py b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/contracts/clients/.gitkeep similarity index 100% rename from examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/__init__.py rename to examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/contracts/clients/.gitkeep diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/contracts/starter_tealscript_react-contracts.algo.ts b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/contracts/starter_tealscript_react-contracts.algo.ts new file mode 100644 index 0000000..91c31d6 --- /dev/null +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/contracts/starter_tealscript_react-contracts.algo.ts @@ -0,0 +1,47 @@ +import { Contract } from '@algorandfoundation/tealscript'; + +// eslint-disable-next-line no-unused-vars +class StarterTealscriptReactContracts extends Contract { + /** + * Calculates the sum of two numbers + * + * @param a + * @param b + * @returns The sum of a and b + */ + private getSum(a: number, b: number): number { + return a + b; + } + + /** + * Calculates the difference between two numbers + * + * @param a + * @param b + * @returns The difference between a and b. + */ + private getDifference(a: number, b: number): number { + return a >= b ? a - b : b - a; + } + + /** + * A method that takes two numbers and does either addition or subtraction + * + * @param a The first number + * @param b The second number + * @param operation The operation to perform. Can be either 'sum' or 'difference' + * + * @returns The result of the operation + */ + doMath(a: number, b: number, operation: string): number { + let result: number; + + if (operation === 'sum') { + result = this.getSum(a, b); + } else if (operation === 'difference') { + result = this.getDifference(a, b); + } else throw Error('Invalid operation'); + + return result; + } +} diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/jest.config.js b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/jest.config.js new file mode 100644 index 0000000..6f5ef4e --- /dev/null +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/jest.config.js @@ -0,0 +1,6 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testTimeout: 60000 +}; diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/package.json b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/package.json new file mode 100644 index 0000000..eed6f55 --- /dev/null +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/package.json @@ -0,0 +1,36 @@ +{ + "name": "starter_tealscript_react-contracts", + "version": "0.0.0", + "license": "MIT", + "scripts": { + "generate-client": "algokit generate client contracts/artifacts/ --language typescript --output contracts/clients/{contract_name}Client.ts", + "compile-contract": "tealscript contracts/*.algo.ts contracts/artifacts", + "generate-components": "algokit-generate-component contracts/artifacts/StarterTealscriptReactContracts.arc32.json contracts/artifacts/components", + "build": "npm run compile-contract && npm run generate-client", + "test": "npm run build && jest", + "lint": "eslint . --ext .ts", + "fix": "eslint . --ext .ts --fix" + }, + "dependencies": { + "@algorandfoundation/algokit-utils": "^5.5.0", + "algosdk": "^2.7.0" + }, + "devDependencies": { + "@algorandfoundation/algokit-client-generator": "^2.3.1", + "@algorandfoundation/tealscript": "latest", + "@jest/globals": "^29.5.0", + "@joe-p/algokit-generate-component": "^0.2.0", + "@typescript-eslint/eslint-plugin": "^5.13.0", + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^7.32.0 || ^8.2.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-airbnb-typescript": "^17.0.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-prettier": "^5.0.1", + "jest": "^29.5.0", + "prettier": "^3.0.3", + "ts-jest": "^29.1.0", + "typescript": "5.0.2" + } +} diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/poetry.toml b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/poetry.toml deleted file mode 100644 index ab1033b..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/poetry.toml +++ /dev/null @@ -1,2 +0,0 @@ -[virtualenvs] -in-project = true diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/pyproject.toml b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/pyproject.toml deleted file mode 100644 index aa2d672..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/pyproject.toml +++ /dev/null @@ -1,44 +0,0 @@ -[tool.poetry] -name = "starter_tealscript_react-contracts" -version = "0.1.0" -description = "Algorand smart contracts" -authors = ["None "] -readme = "README.md" - -[tool.poetry.dependencies] -python = "^3.12" -algokit-utils = "^2.2.0" -python-dotenv = "^1.0.0" -puya = "^0" - -[tool.poetry.group.dev.dependencies] -black = {extras = ["d"], version = "*"} -ruff = "^0.1.6" -mypy = "*" -pip-audit = "*" - -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" - -[tool.ruff] -line-length = 120 -select = ["E", "F", "ANN", "UP", "N", "C4", "B", "A", "YTT", "W", "FBT", "Q", "RUF", "I"] -ignore = [ - "ANN101", # no type for self - "ANN102", # no type for cls -] -unfixable = ["B", "RUF"] - -[tool.ruff.flake8-annotations] -allow-star-arg-any = true -suppress-none-returning = true - -[tool.mypy] -files = "smart_contracts/" -python_version = "3.12" -check_untyped_defs = true -warn_redundant_casts = true -warn_unused_ignores = true -allow_untyped_defs = false -strict_equality = true diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/README.md b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/README.md deleted file mode 100644 index f765c95..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/README.md +++ /dev/null @@ -1,9 +0,0 @@ -## How to add new smart contracts? - -By the default the template creates a single `HelloWorld` contract under hello_world folder in the `smart_contracts` directory. To add a new contract: - -1. From the root of the project (`../`) execute `algokit generate smart-contract`. This will create a new starter smart contract and deployment configuration file under `{your_contract_name}` subfolder under `smart_contracts` directory. -2. Each contract potentially has different creation parameters and deployment steps. Hence, you need to define your deployment logic in `deploy_config.py`file. -3. `config.py` file will automatically build all contracts under `smart_contracts` directory. If you want to build specific contracts manually, modify the default code provided by the template in `config.py` file. - -> Please note, above is just a suggested convention tailored for the base configuration and structure of this template. Default code supplied by the template in `config.py` and `index.ts` (if using ts clients) files are tailored for the suggested convention. You are free to modify the structure and naming conventions as you see fit. diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/__main__.py b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/__main__.py deleted file mode 100644 index 7db8a69..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/__main__.py +++ /dev/null @@ -1,51 +0,0 @@ -import logging -import sys -from pathlib import Path - -from dotenv import load_dotenv - -from smart_contracts.config import contracts -from smart_contracts.helpers.build import build -from smart_contracts.helpers.deploy import deploy - -# Uncomment the following lines 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 -# from algokit_utils.config import config -# config.configure(debug=True, trace_all=True) -logging.basicConfig( - level=logging.DEBUG, format="%(asctime)s %(levelname)-10s: %(message)s" -) -logger = logging.getLogger(__name__) -logger.info("Loading .env") -load_dotenv() -root_path = Path(__file__).parent - - -def main(action: str) -> None: - artifact_path = root_path / "artifacts" - match action: - case "build": - for contract in contracts: - logger.info(f"Building app at {contract.path}") - build(artifact_path / contract.name, contract.path) - case "deploy": - for contract in contracts: - logger.info(f"Deploying app {contract.name}") - app_spec_path = artifact_path / contract.name / "application.json" - if contract.deploy: - deploy(app_spec_path, contract.deploy) - case "all": - for contract in contracts: - logger.info(f"Building app at {contract.path}") - app_spec_path = build(artifact_path / contract.name, contract.path) - logger.info(f"Deploying {contract.path.name}") - if contract.deploy: - deploy(app_spec_path, contract.deploy) - - -if __name__ == "__main__": - if len(sys.argv) > 1: - main(sys.argv[1]) - else: - main("all") diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/config.py b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/config.py deleted file mode 100644 index 7d56d82..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/config.py +++ /dev/null @@ -1,61 +0,0 @@ -import dataclasses -import importlib -from collections.abc import Callable -from pathlib import Path - -from algokit_utils import Account, ApplicationSpecification -from algosdk.v2client.algod import AlgodClient -from algosdk.v2client.indexer import IndexerClient - - -@dataclasses.dataclass -class SmartContract: - path: Path - name: str - deploy: ( - Callable[[AlgodClient, IndexerClient, ApplicationSpecification, Account], None] - | None - ) = None - - -def import_contract(folder: Path) -> Path: - """Imports the contract from a folder if it exists.""" - contract_path = folder / "contract.py" - if contract_path.exists(): - return contract_path - else: - raise Exception(f"Contract not found in {folder}") - - -def import_deploy_if_exists( - folder: Path, -) -> ( - Callable[[AlgodClient, IndexerClient, ApplicationSpecification, Account], None] - | None -): - """Imports the deploy function from a folder if it exists.""" - try: - deploy_module = importlib.import_module( - f"{folder.parent.name}.{folder.name}.deploy_config" - ) - return deploy_module.deploy - except ImportError: - return None - - -def has_contract_file(directory: Path) -> bool: - """Checks whether the directory contains contract.py file.""" - return (directory / "contract.py").exists() - - -# define contracts to build and/or deploy -base_dir = Path("smart_contracts") -contracts = [ - SmartContract( - path=import_contract(folder), - name=folder.name, - deploy=import_deploy_if_exists(folder), - ) - for folder in base_dir.iterdir() - if folder.is_dir() and has_contract_file(folder) -] diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/hello_world/contract.py b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/hello_world/contract.py deleted file mode 100644 index 589aea9..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/hello_world/contract.py +++ /dev/null @@ -1,7 +0,0 @@ -from puyapy import ARC4Contract, arc4 - - -class HelloWorld(ARC4Contract): - @arc4.abimethod() - def hello(self, name: arc4.String) -> arc4.String: - return "Hello, " + name diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/hello_world/deploy_config.py b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/hello_world/deploy_config.py deleted file mode 100644 index 82e67a8..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/hello_world/deploy_config.py +++ /dev/null @@ -1,36 +0,0 @@ -import logging - -import algokit_utils -from algosdk.v2client.algod import AlgodClient -from algosdk.v2client.indexer import IndexerClient - -logger = logging.getLogger(__name__) - - -# define deployment behaviour based on supplied app spec -def deploy( - algod_client: AlgodClient, - indexer_client: IndexerClient, - app_spec: algokit_utils.ApplicationSpecification, - deployer: algokit_utils.Account, -) -> None: - from smart_contracts.artifacts.hello_world.client import ( - HelloWorldClient, - ) - - app_client = HelloWorldClient( - algod_client, - creator=deployer, - indexer_client=indexer_client, - ) - - app_client.deploy( - on_schema_break=algokit_utils.OnSchemaBreak.AppendApp, - on_update=algokit_utils.OnUpdate.AppendApp, - ) - name = "world" - response = app_client.hello(name=name) - logger.info( - f"Called hello on {app_spec.contract.name} ({app_client.app_id}) " - f"with name={name}, received: {response.return_value}" - ) diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/helpers/__init__.py b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/helpers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/helpers/build.py b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/helpers/build.py deleted file mode 100644 index 0dad0c8..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/helpers/build.py +++ /dev/null @@ -1,56 +0,0 @@ -import logging -import subprocess -from pathlib import Path -from shutil import rmtree - -logger = logging.getLogger(__name__) -deployment_extension = "py" - - -def build(output_dir: Path, contract_path: Path) -> Path: - output_dir = output_dir.resolve() - if output_dir.exists(): - rmtree(output_dir) - output_dir.mkdir(exist_ok=True, parents=True) - logger.info(f"Exporting {contract_path} to {output_dir}") - - build_result = subprocess.run( - [ - "poetry", - "run", - "puyapy", - contract_path.absolute(), - f"--out-dir={output_dir}", - "--output-arc32", - ], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - ) - if build_result.returncode: - raise Exception(f"Could not build contract:\n{build_result.stdout}") - - generate_result = subprocess.run( - [ - "algokit", - "generate", - "client", - output_dir / "application.json", - "--output", - output_dir / f"client.{deployment_extension}", - ], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - ) - if generate_result.returncode: - if "No such command" in generate_result.stdout: - raise Exception( - "Could not generate typed client, requires AlgoKit 1.1 or " - "later. Please update AlgoKit" - ) - else: - raise Exception( - f"Could not generate typed client:\n{generate_result.stdout}" - ) - return output_dir / "application.json" diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/helpers/deploy.py b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/helpers/deploy.py deleted file mode 100644 index 08367a3..0000000 --- a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/smart_contracts/helpers/deploy.py +++ /dev/null @@ -1,50 +0,0 @@ -import logging -from collections.abc import Callable -from pathlib import Path - -from algokit_utils import ( - Account, - ApplicationSpecification, - EnsureBalanceParameters, - ensure_funded, - get_account, - get_algod_client, - get_indexer_client, -) -from algosdk.util import algos_to_microalgos -from algosdk.v2client.algod import AlgodClient -from algosdk.v2client.indexer import IndexerClient - -logger = logging.getLogger(__name__) - - -def deploy( - app_spec_path: Path, - deploy_callback: Callable[ - [AlgodClient, IndexerClient, ApplicationSpecification, Account], None - ], - deployer_initial_funds: int = 2, -) -> None: - # get clients - # by default client configuration is loaded from environment variables - algod_client = get_algod_client() - indexer_client = get_indexer_client() - - # get app spec - app_spec = ApplicationSpecification.from_json(app_spec_path.read_text()) - - # get deployer account by name - deployer = get_account(algod_client, "DEPLOYER", fund_with_algos=0) - - minimum_funds_micro_algos = algos_to_microalgos(deployer_initial_funds) - ensure_funded( - algod_client, - EnsureBalanceParameters( - account_to_fund=deployer, - min_spending_balance_micro_algos=minimum_funds_micro_algos, - min_funding_increment_micro_algos=minimum_funds_micro_algos, - ), - ) - - # use provided callback to deploy the app - deploy_callback(algod_client, indexer_client, app_spec, deployer) diff --git a/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/tsconfig.json b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/tsconfig.json new file mode 100644 index 0000000..5e3106d --- /dev/null +++ b/examples/starter_tealscript_react/projects/starter_tealscript_react-contracts/tsconfig.json @@ -0,0 +1,103 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/examples/starter_tealscript_react/starter_tealscript_react.code-workspace b/examples/starter_tealscript_react/starter_tealscript_react.code-workspace index 8c1d171..1aea5cf 100644 --- a/examples/starter_tealscript_react/starter_tealscript_react.code-workspace +++ b/examples/starter_tealscript_react/starter_tealscript_react.code-workspace @@ -24,50 +24,9 @@ "tasks": { "version": "2.0.0", - "tasks": [ - { - "label": "Build artifacts (+ LocalNet)", - "command": "${workspaceFolder}/projects/starter_tealscript_react-contracts/.venv/bin/python", - "windows": { - "command": "${workspaceFolder}/projects/starter_tealscript_react-contracts/.venv/Scripts/python.exe" - }, - "args": ["-m", "smart_contracts", "build"], - "options": { - "cwd": "${workspaceFolder}/projects/starter_tealscript_react-contracts" - }, - "dependsOn": "Start AlgoKit LocalNet", - "problemMatcher": [] - }, - { - "label": "Start AlgoKit LocalNet", - "command": "algokit", - "args": ["localnet", "start"], - "type": "shell", - "options": { - "cwd": "${workspaceFolder}" - }, - "problemMatcher": [] - } - ] + "tasks": [] }, "launch": { - "configurations": [], - "compounds": [ - { - "preLaunchTask": "Build artifacts (+ LocalNet)", - "name": "Run Frontend (+ LocalNet and Smart Contract)", - "configurations": [ - { - "name": "Deploy contracts", - "folder": "starter_tealscript_react-contracts" - }, - { "name": "Run dApp", "folder": "starter_tealscript_react-app" } - ], - "presentation": { - "hidden": false, - "group": "0. Run workspace" - } - } - ] + "configurations": [] } } diff --git a/tests/test_templates.py b/tests/test_templates.py index a260d9e..3b71c36 100644 --- a/tests/test_templates.py +++ b/tests/test_templates.py @@ -267,7 +267,7 @@ def run_init( def get_answered_questions_from_copier_yaml( *, - contract_language: str = "python", + contract_template: str = "python", preset_name: str = "starter", deployment_language: str = "python", ide_vscode: bool = True, @@ -291,9 +291,6 @@ def get_answered_questions_from_copier_yaml( questions = _load_copier_yaml(copier_yaml) answers = {} - answers["preset_name"] = preset_name - answers["deployment_language"] = deployment_language - answers["contract_language"] = contract_language for question_name, details in questions.items(): if question_name in ignored_keys: @@ -308,21 +305,25 @@ def get_answered_questions_from_copier_yaml( default_value = default_template.render(preset_name=preset_name) answers[question_name] = default_value.strip() + answers["preset_name"] = preset_name + answers["deployment_language"] = deployment_language + answers["contract_template"] = contract_template answers["ide_vscode"] = "yes" if ide_vscode else "no" answers["ide_jetbrains"] = "yes" if ide_jetbrains else "no" return answers -@pytest.mark.parametrize("contract_language", ["tealscript", "puya", "beaker"]) -def test_production_preset(contract_language: str, working_dir: Path) -> None: +@pytest.mark.parametrize("contract_template", ["tealscript", "puya", "beaker"]) +def test_production_preset(contract_template: str, working_dir: Path) -> None: response = run_init( working_dir, - f"production_{contract_language}_react", + f"production_{contract_template}_react", answers=get_answered_questions_from_copier_yaml( preset_name="production", deployment_language="python", ide_jetbrains=False, + contract_template=contract_template, ), child_template_default_answer="y", ) @@ -330,15 +331,15 @@ def test_production_preset(contract_language: str, working_dir: Path) -> None: assert response.returncode == 0, response.stdout -@pytest.mark.parametrize("contract_language", ["tealscript", "puya", "beaker"]) -def test_starter_preset(contract_language: str, working_dir: Path) -> None: +@pytest.mark.parametrize("contract_template", ["tealscript", "puya", "beaker"]) +def test_starter_preset(contract_template: str, working_dir: Path) -> None: response = run_init( working_dir, - f"starter_{contract_language}_react", + f"starter_{contract_template}_react", answers=get_answered_questions_from_copier_yaml( preset_name="starter", deployment_language="typescript", - contract_language=contract_language, + contract_template=contract_template, ), child_template_default_answer="n", )