diff --git a/.github/workflows/check-python.yaml b/.github/workflows/check-python.yaml index 830f03f..1f7982f 100644 --- a/.github/workflows/check-python.yaml +++ b/.github/workflows/check-python.yaml @@ -16,6 +16,9 @@ jobs: - name: Install algokit run: pipx install algokit + - name: Run algokit localnet + run: algokit localnet start + - name: Set up Python 3.12 uses: actions/setup-python@v5 with: @@ -23,7 +26,7 @@ jobs: cache: "poetry" - name: Install dependencies - run: poetry install --no-interaction --no-root + run: poetry env use 3.12 && poetry install --no-interaction --no-root - name: Check formatting with Black run: | diff --git a/.gitignore b/.gitignore index fe232bd..b0da2ea 100644 --- a/.gitignore +++ b/.gitignore @@ -176,4 +176,7 @@ examples/**/poetry.lock # playground folder for previewing templates .playground/* -!.playground/.gitkeep + +# Misc +examples/**/smart_contracts/artifacts +examples/**/.algokit/sources diff --git a/.playground/.gitkeep b/.playground/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/.vscode/clear.ps1 b/.vscode/clear.ps1 index 4c1c666..cff941d 100644 --- a/.vscode/clear.ps1 +++ b/.vscode/clear.ps1 @@ -1 +1 @@ -Remove-Item -Recurse -Force test_output -ErrorAction Ignore \ No newline at end of file +Remove-Item -Recurse -Force .playground -ErrorAction Ignore diff --git a/.vscode/tasks.json b/.vscode/tasks.json index a93c48c..45c8a64 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -8,7 +8,7 @@ "-v", "init", "--name", - "test_output", + ".playground", "--no-git", "--defaults", "--UNSAFE-SECURITY-accept-template-url", @@ -23,7 +23,7 @@ "kind": "build", "isDefault": true }, - "dependsOn": ["Delete test_output folder"], + "dependsOn": ["Cleanup .playground folder"], "problemMatcher": [] }, { @@ -33,7 +33,7 @@ "-v", "init", "--name", - "test_output", + ".playground", "--no-git", "--defaults", "--UNSAFE-SECURITY-accept-template-url", @@ -51,7 +51,7 @@ "kind": "build", "isDefault": true }, - "dependsOn": ["Delete test_output folder"], + "dependsOn": ["Cleanup .playground folder"], "problemMatcher": [] }, { @@ -61,7 +61,7 @@ "-v", "init", "--name", - "test_output", + ".playground", "--no-git", "--UNSAFE-SECURITY-accept-template-url", "--template-url", @@ -71,16 +71,13 @@ "--no-bootstrap" ], "type": "shell", - "dependsOn": ["Delete test_output folder"], + "dependsOn": ["Cleanup .playground folder"], "problemMatcher": [] }, { - "label": "Delete test_output folder", + "label": "Cleanup .playground folder", "command": "rm", - "args": [ - "-rf", - "test_output" - ], + "args": ["-rf", ".playground"], "type": "shell", "windows": { "command": "./.vscode/clear.ps1" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 581a677..e5bbfae 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,27 +1,42 @@ # AlgoKit Official Template for contributors -This repository is a template for creating new AlgoKit projects. It includes a basic structure for creating a puya based smart contract project. +This repository is a template for creating new AlgoKit projects. It includes a basic structure for creating a Algorand Python based smart contract project. ## Pre-requisites `poetry install` - Install the dependencies for the project. +`pipx install algokit` - Ensure cli is installed. ## Testing +Ensure localnet is running by executing `algokit localnet reset`. + ```bash -poetry run pytest +poetry run pytest -n auto ``` This will regenerate the tests for default `starter` and `production` presets as well as default tests for `generators` available on the template. ## Development +### Manual + ```bash -poetry run copier copy . .playground/{some_dummy_folder_name} --vcs-ref=HEAD --trust +poetry run copier copy . .playground --vcs-ref=HEAD --trust ``` To generate a dummy project into the `.playground` folder. This is useful for testing the template to quickly preview the output of the template before testing via `pytest`. +### Using VSCode Tasks + +In VSCode IDE, you can find the tasks in the `.vscode/tasks.json` file. To run them: + +1. Open the command palette (`Cmd+Shift+P` on macOS, `Ctrl+Shift+P` on Windows/Linux) and type `> Run Task` +2. Select the task you want to run +3. It will be generated for you under the .playground folder + +To cleanup the .playground folder run dedicated cleanup task. + ## Contributing ### Commits diff --git a/README.md b/README.md index bb545b9..e203436 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,19 @@
- +

- - + +

