Skip to content

feat: adding latest changes from beaker template (debugger, typed simulate) #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions copier.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ author_name:
type: str
help: Package author name
placeholder: "Your Name"
default: "Your Name"

author_email:
type: str
help: Package author email
placeholder: "your@email.tld"
default: "your@email.tld"

ide_vscode:
type: bool
Expand All @@ -55,9 +57,33 @@ deployment_language:
TypeScript: "typescript"
default: "python"

use_python_pytest:
type: bool
when: false
help: Do you want to include unit tests (via pytest)?
# deployment_language is empty when using the default_language
default: |-
{% if deployment_language|length == 0 or deployment_language == 'python' -%}
yes
{%- else -%}
no
{%- endif %}

use_typescript_jest:
type: bool
when: false
help: Do you want to include unit tests (via jest)?
default: |-
{% if deployment_language == 'typescript' -%}
yes
{%- else -%}
no
{%- endif %}

python_linter:
type: str
help: Do you want to use a Python linter?
when: false
choices:
Ruff: "ruff"
Flake8: "flake8"
Expand All @@ -67,39 +93,40 @@ python_linter:

use_python_black:
type: bool
when: false
help: Do you want to use a Python formatter (via Black)?
default: yes

use_python_mypy:
type: bool
when: false
help: Do you want to use a Python type checker (via mypy)?
default: |-
yes

use_python_pytest:
type: bool
help: Do you want to include unit tests (via pytest)?
default: yes

use_python_pip_audit:
type: bool
when: false
help: Do you want to include Python dependency vulnerability scanning (via pip-audit)?
default: yes

use_github_actions:
type: bool
when: false
help: Do you want to include Github Actions workflows for build and testnet deployment?
default: |-
yes

use_pre_commit:
type: bool
when: false
help: Do you want to include pre-commit for linting, type checking and formatting?
default: |-
yes

use_dispenser:
type: bool
when: false
help: Do you want to fund your deployment account using an optional dispenser account?
default: no

Expand Down
1 change: 1 addition & 0 deletions includes/contract_name_kebab.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{- contract_name.split('_')|join('-') -}}
1 change: 1 addition & 0 deletions includes/contract_name_pascal.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{- contract_name.split('_')|map('capitalize')|join -}}
2 changes: 1 addition & 1 deletion template_content/.algokit.toml.jinja
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[algokit]
min_version = "v1.7.3"
min_version = "v1.8.0"