--- -This template provides a production-ready baseline for developing and deploying [Puya](https://github.com/algorand-devrel/puya) smart contracts. +This template provides a production-ready baseline for developing and deploying [Puya](https://github.com/algorandfoundation/puya) smart contracts. -To use it [install AlgoKit](https://github.com/algorandfoundation/algokit-cli#readme) and then either pass in `-t puya` to `algokit init` or select the `puya` template. +To use it [install AlgoKit](https://github.com/algorandfoundation/algokit-cli#readme) and then either pass in `-t python` to `algokit init` or select the `python` template. This is one of the official templates used by AlgoKit to initialize an Algorand smart contract project. It's a [Copier template](https://copier.readthedocs.io/en/stable/). diff --git a/copier.yaml b/copier.yaml index 134017a..683c810 100644 --- a/copier.yaml +++ b/copier.yaml @@ -1,23 +1,18 @@ _subdirectory: template_content _templates_suffix: ".jinja" +use_workspace: + type: bool + when: false # never prompted to user explicitly, instead expect cli to auto fill (supported cli versions > v1.13.x) + help: Automatically filled by AlgoKit CLI (>1.13.x) - passes the --workspace/--no-workspace flag's value, can be used to reason whether this template is currently being instantiated as part of a workspace or not. + default: no + # questions -# project_name should never get prompted, AlgoKit should always pass it by convention project_name: type: str help: Name for this project. placeholder: "algorand-app" -contract_name: - type: str - help: Name of the default smart contract app. - placeholder: "hello_world" - default: "hello_world" - validator: >- - {% if not (contract_name | regex_search('^[a-z]+(?:_[a-z]+)*$')) %} - contract_name must be formatted in snake case. - {% endif %} - author_name: type: str help: Package author name @@ -30,6 +25,16 @@ author_email: placeholder: "your@email.tld" default: "your@email.tld" +contract_name: + type: str + help: Name of the default smart contract app. + placeholder: "hello_world" + default: "hello_world" + validator: >- + {% if not (contract_name | regex_search('^[a-z]+(?:_[a-z]+)*$')) %} + contract_name must be formatted in snake case. + {% endif %} + preset_name: type: str help: Name of the template preset to use. @@ -57,7 +62,7 @@ ide_jetbrains: type: bool help: Do you want to add JetBrains configuration (primarily optimized for PyCharm CE)? when: "{{ preset_name == 'custom' }}" - default: no + default: "{{ 'yes' if preset_name == 'production' else 'no' }}" use_python_pytest: type: bool @@ -79,40 +84,40 @@ python_linter: Ruff: "ruff" Flake8: "flake8" No thanks: "none" - default: "ruff" + default: "{{ 'ruff' if preset_name == 'production' else 'none' }}" use_python_black: type: bool help: Do you want to use a Python formatter (via Black)? when: "{{ preset_name == 'custom' }}" - default: yes + default: "{{ 'yes' if preset_name == 'production' else 'no' }}" use_python_mypy: type: bool when: "{{ preset_name == 'custom' }}" help: Do you want to use a Python type checker (via mypy)? - default: yes + default: "{{ 'yes' if preset_name == 'production' else 'no' }}" use_python_pip_audit: type: bool when: "{{ preset_name == 'custom' }}" help: Do you want to include Python dependency vulnerability scanning (via pip-audit)? - default: yes + default: "{{ 'yes' if preset_name == 'production' else 'no' }}" use_github_actions: type: bool when: "{{ preset_name == 'custom' }}" help: Do you want to include Github Actions workflows for build and testnet deployment? - default: yes + default: "{{ 'yes' if preset_name == 'production' else 'no' }}" use_pre_commit: type: bool when: "{{ preset_name == 'custom' }}" help: Do you want to include pre-commit for linting, type checking and formatting? - default: yes + default: "{{ 'yes' if preset_name == 'production' else 'no' }}" use_dispenser: type: bool when: "{{ preset_name == 'custom' }}" help: Do you want to fund your deployment account using an optional dispenser account? - default: no + default: "{{ 'yes' if preset_name == 'production' else 'no' }}" diff --git a/examples/generators/.gitkeep b/examples/generators/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/examples/generators/production_puya_smart_contract_python/.algokit.toml b/examples/generators/production_puya_smart_contract_python/.algokit.toml deleted file mode 100644 index 40742c4..0000000 --- a/examples/generators/production_puya_smart_contract_python/.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 = 'contract' -name = 'production_puya_smart_contract_python' diff --git a/examples/generators/production_puya_smart_contract_python/.github/workflows/checks.yaml b/examples/generators/production_puya_smart_contract_python/.github/workflows/checks.yaml deleted file mode 100644 index 3415368..0000000 --- a/examples/generators/production_puya_smart_contract_python/.github/workflows/checks.yaml +++ /dev/null @@ -1,82 +0,0 @@ -name: Check code base - -on: - workflow_call: - -jobs: - checks: - runs-on: 'ubuntu-latest' - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Install poetry - run: pipx install poetry - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: '3.12' - cache: 'poetry' - - - name: Install algokit - run: pipx install algokit - - - name: Start LocalNet - run: algokit localnet start - - - name: Bootstrap dependencies - run: algokit bootstrap all - - - name: Configure git - shell: bash - run: | - # set git user and email as test invoke git - git config --global user.email "actions@github.com" && git config --global user.name "github-actions" - - - name: Audit with pip-audit - run: | - # audit non dev dependencies, no exclusions - poetry export --without=dev > requirements.txt && poetry run pip-audit -r requirements.txt - - # audit all dependencies, with exclusions. - # If a vulnerability is found in a dev dependency without an available fix, - # it can be temporarily ignored by adding --ignore-vuln e.g. - # --ignore-vuln "GHSA-hcpj-qp55-gfph" # GitPython vulnerability, dev only dependency - poetry run pip-audit - - - name: Check formatting with Black - run: | - # stop the build if there are files that don't meet formatting requirements - poetry run black --check . - - - name: Check linting with Ruff - run: | - # stop the build if there are Python syntax errors or undefined names - poetry run ruff . - - - name: Check types with mypy - run: poetry run mypy - - - name: Run tests - shell: bash - run: | - set -o pipefail - poetry run pytest --junitxml=pytest-junit.xml - - - name: Build smart contracts - run: poetry run python -m smart_contracts build - - - name: Scan TEAL files for issues - run: algokit task analyze .algokit --recursive --force # add --diff flag if you want output stability checks instead - - - name: Check output stability of the smart contracts - shell: bash - run: | - # Add untracked files as empty so they come up in diff - git add -N ./smart_contracts/artifacts - # Error out if there are any changes in teal after generating output - git diff --exit-code --minimal ./smart_contracts/artifacts || (echo "::error ::Smart contract artifacts have changed, ensure committed artifacts are up to date" && exit 1); - - - name: Run deployer against LocalNet - run: poetry run python -m smart_contracts deploy diff --git a/examples/generators/production_puya_smart_contract_python/.github/workflows/pr.yaml b/examples/generators/production_puya_smart_contract_python/.github/workflows/pr.yaml deleted file mode 100644 index a80f784..0000000 --- a/examples/generators/production_puya_smart_contract_python/.github/workflows/pr.yaml +++ /dev/null @@ -1,8 +0,0 @@ -name: Pull Request validation - -on: [pull_request] - -jobs: - pr-check: - name: Perform Checks - uses: ./.github/workflows/checks.yaml diff --git a/examples/generators/production_puya_smart_contract_typescript/.algokit.toml b/examples/generators/production_puya_smart_contract_typescript/.algokit.toml deleted file mode 100644 index 736d491..0000000 --- a/examples/generators/production_puya_smart_contract_typescript/.algokit.toml +++ /dev/null @@ -1,19 +0,0 @@ -[algokit] -min_version = "v1.8.0" - -[deploy] -command = "npm run deploy:ci" -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 = 'contract' -name = 'production_puya_smart_contract_typescript' diff --git a/examples/generators/production_puya_smart_contract_typescript/.github/workflows/cd.yaml b/examples/generators/production_puya_smart_contract_typescript/.github/workflows/cd.yaml deleted file mode 100644 index 2401bf4..0000000 --- a/examples/generators/production_puya_smart_contract_typescript/.github/workflows/cd.yaml +++ /dev/null @@ -1,50 +0,0 @@ -name: Continuous Delivery of Smart Contract - -on: - push: - branches: - - main - -concurrency: release - -jobs: - ci-check: - name: Perform Checks - uses: ./.github/workflows/checks.yaml - - deploy-testnet: - runs-on: 'ubuntu-latest' - needs: ci-check - environment: Test - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Install poetry - run: pipx install poetry - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: '3.12' - cache: 'poetry' - - - name: Install algokit - run: pipx install algokit - - - name: Bootstrap dependencies - run: algokit bootstrap all - - - name: Configure git - shell: bash - run: | - # set git user and email as test invoke git - git config --global user.email "actions@github.com" && git config --global user.name "github-actions" - - - name: Deploy to testnet - run: algokit deploy testnet - env: - # This is the account that becomes the creator of the contract. - # Since we are not using the optional dispenser account (via DISPENSER_MNEMONIC), - # it must also be funded with enough Algos to deploy and fund the smart contracts created - DEPLOYER_MNEMONIC: ${{ secrets.DEPLOYER_MNEMONIC }} diff --git a/examples/generators/production_puya_smart_contract_typescript/.github/workflows/checks.yaml b/examples/generators/production_puya_smart_contract_typescript/.github/workflows/checks.yaml deleted file mode 100644 index 3994f5d..0000000 --- a/examples/generators/production_puya_smart_contract_typescript/.github/workflows/checks.yaml +++ /dev/null @@ -1,76 +0,0 @@ -name: Check code base - -on: - workflow_call: - -jobs: - checks: - runs-on: 'ubuntu-latest' - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Install poetry - run: pipx install poetry - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: '3.12' - cache: 'poetry' - - - name: Install algokit - run: pipx install algokit - - - name: Start LocalNet - run: algokit localnet start - - - name: Bootstrap dependencies - run: algokit bootstrap all - - - name: Configure git - shell: bash - run: | - # set git user and email as test invoke git - git config --global user.email "actions@github.com" && git config --global user.name "github-actions" - - - name: Audit with pip-audit - run: | - # audit non dev dependencies, no exclusions - poetry export --without=dev > requirements.txt && poetry run pip-audit -r requirements.txt - - # audit all dependencies, with exclusions. - # If a vulnerability is found in a dev dependency without an available fix, - # it can be temporarily ignored by adding --ignore-vuln e.g. - # --ignore-vuln "GHSA-hcpj-qp55-gfph" # GitPython vulnerability, dev only dependency - poetry run pip-audit - - - name: Check formatting with Black - run: | - # stop the build if there are files that don't meet formatting requirements - poetry run black --check . - - - name: Check linting with Ruff - run: | - # stop the build if there are Python syntax errors or undefined names - poetry run ruff . - - - name: Check types with mypy - run: poetry run mypy - - - name: Build smart contracts - run: poetry run python -m smart_contracts build - - - name: Scan TEAL files for issues - run: algokit task analyze .algokit --recursive --force # add --diff flag if you want output stability checks instead - - - name: Check output stability of the smart contracts - shell: bash - run: | - # Add untracked files as empty so they come up in diff - git add -N ./smart_contracts/artifacts - # Error out if there are any changes in teal after generating output - git diff --exit-code --minimal ./smart_contracts/artifacts || (echo "::error ::Smart contract artifacts have changed, ensure committed artifacts are up to date" && exit 1); - - - name: Run deployer against LocalNet - run: npm run --prefix smart_contracts deploy:ci diff --git a/examples/generators/production_puya_smart_contract_typescript/.github/workflows/pr.yaml b/examples/generators/production_puya_smart_contract_typescript/.github/workflows/pr.yaml deleted file mode 100644 index a80f784..0000000 --- a/examples/generators/production_puya_smart_contract_typescript/.github/workflows/pr.yaml +++ /dev/null @@ -1,8 +0,0 @@ -name: Pull Request validation - -on: [pull_request] - -jobs: - pr-check: - name: Perform Checks - uses: ./.github/workflows/checks.yaml diff --git a/examples/generators/production_python_smart_contract_python/.algokit.toml b/examples/generators/production_python_smart_contract_python/.algokit.toml new file mode 100644 index 0000000..31a2642 --- /dev/null +++ b/examples/generators/production_python_smart_contract_python/.algokit.toml @@ -0,0 +1,49 @@ +[algokit] +min_version = "v2.0.0" + +[generate.smart_contract] +description = "Adds new smart contract to existing project" +path = ".algokit/generators/create_contract" + +[project] +type = 'contract' +name = 'production_python_smart_contract_python' +artifacts = 'smart_contracts/artifacts' + +[project.deploy] +command = "poetry run python -m smart_contracts deploy" +environment_secrets = [ + "DEPLOYER_MNEMONIC", + "DISPENSER_MNEMONIC", +] + +[project.deploy.localnet] +environment_secrets = [] + +[project.run] +# Commands intented for use locally and in CI +build = { commands = [ + 'poetry run python -m smart_contracts build', +], description = 'Build all smart contracts in the project' } +test = { commands = [ + 'poetry run pytest', +], description = 'Run smart contract tests' } +audit = { commands = [ + 'poetry export --without=dev -o requirements.txt', + 'poetry run pip-audit -r requirements.txt', +], description = 'Audit with pip-audit' } +lint = { commands = [ + 'poetry run black --check .', + 'poetry run ruff .', + 'poetry run mypy', +], description = 'Perform linting' } +audit-teal = { commands = [ + # 🚨 IMPORTANT 🚨: For strict TEAL validation, remove --exclude statements. The default starter contract is not for production. Ensure thorough testing and adherence to best practices in smart contract development. This is not a replacement for a professional audit. + 'algokit task analyze smart_contracts/artifacts --recursive --force --exclude rekey-to --exclude is-updatable --exclude missing-fee-check --exclude is-deletable --exclude can-close-asset --exclude can-close-account --exclude unprotected-deletable --exclude unprotected-updatable', +], description = 'Audit TEAL files' } + +# Commands intented for CI only, prefixed with `ci-` by convention +ci-teal-diff = { commands = [ + 'git add -N ./smart_contracts/artifacts', + 'git diff --exit-code --minimal ./smart_contracts/artifacts', +], description = 'Check TEAL files for differences' } diff --git a/examples/generators/production_puya_smart_contract_python/.algokit/generators/create_contract/copier.yaml b/examples/generators/production_python_smart_contract_python/.algokit/generators/create_contract/copier.yaml similarity index 100% rename from examples/generators/production_puya_smart_contract_python/.algokit/generators/create_contract/copier.yaml rename to examples/generators/production_python_smart_contract_python/.algokit/generators/create_contract/copier.yaml diff --git a/examples/generators/starter_puya_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 b/examples/generators/production_python_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 similarity index 83% rename from examples/generators/starter_puya_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 rename to examples/generators/production_python_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 index eabbaa0..9828347 100644 --- a/examples/generators/starter_puya_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 +++ b/examples/generators/production_python_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 @@ -1,4 +1,4 @@ -from puyapy import ARC4Contract, arc4 +from algopy import ARC4Contract, arc4 class {{ contract_name.split('_')|map('capitalize')|join }}(ARC4Contract): diff --git a/examples/generators/production_puya_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 b/examples/generators/production_python_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 similarity index 100% rename from examples/generators/production_puya_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 rename to examples/generators/production_python_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 diff --git a/examples/generators/production_puya_smart_contract_python/.copier-answers.yml b/examples/generators/production_python_smart_contract_python/.copier-answers.yml similarity index 80% rename from examples/generators/production_puya_smart_contract_python/.copier-answers.yml rename to examples/generators/production_python_smart_contract_python/.copier-answers.yml index 46b550e..db599c8 100644 --- a/examples/generators/production_puya_smart_contract_python/.copier-answers.yml +++ b/examples/generators/production_python_smart_contract_python/.copier-answers.yml @@ -6,5 +6,5 @@ author_name: None contract_name: hello_world deployment_language: python preset_name: production -project_name: production_puya_smart_contract_python +project_name: production_python_smart_contract_python diff --git a/examples/generators/production_puya_smart_contract_python/.editorconfig b/examples/generators/production_python_smart_contract_python/.editorconfig similarity index 100% rename from examples/generators/production_puya_smart_contract_python/.editorconfig rename to examples/generators/production_python_smart_contract_python/.editorconfig diff --git a/examples/generators/production_puya_smart_contract_python/.env.localnet.template b/examples/generators/production_python_smart_contract_python/.env.localnet.template similarity index 100% rename from examples/generators/production_puya_smart_contract_python/.env.localnet.template rename to examples/generators/production_python_smart_contract_python/.env.localnet.template diff --git a/examples/generators/production_puya_smart_contract_python/.env.template b/examples/generators/production_python_smart_contract_python/.env.template similarity index 100% rename from examples/generators/production_puya_smart_contract_python/.env.template rename to examples/generators/production_python_smart_contract_python/.env.template diff --git a/examples/generators/production_puya_smart_contract_python/.env.testnet.template b/examples/generators/production_python_smart_contract_python/.env.testnet.template similarity index 100% rename from examples/generators/production_puya_smart_contract_python/.env.testnet.template rename to examples/generators/production_python_smart_contract_python/.env.testnet.template diff --git a/examples/generators/production_puya_smart_contract_python/.gitattributes b/examples/generators/production_python_smart_contract_python/.gitattributes similarity index 100% rename from examples/generators/production_puya_smart_contract_python/.gitattributes rename to examples/generators/production_python_smart_contract_python/.gitattributes diff --git a/examples/generators/starter_puya_smart_contract_python/.github/workflows/cd.yaml b/examples/generators/production_python_smart_contract_python/.github/workflows/production-python-smart-contract-python-cd.yaml similarity index 53% rename from examples/generators/starter_puya_smart_contract_python/.github/workflows/cd.yaml rename to examples/generators/production_python_smart_contract_python/.github/workflows/production-python-smart-contract-python-cd.yaml index 2401bf4..8f43367 100644 --- a/examples/generators/starter_puya_smart_contract_python/.github/workflows/cd.yaml +++ b/examples/generators/production_python_smart_contract_python/.github/workflows/production-python-smart-contract-python-cd.yaml @@ -1,21 +1,19 @@ -name: Continuous Delivery of Smart Contract +name: Release production_python_smart_contract_python on: + workflow_call: push: branches: - main -concurrency: release - jobs: - ci-check: - name: Perform Checks - uses: ./.github/workflows/checks.yaml - + validate: + name: Validate production_python_smart_contract_python + uses: ./.github/workflows/production-python-smart-contract-python-ci.yaml deploy-testnet: - runs-on: 'ubuntu-latest' - needs: ci-check - environment: Test + runs-on: "ubuntu-latest" + needs: validate + environment: contract-testnet steps: - name: Checkout source code uses: actions/checkout@v4 @@ -26,14 +24,14 @@ jobs: - name: Set up Python 3.12 uses: actions/setup-python@v5 with: - python-version: '3.12' - cache: 'poetry' + python-version: "3.12" + cache: "poetry" - name: Install algokit run: pipx install algokit - name: Bootstrap dependencies - run: algokit bootstrap all + run: algokit bootstrap all --project-name 'production_python_smart_contract_python' - name: Configure git shell: bash @@ -42,9 +40,9 @@ jobs: git config --global user.email "actions@github.com" && git config --global user.name "github-actions" - name: Deploy to testnet - run: algokit deploy testnet + run: algokit deploy testnet --project-name 'production_python_smart_contract_python' env: - # This is the account that becomes the creator of the contract. - # Since we are not using the optional dispenser account (via DISPENSER_MNEMONIC), - # it must also be funded with enough Algos to deploy and fund the smart contracts created + # This is the account that becomes the creator of the contract DEPLOYER_MNEMONIC: ${{ secrets.DEPLOYER_MNEMONIC }} + # The dispenser account is used to ensure the deployer account is funded + DISPENSER_MNEMONIC: ${{ secrets.DISPENSER_MNEMONIC }} diff --git a/examples/generators/production_python_smart_contract_python/.github/workflows/production-python-smart-contract-python-ci.yaml b/examples/generators/production_python_smart_contract_python/.github/workflows/production-python-smart-contract-python-ci.yaml new file mode 100644 index 0000000..ef01823 --- /dev/null +++ b/examples/generators/production_python_smart_contract_python/.github/workflows/production-python-smart-contract-python-ci.yaml @@ -0,0 +1,60 @@ +name: Validate production_python_smart_contract_python + +on: + workflow_call: + pull_request: + +jobs: + validate: + runs-on: "ubuntu-latest" + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Install poetry + run: pipx install poetry + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: "poetry" + + - name: Install algokit + run: pipx install algokit + + - name: Start LocalNet + run: algokit localnet start + + - name: Bootstrap dependencies + run: algokit bootstrap all --project-name 'production_python_smart_contract_python' + + - name: Configure git + shell: bash + run: | + # set git user and email as test invoke git + git config --global user.email "actions@github.com" && git config --global user.name "github-actions" + + - name: Audit python dependencies + run: algokit project run audit --project-name 'production_python_smart_contract_python' + + - name: Lint and format python dependencies + run: algokit project run lint --project-name 'production_python_smart_contract_python' + + - name: Run tests + shell: bash + run: | + set -o pipefail + algokit project run test --project-name 'production_python_smart_contract_python' + + - name: Build smart contracts + run: algokit project run build --project-name 'production_python_smart_contract_python' + + - name: Scan TEAL files for issues + run: algokit project run audit-teal --project-name 'production_python_smart_contract_python' + + - name: Check output stability of the smart contracts + run: algokit project run ci-teal-diff --project-name 'production_python_smart_contract_python' + + - name: Run deployer against LocalNet + run: algokit project deploy localnet --project-name 'production_python_smart_contract_python' diff --git a/examples/generators/production_puya_smart_contract_python/.gitignore b/examples/generators/production_python_smart_contract_python/.gitignore similarity index 100% rename from examples/generators/production_puya_smart_contract_python/.gitignore rename to examples/generators/production_python_smart_contract_python/.gitignore diff --git a/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Build_Beaker_application.xml b/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Build_Beaker_application.xml new file mode 100644 index 0000000..f0a1d62 --- /dev/null +++ b/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Build_Beaker_application.xml @@ -0,0 +1,35 @@ + + + + + diff --git a/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Build_Beaker_application____LocalNet.xml b/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Build_Beaker_application____LocalNet.xml new file mode 100644 index 0000000..8730b9c --- /dev/null +++ b/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Build_Beaker_application____LocalNet.xml @@ -0,0 +1,37 @@ + + + + + diff --git a/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Build___Deploy_Beaker_application.xml b/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Build___Deploy_Beaker_application.xml new file mode 100644 index 0000000..f4e0f96 --- /dev/null +++ b/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Build___Deploy_Beaker_application.xml @@ -0,0 +1,38 @@ + + + + + diff --git a/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Deploy_Built_Beaker_application.xml b/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Deploy_Built_Beaker_application.xml new file mode 100644 index 0000000..f13cffc --- /dev/null +++ b/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Deploy_Built_Beaker_application.xml @@ -0,0 +1,36 @@ + + + + + diff --git a/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Reset_AlgoKit_LocalNet.xml b/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Reset_AlgoKit_LocalNet.xml new file mode 100644 index 0000000..7f1236a --- /dev/null +++ b/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Reset_AlgoKit_LocalNet.xml @@ -0,0 +1,17 @@ + + + + diff --git a/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml b/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml new file mode 100644 index 0000000..f699a7a --- /dev/null +++ b/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml @@ -0,0 +1,17 @@ + + + + diff --git a/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Stop_AlgoKit_LocalNet.xml b/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Stop_AlgoKit_LocalNet.xml new file mode 100644 index 0000000..e510cbc --- /dev/null +++ b/examples/generators/production_python_smart_contract_python/.idea/runConfigurations/Stop_AlgoKit_LocalNet.xml @@ -0,0 +1,17 @@ + + + + diff --git a/examples/generators/production_puya_smart_contract_python/.pre-commit-config.yaml b/examples/generators/production_python_smart_contract_python/.pre-commit-config.yaml similarity index 100% rename from examples/generators/production_puya_smart_contract_python/.pre-commit-config.yaml rename to examples/generators/production_python_smart_contract_python/.pre-commit-config.yaml diff --git a/examples/generators/production_puya_smart_contract_python/.tours/getting-started-with-your-algokit-project.tour b/examples/generators/production_python_smart_contract_python/.tours/getting-started-with-your-algokit-project.tour similarity index 99% rename from examples/generators/production_puya_smart_contract_python/.tours/getting-started-with-your-algokit-project.tour rename to examples/generators/production_python_smart_contract_python/.tours/getting-started-with-your-algokit-project.tour index c1309a6..6b593eb 100644 --- a/examples/generators/production_puya_smart_contract_python/.tours/getting-started-with-your-algokit-project.tour +++ b/examples/generators/production_python_smart_contract_python/.tours/getting-started-with-your-algokit-project.tour @@ -50,7 +50,7 @@ { "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 + "line": 15 } ] } diff --git a/examples/generators/production_puya_smart_contract_python/.vscode/extensions.json b/examples/generators/production_python_smart_contract_python/.vscode/extensions.json similarity index 100% rename from examples/generators/production_puya_smart_contract_python/.vscode/extensions.json rename to examples/generators/production_python_smart_contract_python/.vscode/extensions.json diff --git a/examples/generators/production_puya_smart_contract_python/.vscode/launch.json b/examples/generators/production_python_smart_contract_python/.vscode/launch.json similarity index 100% rename from examples/generators/production_puya_smart_contract_python/.vscode/launch.json rename to examples/generators/production_python_smart_contract_python/.vscode/launch.json diff --git a/examples/generators/production_puya_smart_contract_python/.vscode/settings.json b/examples/generators/production_python_smart_contract_python/.vscode/settings.json similarity index 95% rename from examples/generators/production_puya_smart_contract_python/.vscode/settings.json rename to examples/generators/production_python_smart_contract_python/.vscode/settings.json index 0c2dfec..61681c0 100644 --- a/examples/generators/production_puya_smart_contract_python/.vscode/settings.json +++ b/examples/generators/production_python_smart_contract_python/.vscode/settings.json @@ -17,9 +17,9 @@ "python.defaultInterpreterPath": "${workspaceFolder}/.venv", "[python]": { "editor.codeActionsOnSave": { - "source.fixAll": true, + "source.fixAll": "explicit", // Prevent default import sorting from running; Ruff will sort imports for us anyway - "source.organizeImports": false + "source.organizeImports": "never" }, "editor.defaultFormatter": "ms-python.black-formatter", }, diff --git a/examples/generators/production_puya_smart_contract_python/.vscode/tasks.json b/examples/generators/production_python_smart_contract_python/.vscode/tasks.json similarity index 100% rename from examples/generators/production_puya_smart_contract_python/.vscode/tasks.json rename to examples/generators/production_python_smart_contract_python/.vscode/tasks.json diff --git a/examples/production_puya/README.md b/examples/generators/production_python_smart_contract_python/README.md similarity index 91% rename from examples/production_puya/README.md rename to examples/generators/production_python_smart_contract_python/README.md index 30f1681..0e37a0e 100644 --- a/examples/production_puya/README.md +++ b/examples/generators/production_python_smart_contract_python/README.md @@ -1,4 +1,4 @@ -# production_puya +# production_python_smart_contract_python This project has been generated using AlgoKit. See below for default getting started instructions. @@ -9,8 +9,6 @@ This project has been generated using AlgoKit. See below for default getting sta - [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. - ### Initial setup 1. Clone this repository locally @@ -48,7 +46,9 @@ This project has been generated using AlgoKit. See below for default getting sta > For guidance on `smart_contracts` folder and adding new contracts to the project please see [README](smart_contracts/README.md) on the respective folder.### Continuous Integration / Continuous Deployment (CI/CD) -This project uses [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to define CI/CD workflows, which are located in the [`.github/workflows`](./.github/workflows) folder. +This project uses [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to define CI/CD workflows, which are located in the [.github/workflows](`.github/workflows`) folder. + +> Please note, if you instantiated the project with --workspace flag in `algokit init` it will automatically attempt to move the contents of the `.github` folder to the root of the workspace. ### Debugging Smart Contracts @@ -98,12 +98,11 @@ For pushes to `main` branch, after the above checks pass, the following deployme # Tools -This project makes use of Python to build Algorand smart contracts. The following tools are in use: +This project makes use of Algorand 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/) +- [Algorand Python](https://github.com/algorandfoundation/puya) - A semantically and syntactically compatible, typed Python language that works with standard Python tooling and allows you to express smart contracts (apps) and smart signatures (logic signatures) for deployment on the Algorand Virtual Machine (AVM); [docs](https://github.com/algorandfoundation/puya), [examples](https://github.com/algorandfoundation/puya/tree/main/examples) - [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. diff --git a/examples/generators/production_puya_smart_contract_python/poetry.toml b/examples/generators/production_python_smart_contract_python/poetry.toml similarity index 100% rename from examples/generators/production_puya_smart_contract_python/poetry.toml rename to examples/generators/production_python_smart_contract_python/poetry.toml diff --git a/examples/generators/production_puya_smart_contract_python/pyproject.toml b/examples/generators/production_python_smart_contract_python/pyproject.toml similarity index 67% rename from examples/generators/production_puya_smart_contract_python/pyproject.toml rename to examples/generators/production_python_smart_contract_python/pyproject.toml index b38bae5..17de885 100644 --- a/examples/generators/production_puya_smart_contract_python/pyproject.toml +++ b/examples/generators/production_python_smart_contract_python/pyproject.toml @@ -1,5 +1,5 @@ [tool.poetry] -name = "production_puya_smart_contract_python" +name = "production_python_smart_contract_python" version = "0.1.0" description = "Algorand smart contracts" authors = ["None "] @@ -9,9 +9,10 @@ readme = "README.md" python = "^3.12" algokit-utils = "^2.2.0" python-dotenv = "^1.0.0" -puya = "^0" +algorand-python = "^1.0.0" [tool.poetry.group.dev.dependencies] +algokit-client-generator = "^1.1.3" black = {extras = ["d"], version = "*"} ruff = "^0.1.6" mypy = "*" @@ -19,6 +20,7 @@ pytest = "*" pytest-cov = "*" pip-audit = "*" pre-commit = "*" +puyapy = "^1.0.0" [build-system] requires = ["poetry-core"] @@ -43,8 +45,19 @@ pythonpath = ["smart_contracts", "tests"] [tool.mypy] files = "smart_contracts/" python_version = "3.12" +disallow_any_generics = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_defs = true +disallow_incomplete_defs = true check_untyped_defs = true +disallow_untyped_decorators = true warn_redundant_casts = true warn_unused_ignores = true -allow_untyped_defs = false +warn_return_any = true strict_equality = true +strict_concatenate = true +disallow_any_unimported = true +disallow_any_expr = true +disallow_any_decorated = true +disallow_any_explicit = true diff --git a/examples/generators/production_puya_smart_contract_python/smart_contracts/README.md b/examples/generators/production_python_smart_contract_python/smart_contracts/README.md similarity index 100% rename from examples/generators/production_puya_smart_contract_python/smart_contracts/README.md rename to examples/generators/production_python_smart_contract_python/smart_contracts/README.md diff --git a/examples/generators/production_puya_smart_contract_python/smart_contracts/__init__.py b/examples/generators/production_python_smart_contract_python/smart_contracts/__init__.py similarity index 100% rename from examples/generators/production_puya_smart_contract_python/smart_contracts/__init__.py rename to examples/generators/production_python_smart_contract_python/smart_contracts/__init__.py diff --git a/examples/production_puya/smart_contracts/__main__.py b/examples/generators/production_python_smart_contract_python/smart_contracts/__main__.py similarity index 82% rename from examples/production_puya/smart_contracts/__main__.py rename to examples/generators/production_python_smart_contract_python/smart_contracts/__main__.py index 7db8a69..aa936c9 100644 --- a/examples/production_puya/smart_contracts/__main__.py +++ b/examples/generators/production_python_smart_contract_python/smart_contracts/__main__.py @@ -7,6 +7,7 @@ from smart_contracts.config import contracts from smart_contracts.helpers.build import build from smart_contracts.helpers.deploy import deploy +from smart_contracts.helpers.util import find_app_spec_file # 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 @@ -32,7 +33,11 @@ def main(action: str) -> None: case "deploy": for contract in contracts: logger.info(f"Deploying app {contract.name}") - app_spec_path = artifact_path / contract.name / "application.json" + output_dir = artifact_path / contract.name + app_spec_file_name = find_app_spec_file(output_dir) + if app_spec_file_name is None: + raise Exception("Could not deploy app, .arc32.json file not found") + app_spec_path = output_dir / app_spec_file_name if contract.deploy: deploy(app_spec_path, contract.deploy) case "all": diff --git a/examples/generators/starter_puya_smart_contract_typescript/smart_contracts/config.py b/examples/generators/production_python_smart_contract_python/smart_contracts/config.py similarity index 96% rename from examples/generators/starter_puya_smart_contract_typescript/smart_contracts/config.py rename to examples/generators/production_python_smart_contract_python/smart_contracts/config.py index 7d56d82..8f3ca93 100644 --- a/examples/generators/starter_puya_smart_contract_typescript/smart_contracts/config.py +++ b/examples/generators/production_python_smart_contract_python/smart_contracts/config.py @@ -38,7 +38,7 @@ def import_deploy_if_exists( deploy_module = importlib.import_module( f"{folder.parent.name}.{folder.name}.deploy_config" ) - return deploy_module.deploy + return deploy_module.deploy # type: ignore except ImportError: return None diff --git a/examples/generators/starter_puya_smart_contract_python/smart_contracts/cool_contract/contract.py b/examples/generators/production_python_smart_contract_python/smart_contracts/cool_contract/contract.py similarity index 79% rename from examples/generators/starter_puya_smart_contract_python/smart_contracts/cool_contract/contract.py rename to examples/generators/production_python_smart_contract_python/smart_contracts/cool_contract/contract.py index 1c9f04d..4c00d34 100644 --- a/examples/generators/starter_puya_smart_contract_python/smart_contracts/cool_contract/contract.py +++ b/examples/generators/production_python_smart_contract_python/smart_contracts/cool_contract/contract.py @@ -1,4 +1,4 @@ -from puyapy import ARC4Contract, arc4 +from algopy import ARC4Contract, arc4 class CoolContract(ARC4Contract): diff --git a/examples/generators/production_puya_smart_contract_python/smart_contracts/cool_contract/deploy_config.py b/examples/generators/production_python_smart_contract_python/smart_contracts/cool_contract/deploy_config.py similarity index 100% rename from examples/generators/production_puya_smart_contract_python/smart_contracts/cool_contract/deploy_config.py rename to examples/generators/production_python_smart_contract_python/smart_contracts/cool_contract/deploy_config.py diff --git a/examples/generators/starter_puya_smart_contract_python/smart_contracts/hello_world/contract.py b/examples/generators/production_python_smart_contract_python/smart_contracts/hello_world/contract.py similarity index 63% rename from examples/generators/starter_puya_smart_contract_python/smart_contracts/hello_world/contract.py rename to examples/generators/production_python_smart_contract_python/smart_contracts/hello_world/contract.py index 589aea9..9052c05 100644 --- a/examples/generators/starter_puya_smart_contract_python/smart_contracts/hello_world/contract.py +++ b/examples/generators/production_python_smart_contract_python/smart_contracts/hello_world/contract.py @@ -1,4 +1,5 @@ -from puyapy import ARC4Contract, arc4 +# pyright: reportMissingModuleSource=false +from algopy import ARC4Contract, arc4 class HelloWorld(ARC4Contract): diff --git a/examples/generators/production_puya_smart_contract_python/smart_contracts/hello_world/deploy_config.py b/examples/generators/production_python_smart_contract_python/smart_contracts/hello_world/deploy_config.py similarity index 100% rename from examples/generators/production_puya_smart_contract_python/smart_contracts/hello_world/deploy_config.py rename to examples/generators/production_python_smart_contract_python/smart_contracts/hello_world/deploy_config.py diff --git a/examples/generators/production_puya_smart_contract_python/smart_contracts/helpers/__init__.py b/examples/generators/production_python_smart_contract_python/smart_contracts/helpers/__init__.py similarity index 100% rename from examples/generators/production_puya_smart_contract_python/smart_contracts/helpers/__init__.py rename to examples/generators/production_python_smart_contract_python/smart_contracts/helpers/__init__.py diff --git a/examples/generators/production_puya_smart_contract_python/smart_contracts/helpers/build.py b/examples/generators/production_python_smart_contract_python/smart_contracts/helpers/build.py similarity index 77% rename from examples/generators/production_puya_smart_contract_python/smart_contracts/helpers/build.py rename to examples/generators/production_python_smart_contract_python/smart_contracts/helpers/build.py index 0dad0c8..d9fc955 100644 --- a/examples/generators/production_puya_smart_contract_python/smart_contracts/helpers/build.py +++ b/examples/generators/production_python_smart_contract_python/smart_contracts/helpers/build.py @@ -3,6 +3,8 @@ from pathlib import Path from shutil import rmtree +from smart_contracts.helpers.util import find_app_spec_file + logger = logging.getLogger(__name__) deployment_extension = "py" @@ -16,9 +18,10 @@ def build(output_dir: Path, contract_path: Path) -> Path: build_result = subprocess.run( [ - "poetry", - "run", - "puyapy", + "algokit", + "--no-color", + "compile", + "python", contract_path.absolute(), f"--out-dir={output_dir}", "--output-arc32", @@ -30,12 +33,16 @@ def build(output_dir: Path, contract_path: Path) -> Path: if build_result.returncode: raise Exception(f"Could not build contract:\n{build_result.stdout}") + app_spec_file_name = find_app_spec_file(output_dir) + if app_spec_file_name is None: + raise Exception("Could not generate typed client, .arc32.json file not found") + generate_result = subprocess.run( [ "algokit", "generate", "client", - output_dir / "application.json", + output_dir / app_spec_file_name, "--output", output_dir / f"client.{deployment_extension}", ], @@ -53,4 +60,4 @@ def build(output_dir: Path, contract_path: Path) -> Path: raise Exception( f"Could not generate typed client:\n{generate_result.stdout}" ) - return output_dir / "application.json" + return output_dir / app_spec_file_name diff --git a/examples/generators/production_puya_smart_contract_python/smart_contracts/helpers/deploy.py b/examples/generators/production_python_smart_contract_python/smart_contracts/helpers/deploy.py similarity index 96% rename from examples/generators/production_puya_smart_contract_python/smart_contracts/helpers/deploy.py rename to examples/generators/production_python_smart_contract_python/smart_contracts/helpers/deploy.py index 08367a3..10185a9 100644 --- a/examples/generators/production_puya_smart_contract_python/smart_contracts/helpers/deploy.py +++ b/examples/generators/production_python_smart_contract_python/smart_contracts/helpers/deploy.py @@ -1,3 +1,6 @@ +# mypy: disable-error-code="no-untyped-call, misc" + + import logging from collections.abc import Callable from pathlib import Path diff --git a/examples/generators/production_python_smart_contract_python/smart_contracts/helpers/util.py b/examples/generators/production_python_smart_contract_python/smart_contracts/helpers/util.py new file mode 100644 index 0000000..6cf7e5b --- /dev/null +++ b/examples/generators/production_python_smart_contract_python/smart_contracts/helpers/util.py @@ -0,0 +1,8 @@ +from pathlib import Path + + +def find_app_spec_file(output_dir: Path) -> str | None: + for file in output_dir.iterdir(): + if file.is_file() and file.suffixes == [".arc32", ".json"]: + return file.name + return None diff --git a/examples/generators/production_puya_smart_contract_python/tests/__init__.py b/examples/generators/production_python_smart_contract_python/tests/__init__.py similarity index 100% rename from examples/generators/production_puya_smart_contract_python/tests/__init__.py rename to examples/generators/production_python_smart_contract_python/tests/__init__.py diff --git a/examples/generators/production_puya_smart_contract_python/tests/conftest.py b/examples/generators/production_python_smart_contract_python/tests/conftest.py similarity index 100% rename from examples/generators/production_puya_smart_contract_python/tests/conftest.py rename to examples/generators/production_python_smart_contract_python/tests/conftest.py diff --git a/examples/generators/production_puya_smart_contract_python/tests/hello_world_test.py b/examples/generators/production_python_smart_contract_python/tests/hello_world_test.py similarity index 100% rename from examples/generators/production_puya_smart_contract_python/tests/hello_world_test.py rename to examples/generators/production_python_smart_contract_python/tests/hello_world_test.py diff --git a/examples/generators/production_python_smart_contract_typescript/.algokit.toml b/examples/generators/production_python_smart_contract_typescript/.algokit.toml new file mode 100644 index 0000000..612f1b2 --- /dev/null +++ b/examples/generators/production_python_smart_contract_typescript/.algokit.toml @@ -0,0 +1,49 @@ +[algokit] +min_version = "v2.0.0" + +[generate.smart_contract] +description = "Adds new smart contract to existing project" +path = ".algokit/generators/create_contract" + +[project] +type = 'contract' +name = 'production_python_smart_contract_typescript' +artifacts = 'smart_contracts/artifacts' + +[project.deploy] +command = "npm run deploy:ci" +environment_secrets = [ + "DEPLOYER_MNEMONIC", + "DISPENSER_MNEMONIC", +] + +[project.deploy.localnet] +environment_secrets = [] + +[project.run] +# Commands intented for use locally and in CI +build = { commands = [ + 'poetry run python -m smart_contracts build', +], description = 'Build all smart contracts in the project' } +test = { commands = [ + 'npm run test', +], description = 'Run smart contract tests using Jest' } +audit = { commands = [ + 'poetry export --without=dev -o requirements.txt', + 'poetry run pip-audit -r requirements.txt', +], description = 'Audit with pip-audit' } +lint = { commands = [ + 'poetry run black --check .', + 'poetry run ruff .', + 'poetry run mypy', +], description = 'Perform linting' } +audit-teal = { commands = [ + # 🚨 IMPORTANT 🚨: For strict TEAL validation, remove --exclude statements. The default starter contract is not for production. Ensure thorough testing and adherence to best practices in smart contract development. This is not a replacement for a professional audit. + 'algokit task analyze smart_contracts/artifacts --recursive --force --exclude rekey-to --exclude is-updatable --exclude missing-fee-check --exclude is-deletable --exclude can-close-asset --exclude can-close-account --exclude unprotected-deletable --exclude unprotected-updatable', +], description = 'Audit TEAL files' } + +# Commands intented for CI only, prefixed with `ci-` by convention +ci-teal-diff = { commands = [ + 'git add -N ./smart_contracts/artifacts', + 'git diff --exit-code --minimal ./smart_contracts/artifacts', +], description = 'Check TEAL files for differences' } diff --git a/examples/generators/production_puya_smart_contract_typescript/.algokit/generators/create_contract/copier.yaml b/examples/generators/production_python_smart_contract_typescript/.algokit/generators/create_contract/copier.yaml similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/.algokit/generators/create_contract/copier.yaml rename to examples/generators/production_python_smart_contract_typescript/.algokit/generators/create_contract/copier.yaml diff --git a/examples/generators/production_puya_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 b/examples/generators/production_python_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 similarity index 83% rename from examples/generators/production_puya_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 rename to examples/generators/production_python_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 index eabbaa0..9828347 100644 --- a/examples/generators/production_puya_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 +++ b/examples/generators/production_python_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 @@ -1,4 +1,4 @@ -from puyapy import ARC4Contract, arc4 +from algopy import ARC4Contract, arc4 class {{ contract_name.split('_')|map('capitalize')|join }}(ARC4Contract): diff --git a/examples/generators/production_puya_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 b/examples/generators/production_python_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 rename to examples/generators/production_python_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 diff --git a/examples/generators/production_puya_smart_contract_typescript/.copier-answers.yml b/examples/generators/production_python_smart_contract_typescript/.copier-answers.yml similarity index 79% rename from examples/generators/production_puya_smart_contract_typescript/.copier-answers.yml rename to examples/generators/production_python_smart_contract_typescript/.copier-answers.yml index 44c46ef..47664f0 100644 --- a/examples/generators/production_puya_smart_contract_typescript/.copier-answers.yml +++ b/examples/generators/production_python_smart_contract_typescript/.copier-answers.yml @@ -6,5 +6,5 @@ author_name: None contract_name: hello_world deployment_language: typescript preset_name: production -project_name: production_puya_smart_contract_typescript +project_name: production_python_smart_contract_typescript diff --git a/examples/generators/production_puya_smart_contract_typescript/.editorconfig b/examples/generators/production_python_smart_contract_typescript/.editorconfig similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/.editorconfig rename to examples/generators/production_python_smart_contract_typescript/.editorconfig diff --git a/examples/generators/production_puya_smart_contract_typescript/.env.localnet.template b/examples/generators/production_python_smart_contract_typescript/.env.localnet.template similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/.env.localnet.template rename to examples/generators/production_python_smart_contract_typescript/.env.localnet.template diff --git a/examples/generators/production_puya_smart_contract_typescript/.env.template b/examples/generators/production_python_smart_contract_typescript/.env.template similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/.env.template rename to examples/generators/production_python_smart_contract_typescript/.env.template diff --git a/examples/generators/production_puya_smart_contract_typescript/.env.testnet.template b/examples/generators/production_python_smart_contract_typescript/.env.testnet.template similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/.env.testnet.template rename to examples/generators/production_python_smart_contract_typescript/.env.testnet.template diff --git a/examples/generators/production_puya_smart_contract_typescript/.gitattributes b/examples/generators/production_python_smart_contract_typescript/.gitattributes similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/.gitattributes rename to examples/generators/production_python_smart_contract_typescript/.gitattributes diff --git a/examples/generators/starter_puya_smart_contract_typescript/.github/workflows/cd.yaml b/examples/generators/production_python_smart_contract_typescript/.github/workflows/production-python-smart-contract-typescript-cd.yaml similarity index 52% rename from examples/generators/starter_puya_smart_contract_typescript/.github/workflows/cd.yaml rename to examples/generators/production_python_smart_contract_typescript/.github/workflows/production-python-smart-contract-typescript-cd.yaml index 2401bf4..5abadce 100644 --- a/examples/generators/starter_puya_smart_contract_typescript/.github/workflows/cd.yaml +++ b/examples/generators/production_python_smart_contract_typescript/.github/workflows/production-python-smart-contract-typescript-cd.yaml @@ -1,21 +1,19 @@ -name: Continuous Delivery of Smart Contract +name: Release production_python_smart_contract_typescript on: + workflow_call: push: branches: - main -concurrency: release - jobs: - ci-check: - name: Perform Checks - uses: ./.github/workflows/checks.yaml - + validate: + name: Validate production_python_smart_contract_typescript + uses: ./.github/workflows/production-python-smart-contract-typescript-ci.yaml deploy-testnet: - runs-on: 'ubuntu-latest' - needs: ci-check - environment: Test + runs-on: "ubuntu-latest" + needs: validate + environment: contract-testnet steps: - name: Checkout source code uses: actions/checkout@v4 @@ -26,14 +24,14 @@ jobs: - name: Set up Python 3.12 uses: actions/setup-python@v5 with: - python-version: '3.12' - cache: 'poetry' + python-version: "3.12" + cache: "poetry" - name: Install algokit run: pipx install algokit - name: Bootstrap dependencies - run: algokit bootstrap all + run: algokit bootstrap all --project-name 'production_python_smart_contract_typescript' - name: Configure git shell: bash @@ -42,9 +40,9 @@ jobs: git config --global user.email "actions@github.com" && git config --global user.name "github-actions" - name: Deploy to testnet - run: algokit deploy testnet + run: algokit deploy testnet --project-name 'production_python_smart_contract_typescript' env: - # This is the account that becomes the creator of the contract. - # Since we are not using the optional dispenser account (via DISPENSER_MNEMONIC), - # it must also be funded with enough Algos to deploy and fund the smart contracts created + # This is the account that becomes the creator of the contract DEPLOYER_MNEMONIC: ${{ secrets.DEPLOYER_MNEMONIC }} + # The dispenser account is used to ensure the deployer account is funded + DISPENSER_MNEMONIC: ${{ secrets.DISPENSER_MNEMONIC }} diff --git a/examples/generators/production_python_smart_contract_typescript/.github/workflows/production-python-smart-contract-typescript-ci.yaml b/examples/generators/production_python_smart_contract_typescript/.github/workflows/production-python-smart-contract-typescript-ci.yaml new file mode 100644 index 0000000..5c2cbd1 --- /dev/null +++ b/examples/generators/production_python_smart_contract_typescript/.github/workflows/production-python-smart-contract-typescript-ci.yaml @@ -0,0 +1,60 @@ +name: Validate production_python_smart_contract_typescript + +on: + workflow_call: + pull_request: + +jobs: + validate: + runs-on: "ubuntu-latest" + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Install poetry + run: pipx install poetry + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: "poetry" + + - name: Install algokit + run: pipx install algokit + + - name: Start LocalNet + run: algokit localnet start + + - name: Bootstrap dependencies + run: algokit bootstrap all --project-name 'production_python_smart_contract_typescript' + + - name: Configure git + shell: bash + run: | + # set git user and email as test invoke git + git config --global user.email "actions@github.com" && git config --global user.name "github-actions" + + - name: Audit python dependencies + run: algokit project run audit --project-name 'production_python_smart_contract_typescript' + + - name: Lint and format python dependencies + run: algokit project run lint --project-name 'production_python_smart_contract_typescript' + + - name: Run tests + shell: bash + run: | + set -o pipefail + algokit project run test --project-name 'production_python_smart_contract_typescript' + + - name: Build smart contracts + run: algokit project run build --project-name 'production_python_smart_contract_typescript' + + - name: Scan TEAL files for issues + run: algokit project run audit-teal --project-name 'production_python_smart_contract_typescript' + + - name: Check output stability of the smart contracts + run: algokit project run ci-teal-diff --project-name 'production_python_smart_contract_typescript' + + - name: Run deployer against LocalNet + run: algokit project deploy localnet --project-name 'production_python_smart_contract_typescript' diff --git a/examples/generators/production_puya_smart_contract_typescript/.gitignore b/examples/generators/production_python_smart_contract_typescript/.gitignore similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/.gitignore rename to examples/generators/production_python_smart_contract_typescript/.gitignore diff --git a/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Build_Beaker_application.xml b/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Build_Beaker_application.xml new file mode 100644 index 0000000..debed6d --- /dev/null +++ b/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Build_Beaker_application.xml @@ -0,0 +1,35 @@ + + + + + diff --git a/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Build_Beaker_application____LocalNet.xml b/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Build_Beaker_application____LocalNet.xml new file mode 100644 index 0000000..a04c6b0 --- /dev/null +++ b/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Build_Beaker_application____LocalNet.xml @@ -0,0 +1,37 @@ + + + + + diff --git a/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Build___Deploy_Beaker_application.xml b/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Build___Deploy_Beaker_application.xml new file mode 100644 index 0000000..0091510 --- /dev/null +++ b/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Build___Deploy_Beaker_application.xml @@ -0,0 +1,18 @@ + + + + diff --git a/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Deploy_Built_Beaker_application.xml b/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Deploy_Built_Beaker_application.xml new file mode 100644 index 0000000..17b8154 --- /dev/null +++ b/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Deploy_Built_Beaker_application.xml @@ -0,0 +1,16 @@ + + + + diff --git a/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Reset_AlgoKit_LocalNet.xml b/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Reset_AlgoKit_LocalNet.xml new file mode 100644 index 0000000..7f1236a --- /dev/null +++ b/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Reset_AlgoKit_LocalNet.xml @@ -0,0 +1,17 @@ + + + + diff --git a/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml b/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml new file mode 100644 index 0000000..f699a7a --- /dev/null +++ b/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml @@ -0,0 +1,17 @@ + + + + diff --git a/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Stop_AlgoKit_LocalNet.xml b/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Stop_AlgoKit_LocalNet.xml new file mode 100644 index 0000000..e510cbc --- /dev/null +++ b/examples/generators/production_python_smart_contract_typescript/.idea/runConfigurations/Stop_AlgoKit_LocalNet.xml @@ -0,0 +1,17 @@ + + + + diff --git a/examples/generators/production_puya_smart_contract_typescript/.pre-commit-config.yaml b/examples/generators/production_python_smart_contract_typescript/.pre-commit-config.yaml similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/.pre-commit-config.yaml rename to examples/generators/production_python_smart_contract_typescript/.pre-commit-config.yaml diff --git a/examples/generators/production_puya_smart_contract_typescript/.prettierignore b/examples/generators/production_python_smart_contract_typescript/.prettierignore similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/.prettierignore rename to examples/generators/production_python_smart_contract_typescript/.prettierignore diff --git a/examples/generators/production_puya_smart_contract_typescript/.prettierrc.js b/examples/generators/production_python_smart_contract_typescript/.prettierrc.js similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/.prettierrc.js rename to examples/generators/production_python_smart_contract_typescript/.prettierrc.js diff --git a/examples/generators/production_puya_smart_contract_typescript/.tours/getting-started-with-your-algokit-project.tour b/examples/generators/production_python_smart_contract_typescript/.tours/getting-started-with-your-algokit-project.tour similarity index 99% rename from examples/generators/production_puya_smart_contract_typescript/.tours/getting-started-with-your-algokit-project.tour rename to examples/generators/production_python_smart_contract_typescript/.tours/getting-started-with-your-algokit-project.tour index 30f150f..bd27ad2 100644 --- a/examples/generators/production_puya_smart_contract_typescript/.tours/getting-started-with-your-algokit-project.tour +++ b/examples/generators/production_python_smart_contract_typescript/.tours/getting-started-with-your-algokit-project.tour @@ -50,7 +50,7 @@ { "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 + "line": 15 } ] } diff --git a/examples/generators/production_puya_smart_contract_typescript/.vscode/extensions.json b/examples/generators/production_python_smart_contract_typescript/.vscode/extensions.json similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/.vscode/extensions.json rename to examples/generators/production_python_smart_contract_typescript/.vscode/extensions.json diff --git a/examples/generators/production_puya_smart_contract_typescript/.vscode/launch.json b/examples/generators/production_python_smart_contract_typescript/.vscode/launch.json similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/.vscode/launch.json rename to examples/generators/production_python_smart_contract_typescript/.vscode/launch.json diff --git a/examples/generators/production_puya_smart_contract_typescript/.vscode/settings.json b/examples/generators/production_python_smart_contract_typescript/.vscode/settings.json similarity index 95% rename from examples/generators/production_puya_smart_contract_typescript/.vscode/settings.json rename to examples/generators/production_python_smart_contract_typescript/.vscode/settings.json index af8031f..dd846d7 100644 --- a/examples/generators/production_puya_smart_contract_typescript/.vscode/settings.json +++ b/examples/generators/production_python_smart_contract_typescript/.vscode/settings.json @@ -20,9 +20,9 @@ "python.defaultInterpreterPath": "${workspaceFolder}/.venv", "[python]": { "editor.codeActionsOnSave": { - "source.fixAll": true, + "source.fixAll": "explicit", // Prevent default import sorting from running; Ruff will sort imports for us anyway - "source.organizeImports": false + "source.organizeImports": "never" }, "editor.defaultFormatter": "ms-python.black-formatter", }, diff --git a/examples/generators/production_puya_smart_contract_typescript/.vscode/tasks.json b/examples/generators/production_python_smart_contract_typescript/.vscode/tasks.json similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/.vscode/tasks.json rename to examples/generators/production_python_smart_contract_typescript/.vscode/tasks.json diff --git a/examples/generators/production_puya_smart_contract_typescript/README.md b/examples/generators/production_python_smart_contract_typescript/README.md similarity index 91% rename from examples/generators/production_puya_smart_contract_typescript/README.md rename to examples/generators/production_python_smart_contract_typescript/README.md index e85ce05..e79f831 100644 --- a/examples/generators/production_puya_smart_contract_typescript/README.md +++ b/examples/generators/production_python_smart_contract_typescript/README.md @@ -1,4 +1,4 @@ -# production_puya_smart_contract_typescript +# production_python_smart_contract_typescript This project has been generated using AlgoKit. See below for default getting started instructions. @@ -9,8 +9,6 @@ This project has been generated using AlgoKit. See below for default getting sta - [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. - ### Initial setup 1. Clone this repository locally @@ -49,7 +47,9 @@ This project has been generated using AlgoKit. See below for default getting sta > For guidance on `smart_contracts` folder and adding new contracts to the project please see [README](smart_contracts/README.md) on the respective folder.### Continuous Integration / Continuous Deployment (CI/CD) -This project uses [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to define CI/CD workflows, which are located in the [`.github/workflows`](./.github/workflows) folder. +This project uses [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to define CI/CD workflows, which are located in the [.github/workflows](`.github/workflows`) folder. + +> Please note, if you instantiated the project with --workspace flag in `algokit init` it will automatically attempt to move the contents of the `.github` folder to the root of the workspace. ### Debugging Smart Contracts @@ -98,12 +98,11 @@ For pushes to `main` branch, after the above checks pass, the following deployme # Tools -This project makes use of Python to build Algorand smart contracts. The following tools are in use: +This project makes use of Algorand 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/) +- [Algorand Python](https://github.com/algorandfoundation/puya) - A semantically and syntactically compatible, typed Python language that works with standard Python tooling and allows you to express smart contracts (apps) and smart signatures (logic signatures) for deployment on the Algorand Virtual Machine (AVM); [docs](https://github.com/algorandfoundation/puya), [examples](https://github.com/algorandfoundation/puya/tree/main/examples) - [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. diff --git a/examples/generators/production_puya_smart_contract_typescript/jest.config.ts b/examples/generators/production_python_smart_contract_typescript/jest.config.ts similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/jest.config.ts rename to examples/generators/production_python_smart_contract_typescript/jest.config.ts diff --git a/examples/generators/production_puya_smart_contract_typescript/package.json b/examples/generators/production_python_smart_contract_typescript/package.json similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/package.json rename to examples/generators/production_python_smart_contract_typescript/package.json diff --git a/examples/generators/production_puya_smart_contract_typescript/poetry.toml b/examples/generators/production_python_smart_contract_typescript/poetry.toml similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/poetry.toml rename to examples/generators/production_python_smart_contract_typescript/poetry.toml diff --git a/examples/generators/production_puya_smart_contract_typescript/pyproject.toml b/examples/generators/production_python_smart_contract_typescript/pyproject.toml similarity index 65% rename from examples/generators/production_puya_smart_contract_typescript/pyproject.toml rename to examples/generators/production_python_smart_contract_typescript/pyproject.toml index 0c90521..230a8b0 100644 --- a/examples/generators/production_puya_smart_contract_typescript/pyproject.toml +++ b/examples/generators/production_python_smart_contract_typescript/pyproject.toml @@ -1,5 +1,5 @@ [tool.poetry] -name = "production_puya_smart_contract_typescript" +name = "production_python_smart_contract_typescript" version = "0.1.0" description = "Algorand smart contracts" authors = ["None "] @@ -9,14 +9,16 @@ readme = "README.md" python = "^3.12" algokit-utils = "^2.2.0" python-dotenv = "^1.0.0" -puya = "^0" +algorand-python = "^1.0.0" [tool.poetry.group.dev.dependencies] +algokit-client-generator = "^1.1.3" black = {extras = ["d"], version = "*"} ruff = "^0.1.6" mypy = "*" pip-audit = "*" pre-commit = "*" +puyapy = "^1.0.0" [build-system] requires = ["poetry-core"] @@ -38,8 +40,19 @@ suppress-none-returning = true [tool.mypy] files = "smart_contracts/" python_version = "3.12" +disallow_any_generics = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_defs = true +disallow_incomplete_defs = true check_untyped_defs = true +disallow_untyped_decorators = true warn_redundant_casts = true warn_unused_ignores = true -allow_untyped_defs = false +warn_return_any = true strict_equality = true +strict_concatenate = true +disallow_any_unimported = true +disallow_any_expr = true +disallow_any_decorated = true +disallow_any_explicit = true diff --git a/examples/generators/production_puya_smart_contract_typescript/smart_contracts/README.md b/examples/generators/production_python_smart_contract_typescript/smart_contracts/README.md similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/smart_contracts/README.md rename to examples/generators/production_python_smart_contract_typescript/smart_contracts/README.md diff --git a/examples/generators/production_puya_smart_contract_typescript/smart_contracts/__init__.py b/examples/generators/production_python_smart_contract_typescript/smart_contracts/__init__.py similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/smart_contracts/__init__.py rename to examples/generators/production_python_smart_contract_typescript/smart_contracts/__init__.py diff --git a/examples/generators/production_puya_smart_contract_typescript/smart_contracts/__main__.py b/examples/generators/production_python_smart_contract_typescript/smart_contracts/__main__.py similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/smart_contracts/__main__.py rename to examples/generators/production_python_smart_contract_typescript/smart_contracts/__main__.py diff --git a/examples/generators/production_puya_smart_contract_python/smart_contracts/config.py b/examples/generators/production_python_smart_contract_typescript/smart_contracts/config.py similarity index 96% rename from examples/generators/production_puya_smart_contract_python/smart_contracts/config.py rename to examples/generators/production_python_smart_contract_typescript/smart_contracts/config.py index 7d56d82..8f3ca93 100644 --- a/examples/generators/production_puya_smart_contract_python/smart_contracts/config.py +++ b/examples/generators/production_python_smart_contract_typescript/smart_contracts/config.py @@ -38,7 +38,7 @@ def import_deploy_if_exists( deploy_module = importlib.import_module( f"{folder.parent.name}.{folder.name}.deploy_config" ) - return deploy_module.deploy + return deploy_module.deploy # type: ignore except ImportError: return None diff --git a/examples/generators/production_puya_smart_contract_typescript/smart_contracts/cool_contract/contract.py b/examples/generators/production_python_smart_contract_typescript/smart_contracts/cool_contract/contract.py similarity index 79% rename from examples/generators/production_puya_smart_contract_typescript/smart_contracts/cool_contract/contract.py rename to examples/generators/production_python_smart_contract_typescript/smart_contracts/cool_contract/contract.py index 1c9f04d..4c00d34 100644 --- a/examples/generators/production_puya_smart_contract_typescript/smart_contracts/cool_contract/contract.py +++ b/examples/generators/production_python_smart_contract_typescript/smart_contracts/cool_contract/contract.py @@ -1,4 +1,4 @@ -from puyapy import ARC4Contract, arc4 +from algopy import ARC4Contract, arc4 class CoolContract(ARC4Contract): diff --git a/examples/generators/production_puya_smart_contract_typescript/smart_contracts/cool_contract/deploy-config.ts b/examples/generators/production_python_smart_contract_typescript/smart_contracts/cool_contract/deploy-config.ts similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/smart_contracts/cool_contract/deploy-config.ts rename to examples/generators/production_python_smart_contract_typescript/smart_contracts/cool_contract/deploy-config.ts diff --git a/examples/generators/starter_puya_smart_contract_typescript/smart_contracts/hello_world/contract.py b/examples/generators/production_python_smart_contract_typescript/smart_contracts/hello_world/contract.py similarity index 63% rename from examples/generators/starter_puya_smart_contract_typescript/smart_contracts/hello_world/contract.py rename to examples/generators/production_python_smart_contract_typescript/smart_contracts/hello_world/contract.py index 589aea9..9052c05 100644 --- a/examples/generators/starter_puya_smart_contract_typescript/smart_contracts/hello_world/contract.py +++ b/examples/generators/production_python_smart_contract_typescript/smart_contracts/hello_world/contract.py @@ -1,4 +1,5 @@ -from puyapy import ARC4Contract, arc4 +# pyright: reportMissingModuleSource=false +from algopy import ARC4Contract, arc4 class HelloWorld(ARC4Contract): diff --git a/examples/generators/production_puya_smart_contract_typescript/smart_contracts/hello_world/deploy-config.ts b/examples/generators/production_python_smart_contract_typescript/smart_contracts/hello_world/deploy-config.ts similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/smart_contracts/hello_world/deploy-config.ts rename to examples/generators/production_python_smart_contract_typescript/smart_contracts/hello_world/deploy-config.ts diff --git a/examples/generators/production_puya_smart_contract_typescript/smart_contracts/helpers/__init__.py b/examples/generators/production_python_smart_contract_typescript/smart_contracts/helpers/__init__.py similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/smart_contracts/helpers/__init__.py rename to examples/generators/production_python_smart_contract_typescript/smart_contracts/helpers/__init__.py diff --git a/examples/generators/production_puya_smart_contract_typescript/smart_contracts/helpers/build.py b/examples/generators/production_python_smart_contract_typescript/smart_contracts/helpers/build.py similarity index 77% rename from examples/generators/production_puya_smart_contract_typescript/smart_contracts/helpers/build.py rename to examples/generators/production_python_smart_contract_typescript/smart_contracts/helpers/build.py index 67377d3..51cea43 100644 --- a/examples/generators/production_puya_smart_contract_typescript/smart_contracts/helpers/build.py +++ b/examples/generators/production_python_smart_contract_typescript/smart_contracts/helpers/build.py @@ -3,6 +3,8 @@ from pathlib import Path from shutil import rmtree +from smart_contracts.helpers.util import find_app_spec_file + logger = logging.getLogger(__name__) deployment_extension = "ts" @@ -16,9 +18,10 @@ def build(output_dir: Path, contract_path: Path) -> Path: build_result = subprocess.run( [ - "poetry", - "run", - "puyapy", + "algokit", + "--no-color", + "compile", + "python", contract_path.absolute(), f"--out-dir={output_dir}", "--output-arc32", @@ -30,12 +33,16 @@ def build(output_dir: Path, contract_path: Path) -> Path: if build_result.returncode: raise Exception(f"Could not build contract:\n{build_result.stdout}") + app_spec_file_name = find_app_spec_file(output_dir) + if app_spec_file_name is None: + raise Exception("Could not generate typed client, .arc32.json file not found") + generate_result = subprocess.run( [ "algokit", "generate", "client", - output_dir / "application.json", + output_dir / app_spec_file_name, "--output", output_dir / f"client.{deployment_extension}", ], @@ -53,4 +60,4 @@ def build(output_dir: Path, contract_path: Path) -> Path: raise Exception( f"Could not generate typed client:\n{generate_result.stdout}" ) - return output_dir / "application.json" + return output_dir / app_spec_file_name diff --git a/examples/generators/production_python_smart_contract_typescript/smart_contracts/helpers/util.py b/examples/generators/production_python_smart_contract_typescript/smart_contracts/helpers/util.py new file mode 100644 index 0000000..6cf7e5b --- /dev/null +++ b/examples/generators/production_python_smart_contract_typescript/smart_contracts/helpers/util.py @@ -0,0 +1,8 @@ +from pathlib import Path + + +def find_app_spec_file(output_dir: Path) -> str | None: + for file in output_dir.iterdir(): + if file.is_file() and file.suffixes == [".arc32", ".json"]: + return file.name + return None diff --git a/examples/generators/production_puya_smart_contract_typescript/smart_contracts/index.ts b/examples/generators/production_python_smart_contract_typescript/smart_contracts/index.ts similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/smart_contracts/index.ts rename to examples/generators/production_python_smart_contract_typescript/smart_contracts/index.ts diff --git a/examples/generators/production_puya_smart_contract_typescript/tests/hello-world.spec.ts b/examples/generators/production_python_smart_contract_typescript/tests/hello-world.spec.ts similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/tests/hello-world.spec.ts rename to examples/generators/production_python_smart_contract_typescript/tests/hello-world.spec.ts diff --git a/examples/generators/production_puya_smart_contract_typescript/tsconfig.json b/examples/generators/production_python_smart_contract_typescript/tsconfig.json similarity index 100% rename from examples/generators/production_puya_smart_contract_typescript/tsconfig.json rename to examples/generators/production_python_smart_contract_typescript/tsconfig.json diff --git a/examples/generators/starter_puya_smart_contract_python/.algokit.toml b/examples/generators/starter_puya_smart_contract_python/.algokit.toml deleted file mode 100644 index cdcf495..0000000 --- a/examples/generators/starter_puya_smart_contract_python/.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 = 'contract' -name = 'starter_puya_smart_contract_python' diff --git a/examples/generators/starter_puya_smart_contract_python/.github/workflows/checks.yaml b/examples/generators/starter_puya_smart_contract_python/.github/workflows/checks.yaml deleted file mode 100644 index 2c75ab4..0000000 --- a/examples/generators/starter_puya_smart_contract_python/.github/workflows/checks.yaml +++ /dev/null @@ -1,76 +0,0 @@ -name: Check code base - -on: - workflow_call: - -jobs: - checks: - runs-on: 'ubuntu-latest' - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Install poetry - run: pipx install poetry - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: '3.12' - cache: 'poetry' - - - name: Install algokit - run: pipx install algokit - - - name: Start LocalNet - run: algokit localnet start - - - name: Bootstrap dependencies - run: algokit bootstrap all - - - name: Configure git - shell: bash - run: | - # set git user and email as test invoke git - git config --global user.email "actions@github.com" && git config --global user.name "github-actions" - - - name: Audit with pip-audit - run: | - # audit non dev dependencies, no exclusions - poetry export --without=dev > requirements.txt && poetry run pip-audit -r requirements.txt - - # audit all dependencies, with exclusions. - # If a vulnerability is found in a dev dependency without an available fix, - # it can be temporarily ignored by adding --ignore-vuln e.g. - # --ignore-vuln "GHSA-hcpj-qp55-gfph" # GitPython vulnerability, dev only dependency - poetry run pip-audit - - - name: Check formatting with Black - run: | - # stop the build if there are files that don't meet formatting requirements - poetry run black --check . - - - name: Check linting with Ruff - run: | - # stop the build if there are Python syntax errors or undefined names - poetry run ruff . - - - name: Check types with mypy - run: poetry run mypy - - - name: Build smart contracts - run: poetry run python -m smart_contracts build - - - name: Scan TEAL files for issues - run: algokit task analyze .algokit --recursive --force # add --diff flag if you want output stability checks instead - - - name: Check output stability of the smart contracts - shell: bash - run: | - # Add untracked files as empty so they come up in diff - git add -N ./smart_contracts/artifacts - # Error out if there are any changes in teal after generating output - git diff --exit-code --minimal ./smart_contracts/artifacts || (echo "::error ::Smart contract artifacts have changed, ensure committed artifacts are up to date" && exit 1); - - - name: Run deployer against LocalNet - run: poetry run python -m smart_contracts deploy diff --git a/examples/generators/starter_puya_smart_contract_python/.github/workflows/pr.yaml b/examples/generators/starter_puya_smart_contract_python/.github/workflows/pr.yaml deleted file mode 100644 index a80f784..0000000 --- a/examples/generators/starter_puya_smart_contract_python/.github/workflows/pr.yaml +++ /dev/null @@ -1,8 +0,0 @@ -name: Pull Request validation - -on: [pull_request] - -jobs: - pr-check: - name: Perform Checks - uses: ./.github/workflows/checks.yaml diff --git a/examples/generators/starter_puya_smart_contract_python/README.md b/examples/generators/starter_puya_smart_contract_python/README.md deleted file mode 100644 index 0763fa5..0000000 --- a/examples/generators/starter_puya_smart_contract_python/README.md +++ /dev/null @@ -1,113 +0,0 @@ -# starter_puya_smart_contract_python - -This project has been generated using AlgoKit. See below for default getting started instructions. - -# 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. - -### 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. - - 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 - -### Subsequently - -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 - -> For guidance on `smart_contracts` folder and adding new contracts to the project please see [README](smart_contracts/README.md) on the respective folder.### Continuous Integration / Continuous Deployment (CI/CD) - -This project uses [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to define CI/CD workflows, which are located in the [`.github/workflows`](./.github/workflows) folder. - -### Debugging Smart Contracts - -This project is optimized to work with AlgoKit AVM Debugger extension. To activate it: -Refer to the commented header in the `__main__.py` file in the `smart_contracts` folder. - -If you have opted in to include VSCode launch configurations in your project, you can also use the `Debug TEAL via AlgoKit AVM Debugger` launch configuration to interactively select an available trace file and launch the debug session for your smart contract. - -For information on using and setting up the `AlgoKit AVM Debugger` VSCode extension refer [here](https://github.com/algorandfoundation/algokit-avm-vscode-debugger). To install the extension from the VSCode Marketplace, use the following link: [AlgoKit AVM Debugger extension](https://marketplace.visualstudio.com/items?itemName=algorandfoundation.algokit-avm-vscode-debugger). - -#### Setting up GitHub for CI/CD workflow and TestNet deployment - - 1. Every time you have a change to your smart contract, and when you first initialize the project you need to [build the contract](#initial-setup) and then commit the `smart_contracts/artifacts` folder so the [output stability](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/articles/output_stability.md) tests pass - 2. Decide what values you want to use for the `allow_update`, `allow_delete` and the `on_schema_break`, `on_update` parameters specified in [`contract.py`](./smart_contracts/hello_world/contract.py). - When deploying to LocalNet these values are both set to allow update and replacement of the app for convenience. But for non-LocalNet networks - the defaults are more conservative. - These default values will allow the smart contract to be deployed initially, but will not allow the app to be updated or deleted if is changed and the build will instead fail. - To help you decide it may be helpful to read the [AlgoKit Utils app deployment documentation](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/docs/capabilities/app-deploy.md) or the [AlgoKit smart contract deployment architecture](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/architecture-decisions/2023-01-12_smart-contract-deployment.md#upgradeable-and-deletable-contracts). - 3. Create a [Github Environment](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#creating-an-environment) named `Test`. - Note: If you have a private repository and don't have GitHub Enterprise then Environments won't work and you'll need to convert the GitHub Action to use a different approach. Ignore this step if you picked `Starter` preset. - 4. Create or obtain a mnemonic for an Algorand account for use on TestNet to deploy apps, referred to as the `DEPLOYER` account. - 5. Store the mnemonic as a [secret](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#environment-secrets) `DEPLOYER_MNEMONIC` - in the Test environment created in step 3. - 6. The account used to deploy the smart contract will require enough funds to create the app, and also fund it. There are two approaches available here: - * Either, ensure the account is funded outside of CI/CD. - In Testnet, funds can be obtained by using the [Algorand TestNet dispenser](https://bank.testnet.algorand.network/) and we recommend provisioning 50 ALGOs. - * Or, fund the account as part of the CI/CD process by using a `DISPENSER_MNEMONIC` GitHub Environment secret to point to a separate `DISPENSER` account that you maintain ALGOs in (similarly, you need to provision ALGOs into this account using the [TestNet dispenser](https://bank.testnet.algorand.network/)). - -#### Continuous Integration - -For pull requests and pushes to `main` branch against this repository the following checks are automatically performed by GitHub Actions: - - Python dependencies are audited using [pip-audit](https://pypi.org/project/pip-audit/) - - Code formatting is checked using [Black](https://github.com/psf/black) - - Linting is checked using [Ruff](https://github.com/charliermarsh/ruff) - - Types are checked using [mypy](https://mypy-lang.org/) - - Smart contract artifacts are built - - Smart contract artifacts are checked for [output stability](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/articles/output_stability.md) - - Smart contract is deployed to a AlgoKit LocalNet instance - -#### Continuous Deployment - -For pushes to `main` branch, after the above checks pass, the following deployment actions are performed: - - The smart contract(s) are deployed to TestNet using [AlgoNode](https://algonode.io). - -> Please note deployment is also performed via `algokit deploy` command which can be invoked both via CI as seen on this project, or locally. For more information on how to use `algokit deploy` please see [AlgoKit documentation](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/deploy.md). - -# 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. - - [pre-commit](https://pre-commit.com/): A framework for managing and maintaining multi-language pre-commit hooks, to enable pre-commit you need to run `pre-commit install` in the root of the repository. This will install the pre-commit hooks and run them against modified files when committing. If any of the hooks fail, the commit will be aborted. To run the hooks on all files, use `pre-commit run --all-files`. -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/generators/starter_puya_smart_contract_python/pyproject.toml b/examples/generators/starter_puya_smart_contract_python/pyproject.toml deleted file mode 100644 index 99c1707..0000000 --- a/examples/generators/starter_puya_smart_contract_python/pyproject.toml +++ /dev/null @@ -1,45 +0,0 @@ -[tool.poetry] -name = "starter_puya_smart_contract_python" -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 = "*" -pre-commit = "*" - -[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/generators/starter_puya_smart_contract_typescript/.algokit.toml b/examples/generators/starter_puya_smart_contract_typescript/.algokit.toml deleted file mode 100644 index 9ef71d2..0000000 --- a/examples/generators/starter_puya_smart_contract_typescript/.algokit.toml +++ /dev/null @@ -1,19 +0,0 @@ -[algokit] -min_version = "v1.8.0" - -[deploy] -command = "npm run deploy:ci" -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 = 'contract' -name = 'starter_puya_smart_contract_typescript' diff --git a/examples/generators/starter_puya_smart_contract_typescript/.github/workflows/checks.yaml b/examples/generators/starter_puya_smart_contract_typescript/.github/workflows/checks.yaml deleted file mode 100644 index 3994f5d..0000000 --- a/examples/generators/starter_puya_smart_contract_typescript/.github/workflows/checks.yaml +++ /dev/null @@ -1,76 +0,0 @@ -name: Check code base - -on: - workflow_call: - -jobs: - checks: - runs-on: 'ubuntu-latest' - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Install poetry - run: pipx install poetry - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: '3.12' - cache: 'poetry' - - - name: Install algokit - run: pipx install algokit - - - name: Start LocalNet - run: algokit localnet start - - - name: Bootstrap dependencies - run: algokit bootstrap all - - - name: Configure git - shell: bash - run: | - # set git user and email as test invoke git - git config --global user.email "actions@github.com" && git config --global user.name "github-actions" - - - name: Audit with pip-audit - run: | - # audit non dev dependencies, no exclusions - poetry export --without=dev > requirements.txt && poetry run pip-audit -r requirements.txt - - # audit all dependencies, with exclusions. - # If a vulnerability is found in a dev dependency without an available fix, - # it can be temporarily ignored by adding --ignore-vuln e.g. - # --ignore-vuln "GHSA-hcpj-qp55-gfph" # GitPython vulnerability, dev only dependency - poetry run pip-audit - - - name: Check formatting with Black - run: | - # stop the build if there are files that don't meet formatting requirements - poetry run black --check . - - - name: Check linting with Ruff - run: | - # stop the build if there are Python syntax errors or undefined names - poetry run ruff . - - - name: Check types with mypy - run: poetry run mypy - - - name: Build smart contracts - run: poetry run python -m smart_contracts build - - - name: Scan TEAL files for issues - run: algokit task analyze .algokit --recursive --force # add --diff flag if you want output stability checks instead - - - name: Check output stability of the smart contracts - shell: bash - run: | - # Add untracked files as empty so they come up in diff - git add -N ./smart_contracts/artifacts - # Error out if there are any changes in teal after generating output - git diff --exit-code --minimal ./smart_contracts/artifacts || (echo "::error ::Smart contract artifacts have changed, ensure committed artifacts are up to date" && exit 1); - - - name: Run deployer against LocalNet - run: npm run --prefix smart_contracts deploy:ci diff --git a/examples/generators/starter_puya_smart_contract_typescript/.github/workflows/pr.yaml b/examples/generators/starter_puya_smart_contract_typescript/.github/workflows/pr.yaml deleted file mode 100644 index a80f784..0000000 --- a/examples/generators/starter_puya_smart_contract_typescript/.github/workflows/pr.yaml +++ /dev/null @@ -1,8 +0,0 @@ -name: Pull Request validation - -on: [pull_request] - -jobs: - pr-check: - name: Perform Checks - uses: ./.github/workflows/checks.yaml diff --git a/examples/generators/starter_puya_smart_contract_typescript/.pre-commit-config.yaml b/examples/generators/starter_puya_smart_contract_typescript/.pre-commit-config.yaml deleted file mode 100644 index 8862f86..0000000 --- a/examples/generators/starter_puya_smart_contract_typescript/.pre-commit-config.yaml +++ /dev/null @@ -1,48 +0,0 @@ -repos: - - repo: local - hooks: - - - id: black - name: black - description: "Black: The uncompromising Python code formatter" - entry: poetry run black - language: system - minimum_pre_commit_version: 2.9.2 - require_serial: true - types_or: [ python, pyi ] - - - - id: ruff - name: ruff - description: "Run 'ruff' for extremely fast Python linting" - entry: poetry run ruff - language: system - types: [ python ] - args: [ --fix ] - require_serial: false - additional_dependencies: [ ] - minimum_pre_commit_version: '0' - files: '^(src|tests)/' - - - - id: mypy - name: mypy - description: '`mypy` will check Python types for correctness' - entry: poetry run mypy - language: system - types_or: [ python, pyi ] - require_serial: true - additional_dependencies: [ ] - minimum_pre_commit_version: '2.9.2' - files: '^(src|tests)/' - - - id: tealer - name: tealer - description: "Run AlgoKit `Tealer` for TEAL static analysis" - entry: algokit - language: system - args: [task, analyze, ".algokit", "--recursive", "--force"] - require_serial: false - additional_dependencies: [] - minimum_pre_commit_version: "0" - files: '^.*\.teal$' diff --git a/examples/generators/starter_puya_smart_contract_typescript/README.md b/examples/generators/starter_puya_smart_contract_typescript/README.md deleted file mode 100644 index 85af394..0000000 --- a/examples/generators/starter_puya_smart_contract_typescript/README.md +++ /dev/null @@ -1,118 +0,0 @@ -# starter_puya_smart_contract_typescript - -This project has been generated using AlgoKit. See below for default getting started instructions. - -# 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. - -### 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. - - 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. - - 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 - 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 - -### Subsequently - -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 - -> For guidance on `smart_contracts` folder and adding new contracts to the project please see [README](smart_contracts/README.md) on the respective folder.### Continuous Integration / Continuous Deployment (CI/CD) - -This project uses [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to define CI/CD workflows, which are located in the [`.github/workflows`](./.github/workflows) folder. - -### Debugging Smart Contracts - -This project is optimized to work with AlgoKit AVM Debugger extension. To activate it: -Refer to the commented header in the `index.ts` file in the `smart_contracts` folder. - -If you have opted in to include VSCode launch configurations in your project, you can also use the `Debug TEAL via AlgoKit AVM Debugger` launch configuration to interactively select an available trace file and launch the debug session for your smart contract. - -For information on using and setting up the `AlgoKit AVM Debugger` VSCode extension refer [here](https://github.com/algorandfoundation/algokit-avm-vscode-debugger). To install the extension from the VSCode Marketplace, use the following link: [AlgoKit AVM Debugger extension](https://marketplace.visualstudio.com/items?itemName=algorandfoundation.algokit-avm-vscode-debugger). - -#### Setting up GitHub for CI/CD workflow and TestNet deployment - - 1. Every time you have a change to your smart contract, and when you first initialize the project you need to [build the contract](#initial-setup) and then commit the `smart_contracts/artifacts` folder so the [output stability](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/articles/output_stability.md) tests pass - 2. Decide what values you want to use for the `allowUpdate` and `allowDelete` parameters specified in [`deploy-config.ts`](./smart_contracts/hello_world/deploy-config.ts). - When deploying to LocalNet these values are both set to `true` for convenience. But for non-LocalNet networks - they are more conservative and use `false` - These default values will allow the smart contract to be deployed initially, but will not allow the app to be updated or deleted if is changed and the build will instead fail. - To help you decide it may be helpful to read the [AlgoKit Utils app deployment documentation](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/docs/capabilities/app-deploy.md) or the [AlgoKit smart contract deployment architecture](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/architecture-decisions/2023-01-12_smart-contract-deployment.md#upgradeable-and-deletable-contracts). - 3. Create a [Github Environment](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#creating-an-environment) named `Test`. - Note: If you have a private repository and don't have GitHub Enterprise then Environments won't work and you'll need to convert the GitHub Action to use a different approach. Ignore this step if you picked `Starter` preset. - 4. Create or obtain a mnemonic for an Algorand account for use on TestNet to deploy apps, referred to as the `DEPLOYER` account. - 5. Store the mnemonic as a [secret](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#environment-secrets) `DEPLOYER_MNEMONIC` - in the Test environment created in step 3. - 6. The account used to deploy the smart contract will require enough funds to create the app, and also fund it. There are two approaches available here: - * Either, ensure the account is funded outside of CI/CD. - In Testnet, funds can be obtained by using the [Algorand TestNet dispenser](https://bank.testnet.algorand.network/) and we recommend provisioning 50 ALGOs. - * Or, fund the account as part of the CI/CD process by using a `DISPENSER_MNEMONIC` GitHub Environment secret to point to a separate `DISPENSER` account that you maintain ALGOs in (similarly, you need to provision ALGOs into this account using the [TestNet dispenser](https://bank.testnet.algorand.network/)). - -#### Continuous Integration - -For pull requests and pushes to `main` branch against this repository the following checks are automatically performed by GitHub Actions: - - Python dependencies are audited using [pip-audit](https://pypi.org/project/pip-audit/) - - Code formatting is checked using [Black](https://github.com/psf/black) - - Linting is checked using [Ruff](https://github.com/charliermarsh/ruff) - - Types are checked using [mypy](https://mypy-lang.org/) - - Smart contract artifacts are built - - Smart contract artifacts are checked for [output stability](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/articles/output_stability.md) - - Smart contract is deployed to a AlgoKit LocalNet instance - -#### Continuous Deployment - -For pushes to `main` branch, after the above checks pass, the following deployment actions are performed: - - The smart contract(s) are deployed to TestNet using [AlgoNode](https://algonode.io). - -> Please note deployment is also performed via `algokit deploy` command which can be invoked both via CI as seen on this project, or locally. For more information on how to use `algokit deploy` please see [AlgoKit documentation](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/deploy.md). - -# 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-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. - - [pre-commit](https://pre-commit.com/): A framework for managing and maintaining multi-language pre-commit hooks, to enable pre-commit you need to run `pre-commit install` in the root of the repository. This will install the pre-commit hooks and run them against modified files when committing. If any of the hooks fail, the commit will be aborted. To run the hooks on all files, use `pre-commit run --all-files`. -- [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/generators/starter_puya_smart_contract_typescript/pyproject.toml b/examples/generators/starter_puya_smart_contract_typescript/pyproject.toml deleted file mode 100644 index 8068b87..0000000 --- a/examples/generators/starter_puya_smart_contract_typescript/pyproject.toml +++ /dev/null @@ -1,45 +0,0 @@ -[tool.poetry] -name = "starter_puya_smart_contract_typescript" -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 = "*" -pre-commit = "*" - -[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/generators/starter_python_smart_contract_python/.algokit.toml b/examples/generators/starter_python_smart_contract_python/.algokit.toml new file mode 100644 index 0000000..2576c3c --- /dev/null +++ b/examples/generators/starter_python_smart_contract_python/.algokit.toml @@ -0,0 +1,38 @@ +[algokit] +min_version = "v2.0.0" + +[generate.smart_contract] +description = "Adds new smart contract to existing project" +path = ".algokit/generators/create_contract" + +[project] +type = 'contract' +name = 'starter_python_smart_contract_python' +artifacts = 'smart_contracts/artifacts' + +[project.deploy] +command = "poetry run python -m smart_contracts deploy" +environment_secrets = [ + "DEPLOYER_MNEMONIC", +] + +[project.deploy.localnet] +environment_secrets = [] + +[project.run] +# Commands intented for use locally and in CI +build = { commands = [ + 'poetry run python -m smart_contracts build', +], description = 'Build all smart contracts in the project' } +lint = { commands = [ +], description = 'Perform linting' } +audit-teal = { commands = [ + # 🚨 IMPORTANT 🚨: For strict TEAL validation, remove --exclude statements. The default starter contract is not for production. Ensure thorough testing and adherence to best practices in smart contract development. This is not a replacement for a professional audit. + 'algokit task analyze smart_contracts/artifacts --recursive --force --exclude rekey-to --exclude is-updatable --exclude missing-fee-check --exclude is-deletable --exclude can-close-asset --exclude can-close-account --exclude unprotected-deletable --exclude unprotected-updatable', +], description = 'Audit TEAL files' } + +# Commands intented for CI only, prefixed with `ci-` by convention +ci-teal-diff = { commands = [ + 'git add -N ./smart_contracts/artifacts', + 'git diff --exit-code --minimal ./smart_contracts/artifacts', +], description = 'Check TEAL files for differences' } diff --git a/examples/generators/starter_puya_smart_contract_python/.algokit/generators/create_contract/copier.yaml b/examples/generators/starter_python_smart_contract_python/.algokit/generators/create_contract/copier.yaml similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/.algokit/generators/create_contract/copier.yaml rename to examples/generators/starter_python_smart_contract_python/.algokit/generators/create_contract/copier.yaml diff --git a/examples/generators/starter_puya_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 b/examples/generators/starter_python_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 similarity index 83% rename from examples/generators/starter_puya_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 rename to examples/generators/starter_python_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 index eabbaa0..9828347 100644 --- a/examples/generators/starter_puya_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 +++ b/examples/generators/starter_python_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 @@ -1,4 +1,4 @@ -from puyapy import ARC4Contract, arc4 +from algopy import ARC4Contract, arc4 class {{ contract_name.split('_')|map('capitalize')|join }}(ARC4Contract): diff --git a/examples/generators/starter_puya_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 b/examples/generators/starter_python_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 rename to examples/generators/starter_python_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 diff --git a/examples/generators/starter_puya_smart_contract_python/.copier-answers.yml b/examples/generators/starter_python_smart_contract_python/.copier-answers.yml similarity index 80% rename from examples/generators/starter_puya_smart_contract_python/.copier-answers.yml rename to examples/generators/starter_python_smart_contract_python/.copier-answers.yml index 74aa0ad..6f932ac 100644 --- a/examples/generators/starter_puya_smart_contract_python/.copier-answers.yml +++ b/examples/generators/starter_python_smart_contract_python/.copier-answers.yml @@ -6,5 +6,5 @@ author_name: None contract_name: hello_world deployment_language: python preset_name: starter -project_name: starter_puya_smart_contract_python +project_name: starter_python_smart_contract_python diff --git a/examples/generators/starter_puya_smart_contract_python/.editorconfig b/examples/generators/starter_python_smart_contract_python/.editorconfig similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/.editorconfig rename to examples/generators/starter_python_smart_contract_python/.editorconfig diff --git a/examples/generators/starter_puya_smart_contract_python/.env.localnet.template b/examples/generators/starter_python_smart_contract_python/.env.localnet.template similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/.env.localnet.template rename to examples/generators/starter_python_smart_contract_python/.env.localnet.template diff --git a/examples/generators/starter_puya_smart_contract_python/.env.template b/examples/generators/starter_python_smart_contract_python/.env.template similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/.env.template rename to examples/generators/starter_python_smart_contract_python/.env.template diff --git a/examples/generators/starter_puya_smart_contract_python/.env.testnet.template b/examples/generators/starter_python_smart_contract_python/.env.testnet.template similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/.env.testnet.template rename to examples/generators/starter_python_smart_contract_python/.env.testnet.template diff --git a/examples/generators/starter_puya_smart_contract_python/.gitattributes b/examples/generators/starter_python_smart_contract_python/.gitattributes similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/.gitattributes rename to examples/generators/starter_python_smart_contract_python/.gitattributes diff --git a/examples/generators/starter_puya_smart_contract_python/.gitignore b/examples/generators/starter_python_smart_contract_python/.gitignore similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/.gitignore rename to examples/generators/starter_python_smart_contract_python/.gitignore diff --git a/examples/production_puya/.tours/getting-started-with-your-algokit-project.tour b/examples/generators/starter_python_smart_contract_python/.tours/getting-started-with-your-algokit-project.tour similarity index 99% rename from examples/production_puya/.tours/getting-started-with-your-algokit-project.tour rename to examples/generators/starter_python_smart_contract_python/.tours/getting-started-with-your-algokit-project.tour index c1309a6..6b593eb 100644 --- a/examples/production_puya/.tours/getting-started-with-your-algokit-project.tour +++ b/examples/generators/starter_python_smart_contract_python/.tours/getting-started-with-your-algokit-project.tour @@ -50,7 +50,7 @@ { "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 + "line": 15 } ] } diff --git a/examples/production_puya/.vscode/extensions.json b/examples/generators/starter_python_smart_contract_python/.vscode/extensions.json similarity index 71% rename from examples/production_puya/.vscode/extensions.json rename to examples/generators/starter_python_smart_contract_python/.vscode/extensions.json index 1d2e7cf..e16b76b 100644 --- a/examples/production_puya/.vscode/extensions.json +++ b/examples/generators/starter_python_smart_contract_python/.vscode/extensions.json @@ -1,9 +1,6 @@ { "recommendations": [ "ms-python.python", - "charliermarsh.ruff", - "matangover.mypy", - "ms-python.black-formatter", "tamasfe.even-better-toml", "editorconfig.editorconfig", "vsls-contrib.codetour", diff --git a/examples/generators/starter_puya_smart_contract_python/.vscode/launch.json b/examples/generators/starter_python_smart_contract_python/.vscode/launch.json similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/.vscode/launch.json rename to examples/generators/starter_python_smart_contract_python/.vscode/launch.json diff --git a/examples/generators/starter_puya_smart_contract_python/.vscode/settings.json b/examples/generators/starter_python_smart_contract_python/.vscode/settings.json similarity index 52% rename from examples/generators/starter_puya_smart_contract_python/.vscode/settings.json rename to examples/generators/starter_python_smart_contract_python/.vscode/settings.json index a7b97d0..0a9b7c8 100644 --- a/examples/generators/starter_puya_smart_contract_python/.vscode/settings.json +++ b/examples/generators/starter_python_smart_contract_python/.vscode/settings.json @@ -17,30 +17,12 @@ "python.defaultInterpreterPath": "${workspaceFolder}/.venv", "[python]": { "editor.codeActionsOnSave": { - "source.fixAll": true, + "source.fixAll": "explicit", // Prevent default import sorting from running; Ruff will sort imports for us anyway - "source.organizeImports": false + "source.organizeImports": "never" }, - "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/generators/starter_puya_smart_contract_python/.vscode/tasks.json b/examples/generators/starter_python_smart_contract_python/.vscode/tasks.json similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/.vscode/tasks.json rename to examples/generators/starter_python_smart_contract_python/.vscode/tasks.json diff --git a/examples/generators/starter_python_smart_contract_python/README.md b/examples/generators/starter_python_smart_contract_python/README.md new file mode 100644 index 0000000..8ea368c --- /dev/null +++ b/examples/generators/starter_python_smart_contract_python/README.md @@ -0,0 +1,59 @@ +# starter_python_smart_contract_python + +This project has been generated using AlgoKit. See below for default getting started instructions. + +# Setup + +### Pre-requisites + +- [Python 3.12](https://www.python.org/downloads/) or later +- [Docker](https://www.docker.com/) (only required for LocalNet) + +### 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. + - 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 + +### Subsequently + +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 + +> For guidance on `smart_contracts` folder and adding new contracts to the project please see [README](smart_contracts/README.md) on the respective folder. + +# Tools + +This project makes use of Algorand 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) +- [Algorand Python](https://github.com/algorandfoundation/puya) - A semantically and syntactically compatible, typed Python language that works with standard Python tooling and allows you to express smart contracts (apps) and smart signatures (logic signatures) for deployment on the Algorand Virtual Machine (AVM); [docs](https://github.com/algorandfoundation/puya), [examples](https://github.com/algorandfoundation/puya/tree/main/examples) +- [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. +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/generators/starter_puya_smart_contract_python/poetry.toml b/examples/generators/starter_python_smart_contract_python/poetry.toml similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/poetry.toml rename to examples/generators/starter_python_smart_contract_python/poetry.toml diff --git a/examples/generators/starter_python_smart_contract_python/pyproject.toml b/examples/generators/starter_python_smart_contract_python/pyproject.toml new file mode 100644 index 0000000..8b70133 --- /dev/null +++ b/examples/generators/starter_python_smart_contract_python/pyproject.toml @@ -0,0 +1,21 @@ +[tool.poetry] +name = "starter_python_smart_contract_python" +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" +algorand-python = "^1.0.0" + +[tool.poetry.group.dev.dependencies] +algokit-client-generator = "^1.1.3" +puyapy = "^1.0.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + diff --git a/examples/generators/starter_puya_smart_contract_python/smart_contracts/README.md b/examples/generators/starter_python_smart_contract_python/smart_contracts/README.md similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/smart_contracts/README.md rename to examples/generators/starter_python_smart_contract_python/smart_contracts/README.md diff --git a/examples/generators/starter_puya_smart_contract_python/smart_contracts/__init__.py b/examples/generators/starter_python_smart_contract_python/smart_contracts/__init__.py similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/smart_contracts/__init__.py rename to examples/generators/starter_python_smart_contract_python/smart_contracts/__init__.py diff --git a/examples/generators/starter_puya_smart_contract_python/smart_contracts/__main__.py b/examples/generators/starter_python_smart_contract_python/smart_contracts/__main__.py similarity index 82% rename from examples/generators/starter_puya_smart_contract_python/smart_contracts/__main__.py rename to examples/generators/starter_python_smart_contract_python/smart_contracts/__main__.py index 7db8a69..aa936c9 100644 --- a/examples/generators/starter_puya_smart_contract_python/smart_contracts/__main__.py +++ b/examples/generators/starter_python_smart_contract_python/smart_contracts/__main__.py @@ -7,6 +7,7 @@ from smart_contracts.config import contracts from smart_contracts.helpers.build import build from smart_contracts.helpers.deploy import deploy +from smart_contracts.helpers.util import find_app_spec_file # 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 @@ -32,7 +33,11 @@ def main(action: str) -> None: case "deploy": for contract in contracts: logger.info(f"Deploying app {contract.name}") - app_spec_path = artifact_path / contract.name / "application.json" + output_dir = artifact_path / contract.name + app_spec_file_name = find_app_spec_file(output_dir) + if app_spec_file_name is None: + raise Exception("Could not deploy app, .arc32.json file not found") + app_spec_path = output_dir / app_spec_file_name if contract.deploy: deploy(app_spec_path, contract.deploy) case "all": diff --git a/examples/generators/production_puya_smart_contract_typescript/smart_contracts/config.py b/examples/generators/starter_python_smart_contract_python/smart_contracts/config.py similarity index 96% rename from examples/generators/production_puya_smart_contract_typescript/smart_contracts/config.py rename to examples/generators/starter_python_smart_contract_python/smart_contracts/config.py index 7d56d82..8f3ca93 100644 --- a/examples/generators/production_puya_smart_contract_typescript/smart_contracts/config.py +++ b/examples/generators/starter_python_smart_contract_python/smart_contracts/config.py @@ -38,7 +38,7 @@ def import_deploy_if_exists( deploy_module = importlib.import_module( f"{folder.parent.name}.{folder.name}.deploy_config" ) - return deploy_module.deploy + return deploy_module.deploy # type: ignore except ImportError: return None diff --git a/examples/generators/production_puya_smart_contract_python/smart_contracts/cool_contract/contract.py b/examples/generators/starter_python_smart_contract_python/smart_contracts/cool_contract/contract.py similarity index 79% rename from examples/generators/production_puya_smart_contract_python/smart_contracts/cool_contract/contract.py rename to examples/generators/starter_python_smart_contract_python/smart_contracts/cool_contract/contract.py index 1c9f04d..4c00d34 100644 --- a/examples/generators/production_puya_smart_contract_python/smart_contracts/cool_contract/contract.py +++ b/examples/generators/starter_python_smart_contract_python/smart_contracts/cool_contract/contract.py @@ -1,4 +1,4 @@ -from puyapy import ARC4Contract, arc4 +from algopy import ARC4Contract, arc4 class CoolContract(ARC4Contract): diff --git a/examples/generators/starter_puya_smart_contract_python/smart_contracts/cool_contract/deploy_config.py b/examples/generators/starter_python_smart_contract_python/smart_contracts/cool_contract/deploy_config.py similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/smart_contracts/cool_contract/deploy_config.py rename to examples/generators/starter_python_smart_contract_python/smart_contracts/cool_contract/deploy_config.py diff --git a/examples/generators/production_puya_smart_contract_python/smart_contracts/hello_world/contract.py b/examples/generators/starter_python_smart_contract_python/smart_contracts/hello_world/contract.py similarity index 63% rename from examples/generators/production_puya_smart_contract_python/smart_contracts/hello_world/contract.py rename to examples/generators/starter_python_smart_contract_python/smart_contracts/hello_world/contract.py index 589aea9..9052c05 100644 --- a/examples/generators/production_puya_smart_contract_python/smart_contracts/hello_world/contract.py +++ b/examples/generators/starter_python_smart_contract_python/smart_contracts/hello_world/contract.py @@ -1,4 +1,5 @@ -from puyapy import ARC4Contract, arc4 +# pyright: reportMissingModuleSource=false +from algopy import ARC4Contract, arc4 class HelloWorld(ARC4Contract): diff --git a/examples/generators/starter_puya_smart_contract_python/smart_contracts/hello_world/deploy_config.py b/examples/generators/starter_python_smart_contract_python/smart_contracts/hello_world/deploy_config.py similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/smart_contracts/hello_world/deploy_config.py rename to examples/generators/starter_python_smart_contract_python/smart_contracts/hello_world/deploy_config.py diff --git a/examples/generators/starter_puya_smart_contract_python/smart_contracts/helpers/__init__.py b/examples/generators/starter_python_smart_contract_python/smart_contracts/helpers/__init__.py similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/smart_contracts/helpers/__init__.py rename to examples/generators/starter_python_smart_contract_python/smart_contracts/helpers/__init__.py diff --git a/examples/production_puya/smart_contracts/helpers/build.py b/examples/generators/starter_python_smart_contract_python/smart_contracts/helpers/build.py similarity index 77% rename from examples/production_puya/smart_contracts/helpers/build.py rename to examples/generators/starter_python_smart_contract_python/smart_contracts/helpers/build.py index 0dad0c8..d9fc955 100644 --- a/examples/production_puya/smart_contracts/helpers/build.py +++ b/examples/generators/starter_python_smart_contract_python/smart_contracts/helpers/build.py @@ -3,6 +3,8 @@ from pathlib import Path from shutil import rmtree +from smart_contracts.helpers.util import find_app_spec_file + logger = logging.getLogger(__name__) deployment_extension = "py" @@ -16,9 +18,10 @@ def build(output_dir: Path, contract_path: Path) -> Path: build_result = subprocess.run( [ - "poetry", - "run", - "puyapy", + "algokit", + "--no-color", + "compile", + "python", contract_path.absolute(), f"--out-dir={output_dir}", "--output-arc32", @@ -30,12 +33,16 @@ def build(output_dir: Path, contract_path: Path) -> Path: if build_result.returncode: raise Exception(f"Could not build contract:\n{build_result.stdout}") + app_spec_file_name = find_app_spec_file(output_dir) + if app_spec_file_name is None: + raise Exception("Could not generate typed client, .arc32.json file not found") + generate_result = subprocess.run( [ "algokit", "generate", "client", - output_dir / "application.json", + output_dir / app_spec_file_name, "--output", output_dir / f"client.{deployment_extension}", ], @@ -53,4 +60,4 @@ def build(output_dir: Path, contract_path: Path) -> Path: raise Exception( f"Could not generate typed client:\n{generate_result.stdout}" ) - return output_dir / "application.json" + return output_dir / app_spec_file_name diff --git a/examples/generators/starter_puya_smart_contract_python/smart_contracts/helpers/deploy.py b/examples/generators/starter_python_smart_contract_python/smart_contracts/helpers/deploy.py similarity index 96% rename from examples/generators/starter_puya_smart_contract_python/smart_contracts/helpers/deploy.py rename to examples/generators/starter_python_smart_contract_python/smart_contracts/helpers/deploy.py index 08367a3..10185a9 100644 --- a/examples/generators/starter_puya_smart_contract_python/smart_contracts/helpers/deploy.py +++ b/examples/generators/starter_python_smart_contract_python/smart_contracts/helpers/deploy.py @@ -1,3 +1,6 @@ +# mypy: disable-error-code="no-untyped-call, misc" + + import logging from collections.abc import Callable from pathlib import Path diff --git a/examples/generators/starter_python_smart_contract_python/smart_contracts/helpers/util.py b/examples/generators/starter_python_smart_contract_python/smart_contracts/helpers/util.py new file mode 100644 index 0000000..6cf7e5b --- /dev/null +++ b/examples/generators/starter_python_smart_contract_python/smart_contracts/helpers/util.py @@ -0,0 +1,8 @@ +from pathlib import Path + + +def find_app_spec_file(output_dir: Path) -> str | None: + for file in output_dir.iterdir(): + if file.is_file() and file.suffixes == [".arc32", ".json"]: + return file.name + return None diff --git a/examples/generators/starter_python_smart_contract_typescript/.algokit.toml b/examples/generators/starter_python_smart_contract_typescript/.algokit.toml new file mode 100644 index 0000000..8ebbc1b --- /dev/null +++ b/examples/generators/starter_python_smart_contract_typescript/.algokit.toml @@ -0,0 +1,38 @@ +[algokit] +min_version = "v2.0.0" + +[generate.smart_contract] +description = "Adds new smart contract to existing project" +path = ".algokit/generators/create_contract" + +[project] +type = 'contract' +name = 'starter_python_smart_contract_typescript' +artifacts = 'smart_contracts/artifacts' + +[project.deploy] +command = "npm run deploy:ci" +environment_secrets = [ + "DEPLOYER_MNEMONIC", +] + +[project.deploy.localnet] +environment_secrets = [] + +[project.run] +# Commands intented for use locally and in CI +build = { commands = [ + 'poetry run python -m smart_contracts build', +], description = 'Build all smart contracts in the project' } +lint = { commands = [ +], description = 'Perform linting' } +audit-teal = { commands = [ + # 🚨 IMPORTANT 🚨: For strict TEAL validation, remove --exclude statements. The default starter contract is not for production. Ensure thorough testing and adherence to best practices in smart contract development. This is not a replacement for a professional audit. + 'algokit task analyze smart_contracts/artifacts --recursive --force --exclude rekey-to --exclude is-updatable --exclude missing-fee-check --exclude is-deletable --exclude can-close-asset --exclude can-close-account --exclude unprotected-deletable --exclude unprotected-updatable', +], description = 'Audit TEAL files' } + +# Commands intented for CI only, prefixed with `ci-` by convention +ci-teal-diff = { commands = [ + 'git add -N ./smart_contracts/artifacts', + 'git diff --exit-code --minimal ./smart_contracts/artifacts', +], description = 'Check TEAL files for differences' } diff --git a/examples/generators/starter_puya_smart_contract_typescript/.algokit/generators/create_contract/copier.yaml b/examples/generators/starter_python_smart_contract_typescript/.algokit/generators/create_contract/copier.yaml similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/.algokit/generators/create_contract/copier.yaml rename to examples/generators/starter_python_smart_contract_typescript/.algokit/generators/create_contract/copier.yaml diff --git a/examples/generators/production_puya_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 b/examples/generators/starter_python_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 similarity index 83% rename from examples/generators/production_puya_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 rename to examples/generators/starter_python_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 index eabbaa0..9828347 100644 --- a/examples/generators/production_puya_smart_contract_python/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 +++ b/examples/generators/starter_python_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 @@ -1,4 +1,4 @@ -from puyapy import ARC4Contract, arc4 +from algopy import ARC4Contract, arc4 class {{ contract_name.split('_')|map('capitalize')|join }}(ARC4Contract): diff --git a/examples/generators/starter_puya_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 b/examples/generators/starter_python_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 rename to examples/generators/starter_python_smart_contract_typescript/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 diff --git a/examples/generators/starter_puya_smart_contract_typescript/.copier-answers.yml b/examples/generators/starter_python_smart_contract_typescript/.copier-answers.yml similarity index 79% rename from examples/generators/starter_puya_smart_contract_typescript/.copier-answers.yml rename to examples/generators/starter_python_smart_contract_typescript/.copier-answers.yml index fecadd3..e943878 100644 --- a/examples/generators/starter_puya_smart_contract_typescript/.copier-answers.yml +++ b/examples/generators/starter_python_smart_contract_typescript/.copier-answers.yml @@ -6,5 +6,5 @@ author_name: None contract_name: hello_world deployment_language: typescript preset_name: starter -project_name: starter_puya_smart_contract_typescript +project_name: starter_python_smart_contract_typescript diff --git a/examples/generators/starter_puya_smart_contract_typescript/.editorconfig b/examples/generators/starter_python_smart_contract_typescript/.editorconfig similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/.editorconfig rename to examples/generators/starter_python_smart_contract_typescript/.editorconfig diff --git a/examples/generators/starter_puya_smart_contract_typescript/.env.localnet.template b/examples/generators/starter_python_smart_contract_typescript/.env.localnet.template similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/.env.localnet.template rename to examples/generators/starter_python_smart_contract_typescript/.env.localnet.template diff --git a/examples/generators/starter_puya_smart_contract_typescript/.env.template b/examples/generators/starter_python_smart_contract_typescript/.env.template similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/.env.template rename to examples/generators/starter_python_smart_contract_typescript/.env.template diff --git a/examples/generators/starter_puya_smart_contract_typescript/.env.testnet.template b/examples/generators/starter_python_smart_contract_typescript/.env.testnet.template similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/.env.testnet.template rename to examples/generators/starter_python_smart_contract_typescript/.env.testnet.template diff --git a/examples/generators/starter_puya_smart_contract_typescript/.gitattributes b/examples/generators/starter_python_smart_contract_typescript/.gitattributes similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/.gitattributes rename to examples/generators/starter_python_smart_contract_typescript/.gitattributes diff --git a/examples/generators/starter_puya_smart_contract_typescript/.gitignore b/examples/generators/starter_python_smart_contract_typescript/.gitignore similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/.gitignore rename to examples/generators/starter_python_smart_contract_typescript/.gitignore diff --git a/examples/generators/starter_puya_smart_contract_typescript/.prettierignore b/examples/generators/starter_python_smart_contract_typescript/.prettierignore similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/.prettierignore rename to examples/generators/starter_python_smart_contract_typescript/.prettierignore diff --git a/examples/generators/starter_puya_smart_contract_typescript/.prettierrc.js b/examples/generators/starter_python_smart_contract_typescript/.prettierrc.js similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/.prettierrc.js rename to examples/generators/starter_python_smart_contract_typescript/.prettierrc.js diff --git a/examples/generators/starter_puya_smart_contract_typescript/.tours/getting-started-with-your-algokit-project.tour b/examples/generators/starter_python_smart_contract_typescript/.tours/getting-started-with-your-algokit-project.tour similarity index 99% rename from examples/generators/starter_puya_smart_contract_typescript/.tours/getting-started-with-your-algokit-project.tour rename to examples/generators/starter_python_smart_contract_typescript/.tours/getting-started-with-your-algokit-project.tour index 30f150f..bd27ad2 100644 --- a/examples/generators/starter_puya_smart_contract_typescript/.tours/getting-started-with-your-algokit-project.tour +++ b/examples/generators/starter_python_smart_contract_typescript/.tours/getting-started-with-your-algokit-project.tour @@ -50,7 +50,7 @@ { "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 + "line": 15 } ] } diff --git a/examples/generators/starter_puya_smart_contract_typescript/.vscode/extensions.json b/examples/generators/starter_python_smart_contract_typescript/.vscode/extensions.json similarity index 73% rename from examples/generators/starter_puya_smart_contract_typescript/.vscode/extensions.json rename to examples/generators/starter_python_smart_contract_typescript/.vscode/extensions.json index 684e4dd..e5ca4ca 100644 --- a/examples/generators/starter_puya_smart_contract_typescript/.vscode/extensions.json +++ b/examples/generators/starter_python_smart_contract_typescript/.vscode/extensions.json @@ -1,9 +1,6 @@ { "recommendations": [ "ms-python.python", - "charliermarsh.ruff", - "matangover.mypy", - "ms-python.black-formatter", "esbenp.prettier-vscode", "tamasfe.even-better-toml", "editorconfig.editorconfig", diff --git a/examples/generators/starter_puya_smart_contract_typescript/.vscode/launch.json b/examples/generators/starter_python_smart_contract_typescript/.vscode/launch.json similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/.vscode/launch.json rename to examples/generators/starter_python_smart_contract_typescript/.vscode/launch.json diff --git a/examples/generators/starter_puya_smart_contract_typescript/.vscode/settings.json b/examples/generators/starter_python_smart_contract_typescript/.vscode/settings.json similarity index 54% rename from examples/generators/starter_puya_smart_contract_typescript/.vscode/settings.json rename to examples/generators/starter_python_smart_contract_typescript/.vscode/settings.json index af8031f..0bffa07 100644 --- a/examples/generators/starter_puya_smart_contract_typescript/.vscode/settings.json +++ b/examples/generators/starter_python_smart_contract_typescript/.vscode/settings.json @@ -20,30 +20,12 @@ "python.defaultInterpreterPath": "${workspaceFolder}/.venv", "[python]": { "editor.codeActionsOnSave": { - "source.fixAll": true, + "source.fixAll": "explicit", // Prevent default import sorting from running; Ruff will sort imports for us anyway - "source.organizeImports": false + "source.organizeImports": "never" }, - "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/generators/starter_puya_smart_contract_typescript/.vscode/tasks.json b/examples/generators/starter_python_smart_contract_typescript/.vscode/tasks.json similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/.vscode/tasks.json rename to examples/generators/starter_python_smart_contract_typescript/.vscode/tasks.json diff --git a/examples/generators/starter_python_smart_contract_typescript/README.md b/examples/generators/starter_python_smart_contract_typescript/README.md new file mode 100644 index 0000000..1107a2d --- /dev/null +++ b/examples/generators/starter_python_smart_contract_typescript/README.md @@ -0,0 +1,64 @@ +# starter_python_smart_contract_typescript + +This project has been generated using AlgoKit. See below for default getting started instructions. + +# Setup + +### Pre-requisites + +- [Python 3.12](https://www.python.org/downloads/) or later +- [Docker](https://www.docker.com/) (only required for LocalNet) + +### 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. + - 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. + - 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 + 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 + +### Subsequently + +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 + +> For guidance on `smart_contracts` folder and adding new contracts to the project please see [README](smart_contracts/README.md) on the respective folder. + +# Tools + +This project makes use of Algorand 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) +- [Algorand Python](https://github.com/algorandfoundation/puya) - A semantically and syntactically compatible, typed Python language that works with standard Python tooling and allows you to express smart contracts (apps) and smart signatures (logic signatures) for deployment on the Algorand Virtual Machine (AVM); [docs](https://github.com/algorandfoundation/puya), [examples](https://github.com/algorandfoundation/puya/tree/main/examples) +- [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 + +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/generators/starter_puya_smart_contract_typescript/package.json b/examples/generators/starter_python_smart_contract_typescript/package.json similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/package.json rename to examples/generators/starter_python_smart_contract_typescript/package.json diff --git a/examples/generators/starter_puya_smart_contract_typescript/poetry.toml b/examples/generators/starter_python_smart_contract_typescript/poetry.toml similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/poetry.toml rename to examples/generators/starter_python_smart_contract_typescript/poetry.toml diff --git a/examples/generators/starter_python_smart_contract_typescript/pyproject.toml b/examples/generators/starter_python_smart_contract_typescript/pyproject.toml new file mode 100644 index 0000000..2b08931 --- /dev/null +++ b/examples/generators/starter_python_smart_contract_typescript/pyproject.toml @@ -0,0 +1,21 @@ +[tool.poetry] +name = "starter_python_smart_contract_typescript" +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" +algorand-python = "^1.0.0" + +[tool.poetry.group.dev.dependencies] +algokit-client-generator = "^1.1.3" +puyapy = "^1.0.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + diff --git a/examples/generators/starter_puya_smart_contract_typescript/smart_contracts/README.md b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/README.md similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/smart_contracts/README.md rename to examples/generators/starter_python_smart_contract_typescript/smart_contracts/README.md diff --git a/examples/generators/starter_puya_smart_contract_typescript/smart_contracts/__init__.py b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/__init__.py similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/smart_contracts/__init__.py rename to examples/generators/starter_python_smart_contract_typescript/smart_contracts/__init__.py diff --git a/examples/generators/starter_puya_smart_contract_typescript/smart_contracts/__main__.py b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/__main__.py similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/smart_contracts/__main__.py rename to examples/generators/starter_python_smart_contract_typescript/smart_contracts/__main__.py diff --git a/examples/generators/starter_puya_smart_contract_python/smart_contracts/config.py b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/config.py similarity index 96% rename from examples/generators/starter_puya_smart_contract_python/smart_contracts/config.py rename to examples/generators/starter_python_smart_contract_typescript/smart_contracts/config.py index 7d56d82..8f3ca93 100644 --- a/examples/generators/starter_puya_smart_contract_python/smart_contracts/config.py +++ b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/config.py @@ -38,7 +38,7 @@ def import_deploy_if_exists( deploy_module = importlib.import_module( f"{folder.parent.name}.{folder.name}.deploy_config" ) - return deploy_module.deploy + return deploy_module.deploy # type: ignore except ImportError: return None diff --git a/examples/generators/starter_puya_smart_contract_typescript/smart_contracts/cool_contract/contract.py b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/cool_contract/contract.py similarity index 79% rename from examples/generators/starter_puya_smart_contract_typescript/smart_contracts/cool_contract/contract.py rename to examples/generators/starter_python_smart_contract_typescript/smart_contracts/cool_contract/contract.py index 1c9f04d..4c00d34 100644 --- a/examples/generators/starter_puya_smart_contract_typescript/smart_contracts/cool_contract/contract.py +++ b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/cool_contract/contract.py @@ -1,4 +1,4 @@ -from puyapy import ARC4Contract, arc4 +from algopy import ARC4Contract, arc4 class CoolContract(ARC4Contract): diff --git a/examples/generators/starter_puya_smart_contract_typescript/smart_contracts/cool_contract/deploy-config.ts b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/cool_contract/deploy-config.ts similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/smart_contracts/cool_contract/deploy-config.ts rename to examples/generators/starter_python_smart_contract_typescript/smart_contracts/cool_contract/deploy-config.ts diff --git a/examples/generators/production_puya_smart_contract_typescript/smart_contracts/hello_world/contract.py b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/hello_world/contract.py similarity index 63% rename from examples/generators/production_puya_smart_contract_typescript/smart_contracts/hello_world/contract.py rename to examples/generators/starter_python_smart_contract_typescript/smart_contracts/hello_world/contract.py index 589aea9..9052c05 100644 --- a/examples/generators/production_puya_smart_contract_typescript/smart_contracts/hello_world/contract.py +++ b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/hello_world/contract.py @@ -1,4 +1,5 @@ -from puyapy import ARC4Contract, arc4 +# pyright: reportMissingModuleSource=false +from algopy import ARC4Contract, arc4 class HelloWorld(ARC4Contract): diff --git a/examples/generators/starter_puya_smart_contract_typescript/smart_contracts/hello_world/deploy-config.ts b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/hello_world/deploy-config.ts similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/smart_contracts/hello_world/deploy-config.ts rename to examples/generators/starter_python_smart_contract_typescript/smart_contracts/hello_world/deploy-config.ts diff --git a/examples/generators/starter_puya_smart_contract_typescript/smart_contracts/helpers/__init__.py b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/helpers/__init__.py similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/smart_contracts/helpers/__init__.py rename to examples/generators/starter_python_smart_contract_typescript/smart_contracts/helpers/__init__.py diff --git a/examples/generators/starter_puya_smart_contract_typescript/smart_contracts/helpers/build.py b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/helpers/build.py similarity index 77% rename from examples/generators/starter_puya_smart_contract_typescript/smart_contracts/helpers/build.py rename to examples/generators/starter_python_smart_contract_typescript/smart_contracts/helpers/build.py index 67377d3..51cea43 100644 --- a/examples/generators/starter_puya_smart_contract_typescript/smart_contracts/helpers/build.py +++ b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/helpers/build.py @@ -3,6 +3,8 @@ from pathlib import Path from shutil import rmtree +from smart_contracts.helpers.util import find_app_spec_file + logger = logging.getLogger(__name__) deployment_extension = "ts" @@ -16,9 +18,10 @@ def build(output_dir: Path, contract_path: Path) -> Path: build_result = subprocess.run( [ - "poetry", - "run", - "puyapy", + "algokit", + "--no-color", + "compile", + "python", contract_path.absolute(), f"--out-dir={output_dir}", "--output-arc32", @@ -30,12 +33,16 @@ def build(output_dir: Path, contract_path: Path) -> Path: if build_result.returncode: raise Exception(f"Could not build contract:\n{build_result.stdout}") + app_spec_file_name = find_app_spec_file(output_dir) + if app_spec_file_name is None: + raise Exception("Could not generate typed client, .arc32.json file not found") + generate_result = subprocess.run( [ "algokit", "generate", "client", - output_dir / "application.json", + output_dir / app_spec_file_name, "--output", output_dir / f"client.{deployment_extension}", ], @@ -53,4 +60,4 @@ def build(output_dir: Path, contract_path: Path) -> Path: raise Exception( f"Could not generate typed client:\n{generate_result.stdout}" ) - return output_dir / "application.json" + return output_dir / app_spec_file_name diff --git a/examples/generators/starter_python_smart_contract_typescript/smart_contracts/helpers/util.py b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/helpers/util.py new file mode 100644 index 0000000..6cf7e5b --- /dev/null +++ b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/helpers/util.py @@ -0,0 +1,8 @@ +from pathlib import Path + + +def find_app_spec_file(output_dir: Path) -> str | None: + for file in output_dir.iterdir(): + if file.is_file() and file.suffixes == [".arc32", ".json"]: + return file.name + return None diff --git a/examples/generators/starter_puya_smart_contract_typescript/smart_contracts/index.ts b/examples/generators/starter_python_smart_contract_typescript/smart_contracts/index.ts similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/smart_contracts/index.ts rename to examples/generators/starter_python_smart_contract_typescript/smart_contracts/index.ts diff --git a/examples/generators/starter_puya_smart_contract_typescript/tsconfig.json b/examples/generators/starter_python_smart_contract_typescript/tsconfig.json similarity index 100% rename from examples/generators/starter_puya_smart_contract_typescript/tsconfig.json rename to examples/generators/starter_python_smart_contract_typescript/tsconfig.json diff --git a/examples/production_puya/.algokit.toml b/examples/production_puya/.algokit.toml deleted file mode 100644 index 3965d2d..0000000 --- a/examples/production_puya/.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 = 'contract' -name = 'production_puya' diff --git a/examples/production_puya/.github/workflows/cd.yaml b/examples/production_puya/.github/workflows/cd.yaml deleted file mode 100644 index 2401bf4..0000000 --- a/examples/production_puya/.github/workflows/cd.yaml +++ /dev/null @@ -1,50 +0,0 @@ -name: Continuous Delivery of Smart Contract - -on: - push: - branches: - - main - -concurrency: release - -jobs: - ci-check: - name: Perform Checks - uses: ./.github/workflows/checks.yaml - - deploy-testnet: - runs-on: 'ubuntu-latest' - needs: ci-check - environment: Test - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Install poetry - run: pipx install poetry - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: '3.12' - cache: 'poetry' - - - name: Install algokit - run: pipx install algokit - - - name: Bootstrap dependencies - run: algokit bootstrap all - - - name: Configure git - shell: bash - run: | - # set git user and email as test invoke git - git config --global user.email "actions@github.com" && git config --global user.name "github-actions" - - - name: Deploy to testnet - run: algokit deploy testnet - env: - # This is the account that becomes the creator of the contract. - # Since we are not using the optional dispenser account (via DISPENSER_MNEMONIC), - # it must also be funded with enough Algos to deploy and fund the smart contracts created - DEPLOYER_MNEMONIC: ${{ secrets.DEPLOYER_MNEMONIC }} diff --git a/examples/production_puya/.github/workflows/checks.yaml b/examples/production_puya/.github/workflows/checks.yaml deleted file mode 100644 index 3415368..0000000 --- a/examples/production_puya/.github/workflows/checks.yaml +++ /dev/null @@ -1,82 +0,0 @@ -name: Check code base - -on: - workflow_call: - -jobs: - checks: - runs-on: 'ubuntu-latest' - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Install poetry - run: pipx install poetry - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: '3.12' - cache: 'poetry' - - - name: Install algokit - run: pipx install algokit - - - name: Start LocalNet - run: algokit localnet start - - - name: Bootstrap dependencies - run: algokit bootstrap all - - - name: Configure git - shell: bash - run: | - # set git user and email as test invoke git - git config --global user.email "actions@github.com" && git config --global user.name "github-actions" - - - name: Audit with pip-audit - run: | - # audit non dev dependencies, no exclusions - poetry export --without=dev > requirements.txt && poetry run pip-audit -r requirements.txt - - # audit all dependencies, with exclusions. - # If a vulnerability is found in a dev dependency without an available fix, - # it can be temporarily ignored by adding --ignore-vuln e.g. - # --ignore-vuln "GHSA-hcpj-qp55-gfph" # GitPython vulnerability, dev only dependency - poetry run pip-audit - - - name: Check formatting with Black - run: | - # stop the build if there are files that don't meet formatting requirements - poetry run black --check . - - - name: Check linting with Ruff - run: | - # stop the build if there are Python syntax errors or undefined names - poetry run ruff . - - - name: Check types with mypy - run: poetry run mypy - - - name: Run tests - shell: bash - run: | - set -o pipefail - poetry run pytest --junitxml=pytest-junit.xml - - - name: Build smart contracts - run: poetry run python -m smart_contracts build - - - name: Scan TEAL files for issues - run: algokit task analyze .algokit --recursive --force # add --diff flag if you want output stability checks instead - - - name: Check output stability of the smart contracts - shell: bash - run: | - # Add untracked files as empty so they come up in diff - git add -N ./smart_contracts/artifacts - # Error out if there are any changes in teal after generating output - git diff --exit-code --minimal ./smart_contracts/artifacts || (echo "::error ::Smart contract artifacts have changed, ensure committed artifacts are up to date" && exit 1); - - - name: Run deployer against LocalNet - run: poetry run python -m smart_contracts deploy diff --git a/examples/production_puya/.github/workflows/pr.yaml b/examples/production_puya/.github/workflows/pr.yaml deleted file mode 100644 index a80f784..0000000 --- a/examples/production_puya/.github/workflows/pr.yaml +++ /dev/null @@ -1,8 +0,0 @@ -name: Pull Request validation - -on: [pull_request] - -jobs: - pr-check: - name: Perform Checks - uses: ./.github/workflows/checks.yaml diff --git a/examples/production_puya/.pre-commit-config.yaml b/examples/production_puya/.pre-commit-config.yaml deleted file mode 100644 index 8862f86..0000000 --- a/examples/production_puya/.pre-commit-config.yaml +++ /dev/null @@ -1,48 +0,0 @@ -repos: - - repo: local - hooks: - - - id: black - name: black - description: "Black: The uncompromising Python code formatter" - entry: poetry run black - language: system - minimum_pre_commit_version: 2.9.2 - require_serial: true - types_or: [ python, pyi ] - - - - id: ruff - name: ruff - description: "Run 'ruff' for extremely fast Python linting" - entry: poetry run ruff - language: system - types: [ python ] - args: [ --fix ] - require_serial: false - additional_dependencies: [ ] - minimum_pre_commit_version: '0' - files: '^(src|tests)/' - - - - id: mypy - name: mypy - description: '`mypy` will check Python types for correctness' - entry: poetry run mypy - language: system - types_or: [ python, pyi ] - require_serial: true - additional_dependencies: [ ] - minimum_pre_commit_version: '2.9.2' - files: '^(src|tests)/' - - - id: tealer - name: tealer - description: "Run AlgoKit `Tealer` for TEAL static analysis" - entry: algokit - language: system - args: [task, analyze, ".algokit", "--recursive", "--force"] - require_serial: false - additional_dependencies: [] - minimum_pre_commit_version: "0" - files: '^.*\.teal$' diff --git a/examples/production_puya/smart_contracts/config.py b/examples/production_puya/smart_contracts/config.py deleted file mode 100644 index 7d56d82..0000000 --- a/examples/production_puya/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_puya/smart_contracts/hello_world/contract.py b/examples/production_puya/smart_contracts/hello_world/contract.py deleted file mode 100644 index 589aea9..0000000 --- a/examples/production_puya/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_python/.algokit.toml b/examples/production_python/.algokit.toml new file mode 100644 index 0000000..17aa09d --- /dev/null +++ b/examples/production_python/.algokit.toml @@ -0,0 +1,49 @@ +[algokit] +min_version = "v2.0.0" + +[generate.smart_contract] +description = "Adds new smart contract to existing project" +path = ".algokit/generators/create_contract" + +[project] +type = 'contract' +name = 'production_python' +artifacts = 'smart_contracts/artifacts' + +[project.deploy] +command = "poetry run python -m smart_contracts deploy" +environment_secrets = [ + "DEPLOYER_MNEMONIC", + "DISPENSER_MNEMONIC", +] + +[project.deploy.localnet] +environment_secrets = [] + +[project.run] +# Commands intented for use locally and in CI +build = { commands = [ + 'poetry run python -m smart_contracts build', +], description = 'Build all smart contracts in the project' } +test = { commands = [ + 'poetry run pytest', +], description = 'Run smart contract tests' } +audit = { commands = [ + 'poetry export --without=dev -o requirements.txt', + 'poetry run pip-audit -r requirements.txt', +], description = 'Audit with pip-audit' } +lint = { commands = [ + 'poetry run black --check .', + 'poetry run ruff .', + 'poetry run mypy', +], description = 'Perform linting' } +audit-teal = { commands = [ + # 🚨 IMPORTANT 🚨: For strict TEAL validation, remove --exclude statements. The default starter contract is not for production. Ensure thorough testing and adherence to best practices in smart contract development. This is not a replacement for a professional audit. + 'algokit task analyze smart_contracts/artifacts --recursive --force --exclude rekey-to --exclude is-updatable --exclude missing-fee-check --exclude is-deletable --exclude can-close-asset --exclude can-close-account --exclude unprotected-deletable --exclude unprotected-updatable', +], description = 'Audit TEAL files' } + +# Commands intented for CI only, prefixed with `ci-` by convention +ci-teal-diff = { commands = [ + 'git add -N ./smart_contracts/artifacts', + 'git diff --exit-code --minimal ./smart_contracts/artifacts', +], description = 'Check TEAL files for differences' } diff --git a/examples/production_puya/.copier-answers.yml b/examples/production_python/.copier-answers.yml similarity index 87% rename from examples/production_puya/.copier-answers.yml rename to examples/production_python/.copier-answers.yml index 74d397a..f9e960d 100644 --- a/examples/production_puya/.copier-answers.yml +++ b/examples/production_python/.copier-answers.yml @@ -6,5 +6,5 @@ author_name: None contract_name: hello_world deployment_language: python preset_name: production -project_name: production_puya +project_name: production_python diff --git a/examples/production_puya/.editorconfig b/examples/production_python/.editorconfig similarity index 100% rename from examples/production_puya/.editorconfig rename to examples/production_python/.editorconfig diff --git a/examples/production_puya/.env.localnet.template b/examples/production_python/.env.localnet.template similarity index 100% rename from examples/production_puya/.env.localnet.template rename to examples/production_python/.env.localnet.template diff --git a/examples/production_puya/.env.template b/examples/production_python/.env.template similarity index 100% rename from examples/production_puya/.env.template rename to examples/production_python/.env.template diff --git a/examples/production_puya/.env.testnet.template b/examples/production_python/.env.testnet.template similarity index 100% rename from examples/production_puya/.env.testnet.template rename to examples/production_python/.env.testnet.template diff --git a/examples/production_puya/.gitattributes b/examples/production_python/.gitattributes similarity index 100% rename from examples/production_puya/.gitattributes rename to examples/production_python/.gitattributes diff --git a/examples/generators/production_puya_smart_contract_python/.github/workflows/cd.yaml b/examples/production_python/.github/workflows/production-python-cd.yaml similarity index 57% rename from examples/generators/production_puya_smart_contract_python/.github/workflows/cd.yaml rename to examples/production_python/.github/workflows/production-python-cd.yaml index 2401bf4..cfb17aa 100644 --- a/examples/generators/production_puya_smart_contract_python/.github/workflows/cd.yaml +++ b/examples/production_python/.github/workflows/production-python-cd.yaml @@ -1,21 +1,19 @@ -name: Continuous Delivery of Smart Contract +name: Release production_python on: + workflow_call: push: branches: - main -concurrency: release - jobs: - ci-check: - name: Perform Checks - uses: ./.github/workflows/checks.yaml - + validate: + name: Validate production_python + uses: ./.github/workflows/production-python-ci.yaml deploy-testnet: - runs-on: 'ubuntu-latest' - needs: ci-check - environment: Test + runs-on: "ubuntu-latest" + needs: validate + environment: contract-testnet steps: - name: Checkout source code uses: actions/checkout@v4 @@ -26,14 +24,14 @@ jobs: - name: Set up Python 3.12 uses: actions/setup-python@v5 with: - python-version: '3.12' - cache: 'poetry' + python-version: "3.12" + cache: "poetry" - name: Install algokit run: pipx install algokit - name: Bootstrap dependencies - run: algokit bootstrap all + run: algokit bootstrap all --project-name 'production_python' - name: Configure git shell: bash @@ -42,9 +40,9 @@ jobs: git config --global user.email "actions@github.com" && git config --global user.name "github-actions" - name: Deploy to testnet - run: algokit deploy testnet + run: algokit deploy testnet --project-name 'production_python' env: - # This is the account that becomes the creator of the contract. - # Since we are not using the optional dispenser account (via DISPENSER_MNEMONIC), - # it must also be funded with enough Algos to deploy and fund the smart contracts created + # This is the account that becomes the creator of the contract DEPLOYER_MNEMONIC: ${{ secrets.DEPLOYER_MNEMONIC }} + # The dispenser account is used to ensure the deployer account is funded + DISPENSER_MNEMONIC: ${{ secrets.DISPENSER_MNEMONIC }} diff --git a/examples/production_python/.github/workflows/production-python-ci.yaml b/examples/production_python/.github/workflows/production-python-ci.yaml new file mode 100644 index 0000000..33e5abf --- /dev/null +++ b/examples/production_python/.github/workflows/production-python-ci.yaml @@ -0,0 +1,60 @@ +name: Validate production_python + +on: + workflow_call: + pull_request: + +jobs: + validate: + runs-on: "ubuntu-latest" + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Install poetry + run: pipx install poetry + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: "poetry" + + - name: Install algokit + run: pipx install algokit + + - name: Start LocalNet + run: algokit localnet start + + - name: Bootstrap dependencies + run: algokit bootstrap all --project-name 'production_python' + + - name: Configure git + shell: bash + run: | + # set git user and email as test invoke git + git config --global user.email "actions@github.com" && git config --global user.name "github-actions" + + - name: Audit python dependencies + run: algokit project run audit --project-name 'production_python' + + - name: Lint and format python dependencies + run: algokit project run lint --project-name 'production_python' + + - name: Run tests + shell: bash + run: | + set -o pipefail + algokit project run test --project-name 'production_python' + + - name: Build smart contracts + run: algokit project run build --project-name 'production_python' + + - name: Scan TEAL files for issues + run: algokit project run audit-teal --project-name 'production_python' + + - name: Check output stability of the smart contracts + run: algokit project run ci-teal-diff --project-name 'production_python' + + - name: Run deployer against LocalNet + run: algokit project deploy localnet --project-name 'production_python' diff --git a/examples/production_puya/.gitignore b/examples/production_python/.gitignore similarity index 100% rename from examples/production_puya/.gitignore rename to examples/production_python/.gitignore diff --git a/examples/production_python/.idea/runConfigurations/Build_Beaker_application.xml b/examples/production_python/.idea/runConfigurations/Build_Beaker_application.xml new file mode 100644 index 0000000..50b329d --- /dev/null +++ b/examples/production_python/.idea/runConfigurations/Build_Beaker_application.xml @@ -0,0 +1,35 @@ + + + + + diff --git a/examples/production_python/.idea/runConfigurations/Build_Beaker_application____LocalNet.xml b/examples/production_python/.idea/runConfigurations/Build_Beaker_application____LocalNet.xml new file mode 100644 index 0000000..b69c607 --- /dev/null +++ b/examples/production_python/.idea/runConfigurations/Build_Beaker_application____LocalNet.xml @@ -0,0 +1,37 @@ + + + + + diff --git a/examples/production_python/.idea/runConfigurations/Build___Deploy_Beaker_application.xml b/examples/production_python/.idea/runConfigurations/Build___Deploy_Beaker_application.xml new file mode 100644 index 0000000..e38dc23 --- /dev/null +++ b/examples/production_python/.idea/runConfigurations/Build___Deploy_Beaker_application.xml @@ -0,0 +1,38 @@ + + + + + diff --git a/examples/production_python/.idea/runConfigurations/Deploy_Built_Beaker_application.xml b/examples/production_python/.idea/runConfigurations/Deploy_Built_Beaker_application.xml new file mode 100644 index 0000000..e75afad --- /dev/null +++ b/examples/production_python/.idea/runConfigurations/Deploy_Built_Beaker_application.xml @@ -0,0 +1,36 @@ + + + + + diff --git a/examples/production_python/.idea/runConfigurations/Reset_AlgoKit_LocalNet.xml b/examples/production_python/.idea/runConfigurations/Reset_AlgoKit_LocalNet.xml new file mode 100644 index 0000000..7f1236a --- /dev/null +++ b/examples/production_python/.idea/runConfigurations/Reset_AlgoKit_LocalNet.xml @@ -0,0 +1,17 @@ + + + + diff --git a/examples/production_python/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml b/examples/production_python/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml new file mode 100644 index 0000000..f699a7a --- /dev/null +++ b/examples/production_python/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml @@ -0,0 +1,17 @@ + + + + diff --git a/examples/production_python/.idea/runConfigurations/Stop_AlgoKit_LocalNet.xml b/examples/production_python/.idea/runConfigurations/Stop_AlgoKit_LocalNet.xml new file mode 100644 index 0000000..e510cbc --- /dev/null +++ b/examples/production_python/.idea/runConfigurations/Stop_AlgoKit_LocalNet.xml @@ -0,0 +1,17 @@ + + + + diff --git a/examples/generators/starter_puya_smart_contract_python/.pre-commit-config.yaml b/examples/production_python/.pre-commit-config.yaml similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/.pre-commit-config.yaml rename to examples/production_python/.pre-commit-config.yaml diff --git a/examples/generators/starter_puya_smart_contract_python/.tours/getting-started-with-your-algokit-project.tour b/examples/production_python/.tours/getting-started-with-your-algokit-project.tour similarity index 99% rename from examples/generators/starter_puya_smart_contract_python/.tours/getting-started-with-your-algokit-project.tour rename to examples/production_python/.tours/getting-started-with-your-algokit-project.tour index c1309a6..6b593eb 100644 --- a/examples/generators/starter_puya_smart_contract_python/.tours/getting-started-with-your-algokit-project.tour +++ b/examples/production_python/.tours/getting-started-with-your-algokit-project.tour @@ -50,7 +50,7 @@ { "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 + "line": 15 } ] } diff --git a/examples/generators/starter_puya_smart_contract_python/.vscode/extensions.json b/examples/production_python/.vscode/extensions.json similarity index 100% rename from examples/generators/starter_puya_smart_contract_python/.vscode/extensions.json rename to examples/production_python/.vscode/extensions.json diff --git a/examples/production_puya/.vscode/launch.json b/examples/production_python/.vscode/launch.json similarity index 100% rename from examples/production_puya/.vscode/launch.json rename to examples/production_python/.vscode/launch.json diff --git a/examples/production_puya/.vscode/settings.json b/examples/production_python/.vscode/settings.json similarity index 95% rename from examples/production_puya/.vscode/settings.json rename to examples/production_python/.vscode/settings.json index 0c2dfec..61681c0 100644 --- a/examples/production_puya/.vscode/settings.json +++ b/examples/production_python/.vscode/settings.json @@ -17,9 +17,9 @@ "python.defaultInterpreterPath": "${workspaceFolder}/.venv", "[python]": { "editor.codeActionsOnSave": { - "source.fixAll": true, + "source.fixAll": "explicit", // Prevent default import sorting from running; Ruff will sort imports for us anyway - "source.organizeImports": false + "source.organizeImports": "never" }, "editor.defaultFormatter": "ms-python.black-formatter", }, diff --git a/examples/production_puya/.vscode/tasks.json b/examples/production_python/.vscode/tasks.json similarity index 100% rename from examples/production_puya/.vscode/tasks.json rename to examples/production_python/.vscode/tasks.json diff --git a/examples/generators/production_puya_smart_contract_python/README.md b/examples/production_python/README.md similarity index 92% rename from examples/generators/production_puya_smart_contract_python/README.md rename to examples/production_python/README.md index 61056c0..ab58bdc 100644 --- a/examples/generators/production_puya_smart_contract_python/README.md +++ b/examples/production_python/README.md @@ -1,4 +1,4 @@ -# production_puya_smart_contract_python +# production_python This project has been generated using AlgoKit. See below for default getting started instructions. @@ -9,8 +9,6 @@ This project has been generated using AlgoKit. See below for default getting sta - [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. - ### Initial setup 1. Clone this repository locally @@ -48,7 +46,9 @@ This project has been generated using AlgoKit. See below for default getting sta > For guidance on `smart_contracts` folder and adding new contracts to the project please see [README](smart_contracts/README.md) on the respective folder.### Continuous Integration / Continuous Deployment (CI/CD) -This project uses [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to define CI/CD workflows, which are located in the [`.github/workflows`](./.github/workflows) folder. +This project uses [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to define CI/CD workflows, which are located in the [.github/workflows](`.github/workflows`) folder. + +> Please note, if you instantiated the project with --workspace flag in `algokit init` it will automatically attempt to move the contents of the `.github` folder to the root of the workspace. ### Debugging Smart Contracts @@ -98,12 +98,11 @@ For pushes to `main` branch, after the above checks pass, the following deployme # Tools -This project makes use of Python to build Algorand smart contracts. The following tools are in use: +This project makes use of Algorand 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/) +- [Algorand Python](https://github.com/algorandfoundation/puya) - A semantically and syntactically compatible, typed Python language that works with standard Python tooling and allows you to express smart contracts (apps) and smart signatures (logic signatures) for deployment on the Algorand Virtual Machine (AVM); [docs](https://github.com/algorandfoundation/puya), [examples](https://github.com/algorandfoundation/puya/tree/main/examples) - [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. diff --git a/examples/production_puya/poetry.toml b/examples/production_python/poetry.toml similarity index 100% rename from examples/production_puya/poetry.toml rename to examples/production_python/poetry.toml diff --git a/examples/production_puya/pyproject.toml b/examples/production_python/pyproject.toml similarity index 68% rename from examples/production_puya/pyproject.toml rename to examples/production_python/pyproject.toml index 5242a69..c247993 100644 --- a/examples/production_puya/pyproject.toml +++ b/examples/production_python/pyproject.toml @@ -1,5 +1,5 @@ [tool.poetry] -name = "production_puya" +name = "production_python" version = "0.1.0" description = "Algorand smart contracts" authors = ["None "] @@ -9,9 +9,10 @@ readme = "README.md" python = "^3.12" algokit-utils = "^2.2.0" python-dotenv = "^1.0.0" -puya = "^0" +algorand-python = "^1.0.0" [tool.poetry.group.dev.dependencies] +algokit-client-generator = "^1.1.3" black = {extras = ["d"], version = "*"} ruff = "^0.1.6" mypy = "*" @@ -19,6 +20,7 @@ pytest = "*" pytest-cov = "*" pip-audit = "*" pre-commit = "*" +puyapy = "^1.0.0" [build-system] requires = ["poetry-core"] @@ -43,8 +45,19 @@ pythonpath = ["smart_contracts", "tests"] [tool.mypy] files = "smart_contracts/" python_version = "3.12" +disallow_any_generics = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_defs = true +disallow_incomplete_defs = true check_untyped_defs = true +disallow_untyped_decorators = true warn_redundant_casts = true warn_unused_ignores = true -allow_untyped_defs = false +warn_return_any = true strict_equality = true +strict_concatenate = true +disallow_any_unimported = true +disallow_any_expr = true +disallow_any_decorated = true +disallow_any_explicit = true diff --git a/examples/production_puya/smart_contracts/README.md b/examples/production_python/smart_contracts/README.md similarity index 100% rename from examples/production_puya/smart_contracts/README.md rename to examples/production_python/smart_contracts/README.md diff --git a/examples/production_puya/smart_contracts/__init__.py b/examples/production_python/smart_contracts/__init__.py similarity index 100% rename from examples/production_puya/smart_contracts/__init__.py rename to examples/production_python/smart_contracts/__init__.py diff --git a/examples/generators/production_puya_smart_contract_python/smart_contracts/__main__.py b/examples/production_python/smart_contracts/__main__.py similarity index 82% rename from examples/generators/production_puya_smart_contract_python/smart_contracts/__main__.py rename to examples/production_python/smart_contracts/__main__.py index 7db8a69..aa936c9 100644 --- a/examples/generators/production_puya_smart_contract_python/smart_contracts/__main__.py +++ b/examples/production_python/smart_contracts/__main__.py @@ -7,6 +7,7 @@ from smart_contracts.config import contracts from smart_contracts.helpers.build import build from smart_contracts.helpers.deploy import deploy +from smart_contracts.helpers.util import find_app_spec_file # 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 @@ -32,7 +33,11 @@ def main(action: str) -> None: case "deploy": for contract in contracts: logger.info(f"Deploying app {contract.name}") - app_spec_path = artifact_path / contract.name / "application.json" + output_dir = artifact_path / contract.name + app_spec_file_name = find_app_spec_file(output_dir) + if app_spec_file_name is None: + raise Exception("Could not deploy app, .arc32.json file not found") + app_spec_path = output_dir / app_spec_file_name if contract.deploy: deploy(app_spec_path, contract.deploy) case "all": diff --git a/examples/production_python/smart_contracts/config.py b/examples/production_python/smart_contracts/config.py new file mode 100644 index 0000000..8f3ca93 --- /dev/null +++ b/examples/production_python/smart_contracts/config.py @@ -0,0 +1,61 @@ +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 # type: ignore + 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_python/smart_contracts/hello_world/contract.py b/examples/production_python/smart_contracts/hello_world/contract.py new file mode 100644 index 0000000..9052c05 --- /dev/null +++ b/examples/production_python/smart_contracts/hello_world/contract.py @@ -0,0 +1,8 @@ +# pyright: reportMissingModuleSource=false +from algopy import ARC4Contract, arc4 + + +class HelloWorld(ARC4Contract): + @arc4.abimethod() + def hello(self, name: arc4.String) -> arc4.String: + return "Hello, " + name diff --git a/examples/production_puya/smart_contracts/hello_world/deploy_config.py b/examples/production_python/smart_contracts/hello_world/deploy_config.py similarity index 100% rename from examples/production_puya/smart_contracts/hello_world/deploy_config.py rename to examples/production_python/smart_contracts/hello_world/deploy_config.py diff --git a/examples/production_puya/smart_contracts/helpers/__init__.py b/examples/production_python/smart_contracts/helpers/__init__.py similarity index 100% rename from examples/production_puya/smart_contracts/helpers/__init__.py rename to examples/production_python/smart_contracts/helpers/__init__.py diff --git a/examples/starter_puya/smart_contracts/helpers/build.py b/examples/production_python/smart_contracts/helpers/build.py similarity index 77% rename from examples/starter_puya/smart_contracts/helpers/build.py rename to examples/production_python/smart_contracts/helpers/build.py index 0dad0c8..d9fc955 100644 --- a/examples/starter_puya/smart_contracts/helpers/build.py +++ b/examples/production_python/smart_contracts/helpers/build.py @@ -3,6 +3,8 @@ from pathlib import Path from shutil import rmtree +from smart_contracts.helpers.util import find_app_spec_file + logger = logging.getLogger(__name__) deployment_extension = "py" @@ -16,9 +18,10 @@ def build(output_dir: Path, contract_path: Path) -> Path: build_result = subprocess.run( [ - "poetry", - "run", - "puyapy", + "algokit", + "--no-color", + "compile", + "python", contract_path.absolute(), f"--out-dir={output_dir}", "--output-arc32", @@ -30,12 +33,16 @@ def build(output_dir: Path, contract_path: Path) -> Path: if build_result.returncode: raise Exception(f"Could not build contract:\n{build_result.stdout}") + app_spec_file_name = find_app_spec_file(output_dir) + if app_spec_file_name is None: + raise Exception("Could not generate typed client, .arc32.json file not found") + generate_result = subprocess.run( [ "algokit", "generate", "client", - output_dir / "application.json", + output_dir / app_spec_file_name, "--output", output_dir / f"client.{deployment_extension}", ], @@ -53,4 +60,4 @@ def build(output_dir: Path, contract_path: Path) -> Path: raise Exception( f"Could not generate typed client:\n{generate_result.stdout}" ) - return output_dir / "application.json" + return output_dir / app_spec_file_name diff --git a/examples/production_puya/smart_contracts/helpers/deploy.py b/examples/production_python/smart_contracts/helpers/deploy.py similarity index 96% rename from examples/production_puya/smart_contracts/helpers/deploy.py rename to examples/production_python/smart_contracts/helpers/deploy.py index 08367a3..10185a9 100644 --- a/examples/production_puya/smart_contracts/helpers/deploy.py +++ b/examples/production_python/smart_contracts/helpers/deploy.py @@ -1,3 +1,6 @@ +# mypy: disable-error-code="no-untyped-call, misc" + + import logging from collections.abc import Callable from pathlib import Path diff --git a/examples/production_python/smart_contracts/helpers/util.py b/examples/production_python/smart_contracts/helpers/util.py new file mode 100644 index 0000000..6cf7e5b --- /dev/null +++ b/examples/production_python/smart_contracts/helpers/util.py @@ -0,0 +1,8 @@ +from pathlib import Path + + +def find_app_spec_file(output_dir: Path) -> str | None: + for file in output_dir.iterdir(): + if file.is_file() and file.suffixes == [".arc32", ".json"]: + return file.name + return None diff --git a/examples/production_puya/tests/__init__.py b/examples/production_python/tests/__init__.py similarity index 100% rename from examples/production_puya/tests/__init__.py rename to examples/production_python/tests/__init__.py diff --git a/examples/production_puya/tests/conftest.py b/examples/production_python/tests/conftest.py similarity index 100% rename from examples/production_puya/tests/conftest.py rename to examples/production_python/tests/conftest.py diff --git a/examples/production_puya/tests/hello_world_test.py b/examples/production_python/tests/hello_world_test.py similarity index 100% rename from examples/production_puya/tests/hello_world_test.py rename to examples/production_python/tests/hello_world_test.py diff --git a/examples/starter_puya/.algokit.toml b/examples/starter_puya/.algokit.toml deleted file mode 100644 index 2cbed35..0000000 --- a/examples/starter_puya/.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 = 'contract' -name = 'starter_puya' diff --git a/examples/starter_puya/.github/workflows/cd.yaml b/examples/starter_puya/.github/workflows/cd.yaml deleted file mode 100644 index 2401bf4..0000000 --- a/examples/starter_puya/.github/workflows/cd.yaml +++ /dev/null @@ -1,50 +0,0 @@ -name: Continuous Delivery of Smart Contract - -on: - push: - branches: - - main - -concurrency: release - -jobs: - ci-check: - name: Perform Checks - uses: ./.github/workflows/checks.yaml - - deploy-testnet: - runs-on: 'ubuntu-latest' - needs: ci-check - environment: Test - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Install poetry - run: pipx install poetry - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: '3.12' - cache: 'poetry' - - - name: Install algokit - run: pipx install algokit - - - name: Bootstrap dependencies - run: algokit bootstrap all - - - name: Configure git - shell: bash - run: | - # set git user and email as test invoke git - git config --global user.email "actions@github.com" && git config --global user.name "github-actions" - - - name: Deploy to testnet - run: algokit deploy testnet - env: - # This is the account that becomes the creator of the contract. - # Since we are not using the optional dispenser account (via DISPENSER_MNEMONIC), - # it must also be funded with enough Algos to deploy and fund the smart contracts created - DEPLOYER_MNEMONIC: ${{ secrets.DEPLOYER_MNEMONIC }} diff --git a/examples/starter_puya/.github/workflows/checks.yaml b/examples/starter_puya/.github/workflows/checks.yaml deleted file mode 100644 index 2c75ab4..0000000 --- a/examples/starter_puya/.github/workflows/checks.yaml +++ /dev/null @@ -1,76 +0,0 @@ -name: Check code base - -on: - workflow_call: - -jobs: - checks: - runs-on: 'ubuntu-latest' - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Install poetry - run: pipx install poetry - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: '3.12' - cache: 'poetry' - - - name: Install algokit - run: pipx install algokit - - - name: Start LocalNet - run: algokit localnet start - - - name: Bootstrap dependencies - run: algokit bootstrap all - - - name: Configure git - shell: bash - run: | - # set git user and email as test invoke git - git config --global user.email "actions@github.com" && git config --global user.name "github-actions" - - - name: Audit with pip-audit - run: | - # audit non dev dependencies, no exclusions - poetry export --without=dev > requirements.txt && poetry run pip-audit -r requirements.txt - - # audit all dependencies, with exclusions. - # If a vulnerability is found in a dev dependency without an available fix, - # it can be temporarily ignored by adding --ignore-vuln e.g. - # --ignore-vuln "GHSA-hcpj-qp55-gfph" # GitPython vulnerability, dev only dependency - poetry run pip-audit - - - name: Check formatting with Black - run: | - # stop the build if there are files that don't meet formatting requirements - poetry run black --check . - - - name: Check linting with Ruff - run: | - # stop the build if there are Python syntax errors or undefined names - poetry run ruff . - - - name: Check types with mypy - run: poetry run mypy - - - name: Build smart contracts - run: poetry run python -m smart_contracts build - - - name: Scan TEAL files for issues - run: algokit task analyze .algokit --recursive --force # add --diff flag if you want output stability checks instead - - - name: Check output stability of the smart contracts - shell: bash - run: | - # Add untracked files as empty so they come up in diff - git add -N ./smart_contracts/artifacts - # Error out if there are any changes in teal after generating output - git diff --exit-code --minimal ./smart_contracts/artifacts || (echo "::error ::Smart contract artifacts have changed, ensure committed artifacts are up to date" && exit 1); - - - name: Run deployer against LocalNet - run: poetry run python -m smart_contracts deploy diff --git a/examples/starter_puya/.github/workflows/pr.yaml b/examples/starter_puya/.github/workflows/pr.yaml deleted file mode 100644 index a80f784..0000000 --- a/examples/starter_puya/.github/workflows/pr.yaml +++ /dev/null @@ -1,8 +0,0 @@ -name: Pull Request validation - -on: [pull_request] - -jobs: - pr-check: - name: Perform Checks - uses: ./.github/workflows/checks.yaml diff --git a/examples/starter_puya/.pre-commit-config.yaml b/examples/starter_puya/.pre-commit-config.yaml deleted file mode 100644 index 8862f86..0000000 --- a/examples/starter_puya/.pre-commit-config.yaml +++ /dev/null @@ -1,48 +0,0 @@ -repos: - - repo: local - hooks: - - - id: black - name: black - description: "Black: The uncompromising Python code formatter" - entry: poetry run black - language: system - minimum_pre_commit_version: 2.9.2 - require_serial: true - types_or: [ python, pyi ] - - - - id: ruff - name: ruff - description: "Run 'ruff' for extremely fast Python linting" - entry: poetry run ruff - language: system - types: [ python ] - args: [ --fix ] - require_serial: false - additional_dependencies: [ ] - minimum_pre_commit_version: '0' - files: '^(src|tests)/' - - - - id: mypy - name: mypy - description: '`mypy` will check Python types for correctness' - entry: poetry run mypy - language: system - types_or: [ python, pyi ] - require_serial: true - additional_dependencies: [ ] - minimum_pre_commit_version: '2.9.2' - files: '^(src|tests)/' - - - id: tealer - name: tealer - description: "Run AlgoKit `Tealer` for TEAL static analysis" - entry: algokit - language: system - args: [task, analyze, ".algokit", "--recursive", "--force"] - require_serial: false - additional_dependencies: [] - minimum_pre_commit_version: "0" - files: '^.*\.teal$' diff --git a/examples/starter_puya/README.md b/examples/starter_puya/README.md deleted file mode 100644 index c83ee6b..0000000 --- a/examples/starter_puya/README.md +++ /dev/null @@ -1,113 +0,0 @@ -# starter_puya - -This project has been generated using AlgoKit. See below for default getting started instructions. - -# 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. - -### 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. - - 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 - -### Subsequently - -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 - -> For guidance on `smart_contracts` folder and adding new contracts to the project please see [README](smart_contracts/README.md) on the respective folder.### Continuous Integration / Continuous Deployment (CI/CD) - -This project uses [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to define CI/CD workflows, which are located in the [`.github/workflows`](./.github/workflows) folder. - -### Debugging Smart Contracts - -This project is optimized to work with AlgoKit AVM Debugger extension. To activate it: -Refer to the commented header in the `__main__.py` file in the `smart_contracts` folder. - -If you have opted in to include VSCode launch configurations in your project, you can also use the `Debug TEAL via AlgoKit AVM Debugger` launch configuration to interactively select an available trace file and launch the debug session for your smart contract. - -For information on using and setting up the `AlgoKit AVM Debugger` VSCode extension refer [here](https://github.com/algorandfoundation/algokit-avm-vscode-debugger). To install the extension from the VSCode Marketplace, use the following link: [AlgoKit AVM Debugger extension](https://marketplace.visualstudio.com/items?itemName=algorandfoundation.algokit-avm-vscode-debugger). - -#### Setting up GitHub for CI/CD workflow and TestNet deployment - - 1. Every time you have a change to your smart contract, and when you first initialize the project you need to [build the contract](#initial-setup) and then commit the `smart_contracts/artifacts` folder so the [output stability](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/articles/output_stability.md) tests pass - 2. Decide what values you want to use for the `allow_update`, `allow_delete` and the `on_schema_break`, `on_update` parameters specified in [`contract.py`](./smart_contracts/hello_world/contract.py). - When deploying to LocalNet these values are both set to allow update and replacement of the app for convenience. But for non-LocalNet networks - the defaults are more conservative. - These default values will allow the smart contract to be deployed initially, but will not allow the app to be updated or deleted if is changed and the build will instead fail. - To help you decide it may be helpful to read the [AlgoKit Utils app deployment documentation](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/docs/capabilities/app-deploy.md) or the [AlgoKit smart contract deployment architecture](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/architecture-decisions/2023-01-12_smart-contract-deployment.md#upgradeable-and-deletable-contracts). - 3. Create a [Github Environment](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#creating-an-environment) named `Test`. - Note: If you have a private repository and don't have GitHub Enterprise then Environments won't work and you'll need to convert the GitHub Action to use a different approach. Ignore this step if you picked `Starter` preset. - 4. Create or obtain a mnemonic for an Algorand account for use on TestNet to deploy apps, referred to as the `DEPLOYER` account. - 5. Store the mnemonic as a [secret](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#environment-secrets) `DEPLOYER_MNEMONIC` - in the Test environment created in step 3. - 6. The account used to deploy the smart contract will require enough funds to create the app, and also fund it. There are two approaches available here: - * Either, ensure the account is funded outside of CI/CD. - In Testnet, funds can be obtained by using the [Algorand TestNet dispenser](https://bank.testnet.algorand.network/) and we recommend provisioning 50 ALGOs. - * Or, fund the account as part of the CI/CD process by using a `DISPENSER_MNEMONIC` GitHub Environment secret to point to a separate `DISPENSER` account that you maintain ALGOs in (similarly, you need to provision ALGOs into this account using the [TestNet dispenser](https://bank.testnet.algorand.network/)). - -#### Continuous Integration - -For pull requests and pushes to `main` branch against this repository the following checks are automatically performed by GitHub Actions: - - Python dependencies are audited using [pip-audit](https://pypi.org/project/pip-audit/) - - Code formatting is checked using [Black](https://github.com/psf/black) - - Linting is checked using [Ruff](https://github.com/charliermarsh/ruff) - - Types are checked using [mypy](https://mypy-lang.org/) - - Smart contract artifacts are built - - Smart contract artifacts are checked for [output stability](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/articles/output_stability.md) - - Smart contract is deployed to a AlgoKit LocalNet instance - -#### Continuous Deployment - -For pushes to `main` branch, after the above checks pass, the following deployment actions are performed: - - The smart contract(s) are deployed to TestNet using [AlgoNode](https://algonode.io). - -> Please note deployment is also performed via `algokit deploy` command which can be invoked both via CI as seen on this project, or locally. For more information on how to use `algokit deploy` please see [AlgoKit documentation](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/deploy.md). - -# 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. - - [pre-commit](https://pre-commit.com/): A framework for managing and maintaining multi-language pre-commit hooks, to enable pre-commit you need to run `pre-commit install` in the root of the repository. This will install the pre-commit hooks and run them against modified files when committing. If any of the hooks fail, the commit will be aborted. To run the hooks on all files, use `pre-commit run --all-files`. -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/pyproject.toml b/examples/starter_puya/pyproject.toml deleted file mode 100644 index f364c33..0000000 --- a/examples/starter_puya/pyproject.toml +++ /dev/null @@ -1,45 +0,0 @@ -[tool.poetry] -name = "starter_puya" -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 = "*" -pre-commit = "*" - -[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_puya/smart_contracts/config.py b/examples/starter_puya/smart_contracts/config.py deleted file mode 100644 index 7d56d82..0000000 --- a/examples/starter_puya/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_puya/smart_contracts/hello_world/contract.py b/examples/starter_puya/smart_contracts/hello_world/contract.py deleted file mode 100644 index 589aea9..0000000 --- a/examples/starter_puya/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_python/.algokit.toml b/examples/starter_python/.algokit.toml new file mode 100644 index 0000000..1d9a006 --- /dev/null +++ b/examples/starter_python/.algokit.toml @@ -0,0 +1,38 @@ +[algokit] +min_version = "v2.0.0" + +[generate.smart_contract] +description = "Adds new smart contract to existing project" +path = ".algokit/generators/create_contract" + +[project] +type = 'contract' +name = 'starter_python' +artifacts = 'smart_contracts/artifacts' + +[project.deploy] +command = "poetry run python -m smart_contracts deploy" +environment_secrets = [ + "DEPLOYER_MNEMONIC", +] + +[project.deploy.localnet] +environment_secrets = [] + +[project.run] +# Commands intented for use locally and in CI +build = { commands = [ + 'poetry run python -m smart_contracts build', +], description = 'Build all smart contracts in the project' } +lint = { commands = [ +], description = 'Perform linting' } +audit-teal = { commands = [ + # 🚨 IMPORTANT 🚨: For strict TEAL validation, remove --exclude statements. The default starter contract is not for production. Ensure thorough testing and adherence to best practices in smart contract development. This is not a replacement for a professional audit. + 'algokit task analyze smart_contracts/artifacts --recursive --force --exclude rekey-to --exclude is-updatable --exclude missing-fee-check --exclude is-deletable --exclude can-close-asset --exclude can-close-account --exclude unprotected-deletable --exclude unprotected-updatable', +], description = 'Audit TEAL files' } + +# Commands intented for CI only, prefixed with `ci-` by convention +ci-teal-diff = { commands = [ + 'git add -N ./smart_contracts/artifacts', + 'git diff --exit-code --minimal ./smart_contracts/artifacts', +], description = 'Check TEAL files for differences' } diff --git a/examples/starter_puya/.copier-answers.yml b/examples/starter_python/.copier-answers.yml similarity index 88% rename from examples/starter_puya/.copier-answers.yml rename to examples/starter_python/.copier-answers.yml index 208b344..dac2839 100644 --- a/examples/starter_puya/.copier-answers.yml +++ b/examples/starter_python/.copier-answers.yml @@ -6,5 +6,5 @@ author_name: None contract_name: hello_world deployment_language: python preset_name: starter -project_name: starter_puya +project_name: starter_python diff --git a/examples/starter_puya/.editorconfig b/examples/starter_python/.editorconfig similarity index 100% rename from examples/starter_puya/.editorconfig rename to examples/starter_python/.editorconfig diff --git a/examples/starter_puya/.env.localnet.template b/examples/starter_python/.env.localnet.template similarity index 100% rename from examples/starter_puya/.env.localnet.template rename to examples/starter_python/.env.localnet.template diff --git a/examples/starter_puya/.env.template b/examples/starter_python/.env.template similarity index 100% rename from examples/starter_puya/.env.template rename to examples/starter_python/.env.template diff --git a/examples/starter_puya/.env.testnet.template b/examples/starter_python/.env.testnet.template similarity index 100% rename from examples/starter_puya/.env.testnet.template rename to examples/starter_python/.env.testnet.template diff --git a/examples/starter_puya/.gitattributes b/examples/starter_python/.gitattributes similarity index 100% rename from examples/starter_puya/.gitattributes rename to examples/starter_python/.gitattributes diff --git a/examples/starter_puya/.gitignore b/examples/starter_python/.gitignore similarity index 100% rename from examples/starter_puya/.gitignore rename to examples/starter_python/.gitignore diff --git a/examples/starter_puya/.tours/getting-started-with-your-algokit-project.tour b/examples/starter_python/.tours/getting-started-with-your-algokit-project.tour similarity index 99% rename from examples/starter_puya/.tours/getting-started-with-your-algokit-project.tour rename to examples/starter_python/.tours/getting-started-with-your-algokit-project.tour index c1309a6..6b593eb 100644 --- a/examples/starter_puya/.tours/getting-started-with-your-algokit-project.tour +++ b/examples/starter_python/.tours/getting-started-with-your-algokit-project.tour @@ -50,7 +50,7 @@ { "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 + "line": 15 } ] } diff --git a/examples/starter_puya/.vscode/extensions.json b/examples/starter_python/.vscode/extensions.json similarity index 71% rename from examples/starter_puya/.vscode/extensions.json rename to examples/starter_python/.vscode/extensions.json index 1d2e7cf..e16b76b 100644 --- a/examples/starter_puya/.vscode/extensions.json +++ b/examples/starter_python/.vscode/extensions.json @@ -1,9 +1,6 @@ { "recommendations": [ "ms-python.python", - "charliermarsh.ruff", - "matangover.mypy", - "ms-python.black-formatter", "tamasfe.even-better-toml", "editorconfig.editorconfig", "vsls-contrib.codetour", diff --git a/examples/starter_puya/.vscode/launch.json b/examples/starter_python/.vscode/launch.json similarity index 100% rename from examples/starter_puya/.vscode/launch.json rename to examples/starter_python/.vscode/launch.json diff --git a/examples/starter_puya/.vscode/settings.json b/examples/starter_python/.vscode/settings.json similarity index 52% rename from examples/starter_puya/.vscode/settings.json rename to examples/starter_python/.vscode/settings.json index a7b97d0..0a9b7c8 100644 --- a/examples/starter_puya/.vscode/settings.json +++ b/examples/starter_python/.vscode/settings.json @@ -17,30 +17,12 @@ "python.defaultInterpreterPath": "${workspaceFolder}/.venv", "[python]": { "editor.codeActionsOnSave": { - "source.fixAll": true, + "source.fixAll": "explicit", // Prevent default import sorting from running; Ruff will sort imports for us anyway - "source.organizeImports": false + "source.organizeImports": "never" }, - "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_puya/.vscode/tasks.json b/examples/starter_python/.vscode/tasks.json similarity index 100% rename from examples/starter_puya/.vscode/tasks.json rename to examples/starter_python/.vscode/tasks.json diff --git a/examples/starter_python/README.md b/examples/starter_python/README.md new file mode 100644 index 0000000..a98b9a1 --- /dev/null +++ b/examples/starter_python/README.md @@ -0,0 +1,59 @@ +# starter_python + +This project has been generated using AlgoKit. See below for default getting started instructions. + +# Setup + +### Pre-requisites + +- [Python 3.12](https://www.python.org/downloads/) or later +- [Docker](https://www.docker.com/) (only required for LocalNet) + +### 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. + - 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 + +### Subsequently + +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 + +> For guidance on `smart_contracts` folder and adding new contracts to the project please see [README](smart_contracts/README.md) on the respective folder. + +# Tools + +This project makes use of Algorand 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) +- [Algorand Python](https://github.com/algorandfoundation/puya) - A semantically and syntactically compatible, typed Python language that works with standard Python tooling and allows you to express smart contracts (apps) and smart signatures (logic signatures) for deployment on the Algorand Virtual Machine (AVM); [docs](https://github.com/algorandfoundation/puya), [examples](https://github.com/algorandfoundation/puya/tree/main/examples) +- [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. +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/poetry.toml b/examples/starter_python/poetry.toml similarity index 100% rename from examples/starter_puya/poetry.toml rename to examples/starter_python/poetry.toml diff --git a/examples/starter_python/pyproject.toml b/examples/starter_python/pyproject.toml new file mode 100644 index 0000000..a440a70 --- /dev/null +++ b/examples/starter_python/pyproject.toml @@ -0,0 +1,21 @@ +[tool.poetry] +name = "starter_python" +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" +algorand-python = "^1.0.0" + +[tool.poetry.group.dev.dependencies] +algokit-client-generator = "^1.1.3" +puyapy = "^1.0.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + diff --git a/examples/starter_puya/smart_contracts/README.md b/examples/starter_python/smart_contracts/README.md similarity index 100% rename from examples/starter_puya/smart_contracts/README.md rename to examples/starter_python/smart_contracts/README.md diff --git a/examples/starter_puya/smart_contracts/__init__.py b/examples/starter_python/smart_contracts/__init__.py similarity index 100% rename from examples/starter_puya/smart_contracts/__init__.py rename to examples/starter_python/smart_contracts/__init__.py diff --git a/examples/starter_puya/smart_contracts/__main__.py b/examples/starter_python/smart_contracts/__main__.py similarity index 82% rename from examples/starter_puya/smart_contracts/__main__.py rename to examples/starter_python/smart_contracts/__main__.py index 7db8a69..aa936c9 100644 --- a/examples/starter_puya/smart_contracts/__main__.py +++ b/examples/starter_python/smart_contracts/__main__.py @@ -7,6 +7,7 @@ from smart_contracts.config import contracts from smart_contracts.helpers.build import build from smart_contracts.helpers.deploy import deploy +from smart_contracts.helpers.util import find_app_spec_file # 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 @@ -32,7 +33,11 @@ def main(action: str) -> None: case "deploy": for contract in contracts: logger.info(f"Deploying app {contract.name}") - app_spec_path = artifact_path / contract.name / "application.json" + output_dir = artifact_path / contract.name + app_spec_file_name = find_app_spec_file(output_dir) + if app_spec_file_name is None: + raise Exception("Could not deploy app, .arc32.json file not found") + app_spec_path = output_dir / app_spec_file_name if contract.deploy: deploy(app_spec_path, contract.deploy) case "all": diff --git a/examples/starter_python/smart_contracts/config.py b/examples/starter_python/smart_contracts/config.py new file mode 100644 index 0000000..8f3ca93 --- /dev/null +++ b/examples/starter_python/smart_contracts/config.py @@ -0,0 +1,61 @@ +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 # type: ignore + 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_python/smart_contracts/hello_world/contract.py b/examples/starter_python/smart_contracts/hello_world/contract.py new file mode 100644 index 0000000..9052c05 --- /dev/null +++ b/examples/starter_python/smart_contracts/hello_world/contract.py @@ -0,0 +1,8 @@ +# pyright: reportMissingModuleSource=false +from algopy import ARC4Contract, arc4 + + +class HelloWorld(ARC4Contract): + @arc4.abimethod() + def hello(self, name: arc4.String) -> arc4.String: + return "Hello, " + name diff --git a/examples/starter_puya/smart_contracts/hello_world/deploy_config.py b/examples/starter_python/smart_contracts/hello_world/deploy_config.py similarity index 100% rename from examples/starter_puya/smart_contracts/hello_world/deploy_config.py rename to examples/starter_python/smart_contracts/hello_world/deploy_config.py diff --git a/examples/starter_puya/smart_contracts/helpers/__init__.py b/examples/starter_python/smart_contracts/helpers/__init__.py similarity index 100% rename from examples/starter_puya/smart_contracts/helpers/__init__.py rename to examples/starter_python/smart_contracts/helpers/__init__.py diff --git a/examples/generators/starter_puya_smart_contract_python/smart_contracts/helpers/build.py b/examples/starter_python/smart_contracts/helpers/build.py similarity index 77% rename from examples/generators/starter_puya_smart_contract_python/smart_contracts/helpers/build.py rename to examples/starter_python/smart_contracts/helpers/build.py index 0dad0c8..d9fc955 100644 --- a/examples/generators/starter_puya_smart_contract_python/smart_contracts/helpers/build.py +++ b/examples/starter_python/smart_contracts/helpers/build.py @@ -3,6 +3,8 @@ from pathlib import Path from shutil import rmtree +from smart_contracts.helpers.util import find_app_spec_file + logger = logging.getLogger(__name__) deployment_extension = "py" @@ -16,9 +18,10 @@ def build(output_dir: Path, contract_path: Path) -> Path: build_result = subprocess.run( [ - "poetry", - "run", - "puyapy", + "algokit", + "--no-color", + "compile", + "python", contract_path.absolute(), f"--out-dir={output_dir}", "--output-arc32", @@ -30,12 +33,16 @@ def build(output_dir: Path, contract_path: Path) -> Path: if build_result.returncode: raise Exception(f"Could not build contract:\n{build_result.stdout}") + app_spec_file_name = find_app_spec_file(output_dir) + if app_spec_file_name is None: + raise Exception("Could not generate typed client, .arc32.json file not found") + generate_result = subprocess.run( [ "algokit", "generate", "client", - output_dir / "application.json", + output_dir / app_spec_file_name, "--output", output_dir / f"client.{deployment_extension}", ], @@ -53,4 +60,4 @@ def build(output_dir: Path, contract_path: Path) -> Path: raise Exception( f"Could not generate typed client:\n{generate_result.stdout}" ) - return output_dir / "application.json" + return output_dir / app_spec_file_name diff --git a/examples/starter_puya/smart_contracts/helpers/deploy.py b/examples/starter_python/smart_contracts/helpers/deploy.py similarity index 96% rename from examples/starter_puya/smart_contracts/helpers/deploy.py rename to examples/starter_python/smart_contracts/helpers/deploy.py index 08367a3..10185a9 100644 --- a/examples/starter_puya/smart_contracts/helpers/deploy.py +++ b/examples/starter_python/smart_contracts/helpers/deploy.py @@ -1,3 +1,6 @@ +# mypy: disable-error-code="no-untyped-call, misc" + + import logging from collections.abc import Callable from pathlib import Path diff --git a/examples/starter_python/smart_contracts/helpers/util.py b/examples/starter_python/smart_contracts/helpers/util.py new file mode 100644 index 0000000..6cf7e5b --- /dev/null +++ b/examples/starter_python/smart_contracts/helpers/util.py @@ -0,0 +1,8 @@ +from pathlib import Path + + +def find_app_spec_file(output_dir: Path) -> str | None: + for file in output_dir.iterdir(): + if file.is_file() and file.suffixes == [".arc32", ".json"]: + return file.name + return None diff --git a/includes/project_name_kebab.jinja b/includes/project_name_kebab.jinja new file mode 100644 index 0000000..9bd9740 --- /dev/null +++ b/includes/project_name_kebab.jinja @@ -0,0 +1 @@ +{{- project_name | trim | replace("_", "-") | replace(" ", "-") | lower() | regex_replace('[-]+', '-') | regex_replace('[^a-z0-9.-]', '') -}} diff --git a/poetry.lock b/poetry.lock index 2450613..4c5b508 100644 --- a/poetry.lock +++ b/poetry.lock @@ -533,85 +533,101 @@ files = [ [[package]] name = "multidict" -version = "6.0.4" +version = "6.0.5" description = "multidict implementation" optional = false python-versions = ">=3.7" files = [ - {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, - {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, - {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, - {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, - {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, - {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, - {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, - {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, - {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, - {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, - {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, - {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, - {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, - {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, - {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, + {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, + {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, + {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, + {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, + {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, + {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, + {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, + {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, + {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, + {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, + {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, + {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, + {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, + {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, + {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, ] [[package]] @@ -937,6 +953,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -944,8 +961,16 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -962,6 +987,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -969,6 +995,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, diff --git a/template_content/.algokit.toml.jinja b/template_content/.algokit.toml.jinja index 8f41d0f..c6b1d4d 100644 --- a/template_content/.algokit.toml.jinja +++ b/template_content/.algokit.toml.jinja @@ -1,7 +1,16 @@ [algokit] -min_version = "v1.8.0" +min_version = "v2.0.0" -[deploy] +[generate.smart_contract] +description = "Adds new smart contract to existing project" +path = ".algokit/generators/create_contract" + +[project] +type = 'contract' +name = '{{ project_name }}' +artifacts = 'smart_contracts/artifacts' + +[project.deploy] {%- if deployment_language == 'python' %} command = "poetry run python -m smart_contracts deploy" {%- elif deployment_language == 'typescript' %} @@ -14,13 +23,49 @@ environment_secrets = [ {%- endif %} ] -[deploy.localnet] +[project.deploy.localnet] environment_secrets = [] -[generate.smart_contract] -description = "Adds new smart contract to existing project" -path = ".algokit/generators/create_contract" +[project.run] +# Commands intented for use locally and in CI +build = { commands = [ + 'poetry run python -m smart_contracts build', +], description = 'Build all smart contracts in the project' } +{%- if deployment_language == 'python' and use_python_pytest %} +test = { commands = [ + 'poetry run pytest', +], description = 'Run smart contract tests' } +{%- elif deployment_language == 'typescript' and use_typescript_jest %} +test = { commands = [ + 'npm run test', +], description = 'Run smart contract tests using Jest' } +{%- endif %} +{%- if use_python_pip_audit %} +audit = { commands = [ + 'poetry export --without=dev -o requirements.txt', + 'poetry run pip-audit -r requirements.txt', +], description = 'Audit with pip-audit' } +{%- endif %} +lint = { commands = [ +{%- if use_python_black %} + 'poetry run black --check .', +{%- endif %} +{%- if python_linter == 'ruff' %} + 'poetry run ruff .', +{%- elif python_linter == 'flake8' %} + 'poetry run flake8 .', +{%- endif %} +{%- if use_python_mypy %} + 'poetry run mypy', +{%- endif %} +], description = 'Perform linting' } +audit-teal = { commands = [ + # 🚨 IMPORTANT 🚨: For strict TEAL validation, remove --exclude statements. The default starter contract is not for production. Ensure thorough testing and adherence to best practices in smart contract development. This is not a replacement for a professional audit. + 'algokit task analyze smart_contracts/artifacts --recursive --force --exclude rekey-to --exclude is-updatable --exclude missing-fee-check --exclude is-deletable --exclude can-close-asset --exclude can-close-account --exclude unprotected-deletable --exclude unprotected-updatable', +], description = 'Audit TEAL files' } -[project] -type = 'contract' -name = '{{ project_name }}' +# Commands intented for CI only, prefixed with `ci-` by convention +ci-teal-diff = { commands = [ + 'git add -N ./smart_contracts/artifacts', + 'git diff --exit-code --minimal ./smart_contracts/artifacts', +], description = 'Check TEAL files for differences' } diff --git a/template_content/.algokit/generators/create_contract/smart_contracts/{% raw %}{{ contract_name }}{% endraw %}/contract.py.j2 b/template_content/.algokit/generators/create_contract/smart_contracts/{% raw %}{{ contract_name }}{% endraw %}/contract.py.j2 index eabbaa0..9828347 100644 --- a/template_content/.algokit/generators/create_contract/smart_contracts/{% raw %}{{ contract_name }}{% endraw %}/contract.py.j2 +++ b/template_content/.algokit/generators/create_contract/smart_contracts/{% raw %}{{ contract_name }}{% endraw %}/contract.py.j2 @@ -1,4 +1,4 @@ -from puyapy import ARC4Contract, arc4 +from algopy import ARC4Contract, arc4 class {{ contract_name.split('_')|map('capitalize')|join }}(ARC4Contract): diff --git a/template_content/README.md.jinja b/template_content/README.md.jinja index d8db073..4666a3b 100644 --- a/template_content/README.md.jinja +++ b/template_content/README.md.jinja @@ -9,8 +9,6 @@ This project has been generated using AlgoKit. See below for default getting sta - [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. - ### Initial setup 1. Clone this repository locally @@ -54,7 +52,9 @@ This project has been generated using AlgoKit. See below for default getting sta {%- if use_github_actions -%} ### Continuous Integration / Continuous Deployment (CI/CD) -This project uses [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to define CI/CD workflows, which are located in the [`.github/workflows`](./.github/workflows) folder. +This project uses [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to define CI/CD workflows, which are located in the [.github/workflows](`{% if use_workspace %}../../.github/workflows{% else %}.github/workflows{% endif %}`) folder. + +> Please note, if you instantiated the project with --workspace flag in `algokit init` it will automatically attempt to move the contents of the `.github` folder to the root of the workspace. ### Debugging Smart Contracts @@ -128,12 +128,11 @@ For pushes to `main` branch, after the above checks pass, the following deployme # Tools -This project makes use of Python to build Algorand smart contracts. The following tools are in use: +This project makes use of Algorand 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/) +- [Algorand Python](https://github.com/algorandfoundation/puya) - A semantically and syntactically compatible, typed Python language that works with standard Python tooling and allows you to express smart contracts (apps) and smart signatures (logic signatures) for deployment on the Algorand Virtual Machine (AVM); [docs](https://github.com/algorandfoundation/puya), [examples](https://github.com/algorandfoundation/puya/tree/main/examples) - [AlgoKit Utils]({% if deployment_language == "typescript" %}https://github.com/algorandfoundation/algokit-utils-ts{% else %}https://github.com/algorandfoundation/algokit-utils-py{% endif %}) - 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. {%- if use_python_black -%} diff --git a/template_content/pyproject.toml.jinja b/template_content/pyproject.toml.jinja index c6a5796..53aa07a 100644 --- a/template_content/pyproject.toml.jinja +++ b/template_content/pyproject.toml.jinja @@ -9,9 +9,10 @@ readme = "README.md" python = "^3.12" algokit-utils = "^2.2.0" python-dotenv = "^1.0.0" -puya = "^0" +algorand-python = "^1.0.0" [tool.poetry.group.dev.dependencies] +algokit-client-generator = "^1.1.3" {% if use_python_black -%} black = {extras = ["d"], version = "*"} {% endif -%} @@ -32,7 +33,9 @@ pip-audit = "*" {% endif -%} {% if use_pre_commit -%} pre-commit = "*" -{% endif %} +{% endif -%} +puyapy = "^1.0.0" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" @@ -59,9 +62,20 @@ pythonpath = ["smart_contracts", "tests"] [tool.mypy] files = "smart_contracts/" python_version = "3.12" +disallow_any_generics = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_defs = true +disallow_incomplete_defs = true check_untyped_defs = true +disallow_untyped_decorators = true warn_redundant_casts = true warn_unused_ignores = true -allow_untyped_defs = false +warn_return_any = true strict_equality = true +strict_concatenate = true +disallow_any_unimported = true +disallow_any_expr = true +disallow_any_decorated = true +disallow_any_explicit = true {% endif -%} diff --git a/template_content/smart_contracts/__main__.py.jinja b/template_content/smart_contracts/__main__.py.jinja index c94b3f3..26e6b8a 100644 --- a/template_content/smart_contracts/__main__.py.jinja +++ b/template_content/smart_contracts/__main__.py.jinja @@ -8,6 +8,7 @@ from smart_contracts.config import contracts from smart_contracts.helpers.build import build {% if deployment_language == 'python' -%} from smart_contracts.helpers.deploy import deploy +from smart_contracts.helpers.util import find_app_spec_file # 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 @@ -35,7 +36,11 @@ def main(action: str) -> None: case "deploy": for contract in contracts: logger.info(f"Deploying app {contract.name}") - app_spec_path = artifact_path / contract.name / "application.json" + output_dir = artifact_path / contract.name + app_spec_file_name = find_app_spec_file(output_dir) + if app_spec_file_name is None: + raise Exception("Could not deploy app, .arc32.json file not found") + app_spec_path = output_dir / app_spec_file_name if contract.deploy: deploy(app_spec_path, contract.deploy) case "all": diff --git a/template_content/smart_contracts/config.py.jinja b/template_content/smart_contracts/config.py.jinja index 7d56d82..8f3ca93 100644 --- a/template_content/smart_contracts/config.py.jinja +++ b/template_content/smart_contracts/config.py.jinja @@ -38,7 +38,7 @@ def import_deploy_if_exists( deploy_module = importlib.import_module( f"{folder.parent.name}.{folder.name}.deploy_config" ) - return deploy_module.deploy + return deploy_module.deploy # type: ignore except ImportError: return None diff --git a/template_content/smart_contracts/helpers/build.py.jinja b/template_content/smart_contracts/helpers/build.py.jinja index 8dd7f79..48b311c 100644 --- a/template_content/smart_contracts/helpers/build.py.jinja +++ b/template_content/smart_contracts/helpers/build.py.jinja @@ -3,6 +3,8 @@ import subprocess from pathlib import Path from shutil import rmtree +from smart_contracts.helpers.util import find_app_spec_file + logger = logging.getLogger(__name__) {% if deployment_language == 'python' -%} deployment_extension = "py" @@ -19,9 +21,10 @@ def build(output_dir: Path, contract_path: Path) -> Path: build_result = subprocess.run( [ - "poetry", - "run", - "puyapy", + "algokit", + "--no-color", + "compile", + "python", contract_path.absolute(), f"--out-dir={output_dir}", "--output-arc32", @@ -33,12 +36,16 @@ def build(output_dir: Path, contract_path: Path) -> Path: if build_result.returncode: raise Exception(f"Could not build contract:\n{build_result.stdout}") + app_spec_file_name = find_app_spec_file(output_dir) + if app_spec_file_name is None: + raise Exception("Could not generate typed client, .arc32.json file not found") + generate_result = subprocess.run( [ "algokit", "generate", "client", - output_dir / "application.json", + output_dir / app_spec_file_name, "--output", output_dir / f"client.{deployment_extension}", ], @@ -56,4 +63,4 @@ def build(output_dir: Path, contract_path: Path) -> Path: raise Exception( f"Could not generate typed client:\n{generate_result.stdout}" ) - return output_dir / "application.json" + return output_dir / app_spec_file_name diff --git a/template_content/smart_contracts/helpers/util.py b/template_content/smart_contracts/helpers/util.py new file mode 100644 index 0000000..6cf7e5b --- /dev/null +++ b/template_content/smart_contracts/helpers/util.py @@ -0,0 +1,8 @@ +from pathlib import Path + + +def find_app_spec_file(output_dir: Path) -> str | None: + for file in output_dir.iterdir(): + if file.is_file() and file.suffixes == [".arc32", ".json"]: + return file.name + return None diff --git a/template_content/smart_contracts/helpers/{% if deployment_language == 'python' %}deploy.py{% endif %}.jinja b/template_content/smart_contracts/helpers/{% if deployment_language == 'python' %}deploy.py{% endif %}.jinja index 08367a3..10185a9 100644 --- a/template_content/smart_contracts/helpers/{% if deployment_language == 'python' %}deploy.py{% endif %}.jinja +++ b/template_content/smart_contracts/helpers/{% if deployment_language == 'python' %}deploy.py{% endif %}.jinja @@ -1,3 +1,6 @@ +# mypy: disable-error-code="no-untyped-call, misc" + + import logging from collections.abc import Callable from pathlib import Path diff --git a/template_content/smart_contracts/{{ contract_name }}/contract.py.jinja b/template_content/smart_contracts/{{ contract_name }}/contract.py.jinja index eabbaa0..0fb0bc7 100644 --- a/template_content/smart_contracts/{{ contract_name }}/contract.py.jinja +++ b/template_content/smart_contracts/{{ contract_name }}/contract.py.jinja @@ -1,4 +1,5 @@ -from puyapy import ARC4Contract, arc4 +# pyright: reportMissingModuleSource=false +from algopy import ARC4Contract, arc4 class {{ contract_name.split('_')|map('capitalize')|join }}(ARC4Contract): diff --git a/template_content/{% if ide_vscode %}.tours{% endif %}/getting-started-with-your-algokit-project.tour.jinja b/template_content/{% if ide_vscode %}.tours{% endif %}/getting-started-with-your-algokit-project.tour.jinja index ca507d4..e582960 100644 --- a/template_content/{% if ide_vscode %}.tours{% endif %}/getting-started-with-your-algokit-project.tour.jinja +++ b/template_content/{% if ide_vscode %}.tours{% endif %}/getting-started-with-your-algokit-project.tour.jinja @@ -50,7 +50,7 @@ { "file": "smart_contracts/{% if deployment_language == 'python' %}__main__.py{% else %}index.ts{% endif %}", "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 + "line": 15 } ] } diff --git a/template_content/{% if ide_vscode %}.vscode{% endif %}/settings.json.jinja b/template_content/{% if ide_vscode %}.vscode{% endif %}/settings.json.jinja index 708895f..5c5dd2b 100644 --- a/template_content/{% if ide_vscode %}.vscode{% endif %}/settings.json.jinja +++ b/template_content/{% if ide_vscode %}.vscode{% endif %}/settings.json.jinja @@ -20,9 +20,9 @@ "python.defaultInterpreterPath": "${workspaceFolder}/.venv", "[python]": { "editor.codeActionsOnSave": { - "source.fixAll": true, + "source.fixAll": "explicit", // Prevent default import sorting from running; Ruff will sort imports for us anyway - "source.organizeImports": false + "source.organizeImports": "never" }, {% if use_python_black -%} "editor.defaultFormatter": "ms-python.black-formatter", diff --git a/template_content/{% if use_github_actions %}.github{% endif %}/workflows/checks.yaml.jinja b/template_content/{% if use_github_actions %}.github{% endif %}/workflows/checks.yaml.jinja deleted file mode 100644 index b5022f5..0000000 --- a/template_content/{% if use_github_actions %}.github{% endif %}/workflows/checks.yaml.jinja +++ /dev/null @@ -1,102 +0,0 @@ -name: Check code base - -on: - workflow_call: - -jobs: - checks: - runs-on: 'ubuntu-latest' - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Install poetry - run: pipx install poetry - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: '3.12' - cache: 'poetry' - - - name: Install algokit - run: pipx install algokit - - - name: Start LocalNet - run: algokit localnet start - - - name: Bootstrap dependencies - run: algokit bootstrap all - - - name: Configure git - shell: bash - run: | - # set git user and email as test invoke git - git config --global user.email "actions@github.com" && git config --global user.name "github-actions" -{%- if use_python_pip_audit %} - - - name: Audit with pip-audit - run: | - # audit non dev dependencies, no exclusions - poetry export --without=dev > requirements.txt && poetry run pip-audit -r requirements.txt - - # audit all dependencies, with exclusions. - # If a vulnerability is found in a dev dependency without an available fix, - # it can be temporarily ignored by adding --ignore-vuln e.g. - # --ignore-vuln "GHSA-hcpj-qp55-gfph" # GitPython vulnerability, dev only dependency - poetry run pip-audit -{%- endif %} -{%- if use_python_black %} - - - name: Check formatting with Black - run: | - # stop the build if there are files that don't meet formatting requirements - poetry run black --check . -{%- endif %} -{%- if python_linter == 'ruff' %} - - - name: Check linting with Ruff - run: | - # stop the build if there are Python syntax errors or undefined names - poetry run ruff . -{%- elif python_linter == 'flake8' %} - - - name: Check linting with flake8 - run: | - # stop the build if there are Python syntax errors or undefined names - poetry run flake8 . -{%- endif %} -{%- if use_python_mypy %} - - - name: Check types with mypy - run: poetry run mypy -{%- endif %} -{%- if use_python_pytest %} - - - name: Run tests - shell: bash - run: | - set -o pipefail - poetry run pytest --junitxml=pytest-junit.xml -{%- endif %} - - - name: Build smart contracts - run: poetry run python -m smart_contracts build - - - name: Scan TEAL files for issues - run: algokit task analyze .algokit --recursive --force # add --diff flag if you want output stability checks instead - - - name: Check output stability of the smart contracts - shell: bash - run: | - # Add untracked files as empty so they come up in diff - git add -N ./smart_contracts/artifacts - # Error out if there are any changes in teal after generating output - git diff --exit-code --minimal ./smart_contracts/artifacts || (echo "::error ::Smart contract artifacts have changed, ensure committed artifacts are up to date" && exit 1); - - - name: Run deployer against LocalNet -{%- if deployment_language == 'typescript' %} - run: npm run --prefix smart_contracts deploy:ci -{%- elif deployment_language == 'python' %} - run: poetry run python -m smart_contracts deploy -{%- endif %} diff --git a/template_content/{% if use_github_actions %}.github{% endif %}/workflows/pr.yaml b/template_content/{% if use_github_actions %}.github{% endif %}/workflows/pr.yaml deleted file mode 100644 index a80f784..0000000 --- a/template_content/{% if use_github_actions %}.github{% endif %}/workflows/pr.yaml +++ /dev/null @@ -1,8 +0,0 @@ -name: Pull Request validation - -on: [pull_request] - -jobs: - pr-check: - name: Perform Checks - uses: ./.github/workflows/checks.yaml diff --git a/template_content/{% if use_github_actions %}.github{% endif %}/workflows/cd.yaml.jinja b/template_content/{% if use_github_actions %}.github{% endif %}/workflows/{% include pathjoin('includes', 'project_name_kebab.jinja') %}-cd.yaml.jinja similarity index 69% rename from template_content/{% if use_github_actions %}.github{% endif %}/workflows/cd.yaml.jinja rename to template_content/{% if use_github_actions %}.github{% endif %}/workflows/{% include pathjoin('includes', 'project_name_kebab.jinja') %}-cd.yaml.jinja index 5a8e0fe..eb5af8a 100644 --- a/template_content/{% if use_github_actions %}.github{% endif %}/workflows/cd.yaml.jinja +++ b/template_content/{% if use_github_actions %}.github{% endif %}/workflows/{% include pathjoin('includes', 'project_name_kebab.jinja') %}-cd.yaml.jinja @@ -1,21 +1,24 @@ -name: Continuous Delivery of Smart Contract +name: Release {{ project_name }} on: + workflow_call: + + {%- if not use_workspace %} push: branches: - main - -concurrency: release + {%- endif %} jobs: - ci-check: - name: Perform Checks - uses: ./.github/workflows/checks.yaml - + {%- if not use_workspace %} + validate: + name: Validate {{ project_name }} + uses: ./.github/workflows/{% include pathjoin('includes', 'project_name_kebab.jinja') %}-ci.yaml + {%- endif %} deploy-testnet: - runs-on: 'ubuntu-latest' - needs: ci-check - environment: Test + runs-on: "ubuntu-latest" + {% if not use_workspace %}needs: validate{% endif %} + environment: contract-testnet steps: - name: Checkout source code uses: actions/checkout@v4 @@ -26,14 +29,14 @@ jobs: - name: Set up Python 3.12 uses: actions/setup-python@v5 with: - python-version: '3.12' - cache: 'poetry' + python-version: "3.12" + cache: "poetry" - name: Install algokit run: pipx install algokit - name: Bootstrap dependencies - run: algokit bootstrap all + run: algokit bootstrap all --project-name '{{ project_name }}' - name: Configure git shell: bash @@ -42,7 +45,7 @@ jobs: git config --global user.email "actions@github.com" && git config --global user.name "github-actions" - name: Deploy to testnet - run: algokit deploy testnet + run: algokit deploy testnet --project-name '{{ project_name }}' env: {%- if use_dispenser %} # This is the account that becomes the creator of the contract diff --git a/template_content/{% if use_github_actions %}.github{% endif %}/workflows/{% include pathjoin('includes', 'project_name_kebab.jinja') %}-ci.yaml.jinja b/template_content/{% if use_github_actions %}.github{% endif %}/workflows/{% include pathjoin('includes', 'project_name_kebab.jinja') %}-ci.yaml.jinja new file mode 100644 index 0000000..03dae94 --- /dev/null +++ b/template_content/{% if use_github_actions %}.github{% endif %}/workflows/{% include pathjoin('includes', 'project_name_kebab.jinja') %}-ci.yaml.jinja @@ -0,0 +1,63 @@ +name: Validate {{ project_name }} + +on: + workflow_call: + + {%- if not use_workspace %} + pull_request: + {%- endif %} + +jobs: + validate: + runs-on: "ubuntu-latest" + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Install poetry + run: pipx install poetry + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: "poetry" + + - name: Install algokit + run: pipx install algokit + + - name: Start LocalNet + run: algokit localnet start + + - name: Bootstrap dependencies + run: algokit bootstrap all --project-name '{{ project_name }}' + + - name: Configure git + shell: bash + run: | + # set git user and email as test invoke git + git config --global user.email "actions@github.com" && git config --global user.name "github-actions" + + - name: Audit python dependencies + run: algokit project run audit --project-name '{{ project_name }}' + + - name: Lint and format python dependencies + run: algokit project run lint --project-name '{{ project_name }}' + + - name: Run tests + shell: bash + run: | + set -o pipefail + algokit project run test --project-name '{{ project_name }}' + + - name: Build smart contracts + run: algokit project run build --project-name '{{ project_name }}' + + - name: Scan TEAL files for issues + run: algokit project run audit-teal --project-name '{{ project_name }}' + + - name: Check output stability of the smart contracts + run: algokit project run ci-teal-diff --project-name '{{ project_name }}' + + - name: Run deployer against LocalNet + run: algokit project deploy localnet --project-name '{{ project_name }}' diff --git a/tests/test_generators.py b/tests/test_generators.py index e2ce48b..6db0d3a 100644 --- a/tests/test_generators.py +++ b/tests/test_generators.py @@ -19,13 +19,9 @@ "author_email": "None", } config_path = Path(__file__).parent.parent / "pyproject.toml" -BLACK_ARGS = ["black", "--check", "--diff", "--config", str(config_path), "."] -RUFF_ARGS = ["ruff", "--diff", "--config", str(config_path), "."] -MYPY_ARGS = [ - "mypy", - "--ignore-missing-imports", # TODO: only ignore missing typed clients in config.py - ".", -] +BUILD_ARGS = ["algokit", "project", "run", "build"] +TEST_ARGS = ["algokit", "project", "run", "test"] +LINT_ARGS = ["algokit", "project", "run", "lint"] def _load_copier_yaml(path: Path) -> dict[str, str | bool | dict]: @@ -52,7 +48,14 @@ def working_dir() -> Iterator[Path]: dest_dir = generated_root / src_dir.stem shutil.rmtree(dest_dir, ignore_errors=True) - shutil.copytree(src_dir, dest_dir, dirs_exist_ok=True) + shutil.copytree( + src_dir, + dest_dir, + dirs_exist_ok=True, + ignore=shutil.ignore_patterns( + ".*_cache", ".venv", "__pycache__", "node_modules" + ), + ) def run_init( @@ -88,7 +91,6 @@ def run_init( "--defaults", "--no-ide", "--no-git", - "--no-bootstrap", "--no-workspace", ] answers = {**DEFAULT_PARAMETERS, **(answers or {})} @@ -121,7 +123,11 @@ def check_codebase(working_dir: Path, test_name: str) -> subprocess.CompletedPro content = src_path_pattern.sub("_src_path: ", content) copier_answers.write_text(content, "utf-8") - check_args = [BLACK_ARGS, RUFF_ARGS, MYPY_ARGS] + check_args = [BUILD_ARGS] + + processed_questions = _load_copier_yaml(copier_answers) + if processed_questions["preset_name"] == "production": + check_args += [LINT_ARGS, TEST_ARGS] for check_arg in check_args: result = subprocess.run( @@ -172,7 +178,7 @@ def run_generator( def test_smart_contract_generator_default_starter_preset( language: str, working_dir: Path ) -> None: - test_name = f"starter_puya_smart_contract_{language}" + test_name = f"starter_python_smart_contract_{language}" response = run_init( working_dir, @@ -203,7 +209,7 @@ def test_smart_contract_generator_default_starter_preset( def test_smart_contract_generator_default_production_preset( language: str, working_dir: Path ) -> None: - test_name = f"production_puya_smart_contract_{language}" + test_name = f"production_python_smart_contract_{language}" response = run_init( working_dir, diff --git a/tests/test_templates.py b/tests/test_templates.py index f4e7d3d..4b00aa6 100644 --- a/tests/test_templates.py +++ b/tests/test_templates.py @@ -19,13 +19,9 @@ "author_email": "None", } config_path = Path(__file__).parent.parent / "pyproject.toml" -BLACK_ARGS = ["black", "--check", "--diff", "--config", str(config_path), "."] -RUFF_ARGS = ["ruff", "--diff", "--config", str(config_path), "."] -MYPY_ARGS = [ - "mypy", - "--ignore-missing-imports", # TODO: only ignore missing typed clients in config.py - ".", -] +BUILD_ARGS = ["algokit", "project", "run", "build"] +TEST_ARGS = ["algokit", "project", "run", "test"] +LINT_ARGS = ["algokit", "project", "run", "lint"] def _load_copier_yaml(path: Path) -> dict[str, str | bool | dict]: @@ -52,7 +48,14 @@ def working_dir() -> Iterator[Path]: dest_dir = generated_root / src_dir.stem shutil.rmtree(dest_dir, ignore_errors=True) - shutil.copytree(src_dir, dest_dir, dirs_exist_ok=True) + shutil.copytree( + src_dir, + dest_dir, + dirs_exist_ok=True, + ignore=shutil.ignore_patterns( + ".*_cache", ".venv", "__pycache__", "node_modules" + ), + ) def run_init( @@ -88,7 +91,6 @@ def run_init( "--defaults", "--no-ide", "--no-git", - "--no-bootstrap", "--no-workspace", ] answers = {**DEFAULT_PARAMETERS, **(answers or {})} @@ -118,7 +120,11 @@ def run_init( content = src_path_pattern.sub("_src_path: ", content) copier_answers.write_text(content, "utf-8") - check_args = [BLACK_ARGS, RUFF_ARGS, MYPY_ARGS] + check_args = [BUILD_ARGS] + + processed_questions = _load_copier_yaml(copier_answers) + if processed_questions["preset_name"] == "production": + check_args += [LINT_ARGS, TEST_ARGS] for check_arg in check_args: result = subprocess.run( @@ -157,7 +163,7 @@ def run_init_kwargs( working_dir: Path, **kwargs: str | bool ) -> subprocess.CompletedProcess: answers = {k: str(v) for k, v in kwargs.items()} - name_suffix = "_".join(f"{v}_puya" for _, v in answers.items()) + name_suffix = "_".join(f"{v}_python" for _, v in answers.items()) return run_init(working_dir, f"{name_suffix}", answers=answers)