[deploy]
{%- if deployment_language == 'python' %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ from puyapy.arc4 import String, abimethod
class {{ contract_name.split('_')|map('capitalize')|join }}(ARC4Contract):
@abimethod()
def hello(self, name: String) -> String:
return String.encode(Bytes(b"Hello ") + name.decode())
return String.encode(Bytes(b"Hello, ") + name.decode())
4 changes: 4 additions & 0 deletions template_content/.gitignore.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ coverage.xml
.hypothesis/
.pytest_cache/
cover/
coverage/

# Translations
*.mo
Expand Down Expand Up @@ -172,3 +173,6 @@ cython_debug/

# NPM
node_modules

# AlgoKit
debug_traces/
13 changes: 13 additions & 0 deletions template_content/README.md.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,19 @@ This project has been generated using AlgoKit. See below for default getting sta

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:
{%- if deployment_language == 'python' %}
Refer to the commented header in the `__main__.py` file in the `smart_contracts` folder.
{%- elif deployment_language == 'typescript' %}
Refer to the commented header in the `index.ts` file in the `smart_contracts` folder.
{%- endif %}

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
Expand Down
4 changes: 2 additions & 2 deletions template_content/pyproject.toml.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ readme = "README.md"

[tool.poetry.dependencies]
python = "^3.12"
algokit-utils = "^2.0.1"
algokit-utils = "^2.2.0"
python-dotenv = "^1.0.0"
puya = "^0.1.3"
puya = "^0.2.0"

[tool.poetry.group.dev.dependencies]
{% if use_python_black -%}
Expand Down
11 changes: 7 additions & 4 deletions template_content/smart_contracts/__main__.py.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ from pathlib import Path
from dotenv import load_dotenv

from smart_contracts.config import contracts
{% if deployment_language == 'python' -%}
from smart_contracts.helpers.build import build
{% if deployment_language == 'python' -%}
from smart_contracts.helpers.deploy import deploy
{%- elif deployment_language == 'typescript' -%}
from smart_contracts.helpers.build import build
{%- endif %}

# Uncomment the following lines to enable auto generation of AVM Debugger compliant sourcemap and simulation trace file.
# Learn more about using AlgoKit AVM Debugger to debug your TEAL source codes and inspect various kinds of
# Algorand transactions in atomic groups -> https://github.com/algorandfoundation/algokit-avm-vscode-debugger
# from algokit_utils.config import config
# config.configure(debug=True, trace_all=True)
{%- endif %}
logging.basicConfig(
level=logging.DEBUG, format="%(asctime)s %(levelname)-10s: %(message)s"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ import * as path from 'path'
import { consoleLogger } from '@algorandfoundation/algokit-utils/types/logging'
import * as algokit from '@algorandfoundation/algokit-utils'

// Uncomment the debug and traceAll options to enable auto generation of AVM Debugger compliant sourceMap and simulation trace file.
// Learn more about using AlgoKit AVM Debugger to debug your TEAL source codes and inspect various kinds of Algorand transactions in atomic groups -> https://github.com/algorandfoundation/algokit-avm-vscode-Debugger

algokit.Config.configure({
logger: consoleLogger,
// debug: true,
// traceAll: true,
})

// base directory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ from puyapy.arc4 import String, abimethod
class {{ contract_name.split('_')|map('capitalize')|join }}(ARC4Contract):
@abimethod()
def hello(self, name: String) -> String:
return String.encode(Bytes(b"Hello ") + name.decode())
return String.encode(Bytes(b"Hello, ") + name.decode())
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ def deploy(
deployer: algokit_utils.Account,
) -> None:
from smart_contracts.artifacts.{{ contract_name }}.client import (
{{ contract_name.split('_')|map('capitalize')|join }}Client,
{% include pathjoin('includes', 'contract_name_pascal.jinja') %}Client,
)

app_client = {{ contract_name.split('_')|map('capitalize')|join }}Client(
app_client = {% include pathjoin('includes', 'contract_name_pascal.jinja') %}Client(
algod_client,
creator=deployer,
indexer_client=indexer_client,
)

app_client.deploy(
on_schema_break=algokit_utils.OnSchemaBreak.AppendApp,
on_update=algokit_utils.OnUpdate.AppendApp,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export async function deploy() {
},
algod,
)
const appClient = new {{ contract_name.split('_')|map('capitalize')|join }}Client(
const appClient = new {% include pathjoin('includes', 'contract_name_pascal.jinja') %}Client(
{
resolveBy: 'creatorAndName',
findExistingUsing: indexer,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "smart_contracts",
"version": "1.0.0",
"description": "Smart contract deployer",
"main": "index.ts",
"scripts": {
"deploy": "ts-node-dev --transpile-only --watch .env -r dotenv/config smart_contracts/index.ts",
"deploy:ci": "ts-node --transpile-only -r dotenv/config smart_contracts/index.ts",
{%- if use_typescript_jest %}
"test": "jest --coverage",
{%- endif %}
"format": "prettier --write ."
},
"engines": {
"node": ">=18.0"
},
"dependencies": {
"@algorandfoundation/algokit-utils": "^5.1.0",
"algosdk": "^2.5.0"
},
"devDependencies": {
{%- if use_typescript_jest %}
"@types/jest": "^29.5.11",
{%- endif %}
"dotenv": "^16.0.3",
"prettier": "^2.8.4",
{%- if use_typescript_jest %}
"ts-jest": "^29.1.1",
{%- endif %}
"ts-node-dev": "^2.0.0",
"typescript": "^4.9.5"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ES2020",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"allowJs": false,
"allowSyntheticDefaultImports": true,
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist", "coverage"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"esbenp.prettier-vscode",
{% endif -%}
"tamasfe.even-better-toml",
"editorconfig.editorconfig"
"editorconfig.editorconfig",
"algorandfoundation.algokit-avm-vscode-debugger"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@
"module": "smart_contracts",
"args": ["build"],
"cwd": "${workspaceFolder}"
},
{
"type": "avm",
"request": "launch",
"name": "Debug TEAL via AlgoKit AVM Debugger",
"simulateTraceFile": "${workspaceFolder}/${command:PickSimulateTraceFile}",
"stopOnEntry": true
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import pytest
from algokit_utils import (
get_algod_client,
get_indexer_client,
is_localnet,
)
from algosdk.v2client.algod import AlgodClient
from algosdk.v2client.indexer import IndexerClient
from dotenv import load_dotenv


Expand All @@ -23,3 +25,8 @@ def algod_client() -> AlgodClient:
# included here to prevent accidentally running against other networks
assert is_localnet(client)
return client


@pytest.fixture(scope="session")
def indexer_client() -> IndexerClient:
return get_indexer_client()
Original file line number Diff line number Diff line change
@@ -1,32 +1,48 @@
import algokit_utils
import pytest
from algokit_utils import get_localnet_default_account
from algokit_utils.config import config
from algosdk.v2client.algod import AlgodClient
from algosdk.v2client.indexer import IndexerClient

from smart_contracts.artifacts.{{ contract_name }}.client import {{ contract_name.split('_')|map('capitalize')|join }}Client
from smart_contracts.artifacts.{{ contract_name }}.client import {% include pathjoin('includes', 'contract_name_pascal.jinja') %}Client


@pytest.fixture(scope="session")
def {{ contract_name }}_client(
algod_client: AlgodClient, indexer_client: IndexerClient
) -> {{ contract_name.split('_')|map('capitalize')|join }}Client:
client = {{ contract_name.split('_')|map('capitalize')|join }}Client(
) -> {% include pathjoin('includes', 'contract_name_pascal.jinja') %}Client:
config.configure(
debug=True,
# trace_all=True,
)

client = {% include pathjoin('includes', 'contract_name_pascal.jinja') %}Client(
algod_client,
creator=get_localnet_default_account(algod_client),
indexer_client=indexer_client,
)

client.deploy(
on_schema_break=algokit_utils.OnSchemaBreak.ReplaceApp,
on_update=algokit_utils.OnUpdate.UpdateApp,
allow_delete=True,
allow_update=True,
on_schema_break=algokit_utils.OnSchemaBreak.AppendApp,
on_update=algokit_utils.OnUpdate.AppendApp,
)
return client


def test_says_hello({{ contract_name }}_client: {{ contract_name.split('_')|map('capitalize')|join }}Client) -> None:
def test_says_hello({{ contract_name }}_client: {% include pathjoin('includes', 'contract_name_pascal.jinja') %}Client) -> None:
result = {{ contract_name }}_client.hello(name="World")

assert result.return_value == "Hello, World"


def test_simulate_says_hello_with_correct_budget_consumed(
{{ contract_name }}_client: {% include pathjoin('includes', 'contract_name_pascal.jinja') %}Client, algod_client: AlgodClient
) -> None:
result = (
{{ contract_name }}_client.compose().hello(name="World").hello(name="Jane").simulate()
)

assert result.abi_results[0].return_value == "Hello, World"
assert result.abi_results[1].return_value == "Hello, Jane"
assert result.simulate_response["txn-groups"][0]["app-budget-consumed"] < 100
Loading