From 94d1525f91bca6f525c9c927bf952a5da56ae742 Mon Sep 17 00:00:00 2001 From: Elton Baidoo Date: Sat, 6 Sep 2025 13:49:22 -0400 Subject: [PATCH 01/16] Initial algokit setup --- ogc-contracts/.algokit.toml | 10 + .../create-devcontainer/copier.yaml | 4 + .../create-devcontainer/devcontainer.json | 19 + ogc-contracts/.editorconfig | 10 + ogc-contracts/.gitattributes | 1 + ogc-contracts/.gitignore | 170 ++++ ogc-contracts/.vscode/settings.json | 5 + ogc-contracts/README.md | 22 + ogc-contracts/ogc-contracts.code-workspace | 32 + ogc-contracts/projects/.gitkeep | 0 .../projects/ogc-contracts/.algokit.toml | 46 ++ .../.algokit/.copier-answers.yml | 10 + .../generators/create_contract/copier.yaml | 10 + .../{{ contract_name }}/contract.py.j2 | 9 + .../{{ contract_name }}/deploy_config.py.j2 | 44 ++ .../generators/create_env_file/copier.yaml | 49 ++ ... %}.env.{{custom_network_name}}{% endif %} | 7 + ... if is_localnet %}.env.localnet{% endif %} | 7 + ...{% if is_mainnet %}.env.mainnet{% endif %} | 3 + ...{% if is_testnet %}.env.testnet{% endif %} | 3 + .../projects/ogc-contracts/.editorconfig | 10 + .../projects/ogc-contracts/.gitignore | 180 +++++ ...ing-started-with-your-algokit-project.tour | 51 ++ .../ogc-contracts/.vscode/extensions.json | 9 + .../ogc-contracts/.vscode/launch.json | 52 ++ .../ogc-contracts/.vscode/settings.json | 34 + .../projects/ogc-contracts/.vscode/tasks.json | 79 ++ .../projects/ogc-contracts/README.md | 101 +++ .../projects/ogc-contracts/poetry.lock | 727 ++++++++++++++++++ .../projects/ogc-contracts/poetry.toml | 3 + .../projects/ogc-contracts/pyproject.toml | 23 + .../ogc-contracts/smart_contracts/__init__.py | 0 .../ogc-contracts/smart_contracts/__main__.py | 211 +++++ .../smart_contracts/ogc_vault/contract.py | 8 + .../ogc_vault/deploy_config.py | 44 ++ 35 files changed, 1993 insertions(+) create mode 100644 ogc-contracts/.algokit.toml create mode 100644 ogc-contracts/.algokit/generators/create-devcontainer/copier.yaml create mode 100644 ogc-contracts/.algokit/generators/create-devcontainer/devcontainer.json create mode 100644 ogc-contracts/.editorconfig create mode 100644 ogc-contracts/.gitattributes create mode 100644 ogc-contracts/.gitignore create mode 100644 ogc-contracts/.vscode/settings.json create mode 100644 ogc-contracts/README.md create mode 100644 ogc-contracts/ogc-contracts.code-workspace create mode 100644 ogc-contracts/projects/.gitkeep create mode 100644 ogc-contracts/projects/ogc-contracts/.algokit.toml create mode 100644 ogc-contracts/projects/ogc-contracts/.algokit/.copier-answers.yml create mode 100644 ogc-contracts/projects/ogc-contracts/.algokit/generators/create_contract/copier.yaml create mode 100644 ogc-contracts/projects/ogc-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 create mode 100644 ogc-contracts/projects/ogc-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 create mode 100644 ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/copier.yaml create mode 100644 ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/{% if is_customnet %}.env.{{custom_network_name}}{% endif %} create mode 100644 ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/{% if is_localnet %}.env.localnet{% endif %} create mode 100644 ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/{% if is_mainnet %}.env.mainnet{% endif %} create mode 100644 ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/{% if is_testnet %}.env.testnet{% endif %} create mode 100644 ogc-contracts/projects/ogc-contracts/.editorconfig create mode 100644 ogc-contracts/projects/ogc-contracts/.gitignore create mode 100644 ogc-contracts/projects/ogc-contracts/.tours/getting-started-with-your-algokit-project.tour create mode 100644 ogc-contracts/projects/ogc-contracts/.vscode/extensions.json create mode 100644 ogc-contracts/projects/ogc-contracts/.vscode/launch.json create mode 100644 ogc-contracts/projects/ogc-contracts/.vscode/settings.json create mode 100644 ogc-contracts/projects/ogc-contracts/.vscode/tasks.json create mode 100644 ogc-contracts/projects/ogc-contracts/README.md create mode 100644 ogc-contracts/projects/ogc-contracts/poetry.lock create mode 100644 ogc-contracts/projects/ogc-contracts/poetry.toml create mode 100644 ogc-contracts/projects/ogc-contracts/pyproject.toml create mode 100644 ogc-contracts/projects/ogc-contracts/smart_contracts/__init__.py create mode 100644 ogc-contracts/projects/ogc-contracts/smart_contracts/__main__.py create mode 100644 ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/contract.py create mode 100644 ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/deploy_config.py diff --git a/ogc-contracts/.algokit.toml b/ogc-contracts/.algokit.toml new file mode 100644 index 0000000..885e1fd --- /dev/null +++ b/ogc-contracts/.algokit.toml @@ -0,0 +1,10 @@ +[algokit] +min_version = "v1.12.1" + +[project] +type = 'workspace' +projects_root_path = 'projects' + +[generate.devcontainer] +description = "Generate a default 'devcontainer.json' configuration that pre-installs algokit and launches Algorand sandbox as part of codespace container provisioning." +path = ".algokit/generators/create-devcontainer" diff --git a/ogc-contracts/.algokit/generators/create-devcontainer/copier.yaml b/ogc-contracts/.algokit/generators/create-devcontainer/copier.yaml new file mode 100644 index 0000000..e98f334 --- /dev/null +++ b/ogc-contracts/.algokit/generators/create-devcontainer/copier.yaml @@ -0,0 +1,4 @@ +_tasks: + - "echo '==== Successfully generated new .devcontainer.json file ๐Ÿš€ ===='" + +_templates_suffix: ".j2" diff --git a/ogc-contracts/.algokit/generators/create-devcontainer/devcontainer.json b/ogc-contracts/.algokit/generators/create-devcontainer/devcontainer.json new file mode 100644 index 0000000..6452c65 --- /dev/null +++ b/ogc-contracts/.algokit/generators/create-devcontainer/devcontainer.json @@ -0,0 +1,19 @@ +{ + "forwardPorts": [4001, 4002, 8980, 5173], + "portsAttributes": { + "4001": { + "label": "algod" + }, + "4002": { + "label": "kmd" + }, + "8980": { + "label": "indexer" + }, + "5173": { + "label": "vite" + } + }, + "postCreateCommand": "mkdir -p ~/.config/algokit && pipx install algokit && sudo chown -R codespace:codespace ~/.config/algokit", + "postStartCommand": "for i in {1..5}; do algokit localnet status > /dev/null 2>&1 && break || sleep 30; algokit localnet reset; done" +} diff --git a/ogc-contracts/.editorconfig b/ogc-contracts/.editorconfig new file mode 100644 index 0000000..5e550a1 --- /dev/null +++ b/ogc-contracts/.editorconfig @@ -0,0 +1,10 @@ +[*] +charset = utf-8 +insert_final_newline = true +end_of_line = lf +indent_style = space +indent_size = 2 +tab_width = 2 +max_line_length = 140 +trim_trailing_whitespace = true +single_quote = true diff --git a/ogc-contracts/.gitattributes b/ogc-contracts/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/ogc-contracts/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/ogc-contracts/.gitignore b/ogc-contracts/.gitignore new file mode 100644 index 0000000..4105eb2 --- /dev/null +++ b/ogc-contracts/.gitignore @@ -0,0 +1,170 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Ruff (linter) +.ruff_cache/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +.idea/ +!.idea/runConfigurations + +# macOS +.DS_Store + +# Received approval test files +*.received.* + +# NPM +node_modules + diff --git a/ogc-contracts/.vscode/settings.json b/ogc-contracts/.vscode/settings.json new file mode 100644 index 0000000..033fd10 --- /dev/null +++ b/ogc-contracts/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + // Disabled due to matangover.mypy extension not supporting monorepos + // To be addressed as part of https://github.com/matangover/mypy-vscode/issues/82 + "mypy.enabled": false +} diff --git a/ogc-contracts/README.md b/ogc-contracts/README.md new file mode 100644 index 0000000..9786725 --- /dev/null +++ b/ogc-contracts/README.md @@ -0,0 +1,22 @@ +# ogc-contracts + +Welcome to your new AlgoKit project! + +This is your workspace root. A `workspace` in AlgoKit is an orchestrated collection of standalone projects (backends, smart contracts, frontend apps and etc). + +By default, `projects_root_path` parameter is set to `projects`. Which instructs AlgoKit CLI to create a new directory under `projects` directory when new project is instantiated via `algokit init` at the root of the workspace. + +## Getting Started + +To get started refer to `README.md` files in respective sub-projects in the `projects` directory. + +To learn more about algokit, visit [documentation](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/algokit.md). + +### GitHub Codespaces + +To get started execute: + +1. `algokit generate devcontainer` - invoking this command from the root of this repository will create a `devcontainer.json` file with all the configuration needed to run this project in a GitHub codespace. [Run the repository inside a codespace](https://docs.github.com/en/codespaces/getting-started/quickstart) to get started. +2. `algokit init` - invoke this command inside a github codespace to launch an interactive wizard to guide you through the process of creating a new AlgoKit project + +Powered by [Copier templates](https://copier.readthedocs.io/en/stable/). diff --git a/ogc-contracts/ogc-contracts.code-workspace b/ogc-contracts/ogc-contracts.code-workspace new file mode 100644 index 0000000..09ecd5c --- /dev/null +++ b/ogc-contracts/ogc-contracts.code-workspace @@ -0,0 +1,32 @@ +{ + "folders": [ + { + "path": "./", + "name": "ROOT" + }, + { + "path": "projects/ogc-contracts" + } + ], + "settings": { + "files.exclude": { + "projects/": true + }, + "jest.disabledWorkspaceFolders": [ + "ROOT", + "projects" + ] + }, + "extensions": { + "recommendations": [ + "joshx.workspace-terminals" + ] + }, + "tasks": { + "version": "2.0.0", + "tasks": [] + }, + "launch": { + "configurations": [] + } +} \ No newline at end of file diff --git a/ogc-contracts/projects/.gitkeep b/ogc-contracts/projects/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/ogc-contracts/projects/ogc-contracts/.algokit.toml b/ogc-contracts/projects/ogc-contracts/.algokit.toml new file mode 100644 index 0000000..1631bd5 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.algokit.toml @@ -0,0 +1,46 @@ +[algokit] +min_version = "v2.0.0" + +[generate.smart-contract] +description = "Generate a new smart contract for existing project" +path = ".algokit/generators/create_contract" + +[generate.env-file] +description = "Generate a new generic or Algorand network specific .env file" +path = ".algokit/generators/create_env_file" + +[project] +type = 'contract' +name = 'ogc-contracts' +artifacts = 'smart_contracts/artifacts' + +[project.deploy] +command = "poetry run python -m smart_contracts deploy" + +[project.deploy.testnet] +environment_secrets = [ + "DEPLOYER_MNEMONIC", +] + +[project.deploy.mainnet] +environment_secrets = [ + "DEPLOYER_MNEMONIC", +] + +[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/ogc-contracts/projects/ogc-contracts/.algokit/.copier-answers.yml b/ogc-contracts/projects/ogc-contracts/.algokit/.copier-answers.yml new file mode 100644 index 0000000..f5b6255 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.algokit/.copier-answers.yml @@ -0,0 +1,10 @@ +# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY +_commit: 1.6.3 +_src_path: gh:algorandfoundation/algokit-python-template +author_email: baidooelton76@gmail.com +author_name: Elton Baidoo +contract_name: ogc_vault +deployment_language: python +preset_name: starter +project_name: ogc-contracts + diff --git a/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_contract/copier.yaml b/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_contract/copier.yaml new file mode 100644 index 0000000..73805de --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_contract/copier.yaml @@ -0,0 +1,10 @@ +_tasks: + - "echo '==== Successfully initialized new smart contract ๐Ÿš€ ===='" + +contract_name: + type: str + help: Name of your new contract. + placeholder: "my-new-contract" + default: "my-new-contract" + +_templates_suffix: ".j2" diff --git a/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 b/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 new file mode 100644 index 0000000..829e3a0 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.py.j2 @@ -0,0 +1,9 @@ +# pyright: reportMissingModuleSource=false +from algopy import ARC4Contract, String +from algopy.arc4 import abimethod + + +class {{ contract_name.split('_')|map('capitalize')|join }}(ARC4Contract): + @abimethod() + def hello(self, name: String) -> String: + return "Hello, " + name diff --git a/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 b/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 new file mode 100644 index 0000000..a2ff46d --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy_config.py.j2 @@ -0,0 +1,44 @@ +import logging + +import algokit_utils + +logger = logging.getLogger(__name__) + + +# define deployment behaviour based on supplied app spec +def deploy() -> None: + from smart_contracts.artifacts.{{ contract_name }}.{{ contract_name }}_client import ( + {{ contract_name.split('_')|map('capitalize')|join }}Factory, + HelloArgs, + ) + + algorand = algokit_utils.AlgorandClient.from_environment() + deployer_ = algorand.account.from_environment("DEPLOYER") + + factory = algorand.client.get_typed_app_factory( + {{ contract_name.split('_')|map('capitalize')|join }}Factory, default_sender=deployer_.address + ) + + app_client, result = factory.deploy( + on_update=algokit_utils.OnUpdate.AppendApp, + on_schema_break=algokit_utils.OnSchemaBreak.AppendApp, + ) + + if result.operation_performed in [ + algokit_utils.OperationPerformed.Create, + algokit_utils.OperationPerformed.Replace, + ]: + algorand.send.payment( + algokit_utils.PaymentParams( + amount=algokit_utils.AlgoAmount(algo=1), + sender=deployer_.address, + receiver=app_client.app_address, + ) + ) + + name = "world" + response = app_client.send.hello(args=HelloArgs(name=name)) + logger.info( + f"Called hello on {app_client.app_name} ({app_client.app_id}) " + f"with name={name}, received: {response.abi_return}" + ) diff --git a/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/copier.yaml b/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/copier.yaml new file mode 100644 index 0000000..afa2cac --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/copier.yaml @@ -0,0 +1,49 @@ +_tasks: + - "echo '==== Successfully generated new .env file ๐Ÿš€ ===='" + +target_network: + type: str + help: Name of your target network. + choices: + - mainnet + - testnet + - localnet + - custom + default: "localnet" + when: "{{ not use_generic_env }}" + +custom_network_name: + type: str + help: Name of your custom Algorand network. + placeholder: "custom" + when: "{{ not use_generic_env and target_network == 'custom' }}" + +is_localnet: + type: bool + help: Whether to deploy on localnet. + placeholder: "true" + default: "{{ target_network == 'localnet' and not use_generic_env }}" + when: 'false' + +is_testnet: + type: bool + help: Whether to deploy on testnet. + placeholder: "true" + default: "{{ target_network == 'testnet' and not use_generic_env }}" + when: 'false' + +is_mainnet: + type: bool + help: Whether to deploy on mainnet. + placeholder: "true" + default: "{{ target_network == 'mainnet' and not use_generic_env }}" + when: 'false' + +is_customnet: + type: bool + help: Whether to deploy on custom network. + placeholder: "true" + default: "{{ target_network == 'custom' and not use_generic_env }}" + when: 'false' + +_templates_suffix: ".j2" diff --git a/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/{% if is_customnet %}.env.{{custom_network_name}}{% endif %} b/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/{% if is_customnet %}.env.{{custom_network_name}}{% endif %} new file mode 100644 index 0000000..cfc9f21 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/{% if is_customnet %}.env.{{custom_network_name}}{% endif %} @@ -0,0 +1,7 @@ +# this file contains algorand network settings for interacting with testnet via algonode +ALGOD_TOKEN={YOUR_ALGOD_TOKEN} +ALGOD_SERVER={YOUR_ALGOD_SERVER_URL} +ALGOD_PORT={YOUR_ALGOD_PORT} +INDEXER_TOKEN={YOUR_INDEXER_TOKEN} +INDEXER_SERVER={YOUR_INDEXER_SERVER_URL} +INDEXER_PORT={YOUR_INDEXER_PORT} diff --git a/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/{% if is_localnet %}.env.localnet{% endif %} b/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/{% if is_localnet %}.env.localnet{% endif %} new file mode 100644 index 0000000..fcbf442 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/{% if is_localnet %}.env.localnet{% endif %} @@ -0,0 +1,7 @@ +# this file should contain environment variables specific to algokit localnet +ALGOD_TOKEN=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +ALGOD_SERVER=http://localhost +ALGOD_PORT=4001 +INDEXER_TOKEN=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +INDEXER_SERVER=http://localhost +INDEXER_PORT=8980 diff --git a/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/{% if is_mainnet %}.env.mainnet{% endif %} b/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/{% if is_mainnet %}.env.mainnet{% endif %} new file mode 100644 index 0000000..bb9a787 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/{% if is_mainnet %}.env.mainnet{% endif %} @@ -0,0 +1,3 @@ +# this file contains algorand network settings for interacting with testnet via algonode +ALGOD_SERVER=https://mainnet-api.algonode.cloud +INDEXER_SERVER=https://mainnet-idx.algonode.cloud diff --git a/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/{% if is_testnet %}.env.testnet{% endif %} b/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/{% if is_testnet %}.env.testnet{% endif %} new file mode 100644 index 0000000..eeea43d --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.algokit/generators/create_env_file/{% if is_testnet %}.env.testnet{% endif %} @@ -0,0 +1,3 @@ +# this file contains algorand network settings for interacting with testnet via algonode +ALGOD_SERVER=https://testnet-api.algonode.cloud +INDEXER_SERVER=https://testnet-idx.algonode.cloud diff --git a/ogc-contracts/projects/ogc-contracts/.editorconfig b/ogc-contracts/projects/ogc-contracts/.editorconfig new file mode 100644 index 0000000..e2fda34 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.editorconfig @@ -0,0 +1,10 @@ +root=true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true + +[*.py] +indent_size = 4 diff --git a/ogc-contracts/projects/ogc-contracts/.gitignore b/ogc-contracts/projects/ogc-contracts/.gitignore new file mode 100644 index 0000000..070829c --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.gitignore @@ -0,0 +1,180 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ +coverage/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +.env.* +!.env.*.template +!.env.template +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Ruff (linter) +.ruff_cache/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +.idea +!.idea/ +.idea/* +!.idea/runConfigurations/ + +# macOS +.DS_Store + +# Received approval test files +*.received.* + +# NPM +node_modules + +# AlgoKit +debug_traces/ +.algokit/static-analysis/ # Replace with .algokit/static-analysis/tealer/ to enable snapshot checks in CI +.algokit/sources diff --git a/ogc-contracts/projects/ogc-contracts/.tours/getting-started-with-your-algokit-project.tour b/ogc-contracts/projects/ogc-contracts/.tours/getting-started-with-your-algokit-project.tour new file mode 100644 index 0000000..728218d --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.tours/getting-started-with-your-algokit-project.tour @@ -0,0 +1,51 @@ +{ + "$schema": "https://aka.ms/codetour-schema", + "title": "Getting Started with Your AlgoKit Project", + "steps": [ + { + "file": "README.md", + "description": "Welcome to your brand new AlgoKit template-based project. In this tour, we will guide you through the main features and capabilities included in the template.", + "line": 3 + }, + { + "file": "README.md", + "description": "Start by ensuring you have followed the setup of pre-requisites.", + "line": 9 + }, + { + "file": ".algokit.toml", + "description": "This is the main configuration file used by algokit-cli to manage the project. The default template includes a starter 'Hello World' contract that is deployed via the `algokit-utils` package (either `ts` or `py`, depending on your choice). To create a new smart contract, you can use the [`algokit generate`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/generate.md) command and invoke a pre-bundled generator template by running `algokit generate smart-contract` (see how it is defined in the `.algokit.toml`, you can create your own generators if needed). This action will create a new folder in the `smart_contracts` directory, named after your project. Each folder contains a `contract.py` file, which is the entry point for your contract implementation, and `deploy_config.py` | `deployConfig.ts` files (depending on the language chosen for the template), that perform the deployment of the contract. Additionally you can define custom commands to run (similar to `npm` scripts), see definitions under `[project]` section in `.algokit.toml`.", + "line": 1 + }, + { + "file": "smart_contracts/hello_world/deploy_config.py", + "description": "The default deployment scripts invoke a sample method on the starter contract that demonstrates how to interact with your deployed Algorand on-chain applications using the [`AlgoKit Typed Clients`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/generate.md#1-typed-clients) feature. The invocation if deploy is aliased in `.algokit.toml` file, allowing simple deployments via `algokit project deploy` command.", + "line": 32 + }, + { + "file": ".env.localnet.template", + "description": "Environment files are a crucial mechanism that allows you to set up the [`algokit deploy`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/deploy.md) feature to simplify deploying your contracts in CI/CD environments (please note we still recommend careful evaluation when it comes to deployment to MainNet). Clone the file and remove the `.template` suffix to apply the changes to deployment scripts and launch configurations. The network prefix `localnet|testnet|mainnet` is primarily optimized for `algokit deploy`. The order of loading the variables is `.env.{network}` < `.env`.", + "line": 2 + }, + { + "file": ".vscode/launch.json", + "description": "Refer to the pre-bundled Visual Studio launch configurations, offering various options on how to execute the build and deployment of your smart contracts. Alternatively execute `algokit project run` to see list of available custom commands.", + "line": 5 + }, + { + "file": ".vscode/extensions.json", + "description": "We highly recommend installing the recommended extensions to get the most out of this template starter project in your VSCode IDE.", + "line": 3 + }, + { + "file": "smart_contracts/__main__.py", + "description": "Uncomment the following lines to enable complementary utilities that will generate artifacts required for the [AlgoKit AVM Debugger](https://github.com/algorandfoundation/algokit-avm-vscode-debugger) VSCode plugin available on the [VSCode Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=algorandfoundation.algokit-avm-vscode-debugger). A new folder will be automatically created in the `.algokit` directory with source maps of all TEAL contracts in this workspace, as well as traces that will appear in a folder at the root of the workspace. You can then use the traces as entry points to trigger the debug extension. Make sure to have the `.algokit.toml` file available at the root of the workspace.", + "line": 15 + }, + { + "file": "smart_contracts/_helpers/__init__.py", + "description": "This folder contains helper scripts for contract management. These automate tasks like compiling, generating clients, and deploying. Usually, you won't need to edit these files, but advanced users can expand them for custom needs.", + "line": 1 + } + ] +} diff --git a/ogc-contracts/projects/ogc-contracts/.vscode/extensions.json b/ogc-contracts/projects/ogc-contracts/.vscode/extensions.json new file mode 100644 index 0000000..e16b76b --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "ms-python.python", + "tamasfe.even-better-toml", + "editorconfig.editorconfig", + "vsls-contrib.codetour", + "algorandfoundation.algokit-avm-vscode-debugger" + ] +} diff --git a/ogc-contracts/projects/ogc-contracts/.vscode/launch.json b/ogc-contracts/projects/ogc-contracts/.vscode/launch.json new file mode 100644 index 0000000..dbaea6e --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.vscode/launch.json @@ -0,0 +1,52 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Build & Deploy contracts", + "type": "python", + "request": "launch", + "module": "smart_contracts", + "cwd": "${workspaceFolder}", + "preLaunchTask": "Start AlgoKit LocalNet", + "env": { + "ALGOD_TOKEN": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ALGOD_SERVER": "http://localhost", + "ALGOD_PORT": "4001", + "INDEXER_TOKEN": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "INDEXER_SERVER": "http://localhost", + "INDEXER_PORT": "8980" + } + }, + { + "name": "Deploy contracts", + "type": "python", + "request": "launch", + "module": "smart_contracts", + "args": ["deploy"], + "cwd": "${workspaceFolder}", + "env": { + "ALGOD_TOKEN": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ALGOD_SERVER": "http://localhost", + "ALGOD_PORT": "4001", + "INDEXER_TOKEN": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "INDEXER_SERVER": "http://localhost", + "INDEXER_PORT": "8980" + } + }, + { + "name": "Build contracts", + "type": "python", + "request": "launch", + "module": "smart_contracts", + "args": ["build"], + "cwd": "${workspaceFolder}" + }, + { + "type": "avm", + "request": "launch", + "name": "Debug TEAL via AlgoKit AVM Debugger", + "simulateTraceFile": "${workspaceFolder}/${command:PickSimulateTraceFile}", + "stopOnEntry": true + } + ] +} diff --git a/ogc-contracts/projects/ogc-contracts/.vscode/settings.json b/ogc-contracts/projects/ogc-contracts/.vscode/settings.json new file mode 100644 index 0000000..bbfb30d --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.vscode/settings.json @@ -0,0 +1,34 @@ +{ + // General - see also /.editorconfig + "editor.formatOnSave": true, + "files.exclude": { + "**/.git": true, + "**/.DS_Store": true, + "**/Thumbs.db": true, + ".mypy_cache": true, + ".pytest_cache": true, + ".ruff_cache": true, + "**/__pycache__": true, + ".idea": true + }, + + // Python + "python.analysis.autoImportCompletions": true, + "python.analysis.extraPaths": ["${workspaceFolder}/smart_contracts"], + "python.analysis.diagnosticSeverityOverrides": { + "reportMissingModuleSource": "none" + }, + "python.defaultInterpreterPath": "${workspaceFolder}/.venv", + "[python]": { + "editor.codeActionsOnSave": { + "source.fixAll": "explicit", + // Prevent default import sorting from running; Ruff will sort imports for us anyway + "source.organizeImports": "never" + }, + "editor.defaultFormatter": null, + }, + + // On Windows, if execution policy is set to Signed (default) then it won't be able to activate the venv + // so instead let's set it to RemoteSigned for VS Code terminal + "terminal.integrated.shellArgs.windows": ["-ExecutionPolicy", "RemoteSigned"], +} diff --git a/ogc-contracts/projects/ogc-contracts/.vscode/tasks.json b/ogc-contracts/projects/ogc-contracts/.vscode/tasks.json new file mode 100644 index 0000000..eb1e767 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/.vscode/tasks.json @@ -0,0 +1,79 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build contracts", + "command": "${workspaceFolder}/.venv/bin/python", + "windows": { + "command": "${workspaceFolder}/.venv/Scripts/python.exe" + }, + "args": ["-m", "smart_contracts", "build"], + "options": { + "cwd": "${workspaceFolder}" + }, + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [] + }, + { + "label": "Build contracts (+ LocalNet)", + "command": "${workspaceFolder}/.venv/bin/python", + "windows": { + "command": "${workspaceFolder}/.venv/Scripts/python.exe" + }, + "args": ["-m", "smart_contracts", "build"], + "options": { + "cwd": "${workspaceFolder}" + }, + "dependsOn": "Start AlgoKit LocalNet", + "problemMatcher": [] + }, + { + "label": "Start AlgoKit LocalNet", + "command": "algokit", + "args": ["localnet", "start"], + "type": "shell", + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [] + }, + { + "label": "Stop AlgoKit LocalNet", + "command": "algokit", + "args": ["localnet", "stop"], + "type": "shell", + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [] + }, + { + "label": "Reset AlgoKit LocalNet", + "command": "algokit", + "args": ["localnet", "reset"], + "type": "shell", + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [] + }, + { + "label": "Analyze TEAL contracts with AlgoKit Tealer integration", + "command": "algokit", + "args": [ + "task", + "analyze", + "${workspaceFolder}/.algokit", + "--recursive", + "--force" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [] + } + ] +} diff --git a/ogc-contracts/projects/ogc-contracts/README.md b/ogc-contracts/projects/ogc-contracts/README.md new file mode 100644 index 0000000..36ddaaf --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/README.md @@ -0,0 +1,101 @@ +# ogc-contracts + +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) + +> For interactive tour over the codebase, download [vsls-contrib.codetour](https://marketplace.visualstudio.com/items?itemName=vsls-contrib.codetour) extension for VS Code, then open the [`.codetour.json`](./.tours/getting-started-with-your-algokit-project.tour) file in code tour extension. + +### Initial Setup + +#### 1. Clone the Repository +Start by cloning this repository to your local machine. + +#### 2. Install Pre-requisites +Ensure the following pre-requisites are installed and properly configured: + +- **Docker**: Required for running a local Algorand network. [Install Docker](https://www.docker.com/). +- **AlgoKit CLI**: Essential for project setup and operations. Install the latest version from [AlgoKit CLI Installation Guide](https://github.com/algorandfoundation/algokit-cli#install). Verify installation with `algokit --version`, expecting `2.0.0` or later. + +#### 3. Bootstrap Your Local Environment +Run the following commands within the project folder: + +- **Install Poetry**: Required for Python dependency management. [Installation Guide](https://python-poetry.org/docs/#installation). Verify with `poetry -V` to see version `1.2`+. +- **Setup Project**: Execute `algokit project bootstrap all` to install dependencies and setup a Python virtual environment in `.venv`. +- **Configure environment**: Execute `algokit generate env-file -a target_network localnet` to create a `.env.localnet` file with default configuration for `localnet`. +- **Start LocalNet**: Use `algokit localnet start` to initiate a local Algorand network. + +### Development Workflow + +#### Terminal +Directly manage and interact with your project using AlgoKit commands: + +1. **Build Contracts**: `algokit project run build` compiles all smart contracts. You can also specify a specific contract by passing the name of the contract folder as an extra argument. +For example: `algokit project run build -- hello_world` will only build the `hello_world` contract. +2. **Deploy**: Use `algokit project deploy localnet` to deploy contracts to the local network. You can also specify a specific contract by passing the name of the contract folder as an extra argument. +For example: `algokit project deploy localnet -- hello_world` will only deploy the `hello_world` contract. + +#### VS Code +For a seamless experience with breakpoint debugging and other features: + +1. **Open Project**: In VS Code, open the repository root. +2. **Install Extensions**: Follow prompts to install recommended extensions. +3. **Debugging**: + - Use `F5` to start debugging. + - **Windows Users**: Select the Python interpreter at `./.venv/Scripts/python.exe` via `Ctrl/Cmd + Shift + P` > `Python: Select Interpreter` before the first run. + +#### JetBrains IDEs +While primarily optimized for VS Code, JetBrains IDEs are supported: + +1. **Open Project**: In your JetBrains IDE, open the repository root. +2. **Automatic Setup**: The IDE should configure the Python interpreter and virtual environment. +3. **Debugging**: Use `Shift+F10` or `Ctrl+R` to start debugging. Note: Windows users may encounter issues with pre-launch tasks due to a known bug. See [JetBrains forums](https://youtrack.jetbrains.com/issue/IDEA-277486/Shell-script-configuration-cannot-run-as-before-launch-task) for workarounds. + +## AlgoKit Workspaces and Project Management +This project supports both standalone and monorepo setups through AlgoKit workspaces. Leverage [`algokit project run`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/project/run.md) commands for efficient monorepo project orchestration and management across multiple projects within a workspace. + +## AlgoKit Generators + +This template provides a set of [algokit generators](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/generate.md) that allow you to further modify the project instantiated from the template to fit your needs, as well as giving you a base to build your own extensions to invoke via the `algokit generate` command. + +### Generate Smart Contract + +By default the template creates a single `HelloWorld` contract under ogc_vault folder in the `smart_contracts` directory. To add a new contract: + +1. From the root of the project (`../`) execute `algokit generate smart-contract`. This will create a new starter smart contract and deployment configuration file under `{your_contract_name}` subfolder in the `smart_contracts` directory. +2. Each contract potentially has different creation parameters and deployment steps. Hence, you need to define your deployment logic in `deploy_config.py`file. +3. `config.py` file will automatically build all contracts in the `smart_contracts` directory. If you want to build specific contracts manually, modify the default code provided by the template in `config.py` file. + +> Please note, above is just a suggested convention tailored for the base configuration and structure of this template. The default code supplied by the template in `config.py` and `index.ts` (if using ts clients) files are tailored for the suggested convention. You are free to modify the structure and naming conventions as you see fit. + +### Generate '.env' files + +By default the template instance does not contain any env files. Using [`algokit project deploy`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/project/deploy.md) against `localnet` | `testnet` | `mainnet` will use default values for `algod` and `indexer` unless overwritten via `.env` or `.env.{target_network}`. + +To generate a new `.env` or `.env.{target_network}` file, run `algokit generate env-file` + +### 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). + +# 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://dev.algorand.co/), [Why Algorand?](https://dev.algorand.co/getting-started/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/ogc-contracts/projects/ogc-contracts/poetry.lock b/ogc-contracts/projects/ogc-contracts/poetry.lock new file mode 100644 index 0000000..a4fe3de --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/poetry.lock @@ -0,0 +1,727 @@ +# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. + +[[package]] +name = "algokit-client-generator" +version = "2.2.0" +description = "Algorand typed client Generator" +optional = false +python-versions = "<4.0,>=3.10" +groups = ["dev"] +files = [ + {file = "algokit_client_generator-2.2.0-py3-none-any.whl", hash = "sha256:f723aa77fb265b38e98836b13b830789c46467ba647815ff4a0a7dee3f64b74a"}, +] + +[package.dependencies] +algokit-utils = ">=4.0.1,<5.0.0" + +[[package]] +name = "algokit-utils" +version = "4.2.0" +description = "Utilities for Algorand development for use by AlgoKit" +optional = false +python-versions = "<4.0,>=3.10" +groups = ["main", "dev"] +files = [ + {file = "algokit_utils-4.2.0-py3-none-any.whl", hash = "sha256:e6adb1779971c5748c5f518b9864e4ca20d80e411c393616ea986bc097df4647"}, +] + +[package.dependencies] +httpx = ">=0.23.1,<=0.28.1" +py-algorand-sdk = ">=2.4.0,<3.0.0" +typing-extensions = ">=4.6.0" + +[[package]] +name = "algorand-python" +version = "2.9.0" +description = "API for writing Algorand Python Smart contracts" +optional = false +python-versions = "<4.0,>=3.12" +groups = ["main"] +files = [ + {file = "algorand_python-2.9.0-py3-none-any.whl", hash = "sha256:37780bcfe8f8fce106a8188d9d643b83d87dd39f5ca3cf3300296b74171af804"}, +] + +[[package]] +name = "algorand-python-testing" +version = "0.5.0" +description = "Algorand Python testing library" +optional = false +python-versions = ">=3.12" +groups = ["main"] +files = [ + {file = "algorand_python_testing-0.5.0-py3-none-any.whl", hash = "sha256:88648812c2faf3a970658b110b4ed0234455b96b3949d32a9981f5066b9dd3e0"}, + {file = "algorand_python_testing-0.5.0.tar.gz", hash = "sha256:d8ecfcfe7e346fcf37bd6d1d5812bc4760395031a1235d215187341cf488ed44"}, +] + +[package.dependencies] +algorand-python = ">=2.0" +coincurve = ">=19.0.1" +ecdsa = ">=0.17.0" +pycryptodomex = ">=3.6.0,<4" +pynacl = ">=1.4.0,<2" + +[[package]] +name = "anyio" +version = "4.10.0" +description = "High-level concurrency and networking framework on top of asyncio or Trio" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1"}, + {file = "anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} + +[package.extras] +trio = ["trio (>=0.26.1)"] + +[[package]] +name = "attrs" +version = "25.3.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, + {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, +] + +[package.extras] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] + +[[package]] +name = "cattrs" +version = "24.1.3" +description = "Composable complex class support for attrs and dataclasses." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "cattrs-24.1.3-py3-none-any.whl", hash = "sha256:adf957dddd26840f27ffbd060a6c4dd3b2192c5b7c2c0525ef1bd8131d8a83f5"}, + {file = "cattrs-24.1.3.tar.gz", hash = "sha256:981a6ef05875b5bb0c7fb68885546186d306f10f0f6718fe9b96c226e68821ff"}, +] + +[package.dependencies] +attrs = ">=23.1.0" + +[package.extras] +bson = ["pymongo (>=4.4.0)"] +cbor2 = ["cbor2 (>=5.4.6)"] +msgpack = ["msgpack (>=1.0.5)"] +msgspec = ["msgspec (>=0.18.5) ; implementation_name == \"cpython\""] +orjson = ["orjson (>=3.9.2) ; implementation_name == \"cpython\""] +pyyaml = ["pyyaml (>=6.0)"] +tomlkit = ["tomlkit (>=0.11.8)"] +ujson = ["ujson (>=5.7.0)"] + +[[package]] +name = "certifi" +version = "2025.8.3" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.7" +groups = ["main", "dev"] +files = [ + {file = "certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"}, + {file = "certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407"}, +] + +[[package]] +name = "cffi" +version = "1.17.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "coincurve" +version = "21.0.0" +description = "Safest and fastest Python library for secp256k1 elliptic curve operations" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "coincurve-21.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:986727bba6cf0c5670990358dc6af9a54f8d3e257979b992a9dbd50dd82fa0dc"}, + {file = "coincurve-21.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1c584059de61ed16c658e7eae87ee488e81438897dae8fabeec55ef408af474"}, + {file = "coincurve-21.0.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4210b35c922b2b36c987a48c0b110ab20e490a2d6a92464ca654cb09e739fcc"}, + {file = "coincurve-21.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf67332cc647ef52ef371679c76000f096843ae266ae6df5e81906eb6463186b"}, + {file = "coincurve-21.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997607a952913c6a4bebe86815f458e77a42467b7a75353ccdc16c3336726880"}, + {file = "coincurve-21.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cfdd0938f284fb147aa1723a69f8794273ec673b10856b6e6f5f63fcc99d0c2e"}, + {file = "coincurve-21.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:88c1e3f6df2f2fbe18152c789a18659ee0429dc604fc77530370c9442395f681"}, + {file = "coincurve-21.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:530b58ed570895612ef510e28df5e8a33204b03baefb5c986e22811fa09622ef"}, + {file = "coincurve-21.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:f920af756a98edd738c0cfa431e81e3109aeec6ffd6dffb5ed4f5b5a37aacba8"}, + {file = "coincurve-21.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:070e060d0d57b496e68e48b39d5e3245681376d122827cb8e09f33669ff8cf1b"}, + {file = "coincurve-21.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:65ec42cab9c60d587fb6275c71f0ebc580625c377a894c4818fb2a2b583a184b"}, + {file = "coincurve-21.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5828cd08eab928db899238874d1aab12fa1236f30fe095a3b7e26a5fc81df0a3"}, + {file = "coincurve-21.0.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54de1cac75182de9f71ce41415faafcaf788303e21cbd0188064e268d61625e5"}, + {file = "coincurve-21.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07cda058d9394bea30d57a92fdc18ee3ca6b5bc8ef776a479a2ffec917105836"}, + {file = "coincurve-21.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9070804d7c71badfe4f0bf19b728cfe7c70c12e733938ead6b1db37920b745c0"}, + {file = "coincurve-21.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:669ab5db393637824b226de058bb7ea0cb9a0236e1842d7b22f74d4a8a1f1ff1"}, + {file = "coincurve-21.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3bcd538af097b3914ec3cb654262e72e224f95f2e9c1eb7fbd75d843ae4e528e"}, + {file = "coincurve-21.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45b6a5e6b5536e1f46f729829d99ce1f8f847308d339e8880fe7fa1646935c10"}, + {file = "coincurve-21.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:87597cf30dfc05fa74218810776efacf8816813ab9fa6ea1490f94e9f8b15e77"}, + {file = "coincurve-21.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:b992d1b1dac85d7f542d9acbcf245667438839484d7f2b032fd032256bcd778e"}, + {file = "coincurve-21.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f60ad56113f08e8c540bb89f4f35f44d434311433195ffff22893ccfa335070c"}, + {file = "coincurve-21.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1cb1cd19fb0be22e68ecb60ad950b41f18b9b02eebeffaac9391dc31f74f08f2"}, + {file = "coincurve-21.0.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05d7e255a697b3475d7ae7640d3bdef3d5bc98ce9ce08dd387f780696606c33b"}, + {file = "coincurve-21.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a366c314df7217e3357bb8c7d2cda540b0bce180705f7a0ce2d1d9e28f62ad4"}, + {file = "coincurve-21.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b04778b75339c6e46deb9ae3bcfc2250fbe48d1324153e4310fc4996e135715"}, + {file = "coincurve-21.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8efcbdcd50cc219989a2662e6c6552f455efc000a15dd6ab3ebf4f9b187f41a3"}, + {file = "coincurve-21.0.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6df44b4e3b7acdc1453ade52a52e3f8a5b53ecdd5a06bd200f1ec4b4e250f7d9"}, + {file = "coincurve-21.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bcc0831f07cb75b91c35c13b1362e7b9dc76c376b27d01ff577bec52005e22a8"}, + {file = "coincurve-21.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:5dd7b66b83b143f3ad3861a68fc0279167a0bae44fe3931547400b7a200e90b1"}, + {file = "coincurve-21.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:78dbe439e8cb22389956a4f2f2312813b4bd0531a0b691d4f8e868c7b366555d"}, + {file = "coincurve-21.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9df5ceb5de603b9caf270629996710cf5ed1d43346887bc3895a11258644b65b"}, + {file = "coincurve-21.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:154467858d23c48f9e5ab380433bc2625027b50617400e2984cc16f5799ab601"}, + {file = "coincurve-21.0.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f57f07c44d14d939bed289cdeaba4acb986bba9f729a796b6a341eab1661eedc"}, + {file = "coincurve-21.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fb03e3a388a93d31ed56a442bdec7983ea404490e21e12af76fb1dbf097082a"}, + {file = "coincurve-21.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d09ba4fd9d26b00b06645fcd768c5ad44832a1fa847ebe8fb44970d3204c3cb7"}, + {file = "coincurve-21.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1a1e7ee73bc1b3bcf14c7b0d1f44e6485785d3b53ef7b16173c36d3cefa57f93"}, + {file = "coincurve-21.0.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ad05952b6edc593a874df61f1bc79db99d716ec48ba4302d699e14a419fe6f51"}, + {file = "coincurve-21.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4d2bf350ced38b73db9efa1ff8fd16a67a1cb35abb2dda50d89661b531f03fd3"}, + {file = "coincurve-21.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:54d9500c56d5499375e579c3917472ffcf804c3584dd79052a79974280985c74"}, + {file = "coincurve-21.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:773917f075ec4b94a7a742637d303a3a082616a115c36568eb6c873a8d950d18"}, + {file = "coincurve-21.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb82ba677fc7600a3bf200edc98f4f9604c317b18c7b3f0a10784b42686e3a53"}, + {file = "coincurve-21.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5001de8324c35eee95f34e011a5c3b4e7d9ae9ca4a862a93b2c89b3f467f511b"}, + {file = "coincurve-21.0.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4d0bb5340bcac695731bef51c3e0126f252453e2d1ae7fa1486d90eff978bf6"}, + {file = "coincurve-21.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a9b49789ff86f3cf86cfc8ff8c6c43bac2607720ec638e8ba471fa7e8765bd2"}, + {file = "coincurve-21.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b85b49e192d2ca1a906a7b978bacb55d4dcb297cc2900fbbd9b9180d50878779"}, + {file = "coincurve-21.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ad6445f0bb61b3a4404d87a857ddb2a74a642cd4d00810237641aab4d6b1a42f"}, + {file = "coincurve-21.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d3f017f1491491f3f2c49e5d2d3a471a872d75117bfcb804d1167061c94bd347"}, + {file = "coincurve-21.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:500e5e38cd4cbc4ea8a5c631ce843b1d52ef19ac41128568214d150f75f1f387"}, + {file = "coincurve-21.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:ef81ca24511a808ad0ebdb8fdaf9c5c87f12f935b3d117acccc6520ad671bcce"}, + {file = "coincurve-21.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:6ec8e859464116a3c90168cd2bd7439527d4b4b5e328b42e3c8e0475f9b0bf71"}, + {file = "coincurve-21.0.0.tar.gz", hash = "sha256:8b37ce4265a82bebf0e796e21a769e56fdbf8420411ccbe3fafee4ed75b6a6e5"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "docstring-parser" +version = "0.17.0" +description = "Parse Python docstrings in reST, Google and Numpydoc format" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708"}, + {file = "docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912"}, +] + +[package.extras] +dev = ["pre-commit (>=2.16.0) ; python_version >= \"3.9\"", "pydoctor (>=25.4.0)", "pytest"] +docs = ["pydoctor (>=25.4.0)"] +test = ["pytest"] + +[[package]] +name = "ecdsa" +version = "0.19.1" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.6" +groups = ["main"] +files = [ + {file = "ecdsa-0.19.1-py2.py3-none-any.whl", hash = "sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3"}, + {file = "ecdsa-0.19.1.tar.gz", hash = "sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "h11" +version = "0.16.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, + {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.16" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<1.0)"] + +[[package]] +name = "httpx" +version = "0.28.1" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" + +[package.extras] +brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +groups = ["main", "dev"] +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "immutabledict" +version = "4.2.1" +description = "Immutable wrapper around dictionaries (a fork of frozendict)" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "immutabledict-4.2.1-py3-none-any.whl", hash = "sha256:c56a26ced38c236f79e74af3ccce53772827cef5c3bce7cab33ff2060f756373"}, + {file = "immutabledict-4.2.1.tar.gz", hash = "sha256:d91017248981c72eb66c8ff9834e99c2f53562346f23e7f51e7a5ebcf66a3bcc"}, +] + +[[package]] +name = "msgpack" +version = "1.1.1" +description = "MessagePack serializer" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "msgpack-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:353b6fc0c36fde68b661a12949d7d49f8f51ff5fa019c1e47c87c4ff34b080ed"}, + {file = "msgpack-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:79c408fcf76a958491b4e3b103d1c417044544b68e96d06432a189b43d1215c8"}, + {file = "msgpack-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78426096939c2c7482bf31ef15ca219a9e24460289c00dd0b94411040bb73ad2"}, + {file = "msgpack-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b17ba27727a36cb73aabacaa44b13090feb88a01d012c0f4be70c00f75048b4"}, + {file = "msgpack-1.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a17ac1ea6ec3c7687d70201cfda3b1e8061466f28f686c24f627cae4ea8efd0"}, + {file = "msgpack-1.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:88d1e966c9235c1d4e2afac21ca83933ba59537e2e2727a999bf3f515ca2af26"}, + {file = "msgpack-1.1.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f6d58656842e1b2ddbe07f43f56b10a60f2ba5826164910968f5933e5178af75"}, + {file = "msgpack-1.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:96decdfc4adcbc087f5ea7ebdcfd3dee9a13358cae6e81d54be962efc38f6338"}, + {file = "msgpack-1.1.1-cp310-cp310-win32.whl", hash = "sha256:6640fd979ca9a212e4bcdf6eb74051ade2c690b862b679bfcb60ae46e6dc4bfd"}, + {file = "msgpack-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:8b65b53204fe1bd037c40c4148d00ef918eb2108d24c9aaa20bc31f9810ce0a8"}, + {file = "msgpack-1.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:71ef05c1726884e44f8b1d1773604ab5d4d17729d8491403a705e649116c9558"}, + {file = "msgpack-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:36043272c6aede309d29d56851f8841ba907a1a3d04435e43e8a19928e243c1d"}, + {file = "msgpack-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a32747b1b39c3ac27d0670122b57e6e57f28eefb725e0b625618d1b59bf9d1e0"}, + {file = "msgpack-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a8b10fdb84a43e50d38057b06901ec9da52baac6983d3f709d8507f3889d43f"}, + {file = "msgpack-1.1.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0c325c3f485dc54ec298d8b024e134acf07c10d494ffa24373bea729acf704"}, + {file = "msgpack-1.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:88daaf7d146e48ec71212ce21109b66e06a98e5e44dca47d853cbfe171d6c8d2"}, + {file = "msgpack-1.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8b55ea20dc59b181d3f47103f113e6f28a5e1c89fd5b67b9140edb442ab67f2"}, + {file = "msgpack-1.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4a28e8072ae9779f20427af07f53bbb8b4aa81151054e882aee333b158da8752"}, + {file = "msgpack-1.1.1-cp311-cp311-win32.whl", hash = "sha256:7da8831f9a0fdb526621ba09a281fadc58ea12701bc709e7b8cbc362feabc295"}, + {file = "msgpack-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fd1b58e1431008a57247d6e7cc4faa41c3607e8e7d4aaf81f7c29ea013cb458"}, + {file = "msgpack-1.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ae497b11f4c21558d95de9f64fff7053544f4d1a17731c866143ed6bb4591238"}, + {file = "msgpack-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:33be9ab121df9b6b461ff91baac6f2731f83d9b27ed948c5b9d1978ae28bf157"}, + {file = "msgpack-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f64ae8fe7ffba251fecb8408540c34ee9df1c26674c50c4544d72dbf792e5ce"}, + {file = "msgpack-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a494554874691720ba5891c9b0b39474ba43ffb1aaf32a5dac874effb1619e1a"}, + {file = "msgpack-1.1.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb643284ab0ed26f6957d969fe0dd8bb17beb567beb8998140b5e38a90974f6c"}, + {file = "msgpack-1.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d275a9e3c81b1093c060c3837e580c37f47c51eca031f7b5fb76f7b8470f5f9b"}, + {file = "msgpack-1.1.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fd6b577e4541676e0cc9ddc1709d25014d3ad9a66caa19962c4f5de30fc09ef"}, + {file = "msgpack-1.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb29aaa613c0a1c40d1af111abf025f1732cab333f96f285d6a93b934738a68a"}, + {file = "msgpack-1.1.1-cp312-cp312-win32.whl", hash = "sha256:870b9a626280c86cff9c576ec0d9cbcc54a1e5ebda9cd26dab12baf41fee218c"}, + {file = "msgpack-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:5692095123007180dca3e788bb4c399cc26626da51629a31d40207cb262e67f4"}, + {file = "msgpack-1.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3765afa6bd4832fc11c3749be4ba4b69a0e8d7b728f78e68120a157a4c5d41f0"}, + {file = "msgpack-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8ddb2bcfd1a8b9e431c8d6f4f7db0773084e107730ecf3472f1dfe9ad583f3d9"}, + {file = "msgpack-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:196a736f0526a03653d829d7d4c5500a97eea3648aebfd4b6743875f28aa2af8"}, + {file = "msgpack-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d592d06e3cc2f537ceeeb23d38799c6ad83255289bb84c2e5792e5a8dea268a"}, + {file = "msgpack-1.1.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4df2311b0ce24f06ba253fda361f938dfecd7b961576f9be3f3fbd60e87130ac"}, + {file = "msgpack-1.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e4141c5a32b5e37905b5940aacbc59739f036930367d7acce7a64e4dec1f5e0b"}, + {file = "msgpack-1.1.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b1ce7f41670c5a69e1389420436f41385b1aa2504c3b0c30620764b15dded2e7"}, + {file = "msgpack-1.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4147151acabb9caed4e474c3344181e91ff7a388b888f1e19ea04f7e73dc7ad5"}, + {file = "msgpack-1.1.1-cp313-cp313-win32.whl", hash = "sha256:500e85823a27d6d9bba1d057c871b4210c1dd6fb01fbb764e37e4e8847376323"}, + {file = "msgpack-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:6d489fba546295983abd142812bda76b57e33d0b9f5d5b71c09a583285506f69"}, + {file = "msgpack-1.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bba1be28247e68994355e028dcd668316db30c1f758d3241a7b903ac78dcd285"}, + {file = "msgpack-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8f93dcddb243159c9e4109c9750ba5b335ab8d48d9522c5308cd05d7e3ce600"}, + {file = "msgpack-1.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fbbc0b906a24038c9958a1ba7ae0918ad35b06cb449d398b76a7d08470b0ed9"}, + {file = "msgpack-1.1.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:61e35a55a546a1690d9d09effaa436c25ae6130573b6ee9829c37ef0f18d5e78"}, + {file = "msgpack-1.1.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:1abfc6e949b352dadf4bce0eb78023212ec5ac42f6abfd469ce91d783c149c2a"}, + {file = "msgpack-1.1.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:996f2609ddf0142daba4cefd767d6db26958aac8439ee41db9cc0db9f4c4c3a6"}, + {file = "msgpack-1.1.1-cp38-cp38-win32.whl", hash = "sha256:4d3237b224b930d58e9d83c81c0dba7aacc20fcc2f89c1e5423aa0529a4cd142"}, + {file = "msgpack-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:da8f41e602574ece93dbbda1fab24650d6bf2a24089f9e9dbb4f5730ec1e58ad"}, + {file = "msgpack-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5be6b6bc52fad84d010cb45433720327ce886009d862f46b26d4d154001994b"}, + {file = "msgpack-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3a89cd8c087ea67e64844287ea52888239cbd2940884eafd2dcd25754fb72232"}, + {file = "msgpack-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d75f3807a9900a7d575d8d6674a3a47e9f227e8716256f35bc6f03fc597ffbf"}, + {file = "msgpack-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d182dac0221eb8faef2e6f44701812b467c02674a322c739355c39e94730cdbf"}, + {file = "msgpack-1.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b13fe0fb4aac1aa5320cd693b297fe6fdef0e7bea5518cbc2dd5299f873ae90"}, + {file = "msgpack-1.1.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:435807eeb1bc791ceb3247d13c79868deb22184e1fc4224808750f0d7d1affc1"}, + {file = "msgpack-1.1.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4835d17af722609a45e16037bb1d4d78b7bdf19d6c0128116d178956618c4e88"}, + {file = "msgpack-1.1.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8ef6e342c137888ebbfb233e02b8fbd689bb5b5fcc59b34711ac47ebd504478"}, + {file = "msgpack-1.1.1-cp39-cp39-win32.whl", hash = "sha256:61abccf9de335d9efd149e2fff97ed5974f2481b3353772e8e2dd3402ba2bd57"}, + {file = "msgpack-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:40eae974c873b2992fd36424a5d9407f93e97656d999f43fca9d29f820899084"}, + {file = "msgpack-1.1.1.tar.gz", hash = "sha256:77b79ce34a2bdab2594f490c8e80dd62a02d650b91a75159a63ec413b8d104cd"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + +[[package]] +name = "networkx" +version = "3.5" +description = "Python package for creating and manipulating graphs and networks" +optional = false +python-versions = ">=3.11" +groups = ["dev"] +files = [ + {file = "networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec"}, + {file = "networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037"}, +] + +[package.extras] +default = ["matplotlib (>=3.8)", "numpy (>=1.25)", "pandas (>=2.0)", "scipy (>=1.11.2)"] +developer = ["mypy (>=1.15)", "pre-commit (>=4.1)"] +doc = ["intersphinx-registry", "myst-nb (>=1.1)", "numpydoc (>=1.8.0)", "pillow (>=10)", "pydata-sphinx-theme (>=0.16)", "sphinx (>=8.0)", "sphinx-gallery (>=0.18)", "texext (>=0.6.7)"] +example = ["cairocffi (>=1.7)", "contextily (>=1.6)", "igraph (>=0.11)", "momepy (>=0.7.2)", "osmnx (>=2.0.0)", "scikit-learn (>=1.5)", "seaborn (>=0.13)"] +extra = ["lxml (>=4.6)", "pydot (>=3.0.1)", "pygraphviz (>=1.14)", "sympy (>=1.10)"] +test = ["pytest (>=7.2)", "pytest-cov (>=4.0)", "pytest-xdist (>=3.0)"] +test-extras = ["pytest-mpl", "pytest-randomly"] + +[[package]] +name = "packaging" +version = "24.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, +] + +[[package]] +name = "puyapy" +version = "4.10.0" +description = "An optimising compiler for Algorand Python" +optional = false +python-versions = "<4.0,>=3.12" +groups = ["dev"] +files = [ + {file = "puyapy-4.10.0-py3-none-any.whl", hash = "sha256:7f243c784272568870c759e69b867260f76583b5fb78f6aec133c82bd96e3c78"}, +] + +[package.dependencies] +attrs = ">=25.3.0,<26.0.0" +cattrs = ">=24.1,<25.0" +colorama = {version = ">=0.4.6,<0.5.0", markers = "sys_platform == \"win32\""} +docstring-parser = ">=0.14.1" +immutabledict = ">=4.2.0,<5.0.0" +mypy_extensions = ">=1.0.0,<2.0.0" +networkx = ">=3.4.2,<4.0.0" +packaging = ">=24.0,<25.0" +pycryptodomex = ">=3.6.0,<4" +structlog = ">=25.2.0,<26.0.0" +typing-extensions = ">=4.11.0,<5.0.0" + +[[package]] +name = "py-algorand-sdk" +version = "2.10.0" +description = "Algorand SDK in Python" +optional = false +python-versions = ">=3.10" +groups = ["main", "dev"] +files = [ + {file = "py_algorand_sdk-2.10.0-py3-none-any.whl", hash = "sha256:511a8f172977b4d6940dceb036abb040d70f2ed2c7b090251818f0ba6f1b3af8"}, + {file = "py_algorand_sdk-2.10.0.tar.gz", hash = "sha256:521f997a53219210feac519e86d0fb36d18be7e36d528162cf328b4b058e423f"}, +] + +[package.dependencies] +msgpack = ">=1.0.0,<2" +pycryptodomex = ">=3.6.0,<4" +pynacl = ">=1.4.0,<2" + +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + +[[package]] +name = "pycryptodomex" +version = "3.23.0" +description = "Cryptographic library for Python" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] +files = [ + {file = "pycryptodomex-3.23.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:add243d204e125f189819db65eed55e6b4713f70a7e9576c043178656529cec7"}, + {file = "pycryptodomex-3.23.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1c6d919fc8429e5cb228ba8c0d4d03d202a560b421c14867a65f6042990adc8e"}, + {file = "pycryptodomex-3.23.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:1c3a65ad441746b250d781910d26b7ed0a396733c6f2dbc3327bd7051ec8a541"}, + {file = "pycryptodomex-3.23.0-cp27-cp27m-win32.whl", hash = "sha256:47f6d318fe864d02d5e59a20a18834819596c4ed1d3c917801b22b92b3ffa648"}, + {file = "pycryptodomex-3.23.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:d9825410197a97685d6a1fa2a86196430b01877d64458a20e95d4fd00d739a08"}, + {file = "pycryptodomex-3.23.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:267a3038f87a8565bd834317dbf053a02055915acf353bf42ededb9edaf72010"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:7b37e08e3871efe2187bc1fd9320cc81d87caf19816c648f24443483005ff886"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:91979028227543010d7b2ba2471cf1d1e398b3f183cb105ac584df0c36dac28d"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8962204c47464d5c1c4038abeadd4514a133b28748bcd9fa5b6d62e3cec6fa"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a33986a0066860f7fcf7c7bd2bc804fa90e434183645595ae7b33d01f3c91ed8"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7947ab8d589e3178da3d7cdeabe14f841b391e17046954f2fbcd941705762b5"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c25e30a20e1b426e1f0fa00131c516f16e474204eee1139d1603e132acffc314"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:da4fa650cef02db88c2b98acc5434461e027dce0ae8c22dd5a69013eaf510006"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58b851b9effd0d072d4ca2e4542bf2a4abcf13c82a29fd2c93ce27ee2a2e9462"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:a9d446e844f08299236780f2efa9898c818fe7e02f17263866b8550c7d5fb328"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:bc65bdd9fc8de7a35a74cab1c898cab391a4add33a8fe740bda00f5976ca4708"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c885da45e70139464f082018ac527fdaad26f1657a99ee13eecdce0f0ca24ab4"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:06698f957fe1ab229a99ba2defeeae1c09af185baa909a31a5d1f9d42b1aaed6"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b2c2537863eccef2d41061e82a881dcabb04944c5c06c5aa7110b577cc487545"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43c446e2ba8df8889e0e16f02211c25b4934898384c1ec1ec04d7889c0333587"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f489c4765093fb60e2edafdf223397bc716491b2b69fe74367b70d6999257a5c"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdc69d0d3d989a1029df0eed67cc5e8e5d968f3724f4519bd03e0ec68df7543c"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6bbcb1dd0f646484939e142462d9e532482bc74475cecf9c4903d4e1cd21f003"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:8a4fcd42ccb04c31268d1efeecfccfd1249612b4de6374205376b8f280321744"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:55ccbe27f049743a4caf4f4221b166560d3438d0b1e5ab929e07ae1702a4d6fd"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-win32.whl", hash = "sha256:189afbc87f0b9f158386bf051f720e20fa6145975f1e76369303d0f31d1a8d7c"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:52e5ca58c3a0b0bd5e100a9fbc8015059b05cffc6c66ce9d98b4b45e023443b9"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:02d87b80778c171445d67e23d1caef279bf4b25c3597050ccd2e13970b57fd51"}, + {file = "pycryptodomex-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:febec69c0291efd056c65691b6d9a339f8b4bc43c6635b8699471248fe897fea"}, + {file = "pycryptodomex-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:c84b239a1f4ec62e9c789aafe0543f0594f0acd90c8d9e15bcece3efe55eca66"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ebfff755c360d674306e5891c564a274a47953562b42fb74a5c25b8fc1fb1cb5"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eca54f4bb349d45afc17e3011ed4264ef1cc9e266699874cdd1349c504e64798"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2596e643d4365e14d0879dc5aafe6355616c61c2176009270f3048f6d9a61f"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdfac7cda115bca3a5abb2f9e43bc2fb66c2b65ab074913643803ca7083a79ea"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:14c37aaece158d0ace436f76a7bb19093db3b4deade9797abfc39ec6cd6cc2fe"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7de1e40a41a5d7f1ac42b6569b10bcdded34339950945948529067d8426d2785"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bffc92138d75664b6d543984db7893a628559b9e78658563b0395e2a5fb47ed9"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df027262368334552db2c0ce39706b3fb32022d1dce34673d0f9422df004b96a"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e79f1aaff5a3a374e92eb462fa9e598585452135012e2945f96874ca6eeb1ff"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:27e13c80ac9a0a1d050ef0a7e0a18cc04c8850101ec891815b6c5a0375e8a245"}, + {file = "pycryptodomex-3.23.0.tar.gz", hash = "sha256:71909758f010c82bc99b0abf4ea12012c98962fbf0583c2164f8b84533c2e4da"}, +] + +[[package]] +name = "pynacl" +version = "1.5.0" +description = "Python binding to the Networking and Cryptography (NaCl) library" +optional = false +python-versions = ">=3.6" +groups = ["main", "dev"] +files = [ + {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, + {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, +] + +[package.dependencies] +cffi = ">=1.4.1" + +[package.extras] +docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] +tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] + +[[package]] +name = "python-dotenv" +version = "1.1.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc"}, + {file = "python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +groups = ["main", "dev"] +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "structlog" +version = "25.4.0" +description = "Structured Logging for Python" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c"}, + {file = "structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4"}, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] + +[metadata] +lock-version = "2.1" +python-versions = "^3.12" +content-hash = "2f3d0bf4e0cf86fb3a41818da3708e5f1b26c7b6ea6117df9a64a15778d55c06" diff --git a/ogc-contracts/projects/ogc-contracts/poetry.toml b/ogc-contracts/projects/ogc-contracts/poetry.toml new file mode 100644 index 0000000..5fcef8c --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/poetry.toml @@ -0,0 +1,3 @@ +[virtualenvs] +in-project = true +prefer-active-python = true diff --git a/ogc-contracts/projects/ogc-contracts/pyproject.toml b/ogc-contracts/projects/ogc-contracts/pyproject.toml new file mode 100644 index 0000000..9f5d0bc --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/pyproject.toml @@ -0,0 +1,23 @@ +[tool.poetry] +name = "ogc-contracts" +version = "0.1.0" +description = "Algorand smart contracts" +authors = ["Elton Baidoo "] +readme = "README.md" +package-mode = false + +[tool.poetry.dependencies] +python = "^3.12" +algokit-utils = "^4.0.0" +python-dotenv = "^1.0.0" +algorand-python = "^2.0.0" +algorand-python-testing = "~0" + +[tool.poetry.group.dev.dependencies] +algokit-client-generator = "^2.1.0" +puyapy = "*" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + diff --git a/ogc-contracts/projects/ogc-contracts/smart_contracts/__init__.py b/ogc-contracts/projects/ogc-contracts/smart_contracts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ogc-contracts/projects/ogc-contracts/smart_contracts/__main__.py b/ogc-contracts/projects/ogc-contracts/smart_contracts/__main__.py new file mode 100644 index 0000000..6bb9664 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/smart_contracts/__main__.py @@ -0,0 +1,211 @@ +import dataclasses +import importlib +import logging +import subprocess +import sys +from collections.abc import Callable +from pathlib import Path +from shutil import rmtree + +from algokit_utils.config import config +from dotenv import load_dotenv + +# Set trace_all to True to capture all transactions, defaults to capturing traces only on failure +# 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 +config.configure(debug=True, trace_all=False) + +# Set up logging and load environment variables. +logging.basicConfig( + level=logging.DEBUG, format="%(asctime)s %(levelname)-10s: %(message)s" +) +logger = logging.getLogger(__name__) +logger.info("Loading .env") +load_dotenv() + +# Determine the root path based on this file's location. +root_path = Path(__file__).parent + +# ----------------------- Contract Configuration ----------------------- # + + +@dataclasses.dataclass +class SmartContract: + path: Path + name: str + deploy: Callable[[], 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[[], None] | None: + """Imports the deploy function from a folder if it exists.""" + try: + module_name = f"{folder.parent.name}.{folder.name}.deploy_config" + deploy_module = importlib.import_module(module_name) + return deploy_module.deploy # type: ignore[no-any-return, misc] + except ImportError: + return None + + +def has_contract_file(directory: Path) -> bool: + """Checks whether the directory contains a contract.py file.""" + return (directory / "contract.py").exists() + + +# Use the current directory (root_path) as the base for contract folders and exclude +# folders that start with '_' (internal helpers). +contracts: list[SmartContract] = [ + SmartContract( + path=import_contract(folder), + name=folder.name, + deploy=import_deploy_if_exists(folder), + ) + for folder in root_path.iterdir() + if folder.is_dir() and has_contract_file(folder) and not folder.name.startswith("_") +] + +# -------------------------- Build Logic -------------------------- # + +deployment_extension = "py" + + +def _get_output_path(output_dir: Path, deployment_extension: str) -> Path: + """Constructs the output path for the generated client file.""" + return output_dir / Path( + "{contract_name}" + + ("_client" if deployment_extension == "py" else "Client") + + f".{deployment_extension}" + ) + + +def build(output_dir: Path, contract_path: Path) -> Path: + """ + Builds the contract by exporting (compiling) its source and generating a client. + If the output directory already exists, it is cleared. + """ + output_dir = output_dir.resolve() + if output_dir.exists(): + rmtree(output_dir) + output_dir.mkdir(exist_ok=True, parents=True) + logger.info(f"Exporting {contract_path} to {output_dir}") + + build_result = subprocess.run( + [ + "algokit", + "--no-color", + "compile", + "python", + str(contract_path.resolve()), + f"--out-dir={output_dir}", + "--no-output-arc32", + "--output-arc56", + "--output-source-map", + ], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + ) + if build_result.returncode: + raise Exception(f"Could not build contract:\n{build_result.stdout}") + + # Look for arc56.json files and generate the client based on them. + app_spec_file_names: list[str] = [ + file.name for file in output_dir.glob("*.arc56.json") + ] + + client_file: str | None = None + if not app_spec_file_names: + logger.warning( + "No '*.arc56.json' file found (likely a logic signature being compiled). Skipping client generation." + ) + else: + for file_name in app_spec_file_names: + client_file = file_name + print(file_name) + generate_result = subprocess.run( + [ + "algokit", + "generate", + "client", + str(output_dir), + "--output", + str(_get_output_path(output_dir, deployment_extension)), + ], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + ) + if generate_result.returncode: + if "No such command" in generate_result.stdout: + raise Exception( + "Could not generate typed client, requires AlgoKit 2.0.0 or later. Please update AlgoKit" + ) + else: + raise Exception( + f"Could not generate typed client:\n{generate_result.stdout}" + ) + if client_file: + return output_dir / client_file + return output_dir + + +# --------------------------- Main Logic --------------------------- # + + +def main(action: str, contract_name: str | None = None) -> None: + """Main entry point to build and/or deploy smart contracts.""" + artifact_path = root_path / "artifacts" + # Filter contracts based on an optional specific contract name. + filtered_contracts = [ + contract + for contract in contracts + if contract_name is None or contract.name == contract_name + ] + + match action: + case "build": + for contract in filtered_contracts: + logger.info(f"Building app at {contract.path}") + build(artifact_path / contract.name, contract.path) + case "deploy": + for contract in filtered_contracts: + output_dir = artifact_path / contract.name + app_spec_file_name = next( + ( + file.name + for file in output_dir.iterdir() + if file.is_file() and file.suffixes == [".arc56", ".json"] + ), + None, + ) + if app_spec_file_name is None: + raise Exception("Could not deploy app, .arc56.json file not found") + if contract.deploy: + logger.info(f"Deploying app {contract.name}") + contract.deploy() + case "all": + for contract in filtered_contracts: + logger.info(f"Building app at {contract.path}") + build(artifact_path / contract.name, contract.path) + if contract.deploy: + logger.info(f"Deploying {contract.name}") + contract.deploy() + case _: + logger.error(f"Unknown action: {action}") + + +if __name__ == "__main__": + if len(sys.argv) > 2: + main(sys.argv[1], sys.argv[2]) + elif len(sys.argv) > 1: + main(sys.argv[1]) + else: + main("all") diff --git a/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/contract.py b/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/contract.py new file mode 100644 index 0000000..00220bb --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/contract.py @@ -0,0 +1,8 @@ +from algopy import ARC4Contract, String +from algopy.arc4 import abimethod + + +class OgcVault(ARC4Contract): + @abimethod() + def hello(self, name: String) -> String: + return "Hello, " + name diff --git a/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/deploy_config.py b/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/deploy_config.py new file mode 100644 index 0000000..38199a1 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/deploy_config.py @@ -0,0 +1,44 @@ +import logging + +import algokit_utils + +logger = logging.getLogger(__name__) + + +# define deployment behaviour based on supplied app spec +def deploy() -> None: + from smart_contracts.artifacts.ogc_vault.ogc_vault_client import ( + HelloArgs, + OgcVaultFactory, + ) + + algorand = algokit_utils.AlgorandClient.from_environment() + deployer_ = algorand.account.from_environment("DEPLOYER") + + factory = algorand.client.get_typed_app_factory( + OgcVaultFactory, default_sender=deployer_.address + ) + + app_client, result = factory.deploy( + on_update=algokit_utils.OnUpdate.AppendApp, + on_schema_break=algokit_utils.OnSchemaBreak.AppendApp, + ) + + if result.operation_performed in [ + algokit_utils.OperationPerformed.Create, + algokit_utils.OperationPerformed.Replace, + ]: + algorand.send.payment( + algokit_utils.PaymentParams( + amount=algokit_utils.AlgoAmount(algo=1), + sender=deployer_.address, + receiver=app_client.app_address, + ) + ) + + name = "world" + response = app_client.send.hello(args=HelloArgs(name=name)) + logger.info( + f"Called hello on {app_client.app_name} ({app_client.app_id}) " + f"with name={name}, received: {response.abi_return}" + ) From c91b3625d377efa8e3385258f4a5bb40538551fc Mon Sep 17 00:00:00 2001 From: Elton Baidoo Date: Sat, 6 Sep 2025 14:43:37 -0400 Subject: [PATCH 02/16] Implement working OGC vault smart contract Add pyteal/beaker crowdfunding vault with gola-based funding Deploy to LocalNet with proper schema (APP_ID: 1005) --- .../artifacts/ogc_vault/application.json | 158 ++++++++ .../artifacts/ogc_vault/approval.teal | 341 ++++++++++++++++++ .../artifacts/ogc_vault/clear.teal | 3 + .../artifacts/ogc_vault/contract.json | 73 ++++ .../artifacts/simple_vault/application.json | 115 ++++++ .../artifacts/simple_vault/approval.teal | 247 +++++++++++++ .../artifacts/simple_vault/clear.teal | 3 + .../artifacts/simple_vault/contract.json | 59 +++ .../artifacts/working_vault/application.json | 136 +++++++ .../artifacts/working_vault/approval.teal | 247 +++++++++++++ .../artifacts/working_vault/clear.teal | 3 + .../artifacts/working_vault/contract.json | 59 +++ .../projects/ogc-contracts/build_vault.py | 23 ++ .../projects/ogc-contracts/deploy_simple.py | 38 ++ .../projects/ogc-contracts/deploy_vault.py | 44 +++ .../projects/ogc-contracts/deploy_working.py | 58 +++ .../projects/ogc-contracts/fund_app.py | 23 ++ .../ogc-contracts/scripts/contribute.py | 46 +++ .../projects/ogc-contracts/simple_vault.py | 60 +++ .../smart_contracts/ogc_vault/contract.py | 94 ++++- .../ogc_vault/deploy_config.py | 63 ++-- ogc-contracts/projects/ogc-contracts/vault.py | 89 +++++ .../projects/ogc-contracts/working_vault.py | 59 +++ 23 files changed, 1997 insertions(+), 44 deletions(-) create mode 100644 ogc-contracts/projects/ogc-contracts/artifacts/ogc_vault/application.json create mode 100644 ogc-contracts/projects/ogc-contracts/artifacts/ogc_vault/approval.teal create mode 100644 ogc-contracts/projects/ogc-contracts/artifacts/ogc_vault/clear.teal create mode 100644 ogc-contracts/projects/ogc-contracts/artifacts/ogc_vault/contract.json create mode 100644 ogc-contracts/projects/ogc-contracts/artifacts/simple_vault/application.json create mode 100644 ogc-contracts/projects/ogc-contracts/artifacts/simple_vault/approval.teal create mode 100644 ogc-contracts/projects/ogc-contracts/artifacts/simple_vault/clear.teal create mode 100644 ogc-contracts/projects/ogc-contracts/artifacts/simple_vault/contract.json create mode 100644 ogc-contracts/projects/ogc-contracts/artifacts/working_vault/application.json create mode 100644 ogc-contracts/projects/ogc-contracts/artifacts/working_vault/approval.teal create mode 100644 ogc-contracts/projects/ogc-contracts/artifacts/working_vault/clear.teal create mode 100644 ogc-contracts/projects/ogc-contracts/artifacts/working_vault/contract.json create mode 100644 ogc-contracts/projects/ogc-contracts/build_vault.py create mode 100644 ogc-contracts/projects/ogc-contracts/deploy_simple.py create mode 100644 ogc-contracts/projects/ogc-contracts/deploy_vault.py create mode 100644 ogc-contracts/projects/ogc-contracts/deploy_working.py create mode 100644 ogc-contracts/projects/ogc-contracts/fund_app.py create mode 100644 ogc-contracts/projects/ogc-contracts/scripts/contribute.py create mode 100644 ogc-contracts/projects/ogc-contracts/simple_vault.py create mode 100644 ogc-contracts/projects/ogc-contracts/vault.py create mode 100644 ogc-contracts/projects/ogc-contracts/working_vault.py diff --git a/ogc-contracts/projects/ogc-contracts/artifacts/ogc_vault/application.json b/ogc-contracts/projects/ogc-contracts/artifacts/ogc_vault/application.json new file mode 100644 index 0000000..6dda22d --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/artifacts/ogc_vault/application.json @@ -0,0 +1,158 @@ +{ + "hints": { + "create(uint64,uint64,address)uint64": { + "call_config": { + "no_op": "CREATE" + } + }, + "opt_in()void": { + "call_config": { + "opt_in": "CALL" + } + }, + "pause()void": { + "call_config": { + "no_op": "CALL" + } + }, + "unpause()void": { + "call_config": { + "no_op": "CALL" + } + }, + "contribute(pay)void": { + "call_config": { + "no_op": "CALL" + } + }, + "release()void": { + "call_config": { + "no_op": "CALL" + } + }, + "refund()void": { + "call_config": { + "no_op": "CALL" + } + } + }, + "source": { + "approval": "I3ByYWdtYSB2ZXJzaW9uIDgKaW50Y2Jsb2NrIDAgMQpieXRlY2Jsb2NrIDB4NjM2ZjZlNzQ3MjY5NjI3NTc0NjU2NCAweDcwNjE3NTczNjU2NCAweDY3NmY2MTZjIDB4NjQ2NTYxNjQ2YzY5NmU2NSAweDcyNjU2MzY1Njk3NjY1NzIKdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMApwdXNoYnl0ZXMgMHg1MjczNTMyMSAvLyAiY3JlYXRlKHVpbnQ2NCx1aW50NjQsYWRkcmVzcyl1aW50NjQiCj09CmJueiBtYWluX2wxNAp0eG5hIEFwcGxpY2F0aW9uQXJncyAwCnB1c2hieXRlcyAweDMwYzZkNThhIC8vICJvcHRfaW4oKXZvaWQiCj09CmJueiBtYWluX2wxMwp0eG5hIEFwcGxpY2F0aW9uQXJncyAwCnB1c2hieXRlcyAweDAxNzhmOTRiIC8vICJwYXVzZSgpdm9pZCIKPT0KYm56IG1haW5fbDEyCnR4bmEgQXBwbGljYXRpb25BcmdzIDAKcHVzaGJ5dGVzIDB4MWI1MjlkZTggLy8gInVucGF1c2UoKXZvaWQiCj09CmJueiBtYWluX2wxMQp0eG5hIEFwcGxpY2F0aW9uQXJncyAwCnB1c2hieXRlcyAweGZlNzE3YjZkIC8vICJjb250cmlidXRlKHBheSl2b2lkIgo9PQpibnogbWFpbl9sMTAKdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMApwdXNoYnl0ZXMgMHgwNzZiYmQ0ZCAvLyAicmVsZWFzZSgpdm9pZCIKPT0KYm56IG1haW5fbDkKdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMApwdXNoYnl0ZXMgMHgyM2U2MjlmNyAvLyAicmVmdW5kKCl2b2lkIgo9PQpibnogbWFpbl9sOAplcnIKbWFpbl9sODoKdHhuIE9uQ29tcGxldGlvbgppbnRjXzAgLy8gTm9PcAo9PQp0eG4gQXBwbGljYXRpb25JRAppbnRjXzAgLy8gMAohPQomJgphc3NlcnQKY2FsbHN1YiByZWZ1bmRjYXN0ZXJfMTQKaW50Y18xIC8vIDEKcmV0dXJuCm1haW5fbDk6CnR4biBPbkNvbXBsZXRpb24KaW50Y18wIC8vIE5vT3AKPT0KdHhuIEFwcGxpY2F0aW9uSUQKaW50Y18wIC8vIDAKIT0KJiYKYXNzZXJ0CmNhbGxzdWIgcmVsZWFzZWNhc3Rlcl8xMwppbnRjXzEgLy8gMQpyZXR1cm4KbWFpbl9sMTA6CnR4biBPbkNvbXBsZXRpb24KaW50Y18wIC8vIE5vT3AKPT0KdHhuIEFwcGxpY2F0aW9uSUQKaW50Y18wIC8vIDAKIT0KJiYKYXNzZXJ0CmNhbGxzdWIgY29udHJpYnV0ZWNhc3Rlcl8xMgppbnRjXzEgLy8gMQpyZXR1cm4KbWFpbl9sMTE6CnR4biBPbkNvbXBsZXRpb24KaW50Y18wIC8vIE5vT3AKPT0KdHhuIEFwcGxpY2F0aW9uSUQKaW50Y18wIC8vIDAKIT0KJiYKYXNzZXJ0CmNhbGxzdWIgdW5wYXVzZWNhc3Rlcl8xMQppbnRjXzEgLy8gMQpyZXR1cm4KbWFpbl9sMTI6CnR4biBPbkNvbXBsZXRpb24KaW50Y18wIC8vIE5vT3AKPT0KdHhuIEFwcGxpY2F0aW9uSUQKaW50Y18wIC8vIDAKIT0KJiYKYXNzZXJ0CmNhbGxzdWIgcGF1c2VjYXN0ZXJfMTAKaW50Y18xIC8vIDEKcmV0dXJuCm1haW5fbDEzOgp0eG4gT25Db21wbGV0aW9uCmludGNfMSAvLyBPcHRJbgo9PQp0eG4gQXBwbGljYXRpb25JRAppbnRjXzAgLy8gMAohPQomJgphc3NlcnQKY2FsbHN1YiBvcHRpbmNhc3Rlcl85CmludGNfMSAvLyAxCnJldHVybgptYWluX2wxNDoKdHhuIE9uQ29tcGxldGlvbgppbnRjXzAgLy8gTm9PcAo9PQp0eG4gQXBwbGljYXRpb25JRAppbnRjXzAgLy8gMAo9PQomJgphc3NlcnQKY2FsbHN1YiBjcmVhdGVjYXN0ZXJfOAppbnRjXzEgLy8gMQpyZXR1cm4KCi8vIGNyZWF0ZQpjcmVhdGVfMDoKcHJvdG8gMyAxCmludGNfMCAvLyAwCmJ5dGVjXzIgLy8gImdvYWwiCmZyYW1lX2RpZyAtMwphcHBfZ2xvYmFsX3B1dApieXRlY18zIC8vICJkZWFkbGluZSIKZnJhbWVfZGlnIC0yCmFwcF9nbG9iYWxfcHV0CmJ5dGVjIDQgLy8gInJlY2VpdmVyIgpmcmFtZV9kaWcgLTEKYXBwX2dsb2JhbF9wdXQKYnl0ZWNfMSAvLyAicGF1c2VkIgppbnRjXzAgLy8gMAphcHBfZ2xvYmFsX3B1dApnbG9iYWwgQ3VycmVudEFwcGxpY2F0aW9uSUQKZnJhbWVfYnVyeSAwCnJldHN1YgoKLy8gb3B0X2luCm9wdGluXzE6CnByb3RvIDAgMAp0eG4gU2VuZGVyCmJ5dGVjXzAgLy8gImNvbnRyaWJ1dGVkIgppbnRjXzAgLy8gMAphcHBfbG9jYWxfcHV0CnJldHN1YgoKLy8gX25vdF9wYXVzZWQKbm90cGF1c2VkXzI6CnByb3RvIDAgMApieXRlY18xIC8vICJwYXVzZWQiCmFwcF9nbG9iYWxfZ2V0CmludGNfMCAvLyAwCj09CmFzc2VydApyZXRzdWIKCi8vIHBhdXNlCnBhdXNlXzM6CnByb3RvIDAgMAp0eG4gU2VuZGVyCmdsb2JhbCBDcmVhdG9yQWRkcmVzcwo9PQphc3NlcnQKYnl0ZWNfMSAvLyAicGF1c2VkIgppbnRjXzEgLy8gMQphcHBfZ2xvYmFsX3B1dApyZXRzdWIKCi8vIHVucGF1c2UKdW5wYXVzZV80Ogpwcm90byAwIDAKdHhuIFNlbmRlcgpnbG9iYWwgQ3JlYXRvckFkZHJlc3MKPT0KYXNzZXJ0CmJ5dGVjXzEgLy8gInBhdXNlZCIKaW50Y18wIC8vIDAKYXBwX2dsb2JhbF9wdXQKcmV0c3ViCgovLyBjb250cmlidXRlCmNvbnRyaWJ1dGVfNToKcHJvdG8gMSAwCmNhbGxzdWIgbm90cGF1c2VkXzIKZnJhbWVfZGlnIC0xCmd0eG5zIFJlY2VpdmVyCmdsb2JhbCBDdXJyZW50QXBwbGljYXRpb25BZGRyZXNzCj09CmZyYW1lX2RpZyAtMQpndHhucyBBbW91bnQKaW50Y18wIC8vIDAKPgomJgphc3NlcnQKdHhuIFNlbmRlcgpieXRlY18wIC8vICJjb250cmlidXRlZCIKdHhuIFNlbmRlcgpieXRlY18wIC8vICJjb250cmlidXRlZCIKYXBwX2xvY2FsX2dldApmcmFtZV9kaWcgLTEKZ3R4bnMgQW1vdW50CisKYXBwX2xvY2FsX3B1dApyZXRzdWIKCi8vIHJlbGVhc2UKcmVsZWFzZV82Ogpwcm90byAwIDAKZ2xvYmFsIFJvdW5kCmJ5dGVjXzMgLy8gImRlYWRsaW5lIgphcHBfZ2xvYmFsX2dldAo+PQpnbG9iYWwgQ3VycmVudEFwcGxpY2F0aW9uQWRkcmVzcwpiYWxhbmNlCmJ5dGVjXzIgLy8gImdvYWwiCmFwcF9nbG9iYWxfZ2V0Cj49CiYmCmFzc2VydAppdHhuX2JlZ2luCmludGNfMSAvLyBwYXkKaXR4bl9maWVsZCBUeXBlRW51bQpieXRlYyA0IC8vICJyZWNlaXZlciIKYXBwX2dsb2JhbF9nZXQKaXR4bl9maWVsZCBSZWNlaXZlcgpnbG9iYWwgQ3VycmVudEFwcGxpY2F0aW9uQWRkcmVzcwpiYWxhbmNlCnB1c2hpbnQgMTAwMDAwMCAvLyAxMDAwMDAwCi0KaXR4bl9maWVsZCBBbW91bnQKaXR4bl9zdWJtaXQKcmV0c3ViCgovLyByZWZ1bmQKcmVmdW5kXzc6CnByb3RvIDAgMApnbG9iYWwgUm91bmQKYnl0ZWNfMyAvLyAiZGVhZGxpbmUiCmFwcF9nbG9iYWxfZ2V0Cj49Cmdsb2JhbCBDdXJyZW50QXBwbGljYXRpb25BZGRyZXNzCmJhbGFuY2UKYnl0ZWNfMiAvLyAiZ29hbCIKYXBwX2dsb2JhbF9nZXQKPAomJgphc3NlcnQKdHhuIFNlbmRlcgpieXRlY18wIC8vICJjb250cmlidXRlZCIKYXBwX2xvY2FsX2dldApzdG9yZSAwCmxvYWQgMAppbnRjXzAgLy8gMAo+CmFzc2VydAppdHhuX2JlZ2luCmludGNfMSAvLyBwYXkKaXR4bl9maWVsZCBUeXBlRW51bQp0eG4gU2VuZGVyCml0eG5fZmllbGQgUmVjZWl2ZXIKbG9hZCAwCml0eG5fZmllbGQgQW1vdW50Cml0eG5fc3VibWl0CnR4biBTZW5kZXIKYnl0ZWNfMCAvLyAiY29udHJpYnV0ZWQiCmludGNfMCAvLyAwCmFwcF9sb2NhbF9wdXQKcmV0c3ViCgovLyBjcmVhdGVfY2FzdGVyCmNyZWF0ZWNhc3Rlcl84Ogpwcm90byAwIDAKaW50Y18wIC8vIDAKZHVwbiAyCnB1c2hieXRlcyAweCAvLyAiIgp0eG5hIEFwcGxpY2F0aW9uQXJncyAxCmJ0b2kKZnJhbWVfYnVyeSAxCnR4bmEgQXBwbGljYXRpb25BcmdzIDIKYnRvaQpmcmFtZV9idXJ5IDIKdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMwpmcmFtZV9idXJ5IDMKZnJhbWVfZGlnIDEKZnJhbWVfZGlnIDIKZnJhbWVfZGlnIDMKY2FsbHN1YiBjcmVhdGVfMApmcmFtZV9idXJ5IDAKcHVzaGJ5dGVzIDB4MTUxZjdjNzUgLy8gMHgxNTFmN2M3NQpmcmFtZV9kaWcgMAppdG9iCmNvbmNhdApsb2cKcmV0c3ViCgovLyBvcHRfaW5fY2FzdGVyCm9wdGluY2FzdGVyXzk6CnByb3RvIDAgMApjYWxsc3ViIG9wdGluXzEKcmV0c3ViCgovLyBwYXVzZV9jYXN0ZXIKcGF1c2VjYXN0ZXJfMTA6CnByb3RvIDAgMApjYWxsc3ViIHBhdXNlXzMKcmV0c3ViCgovLyB1bnBhdXNlX2Nhc3Rlcgp1bnBhdXNlY2FzdGVyXzExOgpwcm90byAwIDAKY2FsbHN1YiB1bnBhdXNlXzQKcmV0c3ViCgovLyBjb250cmlidXRlX2Nhc3Rlcgpjb250cmlidXRlY2FzdGVyXzEyOgpwcm90byAwIDAKaW50Y18wIC8vIDAKdHhuIEdyb3VwSW5kZXgKaW50Y18xIC8vIDEKLQpmcmFtZV9idXJ5IDAKZnJhbWVfZGlnIDAKZ3R4bnMgVHlwZUVudW0KaW50Y18xIC8vIHBheQo9PQphc3NlcnQKZnJhbWVfZGlnIDAKY2FsbHN1YiBjb250cmlidXRlXzUKcmV0c3ViCgovLyByZWxlYXNlX2Nhc3RlcgpyZWxlYXNlY2FzdGVyXzEzOgpwcm90byAwIDAKY2FsbHN1YiByZWxlYXNlXzYKcmV0c3ViCgovLyByZWZ1bmRfY2FzdGVyCnJlZnVuZGNhc3Rlcl8xNDoKcHJvdG8gMCAwCmNhbGxzdWIgcmVmdW5kXzcKcmV0c3Vi", + "clear": "I3ByYWdtYSB2ZXJzaW9uIDgKcHVzaGludCAwIC8vIDAKcmV0dXJu" + }, + "state": { + "global": { + "num_byte_slices": 1, + "num_uints": 3 + }, + "local": { + "num_byte_slices": 0, + "num_uints": 0 + } + }, + "schema": { + "global": { + "declared": { + "deadline": { + "type": "uint64", + "key": "deadline", + "descr": "" + }, + "goal": { + "type": "uint64", + "key": "goal", + "descr": "" + }, + "paused": { + "type": "uint64", + "key": "paused", + "descr": "" + }, + "receiver": { + "type": "bytes", + "key": "receiver", + "descr": "" + } + }, + "reserved": {} + }, + "local": { + "declared": {}, + "reserved": {} + } + }, + "contract": { + "name": "OGC_Vault", + "methods": [ + { + "name": "create", + "args": [ + { + "type": "uint64", + "name": "goal_amount" + }, + { + "type": "uint64", + "name": "deadline_round" + }, + { + "type": "address", + "name": "receiver_addr" + } + ], + "returns": { + "type": "uint64" + } + }, + { + "name": "opt_in", + "args": [], + "returns": { + "type": "void" + } + }, + { + "name": "pause", + "args": [], + "returns": { + "type": "void" + } + }, + { + "name": "unpause", + "args": [], + "returns": { + "type": "void" + } + }, + { + "name": "contribute", + "args": [ + { + "type": "pay", + "name": "payment" + } + ], + "returns": { + "type": "void" + } + }, + { + "name": "release", + "args": [], + "returns": { + "type": "void" + } + }, + { + "name": "refund", + "args": [], + "returns": { + "type": "void" + } + } + ], + "networks": {} + }, + "bare_call_config": {} +} \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/artifacts/ogc_vault/approval.teal b/ogc-contracts/projects/ogc-contracts/artifacts/ogc_vault/approval.teal new file mode 100644 index 0000000..936c36d --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/artifacts/ogc_vault/approval.teal @@ -0,0 +1,341 @@ +#pragma version 8 +intcblock 0 1 +bytecblock 0x636f6e7472696275746564 0x706175736564 0x676f616c 0x646561646c696e65 0x7265636569766572 +txna ApplicationArgs 0 +pushbytes 0x52735321 // "create(uint64,uint64,address)uint64" +== +bnz main_l14 +txna ApplicationArgs 0 +pushbytes 0x30c6d58a // "opt_in()void" +== +bnz main_l13 +txna ApplicationArgs 0 +pushbytes 0x0178f94b // "pause()void" +== +bnz main_l12 +txna ApplicationArgs 0 +pushbytes 0x1b529de8 // "unpause()void" +== +bnz main_l11 +txna ApplicationArgs 0 +pushbytes 0xfe717b6d // "contribute(pay)void" +== +bnz main_l10 +txna ApplicationArgs 0 +pushbytes 0x076bbd4d // "release()void" +== +bnz main_l9 +txna ApplicationArgs 0 +pushbytes 0x23e629f7 // "refund()void" +== +bnz main_l8 +err +main_l8: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +callsub refundcaster_14 +intc_1 // 1 +return +main_l9: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +callsub releasecaster_13 +intc_1 // 1 +return +main_l10: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +callsub contributecaster_12 +intc_1 // 1 +return +main_l11: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +callsub unpausecaster_11 +intc_1 // 1 +return +main_l12: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +callsub pausecaster_10 +intc_1 // 1 +return +main_l13: +txn OnCompletion +intc_1 // OptIn +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +callsub optincaster_9 +intc_1 // 1 +return +main_l14: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +== +&& +assert +callsub createcaster_8 +intc_1 // 1 +return + +// create +create_0: +proto 3 1 +intc_0 // 0 +bytec_2 // "goal" +frame_dig -3 +app_global_put +bytec_3 // "deadline" +frame_dig -2 +app_global_put +bytec 4 // "receiver" +frame_dig -1 +app_global_put +bytec_1 // "paused" +intc_0 // 0 +app_global_put +global CurrentApplicationID +frame_bury 0 +retsub + +// opt_in +optin_1: +proto 0 0 +txn Sender +bytec_0 // "contributed" +intc_0 // 0 +app_local_put +retsub + +// _not_paused +notpaused_2: +proto 0 0 +bytec_1 // "paused" +app_global_get +intc_0 // 0 +== +assert +retsub + +// pause +pause_3: +proto 0 0 +txn Sender +global CreatorAddress +== +assert +bytec_1 // "paused" +intc_1 // 1 +app_global_put +retsub + +// unpause +unpause_4: +proto 0 0 +txn Sender +global CreatorAddress +== +assert +bytec_1 // "paused" +intc_0 // 0 +app_global_put +retsub + +// contribute +contribute_5: +proto 1 0 +callsub notpaused_2 +frame_dig -1 +gtxns Receiver +global CurrentApplicationAddress +== +frame_dig -1 +gtxns Amount +intc_0 // 0 +> +&& +assert +txn Sender +bytec_0 // "contributed" +txn Sender +bytec_0 // "contributed" +app_local_get +frame_dig -1 +gtxns Amount ++ +app_local_put +retsub + +// release +release_6: +proto 0 0 +global Round +bytec_3 // "deadline" +app_global_get +>= +global CurrentApplicationAddress +balance +bytec_2 // "goal" +app_global_get +>= +&& +assert +itxn_begin +intc_1 // pay +itxn_field TypeEnum +bytec 4 // "receiver" +app_global_get +itxn_field Receiver +global CurrentApplicationAddress +balance +pushint 1000000 // 1000000 +- +itxn_field Amount +itxn_submit +retsub + +// refund +refund_7: +proto 0 0 +global Round +bytec_3 // "deadline" +app_global_get +>= +global CurrentApplicationAddress +balance +bytec_2 // "goal" +app_global_get +< +&& +assert +txn Sender +bytec_0 // "contributed" +app_local_get +store 0 +load 0 +intc_0 // 0 +> +assert +itxn_begin +intc_1 // pay +itxn_field TypeEnum +txn Sender +itxn_field Receiver +load 0 +itxn_field Amount +itxn_submit +txn Sender +bytec_0 // "contributed" +intc_0 // 0 +app_local_put +retsub + +// create_caster +createcaster_8: +proto 0 0 +intc_0 // 0 +dupn 2 +pushbytes 0x // "" +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +txna ApplicationArgs 3 +frame_bury 3 +frame_dig 1 +frame_dig 2 +frame_dig 3 +callsub create_0 +frame_bury 0 +pushbytes 0x151f7c75 // 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// opt_in_caster +optincaster_9: +proto 0 0 +callsub optin_1 +retsub + +// pause_caster +pausecaster_10: +proto 0 0 +callsub pause_3 +retsub + +// unpause_caster +unpausecaster_11: +proto 0 0 +callsub unpause_4 +retsub + +// contribute_caster +contributecaster_12: +proto 0 0 +intc_0 // 0 +txn GroupIndex +intc_1 // 1 +- +frame_bury 0 +frame_dig 0 +gtxns TypeEnum +intc_1 // pay +== +assert +frame_dig 0 +callsub contribute_5 +retsub + +// release_caster +releasecaster_13: +proto 0 0 +callsub release_6 +retsub + +// refund_caster +refundcaster_14: +proto 0 0 +callsub refund_7 +retsub \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/artifacts/ogc_vault/clear.teal b/ogc-contracts/projects/ogc-contracts/artifacts/ogc_vault/clear.teal new file mode 100644 index 0000000..e741f0e --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/artifacts/ogc_vault/clear.teal @@ -0,0 +1,3 @@ +#pragma version 8 +pushint 0 // 0 +return \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/artifacts/ogc_vault/contract.json b/ogc-contracts/projects/ogc-contracts/artifacts/ogc_vault/contract.json new file mode 100644 index 0000000..bd7bd56 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/artifacts/ogc_vault/contract.json @@ -0,0 +1,73 @@ +{ + "name": "OGC_Vault", + "methods": [ + { + "name": "create", + "args": [ + { + "type": "uint64", + "name": "goal_amount" + }, + { + "type": "uint64", + "name": "deadline_round" + }, + { + "type": "address", + "name": "receiver_addr" + } + ], + "returns": { + "type": "uint64" + } + }, + { + "name": "opt_in", + "args": [], + "returns": { + "type": "void" + } + }, + { + "name": "pause", + "args": [], + "returns": { + "type": "void" + } + }, + { + "name": "unpause", + "args": [], + "returns": { + "type": "void" + } + }, + { + "name": "contribute", + "args": [ + { + "type": "pay", + "name": "payment" + } + ], + "returns": { + "type": "void" + } + }, + { + "name": "release", + "args": [], + "returns": { + "type": "void" + } + }, + { + "name": "refund", + "args": [], + "returns": { + "type": "void" + } + } + ], + "networks": {} +} \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/artifacts/simple_vault/application.json b/ogc-contracts/projects/ogc-contracts/artifacts/simple_vault/application.json new file mode 100644 index 0000000..b84dc86 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/artifacts/simple_vault/application.json @@ -0,0 +1,115 @@ +{ + "hints": { + "create(uint64,uint64,address)uint64": { + "call_config": { + "no_op": "CREATE" + } + }, + "contribute(pay)void": { + "call_config": { + "no_op": "CALL" + } + }, + "release()void": { + "call_config": { + "no_op": "CALL" + } + }, + "get_goal()uint64": { + "read_only": true, + "call_config": { + "no_op": "CALL" + } + }, + "get_total()uint64": { + "read_only": true, + "call_config": { + "no_op": "CALL" + } + } + }, + "source": { + "approval": "I3ByYWdtYSB2ZXJzaW9uIDgKaW50Y2Jsb2NrIDAgMQpieXRlY2Jsb2NrIDB4NzQ2Zjc0NjE2YyAweDY3NmY2MTZjIDB4MTUxZjdjNzUgMHg2NDY1NjE2NDZjNjk2ZTY1IDB4NzI2NTYzNjU2OTc2NjU3Mgp0eG5hIEFwcGxpY2F0aW9uQXJncyAwCnB1c2hieXRlcyAweDUyNzM1MzIxIC8vICJjcmVhdGUodWludDY0LHVpbnQ2NCxhZGRyZXNzKXVpbnQ2NCIKPT0KYm56IG1haW5fbDEwCnR4bmEgQXBwbGljYXRpb25BcmdzIDAKcHVzaGJ5dGVzIDB4ZmU3MTdiNmQgLy8gImNvbnRyaWJ1dGUocGF5KXZvaWQiCj09CmJueiBtYWluX2w5CnR4bmEgQXBwbGljYXRpb25BcmdzIDAKcHVzaGJ5dGVzIDB4MDc2YmJkNGQgLy8gInJlbGVhc2UoKXZvaWQiCj09CmJueiBtYWluX2w4CnR4bmEgQXBwbGljYXRpb25BcmdzIDAKcHVzaGJ5dGVzIDB4OTU5OWJiMjUgLy8gImdldF9nb2FsKCl1aW50NjQiCj09CmJueiBtYWluX2w3CnR4bmEgQXBwbGljYXRpb25BcmdzIDAKcHVzaGJ5dGVzIDB4M2Q1NDYwNjQgLy8gImdldF90b3RhbCgpdWludDY0Igo9PQpibnogbWFpbl9sNgplcnIKbWFpbl9sNjoKdHhuIE9uQ29tcGxldGlvbgppbnRjXzAgLy8gTm9PcAo9PQp0eG4gQXBwbGljYXRpb25JRAppbnRjXzAgLy8gMAohPQomJgphc3NlcnQKY2FsbHN1YiBnZXR0b3RhbGNhc3Rlcl85CmludGNfMSAvLyAxCnJldHVybgptYWluX2w3Ogp0eG4gT25Db21wbGV0aW9uCmludGNfMCAvLyBOb09wCj09CnR4biBBcHBsaWNhdGlvbklECmludGNfMCAvLyAwCiE9CiYmCmFzc2VydApjYWxsc3ViIGdldGdvYWxjYXN0ZXJfOAppbnRjXzEgLy8gMQpyZXR1cm4KbWFpbl9sODoKdHhuIE9uQ29tcGxldGlvbgppbnRjXzAgLy8gTm9PcAo9PQp0eG4gQXBwbGljYXRpb25JRAppbnRjXzAgLy8gMAohPQomJgphc3NlcnQKY2FsbHN1YiByZWxlYXNlY2FzdGVyXzcKaW50Y18xIC8vIDEKcmV0dXJuCm1haW5fbDk6CnR4biBPbkNvbXBsZXRpb24KaW50Y18wIC8vIE5vT3AKPT0KdHhuIEFwcGxpY2F0aW9uSUQKaW50Y18wIC8vIDAKIT0KJiYKYXNzZXJ0CmNhbGxzdWIgY29udHJpYnV0ZWNhc3Rlcl82CmludGNfMSAvLyAxCnJldHVybgptYWluX2wxMDoKdHhuIE9uQ29tcGxldGlvbgppbnRjXzAgLy8gTm9PcAo9PQp0eG4gQXBwbGljYXRpb25JRAppbnRjXzAgLy8gMAo9PQomJgphc3NlcnQKY2FsbHN1YiBjcmVhdGVjYXN0ZXJfNQppbnRjXzEgLy8gMQpyZXR1cm4KCi8vIGNyZWF0ZQpjcmVhdGVfMDoKcHJvdG8gMyAxCmludGNfMCAvLyAwCmJ5dGVjXzEgLy8gImdvYWwiCmZyYW1lX2RpZyAtMwphcHBfZ2xvYmFsX3B1dApieXRlY18zIC8vICJkZWFkbGluZSIKZnJhbWVfZGlnIC0yCmFwcF9nbG9iYWxfcHV0CmJ5dGVjIDQgLy8gInJlY2VpdmVyIgpmcmFtZV9kaWcgLTEKYXBwX2dsb2JhbF9wdXQKYnl0ZWNfMCAvLyAidG90YWwiCmludGNfMCAvLyAwCmFwcF9nbG9iYWxfcHV0Cmdsb2JhbCBDdXJyZW50QXBwbGljYXRpb25JRApmcmFtZV9idXJ5IDAKcmV0c3ViCgovLyBjb250cmlidXRlCmNvbnRyaWJ1dGVfMToKcHJvdG8gMSAwCmZyYW1lX2RpZyAtMQpndHhucyBSZWNlaXZlcgpnbG9iYWwgQ3VycmVudEFwcGxpY2F0aW9uQWRkcmVzcwo9PQpmcmFtZV9kaWcgLTEKZ3R4bnMgQW1vdW50CmludGNfMCAvLyAwCj4KJiYKYXNzZXJ0CmJ5dGVjXzAgLy8gInRvdGFsIgpieXRlY18wIC8vICJ0b3RhbCIKYXBwX2dsb2JhbF9nZXQKZnJhbWVfZGlnIC0xCmd0eG5zIEFtb3VudAorCmFwcF9nbG9iYWxfcHV0CnJldHN1YgoKLy8gcmVsZWFzZQpyZWxlYXNlXzI6CnByb3RvIDAgMApnbG9iYWwgUm91bmQKYnl0ZWNfMyAvLyAiZGVhZGxpbmUiCmFwcF9nbG9iYWxfZ2V0Cj49Cmdsb2JhbCBDdXJyZW50QXBwbGljYXRpb25BZGRyZXNzCmJhbGFuY2UKYnl0ZWNfMSAvLyAiZ29hbCIKYXBwX2dsb2JhbF9nZXQKPj0KJiYKYXNzZXJ0Cml0eG5fYmVnaW4KaW50Y18xIC8vIHBheQppdHhuX2ZpZWxkIFR5cGVFbnVtCmJ5dGVjIDQgLy8gInJlY2VpdmVyIgphcHBfZ2xvYmFsX2dldAppdHhuX2ZpZWxkIFJlY2VpdmVyCmdsb2JhbCBDdXJyZW50QXBwbGljYXRpb25BZGRyZXNzCmJhbGFuY2UKcHVzaGludCAxMDAwMDAwIC8vIDEwMDAwMDAKLQppdHhuX2ZpZWxkIEFtb3VudAppdHhuX3N1Ym1pdApyZXRzdWIKCi8vIGdldF9nb2FsCmdldGdvYWxfMzoKcHJvdG8gMCAxCmludGNfMCAvLyAwCmJ5dGVjXzEgLy8gImdvYWwiCmFwcF9nbG9iYWxfZ2V0CmZyYW1lX2J1cnkgMApyZXRzdWIKCi8vIGdldF90b3RhbApnZXR0b3RhbF80Ogpwcm90byAwIDEKaW50Y18wIC8vIDAKYnl0ZWNfMCAvLyAidG90YWwiCmFwcF9nbG9iYWxfZ2V0CmZyYW1lX2J1cnkgMApyZXRzdWIKCi8vIGNyZWF0ZV9jYXN0ZXIKY3JlYXRlY2FzdGVyXzU6CnByb3RvIDAgMAppbnRjXzAgLy8gMApkdXBuIDIKcHVzaGJ5dGVzIDB4IC8vICIiCnR4bmEgQXBwbGljYXRpb25BcmdzIDEKYnRvaQpmcmFtZV9idXJ5IDEKdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgpidG9pCmZyYW1lX2J1cnkgMgp0eG5hIEFwcGxpY2F0aW9uQXJncyAzCmZyYW1lX2J1cnkgMwpmcmFtZV9kaWcgMQpmcmFtZV9kaWcgMgpmcmFtZV9kaWcgMwpjYWxsc3ViIGNyZWF0ZV8wCmZyYW1lX2J1cnkgMApieXRlY18yIC8vIDB4MTUxZjdjNzUKZnJhbWVfZGlnIDAKaXRvYgpjb25jYXQKbG9nCnJldHN1YgoKLy8gY29udHJpYnV0ZV9jYXN0ZXIKY29udHJpYnV0ZWNhc3Rlcl82Ogpwcm90byAwIDAKaW50Y18wIC8vIDAKdHhuIEdyb3VwSW5kZXgKaW50Y18xIC8vIDEKLQpmcmFtZV9idXJ5IDAKZnJhbWVfZGlnIDAKZ3R4bnMgVHlwZUVudW0KaW50Y18xIC8vIHBheQo9PQphc3NlcnQKZnJhbWVfZGlnIDAKY2FsbHN1YiBjb250cmlidXRlXzEKcmV0c3ViCgovLyByZWxlYXNlX2Nhc3RlcgpyZWxlYXNlY2FzdGVyXzc6CnByb3RvIDAgMApjYWxsc3ViIHJlbGVhc2VfMgpyZXRzdWIKCi8vIGdldF9nb2FsX2Nhc3RlcgpnZXRnb2FsY2FzdGVyXzg6CnByb3RvIDAgMAppbnRjXzAgLy8gMApjYWxsc3ViIGdldGdvYWxfMwpmcmFtZV9idXJ5IDAKYnl0ZWNfMiAvLyAweDE1MWY3Yzc1CmZyYW1lX2RpZyAwCml0b2IKY29uY2F0CmxvZwpyZXRzdWIKCi8vIGdldF90b3RhbF9jYXN0ZXIKZ2V0dG90YWxjYXN0ZXJfOToKcHJvdG8gMCAwCmludGNfMCAvLyAwCmNhbGxzdWIgZ2V0dG90YWxfNApmcmFtZV9idXJ5IDAKYnl0ZWNfMiAvLyAweDE1MWY3Yzc1CmZyYW1lX2RpZyAwCml0b2IKY29uY2F0CmxvZwpyZXRzdWI=", + "clear": "I3ByYWdtYSB2ZXJzaW9uIDgKcHVzaGludCAwIC8vIDAKcmV0dXJu" + }, + "state": { + "global": { + "num_byte_slices": 0, + "num_uints": 0 + }, + "local": { + "num_byte_slices": 0, + "num_uints": 0 + } + }, + "schema": { + "global": { + "declared": {}, + "reserved": {} + }, + "local": { + "declared": {}, + "reserved": {} + } + }, + "contract": { + "name": "SimpleVault", + "methods": [ + { + "name": "create", + "args": [ + { + "type": "uint64", + "name": "goal_amount" + }, + { + "type": "uint64", + "name": "deadline_round" + }, + { + "type": "address", + "name": "receiver_addr" + } + ], + "returns": { + "type": "uint64" + } + }, + { + "name": "contribute", + "args": [ + { + "type": "pay", + "name": "payment" + } + ], + "returns": { + "type": "void" + } + }, + { + "name": "release", + "args": [], + "returns": { + "type": "void" + } + }, + { + "name": "get_goal", + "args": [], + "returns": { + "type": "uint64" + } + }, + { + "name": "get_total", + "args": [], + "returns": { + "type": "uint64" + } + } + ], + "networks": {} + }, + "bare_call_config": {} +} \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/artifacts/simple_vault/approval.teal b/ogc-contracts/projects/ogc-contracts/artifacts/simple_vault/approval.teal new file mode 100644 index 0000000..77eb4b3 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/artifacts/simple_vault/approval.teal @@ -0,0 +1,247 @@ +#pragma version 8 +intcblock 0 1 +bytecblock 0x746f74616c 0x676f616c 0x151f7c75 0x646561646c696e65 0x7265636569766572 +txna ApplicationArgs 0 +pushbytes 0x52735321 // "create(uint64,uint64,address)uint64" +== +bnz main_l10 +txna ApplicationArgs 0 +pushbytes 0xfe717b6d // "contribute(pay)void" +== +bnz main_l9 +txna ApplicationArgs 0 +pushbytes 0x076bbd4d // "release()void" +== +bnz main_l8 +txna ApplicationArgs 0 +pushbytes 0x9599bb25 // "get_goal()uint64" +== +bnz main_l7 +txna ApplicationArgs 0 +pushbytes 0x3d546064 // "get_total()uint64" +== +bnz main_l6 +err +main_l6: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +callsub gettotalcaster_9 +intc_1 // 1 +return +main_l7: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +callsub getgoalcaster_8 +intc_1 // 1 +return +main_l8: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +callsub releasecaster_7 +intc_1 // 1 +return +main_l9: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +callsub contributecaster_6 +intc_1 // 1 +return +main_l10: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +== +&& +assert +callsub createcaster_5 +intc_1 // 1 +return + +// create +create_0: +proto 3 1 +intc_0 // 0 +bytec_1 // "goal" +frame_dig -3 +app_global_put +bytec_3 // "deadline" +frame_dig -2 +app_global_put +bytec 4 // "receiver" +frame_dig -1 +app_global_put +bytec_0 // "total" +intc_0 // 0 +app_global_put +global CurrentApplicationID +frame_bury 0 +retsub + +// contribute +contribute_1: +proto 1 0 +frame_dig -1 +gtxns Receiver +global CurrentApplicationAddress +== +frame_dig -1 +gtxns Amount +intc_0 // 0 +> +&& +assert +bytec_0 // "total" +bytec_0 // "total" +app_global_get +frame_dig -1 +gtxns Amount ++ +app_global_put +retsub + +// release +release_2: +proto 0 0 +global Round +bytec_3 // "deadline" +app_global_get +>= +global CurrentApplicationAddress +balance +bytec_1 // "goal" +app_global_get +>= +&& +assert +itxn_begin +intc_1 // pay +itxn_field TypeEnum +bytec 4 // "receiver" +app_global_get +itxn_field Receiver +global CurrentApplicationAddress +balance +pushint 1000000 // 1000000 +- +itxn_field Amount +itxn_submit +retsub + +// get_goal +getgoal_3: +proto 0 1 +intc_0 // 0 +bytec_1 // "goal" +app_global_get +frame_bury 0 +retsub + +// get_total +gettotal_4: +proto 0 1 +intc_0 // 0 +bytec_0 // "total" +app_global_get +frame_bury 0 +retsub + +// create_caster +createcaster_5: +proto 0 0 +intc_0 // 0 +dupn 2 +pushbytes 0x // "" +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +txna ApplicationArgs 3 +frame_bury 3 +frame_dig 1 +frame_dig 2 +frame_dig 3 +callsub create_0 +frame_bury 0 +bytec_2 // 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// contribute_caster +contributecaster_6: +proto 0 0 +intc_0 // 0 +txn GroupIndex +intc_1 // 1 +- +frame_bury 0 +frame_dig 0 +gtxns TypeEnum +intc_1 // pay +== +assert +frame_dig 0 +callsub contribute_1 +retsub + +// release_caster +releasecaster_7: +proto 0 0 +callsub release_2 +retsub + +// get_goal_caster +getgoalcaster_8: +proto 0 0 +intc_0 // 0 +callsub getgoal_3 +frame_bury 0 +bytec_2 // 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// get_total_caster +gettotalcaster_9: +proto 0 0 +intc_0 // 0 +callsub gettotal_4 +frame_bury 0 +bytec_2 // 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/artifacts/simple_vault/clear.teal b/ogc-contracts/projects/ogc-contracts/artifacts/simple_vault/clear.teal new file mode 100644 index 0000000..e741f0e --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/artifacts/simple_vault/clear.teal @@ -0,0 +1,3 @@ +#pragma version 8 +pushint 0 // 0 +return \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/artifacts/simple_vault/contract.json b/ogc-contracts/projects/ogc-contracts/artifacts/simple_vault/contract.json new file mode 100644 index 0000000..206bbef --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/artifacts/simple_vault/contract.json @@ -0,0 +1,59 @@ +{ + "name": "SimpleVault", + "methods": [ + { + "name": "create", + "args": [ + { + "type": "uint64", + "name": "goal_amount" + }, + { + "type": "uint64", + "name": "deadline_round" + }, + { + "type": "address", + "name": "receiver_addr" + } + ], + "returns": { + "type": "uint64" + } + }, + { + "name": "contribute", + "args": [ + { + "type": "pay", + "name": "payment" + } + ], + "returns": { + "type": "void" + } + }, + { + "name": "release", + "args": [], + "returns": { + "type": "void" + } + }, + { + "name": "get_goal", + "args": [], + "returns": { + "type": "uint64" + } + }, + { + "name": "get_total", + "args": [], + "returns": { + "type": "uint64" + } + } + ], + "networks": {} +} \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/artifacts/working_vault/application.json b/ogc-contracts/projects/ogc-contracts/artifacts/working_vault/application.json new file mode 100644 index 0000000..526e2ca --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/artifacts/working_vault/application.json @@ -0,0 +1,136 @@ +{ + "hints": { + "create(uint64,uint64,address)uint64": { + "call_config": { + "no_op": "CREATE" + } + }, + "contribute(pay)void": { + "call_config": { + "no_op": "CALL" + } + }, + "release()void": { + "call_config": { + "no_op": "CALL" + } + }, + "get_goal()uint64": { + "read_only": true, + "call_config": { + "no_op": "CALL" + } + }, + "get_total()uint64": { + "read_only": true, + "call_config": { + "no_op": "CALL" + } + } + }, + "source": { + "approval": "I3ByYWdtYSB2ZXJzaW9uIDgKaW50Y2Jsb2NrIDAgMQpieXRlY2Jsb2NrIDB4NzQ2Zjc0NjE2YyAweDY3NmY2MTZjIDB4MTUxZjdjNzUgMHg2NDY1NjE2NDZjNjk2ZTY1IDB4NzI2NTYzNjU2OTc2NjU3Mgp0eG5hIEFwcGxpY2F0aW9uQXJncyAwCnB1c2hieXRlcyAweDUyNzM1MzIxIC8vICJjcmVhdGUodWludDY0LHVpbnQ2NCxhZGRyZXNzKXVpbnQ2NCIKPT0KYm56IG1haW5fbDEwCnR4bmEgQXBwbGljYXRpb25BcmdzIDAKcHVzaGJ5dGVzIDB4ZmU3MTdiNmQgLy8gImNvbnRyaWJ1dGUocGF5KXZvaWQiCj09CmJueiBtYWluX2w5CnR4bmEgQXBwbGljYXRpb25BcmdzIDAKcHVzaGJ5dGVzIDB4MDc2YmJkNGQgLy8gInJlbGVhc2UoKXZvaWQiCj09CmJueiBtYWluX2w4CnR4bmEgQXBwbGljYXRpb25BcmdzIDAKcHVzaGJ5dGVzIDB4OTU5OWJiMjUgLy8gImdldF9nb2FsKCl1aW50NjQiCj09CmJueiBtYWluX2w3CnR4bmEgQXBwbGljYXRpb25BcmdzIDAKcHVzaGJ5dGVzIDB4M2Q1NDYwNjQgLy8gImdldF90b3RhbCgpdWludDY0Igo9PQpibnogbWFpbl9sNgplcnIKbWFpbl9sNjoKdHhuIE9uQ29tcGxldGlvbgppbnRjXzAgLy8gTm9PcAo9PQp0eG4gQXBwbGljYXRpb25JRAppbnRjXzAgLy8gMAohPQomJgphc3NlcnQKY2FsbHN1YiBnZXR0b3RhbGNhc3Rlcl85CmludGNfMSAvLyAxCnJldHVybgptYWluX2w3Ogp0eG4gT25Db21wbGV0aW9uCmludGNfMCAvLyBOb09wCj09CnR4biBBcHBsaWNhdGlvbklECmludGNfMCAvLyAwCiE9CiYmCmFzc2VydApjYWxsc3ViIGdldGdvYWxjYXN0ZXJfOAppbnRjXzEgLy8gMQpyZXR1cm4KbWFpbl9sODoKdHhuIE9uQ29tcGxldGlvbgppbnRjXzAgLy8gTm9PcAo9PQp0eG4gQXBwbGljYXRpb25JRAppbnRjXzAgLy8gMAohPQomJgphc3NlcnQKY2FsbHN1YiByZWxlYXNlY2FzdGVyXzcKaW50Y18xIC8vIDEKcmV0dXJuCm1haW5fbDk6CnR4biBPbkNvbXBsZXRpb24KaW50Y18wIC8vIE5vT3AKPT0KdHhuIEFwcGxpY2F0aW9uSUQKaW50Y18wIC8vIDAKIT0KJiYKYXNzZXJ0CmNhbGxzdWIgY29udHJpYnV0ZWNhc3Rlcl82CmludGNfMSAvLyAxCnJldHVybgptYWluX2wxMDoKdHhuIE9uQ29tcGxldGlvbgppbnRjXzAgLy8gTm9PcAo9PQp0eG4gQXBwbGljYXRpb25JRAppbnRjXzAgLy8gMAo9PQomJgphc3NlcnQKY2FsbHN1YiBjcmVhdGVjYXN0ZXJfNQppbnRjXzEgLy8gMQpyZXR1cm4KCi8vIGNyZWF0ZQpjcmVhdGVfMDoKcHJvdG8gMyAxCmludGNfMCAvLyAwCmJ5dGVjXzEgLy8gImdvYWwiCmZyYW1lX2RpZyAtMwphcHBfZ2xvYmFsX3B1dApieXRlY18zIC8vICJkZWFkbGluZSIKZnJhbWVfZGlnIC0yCmFwcF9nbG9iYWxfcHV0CmJ5dGVjIDQgLy8gInJlY2VpdmVyIgpmcmFtZV9kaWcgLTEKYXBwX2dsb2JhbF9wdXQKYnl0ZWNfMCAvLyAidG90YWwiCmludGNfMCAvLyAwCmFwcF9nbG9iYWxfcHV0Cmdsb2JhbCBDdXJyZW50QXBwbGljYXRpb25JRApmcmFtZV9idXJ5IDAKcmV0c3ViCgovLyBjb250cmlidXRlCmNvbnRyaWJ1dGVfMToKcHJvdG8gMSAwCmZyYW1lX2RpZyAtMQpndHhucyBSZWNlaXZlcgpnbG9iYWwgQ3VycmVudEFwcGxpY2F0aW9uQWRkcmVzcwo9PQpmcmFtZV9kaWcgLTEKZ3R4bnMgQW1vdW50CmludGNfMCAvLyAwCj4KJiYKYXNzZXJ0CmJ5dGVjXzAgLy8gInRvdGFsIgpieXRlY18wIC8vICJ0b3RhbCIKYXBwX2dsb2JhbF9nZXQKZnJhbWVfZGlnIC0xCmd0eG5zIEFtb3VudAorCmFwcF9nbG9iYWxfcHV0CnJldHN1YgoKLy8gcmVsZWFzZQpyZWxlYXNlXzI6CnByb3RvIDAgMApnbG9iYWwgUm91bmQKYnl0ZWNfMyAvLyAiZGVhZGxpbmUiCmFwcF9nbG9iYWxfZ2V0Cj49Cmdsb2JhbCBDdXJyZW50QXBwbGljYXRpb25BZGRyZXNzCmJhbGFuY2UKYnl0ZWNfMSAvLyAiZ29hbCIKYXBwX2dsb2JhbF9nZXQKPj0KJiYKYXNzZXJ0Cml0eG5fYmVnaW4KaW50Y18xIC8vIHBheQppdHhuX2ZpZWxkIFR5cGVFbnVtCmJ5dGVjIDQgLy8gInJlY2VpdmVyIgphcHBfZ2xvYmFsX2dldAppdHhuX2ZpZWxkIFJlY2VpdmVyCmdsb2JhbCBDdXJyZW50QXBwbGljYXRpb25BZGRyZXNzCmJhbGFuY2UKcHVzaGludCAxMDAwMDAwIC8vIDEwMDAwMDAKLQppdHhuX2ZpZWxkIEFtb3VudAppdHhuX3N1Ym1pdApyZXRzdWIKCi8vIGdldF9nb2FsCmdldGdvYWxfMzoKcHJvdG8gMCAxCmludGNfMCAvLyAwCmJ5dGVjXzEgLy8gImdvYWwiCmFwcF9nbG9iYWxfZ2V0CmZyYW1lX2J1cnkgMApyZXRzdWIKCi8vIGdldF90b3RhbApnZXR0b3RhbF80Ogpwcm90byAwIDEKaW50Y18wIC8vIDAKYnl0ZWNfMCAvLyAidG90YWwiCmFwcF9nbG9iYWxfZ2V0CmZyYW1lX2J1cnkgMApyZXRzdWIKCi8vIGNyZWF0ZV9jYXN0ZXIKY3JlYXRlY2FzdGVyXzU6CnByb3RvIDAgMAppbnRjXzAgLy8gMApkdXBuIDIKcHVzaGJ5dGVzIDB4IC8vICIiCnR4bmEgQXBwbGljYXRpb25BcmdzIDEKYnRvaQpmcmFtZV9idXJ5IDEKdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgpidG9pCmZyYW1lX2J1cnkgMgp0eG5hIEFwcGxpY2F0aW9uQXJncyAzCmZyYW1lX2J1cnkgMwpmcmFtZV9kaWcgMQpmcmFtZV9kaWcgMgpmcmFtZV9kaWcgMwpjYWxsc3ViIGNyZWF0ZV8wCmZyYW1lX2J1cnkgMApieXRlY18yIC8vIDB4MTUxZjdjNzUKZnJhbWVfZGlnIDAKaXRvYgpjb25jYXQKbG9nCnJldHN1YgoKLy8gY29udHJpYnV0ZV9jYXN0ZXIKY29udHJpYnV0ZWNhc3Rlcl82Ogpwcm90byAwIDAKaW50Y18wIC8vIDAKdHhuIEdyb3VwSW5kZXgKaW50Y18xIC8vIDEKLQpmcmFtZV9idXJ5IDAKZnJhbWVfZGlnIDAKZ3R4bnMgVHlwZUVudW0KaW50Y18xIC8vIHBheQo9PQphc3NlcnQKZnJhbWVfZGlnIDAKY2FsbHN1YiBjb250cmlidXRlXzEKcmV0c3ViCgovLyByZWxlYXNlX2Nhc3RlcgpyZWxlYXNlY2FzdGVyXzc6CnByb3RvIDAgMApjYWxsc3ViIHJlbGVhc2VfMgpyZXRzdWIKCi8vIGdldF9nb2FsX2Nhc3RlcgpnZXRnb2FsY2FzdGVyXzg6CnByb3RvIDAgMAppbnRjXzAgLy8gMApjYWxsc3ViIGdldGdvYWxfMwpmcmFtZV9idXJ5IDAKYnl0ZWNfMiAvLyAweDE1MWY3Yzc1CmZyYW1lX2RpZyAwCml0b2IKY29uY2F0CmxvZwpyZXRzdWIKCi8vIGdldF90b3RhbF9jYXN0ZXIKZ2V0dG90YWxjYXN0ZXJfOToKcHJvdG8gMCAwCmludGNfMCAvLyAwCmNhbGxzdWIgZ2V0dG90YWxfNApmcmFtZV9idXJ5IDAKYnl0ZWNfMiAvLyAweDE1MWY3Yzc1CmZyYW1lX2RpZyAwCml0b2IKY29uY2F0CmxvZwpyZXRzdWI=", + "clear": "I3ByYWdtYSB2ZXJzaW9uIDgKcHVzaGludCAwIC8vIDAKcmV0dXJu" + }, + "state": { + "global": { + "num_byte_slices": 1, + "num_uints": 3 + }, + "local": { + "num_byte_slices": 0, + "num_uints": 0 + } + }, + "schema": { + "global": { + "declared": { + "deadline": { + "type": "uint64", + "key": "deadline", + "descr": "" + }, + "goal": { + "type": "uint64", + "key": "goal", + "descr": "" + }, + "receiver": { + "type": "bytes", + "key": "receiver", + "descr": "" + }, + "total": { + "type": "uint64", + "key": "total", + "descr": "" + } + }, + "reserved": {} + }, + "local": { + "declared": {}, + "reserved": {} + } + }, + "contract": { + "name": "WorkingVault", + "methods": [ + { + "name": "create", + "args": [ + { + "type": "uint64", + "name": "goal_amount" + }, + { + "type": "uint64", + "name": "deadline_round" + }, + { + "type": "address", + "name": "receiver_addr" + } + ], + "returns": { + "type": "uint64" + } + }, + { + "name": "contribute", + "args": [ + { + "type": "pay", + "name": "payment" + } + ], + "returns": { + "type": "void" + } + }, + { + "name": "release", + "args": [], + "returns": { + "type": "void" + } + }, + { + "name": "get_goal", + "args": [], + "returns": { + "type": "uint64" + } + }, + { + "name": "get_total", + "args": [], + "returns": { + "type": "uint64" + } + } + ], + "networks": {} + }, + "bare_call_config": {} +} \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/artifacts/working_vault/approval.teal b/ogc-contracts/projects/ogc-contracts/artifacts/working_vault/approval.teal new file mode 100644 index 0000000..77eb4b3 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/artifacts/working_vault/approval.teal @@ -0,0 +1,247 @@ +#pragma version 8 +intcblock 0 1 +bytecblock 0x746f74616c 0x676f616c 0x151f7c75 0x646561646c696e65 0x7265636569766572 +txna ApplicationArgs 0 +pushbytes 0x52735321 // "create(uint64,uint64,address)uint64" +== +bnz main_l10 +txna ApplicationArgs 0 +pushbytes 0xfe717b6d // "contribute(pay)void" +== +bnz main_l9 +txna ApplicationArgs 0 +pushbytes 0x076bbd4d // "release()void" +== +bnz main_l8 +txna ApplicationArgs 0 +pushbytes 0x9599bb25 // "get_goal()uint64" +== +bnz main_l7 +txna ApplicationArgs 0 +pushbytes 0x3d546064 // "get_total()uint64" +== +bnz main_l6 +err +main_l6: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +callsub gettotalcaster_9 +intc_1 // 1 +return +main_l7: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +callsub getgoalcaster_8 +intc_1 // 1 +return +main_l8: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +callsub releasecaster_7 +intc_1 // 1 +return +main_l9: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +callsub contributecaster_6 +intc_1 // 1 +return +main_l10: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +== +&& +assert +callsub createcaster_5 +intc_1 // 1 +return + +// create +create_0: +proto 3 1 +intc_0 // 0 +bytec_1 // "goal" +frame_dig -3 +app_global_put +bytec_3 // "deadline" +frame_dig -2 +app_global_put +bytec 4 // "receiver" +frame_dig -1 +app_global_put +bytec_0 // "total" +intc_0 // 0 +app_global_put +global CurrentApplicationID +frame_bury 0 +retsub + +// contribute +contribute_1: +proto 1 0 +frame_dig -1 +gtxns Receiver +global CurrentApplicationAddress +== +frame_dig -1 +gtxns Amount +intc_0 // 0 +> +&& +assert +bytec_0 // "total" +bytec_0 // "total" +app_global_get +frame_dig -1 +gtxns Amount ++ +app_global_put +retsub + +// release +release_2: +proto 0 0 +global Round +bytec_3 // "deadline" +app_global_get +>= +global CurrentApplicationAddress +balance +bytec_1 // "goal" +app_global_get +>= +&& +assert +itxn_begin +intc_1 // pay +itxn_field TypeEnum +bytec 4 // "receiver" +app_global_get +itxn_field Receiver +global CurrentApplicationAddress +balance +pushint 1000000 // 1000000 +- +itxn_field Amount +itxn_submit +retsub + +// get_goal +getgoal_3: +proto 0 1 +intc_0 // 0 +bytec_1 // "goal" +app_global_get +frame_bury 0 +retsub + +// get_total +gettotal_4: +proto 0 1 +intc_0 // 0 +bytec_0 // "total" +app_global_get +frame_bury 0 +retsub + +// create_caster +createcaster_5: +proto 0 0 +intc_0 // 0 +dupn 2 +pushbytes 0x // "" +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +txna ApplicationArgs 3 +frame_bury 3 +frame_dig 1 +frame_dig 2 +frame_dig 3 +callsub create_0 +frame_bury 0 +bytec_2 // 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// contribute_caster +contributecaster_6: +proto 0 0 +intc_0 // 0 +txn GroupIndex +intc_1 // 1 +- +frame_bury 0 +frame_dig 0 +gtxns TypeEnum +intc_1 // pay +== +assert +frame_dig 0 +callsub contribute_1 +retsub + +// release_caster +releasecaster_7: +proto 0 0 +callsub release_2 +retsub + +// get_goal_caster +getgoalcaster_8: +proto 0 0 +intc_0 // 0 +callsub getgoal_3 +frame_bury 0 +bytec_2 // 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// get_total_caster +gettotalcaster_9: +proto 0 0 +intc_0 // 0 +callsub gettotal_4 +frame_bury 0 +bytec_2 // 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/artifacts/working_vault/clear.teal b/ogc-contracts/projects/ogc-contracts/artifacts/working_vault/clear.teal new file mode 100644 index 0000000..e741f0e --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/artifacts/working_vault/clear.teal @@ -0,0 +1,3 @@ +#pragma version 8 +pushint 0 // 0 +return \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/artifacts/working_vault/contract.json b/ogc-contracts/projects/ogc-contracts/artifacts/working_vault/contract.json new file mode 100644 index 0000000..1b9deb9 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/artifacts/working_vault/contract.json @@ -0,0 +1,59 @@ +{ + "name": "WorkingVault", + "methods": [ + { + "name": "create", + "args": [ + { + "type": "uint64", + "name": "goal_amount" + }, + { + "type": "uint64", + "name": "deadline_round" + }, + { + "type": "address", + "name": "receiver_addr" + } + ], + "returns": { + "type": "uint64" + } + }, + { + "name": "contribute", + "args": [ + { + "type": "pay", + "name": "payment" + } + ], + "returns": { + "type": "void" + } + }, + { + "name": "release", + "args": [], + "returns": { + "type": "void" + } + }, + { + "name": "get_goal", + "args": [], + "returns": { + "type": "uint64" + } + }, + { + "name": "get_total", + "args": [], + "returns": { + "type": "uint64" + } + } + ], + "networks": {} +} \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/build_vault.py b/ogc-contracts/projects/ogc-contracts/build_vault.py new file mode 100644 index 0000000..443a5ab --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/build_vault.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +"""Build script for OGC Vault contract""" + +import os +from smart_contracts.ogc_vault.contract import app + +def main(): + # Create artifacts directory + artifacts_dir = "artifacts/ogc_vault" + os.makedirs(artifacts_dir, exist_ok=True) + + # Build and export the contract + print("Building OGC Vault contract...") + app.build().export(artifacts_dir) + print(f"Contract artifacts exported to {artifacts_dir}") + + # List generated files + print("\nGenerated files:") + for file in os.listdir(artifacts_dir): + print(f" {file}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/deploy_simple.py b/ogc-contracts/projects/ogc-contracts/deploy_simple.py new file mode 100644 index 0000000..dab59fa --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/deploy_simple.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""Deploy simple vault""" + +from beaker import sandbox, client +from simple_vault import app + +def main(): + algod_client = sandbox.get_algod_client() + acct = sandbox.get_accounts().pop() + + goal = 1_000_000 # 1 ALGO + current_round = algod_client.status()["last-round"] + deadline_round = current_round + 1000 + receiver = acct.address + + print(f"Deploying Simple Vault...") + print(f" Goal: {goal} microALGO") + print(f" Deadline: Round {deadline_round}") + print(f" Receiver: {receiver}") + + app_client = client.ApplicationClient( + client=algod_client, + app=app, + signer=acct.signer, + ) + + app_id, app_addr, _ = app_client.create( + goal_amount=goal, + deadline_round=deadline_round, + receiver_addr=receiver, + ) + + print(f"\nโœ… Deployed Simple Vault:") + print(f" APP_ID: {app_id}") + print(f" APP_ADDRESS: {app_addr}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/deploy_vault.py b/ogc-contracts/projects/ogc-contracts/deploy_vault.py new file mode 100644 index 0000000..d2907ba --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/deploy_vault.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""Deploy script for OGC Vault contract""" + +from beaker import sandbox, client +from smart_contracts.ogc_vault.contract import app + +def main(): + # Get sandbox client and account + algod_client = sandbox.get_algod_client() + acct = sandbox.get_accounts().pop() + + # Deploy parameters + goal = 1_000_000 # 1 ALGO goal + current_round = algod_client.status()["last-round"] + deadline_round = current_round + 1000 # ~1 hour from now + receiver = acct.address # Use deployer as receiver for demo + + print(f"Deploying OGC Vault...") + print(f" Goal: {goal} microALGO ({goal/1_000_000} ALGO)") + print(f" Deadline: Round {deadline_round} (current: {current_round})") + print(f" Receiver: {receiver}") + + # Create the app + app_client = client.ApplicationClient( + client=algod_client, + app=app, + signer=acct.signer, + ) + + app_id, app_addr, _ = app_client.create( + goal_amount=goal, + deadline_round=deadline_round, + receiver_addr=receiver, + ) + + print(f"\nโœ… Deployed OGC Vault:") + print(f" APP_ID: {app_id}") + print(f" APP_ADDRESS: {app_addr}") + print(f"\nNext steps:") + print(f"1. Fund the app: python fund_app.py") + print(f"2. Test contribute: python scripts/contribute.py") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/deploy_working.py b/ogc-contracts/projects/ogc-contracts/deploy_working.py new file mode 100644 index 0000000..f16432b --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/deploy_working.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +"""Deploy working vault""" + +from beaker import sandbox, client +from working_vault import app + +def main(): + algod_client = sandbox.get_algod_client() + acct = sandbox.get_accounts().pop() + + goal = 1_000_000 # 1 ALGO + current_round = algod_client.status()["last-round"] + deadline_round = current_round + 1000 + receiver = acct.address + + print(f"Deploying Working Vault...") + print(f" Goal: {goal} microALGO") + print(f" Deadline: Round {deadline_round}") + print(f" Receiver: {receiver}") + + app_client = client.ApplicationClient( + client=algod_client, + app=app, + signer=acct.signer, + ) + + app_id, app_addr, _ = app_client.create( + goal_amount=goal, + deadline_round=deadline_round, + receiver_addr=receiver, + ) + + print(f"\nโœ… Deployed Working Vault:") + print(f" APP_ID: {app_id}") + print(f" APP_ADDRESS: {app_addr}") + + # Fund the app for inner transactions + from algosdk import transaction as tx + sp = algod_client.suggested_params() + fund_txn = tx.PaymentTxn(acct.address, sp, app_addr, 3_000_000) + signed_fund = fund_txn.sign(acct.private_key) + algod_client.send_transaction(signed_fund) + print(f" Funded with 3 ALGO for inner transactions") + + # Test contribute + contribute_amount = 500_000 # 0.5 ALGO + from algosdk.atomic_transaction_composer import TransactionWithSigner + pmt = tx.PaymentTxn(acct.address, sp, app_addr, contribute_amount) + pmt_with_signer = TransactionWithSigner(pmt, acct.signer) + result = app_client.call("contribute", payment=pmt_with_signer) + print(f" Test contribution: {contribute_amount} microALGO - Success!") + + # Check total + total_result = app_client.call("get_total") + print(f" Current total: {total_result.return_value} microALGO") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/fund_app.py b/ogc-contracts/projects/ogc-contracts/fund_app.py new file mode 100644 index 0000000..8490730 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/fund_app.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +"""Fund the deployed app for inner transactions""" + +from beaker import sandbox +from algosdk import transaction as tx + +def main(): + algod = sandbox.get_algod_client() + acct = sandbox.get_accounts().pop() + sp = algod.suggested_params() + + app_addr = input("Paste APP_ADDRESS: ").strip() + + # Fund with 3 ALGO for inner transactions + txn = tx.PaymentTxn(acct.address, sp, app_addr, 3_000_000) + signed_txn = txn.sign(acct.private_key) + + txid = algod.send_transaction(signed_txn) + print(f"Funding transaction sent: {txid}") + print("App funded with 3 ALGO for inner transactions") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/scripts/contribute.py b/ogc-contracts/projects/ogc-contracts/scripts/contribute.py new file mode 100644 index 0000000..99b4440 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/scripts/contribute.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +"""Test contribution to the vault""" + +from beaker import sandbox, client +import sys +sys.path.append('..') +from smart_contracts.ogc_vault.contract import app +from algosdk import transaction as tx + +def main(): + APP_ID = int(input("APP_ID: ").strip()) + AMOUNT = 200_000 # 0.2 ALGO + + algod = sandbox.get_algod_client() + acct = sandbox.get_accounts().pop() + sp = algod.suggested_params() + + # Create app client + app_client = client.ApplicationClient( + client=algod, + app=app, + app_id=APP_ID, + signer=acct.signer, + ) + + # First opt-in to the app + try: + app_client.opt_in() + print("Opted into the app") + except Exception as e: + print(f"Opt-in failed (might already be opted in): {e}") + + # Create payment transaction + pmt = tx.PaymentTxn(acct.address, sp, app_client.app_addr, AMOUNT) + + # Call contribute method with payment + result = app_client.call( + "contribute", + payment=pmt, + ) + + print(f"โœ… Contributed {AMOUNT} microALGO ({AMOUNT/1_000_000} ALGO)") + print(f"Transaction ID: {result.tx_id}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/simple_vault.py b/ogc-contracts/projects/ogc-contracts/simple_vault.py new file mode 100644 index 0000000..44607ae --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/simple_vault.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Simple working vault contract""" + +from pyteal import * +from beaker import * + +# Simple vault without local state for now +app = Application("SimpleVault") + +# Global state +goal = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("goal"), default=Int(0)) +deadline = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("deadline"), default=Int(0)) +receiver = GlobalStateValue(stack_type=TealType.bytes, key=Bytes("receiver"), default=Bytes("")) +total_contributed = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("total"), default=Int(0)) + +@app.create +def create(goal_amount: abi.Uint64, deadline_round: abi.Uint64, receiver_addr: abi.Address, *, output: abi.Uint64): + return Seq( + goal.set(goal_amount.get()), + deadline.set(deadline_round.get()), + receiver.set(receiver_addr.get()), + total_contributed.set(Int(0)), + output.set(Global.current_application_id()), + ) + +@app.external +def contribute(payment: abi.PaymentTransaction): + return Seq( + Assert(And( + payment.get().receiver() == Global.current_application_address(), + payment.get().amount() > Int(0), + )), + total_contributed.set(total_contributed.get() + payment.get().amount()), + ) + +@app.external +def release(): + now = Global.round() + bal = Balance(Global.current_application_address()) + return Seq( + Assert(And(now >= deadline.get(), bal >= goal.get())), + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields({ + TxnField.type_enum: TxnType.Payment, + TxnField.receiver: receiver.get(), + TxnField.amount: bal - Int(1_000_000), + }), + InnerTxnBuilder.Submit(), + ) + +@app.external(read_only=True) +def get_goal(*, output: abi.Uint64): + return output.set(goal.get()) + +@app.external(read_only=True) +def get_total(*, output: abi.Uint64): + return output.set(total_contributed.get()) + +if __name__ == "__main__": + app.build().export("./artifacts/simple_vault") \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/contract.py b/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/contract.py index 00220bb..8a51713 100644 --- a/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/contract.py +++ b/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/contract.py @@ -1,8 +1,90 @@ -from algopy import ARC4Contract, String -from algopy.arc4 import abimethod +from pyteal import * +from beaker import * +class VaultState: + goal = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("goal"), default=Int(0)) + deadline = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("deadline"), default=Int(0)) + receiver = GlobalStateValue(stack_type=TealType.bytes, key=Bytes("receiver"), default=Bytes("")) + paused = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("paused"), default=Int(0)) -class OgcVault(ARC4Contract): - @abimethod() - def hello(self, name: String) -> String: - return "Hello, " + name +class VaultAccountState: + contributed = LocalStateValue(stack_type=TealType.uint64, key=Bytes("contributed"), default=Int(0)) + +app = Application("OGC_Vault", state=VaultState, acct_state=VaultAccountState) + +@app.create +def create(goal_amount: abi.Uint64, deadline_round: abi.Uint64, receiver_addr: abi.Address, *, output: abi.Uint64): + return Seq( + app.state.goal.set(goal_amount.get()), + app.state.deadline.set(deadline_round.get()), + app.state.receiver.set(receiver_addr.get()), + app.state.paused.set(Int(0)), + output.set(Global.current_application_id()), + ) + +@app.opt_in +def opt_in(): + return VaultAccountState.contributed.set(Int(0)) + +@Subroutine(TealType.none) +def _not_paused(): + return Assert(app.state.paused.get() == Int(0)) + +@app.external +def pause(): + return Seq( + Assert(Txn.sender() == Global.creator_address()), + app.state.paused.set(Int(1)) + ) + +@app.external +def unpause(): + return Seq( + Assert(Txn.sender() == Global.creator_address()), + app.state.paused.set(Int(0)) + ) + +@app.external +def contribute(payment: abi.PaymentTransaction): + return Seq( + _not_paused(), + Assert(And( + payment.get().receiver() == Global.current_application_address(), + payment.get().amount() > Int(0), + )), + VaultAccountState.contributed.set(VaultAccountState.contributed.get() + payment.get().amount()), + ) + +@app.external +def release(): + now = Global.round() + bal = Balance(Global.current_application_address()) + return Seq( + Assert(And(now >= app.state.deadline.get(), bal >= app.state.goal.get())), + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields({ + TxnField.type_enum: TxnType.Payment, + TxnField.receiver: app.state.receiver.get(), + TxnField.amount: bal - Int(1_000_000), + }), + InnerTxnBuilder.Submit(), + ) + +@app.external +def refund(): + now = Global.round() + bal = Balance(Global.current_application_address()) + amt = ScratchVar(TealType.uint64) + return Seq( + Assert(And(now >= app.state.deadline.get(), bal < app.state.goal.get())), + amt.store(VaultAccountState.contributed.get()), + Assert(amt.load() > Int(0)), + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields({ + TxnField.type_enum: TxnType.Payment, + TxnField.receiver: Txn.sender(), + TxnField.amount: amt.load(), + }), + InnerTxnBuilder.Submit(), + VaultAccountState.contributed.set(Int(0)), + ) diff --git a/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/deploy_config.py b/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/deploy_config.py index 38199a1..f545be1 100644 --- a/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/deploy_config.py +++ b/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/deploy_config.py @@ -1,44 +1,31 @@ import logging - -import algokit_utils +from beaker import sandbox +from smart_contracts.ogc_vault.contract import app logger = logging.getLogger(__name__) - -# define deployment behaviour based on supplied app spec def deploy() -> None: - from smart_contracts.artifacts.ogc_vault.ogc_vault_client import ( - HelloArgs, - OgcVaultFactory, - ) - - algorand = algokit_utils.AlgorandClient.from_environment() - deployer_ = algorand.account.from_environment("DEPLOYER") - - factory = algorand.client.get_typed_app_factory( - OgcVaultFactory, default_sender=deployer_.address - ) - - app_client, result = factory.deploy( - on_update=algokit_utils.OnUpdate.AppendApp, - on_schema_break=algokit_utils.OnSchemaBreak.AppendApp, - ) - - if result.operation_performed in [ - algokit_utils.OperationPerformed.Create, - algokit_utils.OperationPerformed.Replace, - ]: - algorand.send.payment( - algokit_utils.PaymentParams( - amount=algokit_utils.AlgoAmount(algo=1), - sender=deployer_.address, - receiver=app_client.app_address, - ) - ) - - name = "world" - response = app_client.send.hello(args=HelloArgs(name=name)) - logger.info( - f"Called hello on {app_client.app_name} ({app_client.app_id}) " - f"with name={name}, received: {response.abi_return}" + # Get sandbox client and account + algod_client = sandbox.get_algod_client() + acct = sandbox.get_accounts().pop() + + # Deploy parameters + goal = 1_000_000 # 1 ALGO goal + deadline_round = algod_client.status()["last-round"] + 1000 # ~1 hour from now + receiver = acct.address # Use deployer as receiver for demo + + # Create the app + app_id, app_addr, _ = app.create( + sender=acct, + suggested_params=algod_client.suggested_params(), + goal=goal, + deadline_round=deadline_round, + receiver=receiver, ) + + logger.info(f"Deployed OGC Vault:") + logger.info(f" APP_ID: {app_id}") + logger.info(f" APP_ADDRESS: {app_addr}") + logger.info(f" Goal: {goal} microALGO") + logger.info(f" Deadline: Round {deadline_round}") + logger.info(f" Receiver: {receiver}") diff --git a/ogc-contracts/projects/ogc-contracts/vault.py b/ogc-contracts/projects/ogc-contracts/vault.py new file mode 100644 index 0000000..3d901ba --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/vault.py @@ -0,0 +1,89 @@ +# smart_contracts/vault.py +from pyteal import * +from beaker import * + +class G: + goal = GlobalStateValue(stack_type=TealType.uint64, default=Int(0)) + deadline = GlobalStateValue(stack_type=TealType.uint64, default=Int(0)) # round # + receiver = GlobalStateValue(stack_type=TealType.bytes, default=Bytes("")) + paused = GlobalStateValue(stack_type=TealType.uint64, default=Int(0)) + +class L: + contributed = LocalStateValue(stack_type=TealType.uint64, default=Int(0)) + +app = Application("OGC_Vault", state=G(), acct_state=L()) + +@app.create +def create(goal: abi.Uint64, deadline_round: abi.Uint64, receiver: abi.Address, *, output: abi.Uint64): + return Seq( + app.state.goal.set(goal.get()), + app.state.deadline.set(deadline_round.get()), + app.state.receiver.set(receiver.get()), + app.state.paused.set(Int(0)), + output.set(Global.current_application_id()), + ) + +@app.opt_in +def opt_in(): + return app.acct_state.contributed.set(Int(0)) + +@Subroutine(TealType.none) +def _not_paused(): + return Assert(app.state.paused.get() == Int(0)) + +@app.external +def pause(): + return Assert(Txn.sender() == Global.creator_address(), app.state.paused.set(Int(1))) + +@app.external +def unpause(): + return Assert(Txn.sender() == Global.creator_address(), app.state.paused.set(Int(0))) + +@app.external +def contribute(*, pay: abi.PaymentTransaction): + """Expect group: [ Payment->app , AppCall(contribute, pay=Gtxn[0]) ]""" + return Seq( + _not_paused(), + Assert(And( + pay.get().receiver() == Global.current_application_address(), + pay.get().amount() > Int(0), + )), + app.acct_state.contributed.set(app.acct_state.contributed.get() + pay.get().amount()), + ) + +@app.external +def release(): + now = Global.round() + bal = Balance(Global.current_application_address()) + return Seq( + Assert(And(now >= app.state.deadline.get(), bal >= app.state.goal.get())), + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields({ + TxnField.type_enum: TxnType.Payment, + TxnField.receiver: app.state.receiver.get(), + TxnField.amount: bal - Int(1_000_000), # leave min balance + }), + InnerTxnBuilder.Submit(), + ) + +@app.external +def refund(): + now = Global.round() + bal = Balance(Global.current_application_address()) + amt = ScratchVar(TealType.uint64) + return Seq( + Assert(And(now >= app.state.deadline.get(), bal < app.state.goal.get())), + amt.store(app.acct_state.contributed.get()), + Assert(amt.load() > Int(0)), + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields({ + TxnField.type_enum: TxnType.Payment, + TxnField.receiver: Txn.sender(), + TxnField.amount: amt.load(), + }), + InnerTxnBuilder.Submit(), + app.acct_state.contributed.set(Int(0)), + ) + +if __name__ == "__main__": + app.build().export("./artifacts") diff --git a/ogc-contracts/projects/ogc-contracts/working_vault.py b/ogc-contracts/projects/ogc-contracts/working_vault.py new file mode 100644 index 0000000..0f8d0ec --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/working_vault.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +"""Working vault with proper schema""" + +from pyteal import * +from beaker import * + +class VaultState: + goal = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("goal"), default=Int(0)) + deadline = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("deadline"), default=Int(0)) + receiver = GlobalStateValue(stack_type=TealType.bytes, key=Bytes("receiver"), default=Bytes("")) + total = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("total"), default=Int(0)) + +app = Application("WorkingVault", state=VaultState) + +@app.create +def create(goal_amount: abi.Uint64, deadline_round: abi.Uint64, receiver_addr: abi.Address, *, output: abi.Uint64): + return Seq( + app.state.goal.set(goal_amount.get()), + app.state.deadline.set(deadline_round.get()), + app.state.receiver.set(receiver_addr.get()), + app.state.total.set(Int(0)), + output.set(Global.current_application_id()), + ) + +@app.external +def contribute(payment: abi.PaymentTransaction): + return Seq( + Assert(And( + payment.get().receiver() == Global.current_application_address(), + payment.get().amount() > Int(0), + )), + app.state.total.set(app.state.total.get() + payment.get().amount()), + ) + +@app.external +def release(): + now = Global.round() + bal = Balance(Global.current_application_address()) + return Seq( + Assert(And(now >= app.state.deadline.get(), bal >= app.state.goal.get())), + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields({ + TxnField.type_enum: TxnType.Payment, + TxnField.receiver: app.state.receiver.get(), + TxnField.amount: bal - Int(1_000_000), + }), + InnerTxnBuilder.Submit(), + ) + +@app.external(read_only=True) +def get_goal(*, output: abi.Uint64): + return output.set(app.state.goal.get()) + +@app.external(read_only=True) +def get_total(*, output: abi.Uint64): + return output.set(app.state.total.get()) + +if __name__ == "__main__": + app.build().export("./artifacts/working_vault") \ No newline at end of file From 7bd3c480b0d8327cc079a76405b98a97be5efc95 Mon Sep 17 00:00:00 2001 From: Elton Baidoo Date: Sat, 6 Sep 2025 15:25:10 -0400 Subject: [PATCH 03/16] Update contracts with test and demo --- .../projects/ogc-contracts/README.md | 155 +++++++++-------- .../projects/ogc-contracts/ogc_demo.py | 160 ++++++++++++++++++ .../projects/ogc-contracts/test_vault.py | 141 +++++++++++++++ 3 files changed, 383 insertions(+), 73 deletions(-) create mode 100644 ogc-contracts/projects/ogc-contracts/ogc_demo.py create mode 100644 ogc-contracts/projects/ogc-contracts/test_vault.py diff --git a/ogc-contracts/projects/ogc-contracts/README.md b/ogc-contracts/projects/ogc-contracts/README.md index 36ddaaf..901e38b 100644 --- a/ogc-contracts/projects/ogc-contracts/README.md +++ b/ogc-contracts/projects/ogc-contracts/README.md @@ -1,101 +1,110 @@ -# ogc-contracts +# OGC - Out The Groupchat ๐Ÿš€ -This project has been generated using AlgoKit. See below for default getting started instructions. +**Decentralized Group Funding on Algorand** -# Setup +A hackathon project that solves the classic group coordination problem: "Who's actually going to pay?" -### Pre-requisites +## ๐Ÿ’ก The Problem -- [Python 3.12](https://www.python.org/downloads/) or later -- [Docker](https://www.docker.com/) (only required for LocalNet) +We've all been there: +- Group chat: "Let's book that Airbnb!" +- Everyone: "I'm in!" +- Reality: Only 3 people actually pay, trip cancelled ๐Ÿ˜” -> For interactive tour over the codebase, download [vsls-contrib.codetour](https://marketplace.visualstudio.com/items?itemName=vsls-contrib.codetour) extension for VS Code, then open the [`.codetour.json`](./.tours/getting-started-with-your-algokit-project.tour) file in code tour extension. +## โœจ The Solution -### Initial Setup +**OGC** creates trustless, goal-based funding pools on Algorand: +- Set a funding goal and deadline +- Everyone contributes to a smart contract +- Funds only release when goal is met +- If goal fails, everyone gets refunded automatically -#### 1. Clone the Repository -Start by cloning this repository to your local machine. +## ๐Ÿ—๏ธ How It Works -#### 2. Install Pre-requisites -Ensure the following pre-requisites are installed and properly configured: +``` +1. Create Fund โ†’ Smart contract deployed with goal & deadline +2. Share Link โ†’ Friends contribute directly to contract +3. Goal Met? โ†’ Funds released to organizer +4. Goal Failed? โ†’ Everyone gets refunded automatically +``` -- **Docker**: Required for running a local Algorand network. [Install Docker](https://www.docker.com/). -- **AlgoKit CLI**: Essential for project setup and operations. Install the latest version from [AlgoKit CLI Installation Guide](https://github.com/algorandfoundation/algokit-cli#install). Verify installation with `algokit --version`, expecting `2.0.0` or later. +## ๐Ÿงช Testing -#### 3. Bootstrap Your Local Environment -Run the following commands within the project folder: +Run the complete demo: +```bash +# Activate environment +source venv/bin/activate -- **Install Poetry**: Required for Python dependency management. [Installation Guide](https://python-poetry.org/docs/#installation). Verify with `poetry -V` to see version `1.2`+. -- **Setup Project**: Execute `algokit project bootstrap all` to install dependencies and setup a Python virtual environment in `.venv`. -- **Configure environment**: Execute `algokit generate env-file -a target_network localnet` to create a `.env.localnet` file with default configuration for `localnet`. -- **Start LocalNet**: Use `algokit localnet start` to initiate a local Algorand network. +# Run hackathon demo +python ogc_demo.py -### Development Workflow +# Run comprehensive tests +python test_vault.py +``` -#### Terminal -Directly manage and interact with your project using AlgoKit commands: +## ๐Ÿ“Š Demo Results -1. **Build Contracts**: `algokit project run build` compiles all smart contracts. You can also specify a specific contract by passing the name of the contract folder as an extra argument. -For example: `algokit project run build -- hello_world` will only build the `hello_world` contract. -2. **Deploy**: Use `algokit project deploy localnet` to deploy contracts to the local network. You can also specify a specific contract by passing the name of the contract folder as an extra argument. -For example: `algokit project deploy localnet -- hello_world` will only deploy the `hello_world` contract. +``` +๐ŸŽซ Concert Fund Example: + Target: 50.0 ALGO + Alice contributes: 15.0 ALGO + Bob contributes: 20.0 ALGO + Charlie contributes: 15.0 ALGO + +๐ŸŽ‰ SUCCESS! Goal reached! + Smart Contract: APP_ID 1020 + Total Raised: 50.0 ALGO +``` -#### VS Code -For a seamless experience with breakpoint debugging and other features: +## ๐Ÿ”ง Technical Stack -1. **Open Project**: In VS Code, open the repository root. -2. **Install Extensions**: Follow prompts to install recommended extensions. -3. **Debugging**: - - Use `F5` to start debugging. - - **Windows Users**: Select the Python interpreter at `./.venv/Scripts/python.exe` via `Ctrl/Cmd + Shift + P` > `Python: Select Interpreter` before the first run. +- **Blockchain**: Algorand +- **Smart Contracts**: PyTeal + Beaker +- **Language**: Python +- **Testing**: LocalNet sandbox -#### JetBrains IDEs -While primarily optimized for VS Code, JetBrains IDEs are supported: +## ๐Ÿš€ Features -1. **Open Project**: In your JetBrains IDE, open the repository root. -2. **Automatic Setup**: The IDE should configure the Python interpreter and virtual environment. -3. **Debugging**: Use `Shift+F10` or `Ctrl+R` to start debugging. Note: Windows users may encounter issues with pre-launch tasks due to a known bug. See [JetBrains forums](https://youtrack.jetbrains.com/issue/IDEA-277486/Shell-script-configuration-cannot-run-as-before-launch-task) for workarounds. +- โœ… **Trustless**: No central authority needed +- โœ… **Transparent**: All transactions on-chain +- โœ… **Secure**: Funds locked until conditions met +- โœ… **Automatic**: Refunds if goal not reached +- โœ… **Decentralized**: Runs entirely on Algorand -## AlgoKit Workspaces and Project Management -This project supports both standalone and monorepo setups through AlgoKit workspaces. Leverage [`algokit project run`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/project/run.md) commands for efficient monorepo project orchestration and management across multiple projects within a workspace. +## ๐Ÿ“ Project Structure -## AlgoKit Generators +``` +ogc-contracts/ +โ”œโ”€โ”€ working_vault.py # Main smart contract +โ”œโ”€โ”€ deploy_working.py # Deployment script +โ”œโ”€โ”€ ogc_demo.py # Hackathon demo +โ”œโ”€โ”€ test_vault.py # Test suite +โ””โ”€โ”€ artifacts/ # Compiled contracts +``` -This template provides a set of [algokit generators](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/generate.md) that allow you to further modify the project instantiated from the template to fit your needs, as well as giving you a base to build your own extensions to invoke via the `algokit generate` command. +## ๐ŸŽฏ Use Cases -### Generate Smart Contract +- **Group Travel**: Airbnb, flights, activities +- **Event Planning**: Concert tickets, restaurant bookings +- **Shared Purchases**: Group gifts, bulk orders +- **Community Projects**: Local initiatives, fundraising -By default the template creates a single `HelloWorld` contract under ogc_vault folder in the `smart_contracts` directory. To add a new contract: +## ๐Ÿ† Hackathon Highlights -1. From the root of the project (`../`) execute `algokit generate smart-contract`. This will create a new starter smart contract and deployment configuration file under `{your_contract_name}` subfolder in the `smart_contracts` directory. -2. Each contract potentially has different creation parameters and deployment steps. Hence, you need to define your deployment logic in `deploy_config.py`file. -3. `config.py` file will automatically build all contracts in the `smart_contracts` directory. If you want to build specific contracts manually, modify the default code provided by the template in `config.py` file. +- **Problem**: Group coordination & payment trust +- **Solution**: Algorand smart contract escrow +- **Innovation**: Automatic goal-based fund management +- **Impact**: Eliminates payment coordination friction -> Please note, above is just a suggested convention tailored for the base configuration and structure of this template. The default code supplied by the template in `config.py` and `index.ts` (if using ts clients) files are tailored for the suggested convention. You are free to modify the structure and naming conventions as you see fit. +## ๐Ÿ”ฎ Future Roadmap -### Generate '.env' files +- [ ] Web frontend with Pera Wallet integration +- [ ] Mobile app for easy group creation +- [ ] Multi-token support (ASAs) +- [ ] Recurring payment schedules +- [ ] Integration with group chat platforms -By default the template instance does not contain any env files. Using [`algokit project deploy`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/project/deploy.md) against `localnet` | `testnet` | `mainnet` will use default values for `algod` and `indexer` unless overwritten via `.env` or `.env.{target_network}`. - -To generate a new `.env` or `.env.{target_network}` file, run `algokit generate env-file` - -### 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). - -# 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://dev.algorand.co/), [Why Algorand?](https://dev.algorand.co/getting-started/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. +--- +**Built for Algorand Hackathon** ๐Ÿ—๏ธ +*Making group coordination trustless, one fund at a time* \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/ogc_demo.py b/ogc-contracts/projects/ogc-contracts/ogc_demo.py new file mode 100644 index 0000000..0c19618 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/ogc_demo.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +""" +OGC - Out The Groupchat Demo +Hackathon project showcasing decentralized group funding on Algorand +""" + +from beaker import sandbox, client +from working_vault import app +from algosdk import transaction as tx +from algosdk.atomic_transaction_composer import TransactionWithSigner +import time + +def ogc_demo(): + print("๐Ÿš€ OGC - Out The Groupchat") + print("=" * 50) + print("Decentralized Group Funding on Algorand") + print("Hackathon Demo\n") + + # Simulate group chat scenario + print("๐Ÿ’ฌ Scenario: Group Chat Planning") + print(" 'Hey guys, let's go to that concert!'") + print(" 'I'm in if we get 10 people'") + print(" 'Same, but I need commitment first'") + print(" 'Let's use OGC to collect funds!'") + print() + + # Setup + algod_client = sandbox.get_algod_client() + organizer = sandbox.get_accounts().pop() + alice = sandbox.get_accounts().pop() + bob = sandbox.get_accounts().pop() + charlie = sandbox.get_accounts().pop() + + # Concert ticket scenario + ticket_price = 50_000_000 # 50 ALGO total for group tickets + current_round = algod_client.status()["last-round"] + deadline_round = current_round + 20 # 20 rounds to decide + + print(f"๐ŸŽซ Creating Concert Fund:") + print(f" Organizer: {organizer.address[:10]}...") + print(f" Target: {ticket_price/1_000_000} ALGO") + print(f" Deadline: {deadline_round - current_round} rounds") + + # Deploy vault + app_client = client.ApplicationClient( + client=algod_client, + app=app, + signer=organizer.signer, + ) + + app_id, app_addr, _ = app_client.create( + goal_amount=ticket_price, + deadline_round=deadline_round, + receiver_addr=organizer.address, # Organizer handles ticket purchase + ) + + print(f" โœ… Fund Created: APP_ID {app_id}") + + # Fund app for operations + sp = algod_client.suggested_params() + fund_txn = tx.PaymentTxn(organizer.address, sp, app_addr, 5_000_000) + algod_client.send_transaction(fund_txn.sign(organizer.private_key)) + + print(f"\n๐Ÿ’ฐ Group Members Contributing:") + + # Alice contributes + alice_amount = 15_000_000 # 15 ALGO + print(f" Alice: 'I'm in!' - Contributing {alice_amount/1_000_000} ALGO") + + pmt_alice = tx.PaymentTxn(alice.address, sp, app_addr, alice_amount) + pmt_alice_signed = TransactionWithSigner(pmt_alice, alice.signer) + + app_client_alice = client.ApplicationClient( + client=algod_client, app=app, app_id=app_id, signer=alice.signer + ) + app_client_alice.call("contribute", payment=pmt_alice_signed) + + total_after_alice = app_client.call("get_total").return_value + print(f" ๐Ÿ’ณ Total: {total_after_alice/1_000_000} ALGO") + + # Bob contributes + bob_amount = 20_000_000 # 20 ALGO + print(f" Bob: 'Count me in!' - Contributing {bob_amount/1_000_000} ALGO") + + pmt_bob = tx.PaymentTxn(bob.address, sp, app_addr, bob_amount) + pmt_bob_signed = TransactionWithSigner(pmt_bob, bob.signer) + + app_client_bob = client.ApplicationClient( + client=algod_client, app=app, app_id=app_id, signer=bob.signer + ) + app_client_bob.call("contribute", payment=pmt_bob_signed) + + total_after_bob = app_client.call("get_total").return_value + print(f" ๐Ÿ’ณ Total: {total_after_bob/1_000_000} ALGO") + + # Charlie contributes + charlie_amount = 15_000_000 # 15 ALGO + print(f" Charlie: 'Let's do this!' - Contributing {charlie_amount/1_000_000} ALGO") + + pmt_charlie = tx.PaymentTxn(charlie.address, sp, app_addr, charlie_amount) + pmt_charlie_signed = TransactionWithSigner(pmt_charlie, charlie.signer) + + app_client_charlie = client.ApplicationClient( + client=algod_client, app=app, app_id=app_id, signer=charlie.signer + ) + app_client_charlie.call("contribute", payment=pmt_charlie_signed) + + final_total = app_client.call("get_total").return_value + goal = app_client.call("get_goal").return_value + + print(f" ๐Ÿ’ณ Final Total: {final_total/1_000_000} ALGO") + print(f" ๐ŸŽฏ Goal: {goal/1_000_000} ALGO") + + # Check if goal reached + if final_total >= goal: + print(f"\n๐ŸŽ‰ SUCCESS! Goal Reached!") + print(f" 'We did it! Concert tickets secured!'") + print(f" 'OGC made this so easy and trustless!'") + + # Simulate waiting for deadline + print(f"\nโฐ Waiting for deadline to release funds...") + current_round = algod_client.status()["last-round"] + + if current_round >= deadline_round: + print(f" โœ… Deadline reached - releasing funds") + try: + organizer_balance_before = algod_client.account_info(organizer.address)["amount"] + app_client.call("release") + organizer_balance_after = algod_client.account_info(organizer.address)["amount"] + received = organizer_balance_after - organizer_balance_before + print(f" ๐Ÿ’ฐ Organizer received {received/1_000_000} ALGO") + print(f" ๐ŸŽซ 'Buying tickets now!'") + except Exception as e: + print(f" โš ๏ธ Release will work after deadline: {e}") + else: + print(f" โณ {deadline_round - current_round} rounds until release") + else: + needed = goal - final_total + print(f"\n๐Ÿ˜” Goal not reached - need {needed/1_000_000} more ALGO") + print(f" 'Maybe next time... at least our funds are safe!'") + + print(f"\n๐Ÿ† OGC Demo Complete!") + print(f" Smart Contract: {app_id}") + print(f" Trustless: โœ… No central authority") + print(f" Transparent: โœ… All transactions on-chain") + print(f" Secure: โœ… Funds locked until goal/deadline") + print(f" Decentralized: โœ… Runs on Algorand") + + return { + "app_id": app_id, + "total_raised": final_total, + "goal": goal, + "success": final_total >= goal + } + +if __name__ == "__main__": + result = ogc_demo() + print(f"\n๐Ÿ“Š Final Stats:") + print(f" Raised: {result['total_raised']/1_000_000} ALGO") + print(f" Success: {'๐ŸŽ‰' if result['success'] else '๐Ÿ˜”'}") \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/test_vault.py b/ogc-contracts/projects/ogc-contracts/test_vault.py new file mode 100644 index 0000000..b175c79 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/test_vault.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 +"""Comprehensive test suite for OGC Vault""" + +from beaker import sandbox, client +from working_vault import app +from algosdk import transaction as tx +from algosdk.atomic_transaction_composer import TransactionWithSigner + +def test_vault_complete_flow(): + print("๐Ÿงช Testing OGC Vault Complete Flow\n") + + # Setup + algod_client = sandbox.get_algod_client() + creator = sandbox.get_accounts().pop() + contributor1 = sandbox.get_accounts().pop() + contributor2 = sandbox.get_accounts().pop() + + goal = 2_000_000 # 2 ALGO goal + current_round = algod_client.status()["last-round"] + deadline_round = current_round + 10 # Short deadline for testing + receiver = creator.address + + print(f"๐Ÿ“‹ Test Parameters:") + print(f" Goal: {goal/1_000_000} ALGO") + print(f" Deadline: Round {deadline_round}") + print(f" Current Round: {current_round}") + + # Deploy + app_client = client.ApplicationClient( + client=algod_client, + app=app, + signer=creator.signer, + ) + + app_id, app_addr, _ = app_client.create( + goal_amount=goal, + deadline_round=deadline_round, + receiver_addr=receiver, + ) + + print(f"\nโœ… Deployed Vault:") + print(f" APP_ID: {app_id}") + print(f" APP_ADDRESS: {app_addr}") + + # Fund app for inner transactions + sp = algod_client.suggested_params() + fund_txn = tx.PaymentTxn(creator.address, sp, app_addr, 3_000_000) + algod_client.send_transaction(fund_txn.sign(creator.private_key)) + print(f" Funded with 3 ALGO") + + # Test 1: Multiple contributions + print(f"\n๐Ÿ”„ Test 1: Multiple Contributions") + + # Contribution 1: 0.8 ALGO + contrib1_amount = 800_000 + pmt1 = tx.PaymentTxn(contributor1.address, sp, app_addr, contrib1_amount) + pmt1_with_signer = TransactionWithSigner(pmt1, contributor1.signer) + + app_client_contrib1 = client.ApplicationClient( + client=algod_client, + app=app, + app_id=app_id, + signer=contributor1.signer, + ) + app_client_contrib1.call("contribute", payment=pmt1_with_signer) + + total1 = app_client.call("get_total").return_value + print(f" After contrib1: {total1/1_000_000} ALGO") + + # Contribution 2: 0.7 ALGO + contrib2_amount = 700_000 + pmt2 = tx.PaymentTxn(contributor2.address, sp, app_addr, contrib2_amount) + pmt2_with_signer = TransactionWithSigner(pmt2, contributor2.signer) + + app_client_contrib2 = client.ApplicationClient( + client=algod_client, + app=app, + app_id=app_id, + signer=contributor2.signer, + ) + app_client_contrib2.call("contribute", payment=pmt2_with_signer) + + total2 = app_client.call("get_total").return_value + print(f" After contrib2: {total2/1_000_000} ALGO") + + # Test 2: Check goal status + print(f"\n๐Ÿ“Š Test 2: Goal Status") + goal_amount = app_client.call("get_goal").return_value + current_total = app_client.call("get_total").return_value + + print(f" Goal: {goal_amount/1_000_000} ALGO") + print(f" Current: {current_total/1_000_000} ALGO") + print(f" Progress: {(current_total/goal_amount)*100:.1f}%") + + if current_total >= goal_amount: + print(f" ๐ŸŽฏ GOAL REACHED!") + else: + print(f" โณ Need {(goal_amount-current_total)/1_000_000} more ALGO") + + # Test 3: Wait for deadline and release + print(f"\nโฐ Test 3: Release Mechanism") + current_round = algod_client.status()["last-round"] + print(f" Current round: {current_round}") + print(f" Deadline round: {deadline_round}") + + if current_round >= deadline_round: + print(f" โœ… Past deadline - can release") + if current_total >= goal_amount: + try: + receiver_balance_before = algod_client.account_info(receiver)["amount"] + app_client.call("release") + receiver_balance_after = algod_client.account_info(receiver)["amount"] + received = receiver_balance_after - receiver_balance_before + print(f" ๐Ÿ’ฐ Released {received/1_000_000} ALGO to receiver") + except Exception as e: + print(f" โŒ Release failed: {e}") + else: + print(f" โŒ Goal not reached - cannot release") + else: + print(f" โณ Before deadline - cannot release yet") + + # Test 4: App balance verification + print(f"\n๐Ÿ’ฐ Test 4: Balance Verification") + app_balance = algod_client.account_info(app_addr)["amount"] + print(f" App balance: {app_balance/1_000_000} ALGO") + print(f" Tracked total: {current_total/1_000_000} ALGO") + + return { + "app_id": app_id, + "app_address": app_addr, + "goal": goal_amount, + "total": current_total, + "goal_reached": current_total >= goal_amount + } + +if __name__ == "__main__": + result = test_vault_complete_flow() + print(f"\n๐Ÿ Test Summary:") + print(f" App ID: {result['app_id']}") + print(f" Goal Reached: {'โœ…' if result['goal_reached'] else 'โŒ'}") + print(f" Final Total: {result['total']/1_000_000} ALGO") \ No newline at end of file From 0b5bfff7f40efbf69c41493bc3261776e1da1f70 Mon Sep 17 00:00:00 2001 From: Elton Baidoo Date: Sat, 6 Sep 2025 15:29:28 -0400 Subject: [PATCH 04/16] Git CI workflow for file tests --- .github/workflows/ci.yml | 65 ++++++++++++++++++ .python-version | 1 + ogc-contracts/projects/ogc-contracts/Makefile | 33 +++++++++ .../projects/ogc-contracts/README.md | 35 ++++++++++ .../__pycache__/simple_vault.cpython-313.pyc | Bin 0 -> 4414 bytes .../__pycache__/working_vault.cpython-313.pyc | Bin 0 -> 5248 bytes .../projects/ogc-contracts/ogc_demo.py | 64 +++++++++++++++++ .../projects/ogc-contracts/pyproject.toml | 32 +++++++++ .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 184 bytes .../__pycache__/__main__.cpython-313.pyc | Bin 0 -> 8711 bytes .../__pycache__/contract.cpython-313.pyc | Bin 0 -> 7694 bytes .../__pycache__/deploy_config.cpython-313.pyc | Bin 0 -> 1637 bytes .../projects/ogc-contracts/test_vault.py | 62 +++++++++++++++++ .../projects/ogc-contracts/working_vault.py | 59 ++++++++++++++++ 14 files changed, 351 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .python-version create mode 100644 ogc-contracts/projects/ogc-contracts/Makefile create mode 100644 ogc-contracts/projects/ogc-contracts/README.md create mode 100644 ogc-contracts/projects/ogc-contracts/__pycache__/simple_vault.cpython-313.pyc create mode 100644 ogc-contracts/projects/ogc-contracts/__pycache__/working_vault.cpython-313.pyc create mode 100644 ogc-contracts/projects/ogc-contracts/ogc_demo.py create mode 100644 ogc-contracts/projects/ogc-contracts/pyproject.toml create mode 100644 ogc-contracts/projects/ogc-contracts/smart_contracts/__pycache__/__init__.cpython-313.pyc create mode 100644 ogc-contracts/projects/ogc-contracts/smart_contracts/__pycache__/__main__.cpython-313.pyc create mode 100644 ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/__pycache__/contract.cpython-313.pyc create mode 100644 ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/__pycache__/deploy_config.cpython-313.pyc create mode 100644 ogc-contracts/projects/ogc-contracts/test_vault.py create mode 100644 ogc-contracts/projects/ogc-contracts/working_vault.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..39f21bf --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,65 @@ +name: OGC CI + +on: + push: + branches: [ main, develop, ci-runner ] + pull_request: + branches: [ main, develop ] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: '3.13' + + - name: Install pipx and AlgoKit + run: | + python -m pip install --user pipx + python -m pipx ensurepath + pipx install algokit==2.9.0 + echo "$HOME/.local/bin" >> $GITHUB_PATH + + - name: Verify AlgoKit installation + run: algokit --version + + - name: Start LocalNet + run: | + algokit localnet start + sleep 10 # Give LocalNet time to fully start + + - name: Install Poetry + run: | + python -m pip install poetry==1.8.3 + poetry --version + + - name: Install dependencies + working-directory: ogc-contracts/projects/ogc-contracts + run: | + poetry install --no-interaction --no-root + + - name: Build contracts + working-directory: ogc-contracts/projects/ogc-contracts + run: | + poetry run python working_vault.py + ls -la artifacts/working_vault/ || echo "Artifacts directory not found" + + - name: Run tests + working-directory: ogc-contracts/projects/ogc-contracts + run: | + poetry run python test_vault.py + + - name: Run OGC demo + working-directory: ogc-contracts/projects/ogc-contracts + run: | + poetry run python ogc_demo.py + + - name: Stop LocalNet + if: always() + run: algokit localnet stop \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..7acdc73 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13.7 \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/Makefile b/ogc-contracts/projects/ogc-contracts/Makefile new file mode 100644 index 0000000..3a16316 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/Makefile @@ -0,0 +1,33 @@ +.PHONY: help install build deploy test demo clean + +help: ## Show this help message + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' + +install: ## Install dependencies + poetry install --no-interaction + +build: ## Build smart contracts + poetry run python working_vault.py + @echo "โœ… Contracts built in artifacts/" + +deploy: ## Deploy to LocalNet + poetry run python deploy_working.py + +test: ## Run test suite + poetry run python test_vault.py + +demo: ## Run OGC demo + poetry run python ogc_demo.py + +clean: ## Clean artifacts + rm -rf artifacts/ + rm -rf __pycache__/ + rm -rf .pytest_cache/ + +localnet-start: ## Start LocalNet + algokit localnet start + +localnet-stop: ## Stop LocalNet + algokit localnet stop + +ci: install build test demo ## Run full CI pipeline \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/README.md b/ogc-contracts/projects/ogc-contracts/README.md new file mode 100644 index 0000000..0e659a5 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/README.md @@ -0,0 +1,35 @@ +# OGC Smart Contracts + +Smart contracts for the OGC (Out The Groupchat) project. + +## Quick Start + +```bash +# Install dependencies +make install + +# Build contracts +make build + +# Run tests +make test + +# Run demo +make demo +``` + +## CI/CD + +This project uses GitHub Actions for continuous integration: +- โœ… Builds contracts on every push +- โœ… Runs test suite +- โœ… Validates demo scenarios +- โœ… Uses pinned dependencies for reproducibility + +## Dependencies + +All dependencies are pinned in `pyproject.toml` for reproducible builds: +- `beaker-pyteal==1.1.1` +- `pyteal==0.24.1` +- `py-algorand-sdk==2.10.0` +- `algokit-utils==2.4.0` \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/__pycache__/simple_vault.cpython-313.pyc b/ogc-contracts/projects/ogc-contracts/__pycache__/simple_vault.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f9b31b9ee6b3f00c22237df4b918f67d764ce067 GIT binary patch literal 4414 zcmcIm|8Eq>6`#Ak+uOTafB(X9*vHwp;Ny!AVjGA6g7oiW3gfNmklJ}5j zmp5)xUP6s?-ALjFJ+?IVjr+UNIG?I#%eeix0Fpi?IUascv`7C@K6Lz6kstCgt{0 z-OBOa+xr+tG}QQQD_Y;^S)B{hJ4FB`f2fLCgKM!)Sj`b|;f?9mP~o7^=RB{fSe zFMFPAsaI-!W~VhjaFw=aR*8(SmD-1Xj*uKj2v%f9GNYhptkkX=W|frIFrUWJWMndu zSbL#U%1=!fl=$04d?v3>#?Q&6f*#KnRUOM&{Xax8;juiMt*|XPM&>QwWKk|8I4h*- za`sF{pP5!H?u;^H1#-$sDk0<)IakQ5iX~trtK`oq*g}?HFY0o^3KR8=JXI{Ix>e64 zGZ@A>tB%QM zueWDQ7%Qrtk*B8%`K+wxi)tpHOZaddS%c(GOiqP~p_&hVacI_|}o-~CYJrU~XLvx{PJIgKGjh5|Z%S)!vUkURfzl3-{Ux96r6_}Q1 zrr<`hP5=Wp3?B{d3(JF>K>($l;9fnXQ|vV(yHy)=blMG3a{bF1lN$OdLND0E2}E}Z zrk!qLJU2*(ehoRQ3R4!a>1HOEYGah!*UYX0-yd$}HrR?;!Yu1SnAnsEV}ccn->X3k zLd5S?bC{S&BOa4y*IKtySd-MHXqv^_Ne-_eI_>1w3crL0cS5HTx5sf5{#M9^4c-6@ z!qhIquNs{7ztx_IE%S%w4qZ7?7P}3x+Z1~~;mf@TjoyQ1?_oncTr(dx#JDMT-;+LW z`e@7$w^aOGW2l0-VEDfR3P$Z%{W_K`$NG&}zZo0+EbyoBxSdUm>*97B=p)nHm?UI zyu}x=YHXAg4`>hY`lMAw%Rd5#wQmEnego=|NeOV~j=#$fMKvfkya$en6$`^7_ zPWbmJlX=w&N{aqQUMb`>OMqEQT0v5hWE8bDWd(paLJE|?&+Ke#W0zbMg{8$yrHQG$ zp72_nT728$;H}aiq~Z0kp&7tRL6HIfJAg^~#~c38`Y7@+^82W<`4zKak170kQCMTc z(G=EGy2}u{7RA=G7&FA!y}MX^5S-&fk9b#=AE!0%QK@+|ldOz5Y8hAH8aH z?pO-=HUPqXfbb9sM#@6W5MrhfuQUb8lN0pw6u~1<%mv{I%w>vdVaCBTNkqg~6wi?I z0nd<633pjry})w(Ue$!+!84rTXwIW`1I?WeJoXDugsN98KqZkOMIqh^%zpSZ0Jl{%^M90Wg7CPjfd7Msmp7%pD}Q)6xg;hAC2Z zYoxqzjQ_fQQ@MSM(Z0oO-)4sU7JPm50G5!+Ov!mQlerM-OUhW!pM;8AOV*Yw!-)L`9fkMYA6yf-4F$%RfQ6q$aSRDRKf>bRuu@qqbsTZ}m+Rl=DzQsbEQS6;UHtRy8ml z3Of3DQKV2;(kme+RpluK&fuRQ;I(+={B#lP4-mZx8)m|usba2FP+rAHU~l48?F#&t zIFHBkPqg71)VhdTzvWS&`RX>4kI!;I3z0j!@4t5Uwa5O?PJUkZm9`*_{Pmm>7@PI| zBfRd;`TOtPeedz>Uu-t_95LSd#X@@AeCwDIK0Yhhvls7Pd_4F?)O_WzF_vB!KW>g? zjPMDVZSDC~c+~K);m@OA?)uAddBQxFHCl5EsOhQ4cR6|~y5tRb`0L#5(2dX% z0`k-Sj}AUONXaj?zrFL-JCt0QJYAkVYfPT45Sa>xydqUxYDQlF<&H}o)n&?&J|oiS z2-QC;^sQI4m4Oe`Qb$$!x^K2)!PB|q^?15%Qz{5ZvQ3OKm-z2WCa<^Lj^2n8$)qEh q1WBLxn+qnl_K7cW`M{+EzdBU*wHv}tjv#qPQ3lqm1-8Df0GZ3eECk!;Xvpf;t@tX!#f6Nv3wweuDZJOD%b<-*kYs4fqu>U@h%5nwnm?Ofqr=i73L@dnXT(>@j<@g5W~M{ zr4GJw%}nJ#(n{4DE5XqUzPjfkLSh&pu()gex~z3af3}{-Z|P>t(J&g{I=(ehb2I#E z8edDwHgqDfy;^4P?00v?`{S(78y^U7FmoYW#)eS1;=PYg*va%!zM;jgNlu*^#9qe|A5;svmtti@m-qyrIWL zEjTuF+T`{w^*+fwy`&#GuU!(B-x$*`#kF7pxLn;M_Oswp@X5&2gI~U`ja*!Q?UFtc z)40)hPvTl@DF&BE3cX|igqxg*WCX|^3*tmNBdbQab&~>y?}Sli-KQWX64>yhGwNhU zjXS|8Ap%ActjTu{(U2U=0~DcHKJ+3KbpgMGqMnig_>5ZNy@)k}qU1_T$O;mcTW>O# z4U}!?Vt#_Th`*JP?OcwZU@n_fWj9x>+FB6LSSM!vk2q~L3P~xt&C*@{FFpNk;_pDa z(URkw87?@($xGJ_r#Lx@8-PW@X*d;0HJsy;N@2Q+PrMyO43CL-qb8ogSdvu%lvBw# zz&0%l$wb738;PYbX?BvP2^xj?Ru~A^LC5gKu>@X?_reE3xAI45X3%$m^5<+|&Nu6u zE1xaTvLTHP%^zMovT#JNZPD4*Jkw-k*#?bmnE!RQai7+>PjBqh*)BVQx3i5MT4RUa zxL;>GE%U+IV3w`b*xLJj512o^th2kGvE_6AS^wPw+3F6hx#}%yJU+OqxBZih?ZG; ztN)f%&*@F)AO~JTmUgVtVywDg&wB`2^_k_4u|hRvR~OmUMA03V<<Rf@R`_$s=?naaMLv##tq2W^ z{R{nzLkmOMx^}IuU9a2s_|zAJpAY`&Y_{vP)^%F%8qn$nbat==8F5%%fyhx)K+K!D zrof0608tn!6u@#3NMZ%{Mqsh7OlD$*j1((mp;!SVBPdpg_IA$(yQhI&DQT3Bo28WZxR?^;tYxqcQE=Y1_!tlZ%EtI|I&BS7cm3_pznQZyY*hEr|I1Hzp zzF|0_&{jynW+hTDmQs=kq3I|L?t-84PiP=e_$%gGW?SBGo9TP*ue`e_Th*mib?H?H zbpJsZB6$|V$OoPGcP&O1BH2)j7HY|cI2knl;amWG{4=-eZjwm3_}7@(59+-{VUEvQ0C#hi<{ zX+$sb65<=wOO_b33Mz{7EYw#5eYl+jDH7)ug2oy2ZJ=_lXSU~V{KMp(zWAyPc=+q_Z^< zttO(^?9~G;%dQr)27^cHgqV~C;pUFEb`h(|%k2Qf+tzA(4*51JyGL&rC68 zc*Z30nuO_#&j=h_ty;|+BM}BF;jyIQ88Kh2-edVB&Pr2Itjb`cDv#4)EXoSZ3pFpz z(G-7}+$7x;t{}$TVTUok%*+H%NS^+DSszvk(jaem9VZijA#Rvcc&Gw)pVFZfpwbRYM9G4S~S z?fymi`&)l~i*}dCuV%-uYvb2*L?-7%4vs3WR3V4^cHOPIyqWCwHf?)bL8yGK&<_sI zREBZrt<>d}=Up>(%S`=>gJD|CrQ{HF$utdgaK*D;vUR?CF|-gOlC1^FR*-aYzrCqD icRX`>Z};Eoe|IqJs?l6E%iH$= goal: + print("๐ŸŽ‰ Goal reached!") + else: + print(f"โณ Need {(goal-total)/1_000_000} more ALGO") + + return {"app_id": app_id, "total": total, "goal": goal} + +if __name__ == "__main__": + result = ogc_demo() + print(f"๐Ÿ Demo complete: {result['total']/1_000_000} ALGO raised") \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/pyproject.toml b/ogc-contracts/projects/ogc-contracts/pyproject.toml new file mode 100644 index 0000000..1d46d84 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/pyproject.toml @@ -0,0 +1,32 @@ +[tool.poetry] +name = "ogc-contracts" +version = "0.1.0" +description = "OGC - Out The Groupchat: Decentralized group funding on Algorand" +authors = ["OGC Team"] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.13" +beaker-pyteal = "1.1.1" +pyteal = "0.24.1" +py-algorand-sdk = "2.10.0" +algokit-utils = "2.4.0" +python-dotenv = "1.1.1" +setuptools = "80.9.0" + +[tool.poetry.group.dev.dependencies] +pytest = "^8.0.0" +black = "^24.0.0" +isort = "^5.13.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.black] +line-length = 88 +target-version = ['py313'] + +[tool.isort] +profile = "black" +line_length = 88 \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/smart_contracts/__pycache__/__init__.cpython-313.pyc b/ogc-contracts/projects/ogc-contracts/smart_contracts/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ebf031b2a6bfce9e343df4f50d60cf80fea902e2 GIT binary patch literal 184 zcmey&%ge<81oMmbWPs?$AOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkqYk{m|mnqGJ8j zoRa*!q{PgW{Cs_XcW3?l^km)S{JfH)#N?7<{eq(WtW*#KRk%1ev8W^-p)5W=GcU6w lK3=b&@)n0pZhlH>PO4oIE6^U0JBmS!kIamWj77{q765|gF@yjB literal 0 HcmV?d00001 diff --git a/ogc-contracts/projects/ogc-contracts/smart_contracts/__pycache__/__main__.cpython-313.pyc b/ogc-contracts/projects/ogc-contracts/smart_contracts/__pycache__/__main__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..45e3fa55a76ed862d92673b3c27549d73dce9968 GIT binary patch literal 8711 zcmd5>U2q%Mb-oKM7XLpaKoSIhv?M{1kVKG{C`D38%CaQuhmz>Q3YHR5g8_jh2@}M^ zcR|@gqp@taBWjXVbW)AzPN$-$51~4fF*E5j>P%{%L?t+>L-mlksL&54hS_yl{P-wqatL9z=JF@$b;5evL zTeVrSPjHTz1lO3UpA~9+=rtBMm0P%8;D5i)051qkj}ajlQAOY4|7 zbga6bR?=2+qgRov>xG{iok<{+LSYm-u8peZ+W78rQyi$h;6rGvwH@BjcD|)IfXG}g zRp-!3p;KC^nhSN?=ZKOOhG7LC@V065Dm%|Q*1UO)P=7@KitfTRLRQ4X?otSMjQPiW zglA)&LPHnif*0==8i8NFu`bGIPH3V$>!z*EW$trA3vCSuokA;&+yy_%b-@Kv-~^uZ zzQ*7^V{W1C=oN&b60ETosAfY@+n%(23hhV#3dX`&9NY7CV}%Y%?~d~b?W9jTH9VSR zzQvjybj|v2UQ?rz&I|Mj{>?3uj12lmxH^FXyT2e?ycl2<(}`#@868iG4;Y1gC7PLn zHV)5ZuqcvJOiE28CZS|aO3`>EE@i~jwM#Dtbc!J^j?Ydi<_wO;#0X514Rm%#|N$4f7({6>Eknl`2leL|P=@Y3*J_z&2HuGO! zu56rv!d*0mi0J~e#bA9Wh-=Is=*Yns=iHJVCEnP?^#%D7kjKXf{+khWZF879I5zD<|G9#tNqlvgA^^TlA(JM{HdO$jqdbu}^rPst* zM&8yelRZYN!+`i`A{B`Q)7P;BW~UDx3No>;x6l)dDC*%U)RLf~`|+26*i@VE;`1=P<9riuwO zq0QrK^uZ~GkpmsjX-8UQ0{U#<*_pJ2Gcuo<5_xTx`~;R}_$aSZ z$0sKEM1~jNOvo8I_(!WY3OJChKT+l%4+4Vmv#B_rNbztwId!~ZKJ{iyOcT)sbhrfw zRqWaX5pq^!pcC;t2JZ`^PEcwp?8bzr09j)Jn`Xl(wf3p=(=mfDB;t z&ON#VY>F8rQllxvX{Blorkc^XpVEbOAyc(aEBR3y8r7f&GNLHRk4L-Eq<#Vo>UW_D zCctLDvsr4YlJOI>DX<(;>UqLbm|)fsR9_%W2E%kQz$sihibc?a8X!V3t1?I?#ua;6 z9ICofl~b(h(x)(%a6qq0)LPbOqTq_XJiXc`ljFy0@@*(jSRRs?h^VIo_rRz=_{rq3 z-$W(WVDJhkxA&2^ciFn) z9hkGjAt*RI^3IN&spG%ato0j+F?b$3-6cKbf0T*Ec;~qBxCQ;%VmaQR|8+eBPsJQh zV9}h>!{G8^pU;)$e(#=SKlu_Q;7TF*YspZ%m1xQkd#hWd*8XU_N9}E=>zS zjrfT~QVe1qRthk||2Jgkwj^FmP=|J8X3ecCax8Yfs+V zvtsSdvAy*CD@+7L<$^&}#Ht|88UeDROJ7$k(rhL@n~4Ceibs{t44erN5cn$@&Q)yl zu_uR{a_2=T5blgtOcJOFBj_k1fHwsF2qUmWyn-Rj5c5OIni3F$23+HSYhchE`0p z6U-1XCYTt^Eyl+cnG`dTG8@Rs_y`Png$x^j2k_Tg_cv_O)L8WHTV_|h{kM-i>|WP1 z=9A15n?2`f&)GY27XP|IXYSn4vyI$3Vr^Uru?9{x`5iFJ7A_M1LAm${o`j3fJ6#l` zlF{4JpBR4N=_qlWG*wY5Pv!?VzC8&H{OL%HFj_H3A(&%(67)oV0!y6;t4R>vvS}kj zWIx*yU?_vYk?3WVSQna}@4&Q~1+(g|NCYF;dd-{GA)34`ZR+)G>w+rm(0+uaCYPwn zwnQ|Xq^K7-*Ql1^9S0a_!1~+MRu>99B~EQy5}evDAkxrRA`Re=bYQ3W4lp}tC&BC~ zHl-I_DMOgmsEPx{n|fXwP(+zvAPE>IRF zdQp{bC2kd&O|1<(zrR)HL{4FYCZ8x6l`96-l7+#Vm16{O*8y+#w&kVSL^8e&UX5P| zG(}(+f$QBdX(pWj$U7MTm?iVFG>c;*9|a^;ws>R|_zP;sf|j!rn=NG3wM3IxjK;~# z1gGsIkS!s@TO#=Oc+heQFqw(=MWkuPsCs-kYdKXRB+K#{iC2u#7-ACJY?X904^{BdCPFo#wd=Vep^BZVDhJO={d=pljgE(D6O^$xi@{0E)Dr+U7z{N4}iJqz)rlQ~j)VU4xi8h(3te$T?yg^{KH zWy{hJa;||@cJOm|%e|qW3<1(A+Pp>b^sL#O^Y((PGwXFNL=dIgstvMSDPCoDCi}srJ7N>{1dA`IW69hD^@V`can1jp7 z!lASILuYdb&*i+sD~|Iy)A^DK?F!tN-j|kAtF40@dfjeLX&s!8-!cEloNpa0>5;)( zLeNSEcHA8Pt+f^Qc*k+uQMA4OrCw*YY?zR*XYOp#*D*J|W~-SGtlFB_TD$M3{yO!< z(^B-b7Cp^to`wa>Pe-5vrB!eDl5h3k*%j}(70+)>=bbp`kn9 z&|R!+TyL=La?FjCe8{z{;0(Zj-We#m{Y%b5_fWojXvICW=IbbWUk9q2>Ve3*`e!E8 zd>%MwA%)lmPa=L#L ztUK*z6%Otn0HSURElDV{1#6MfdiZVpJv{HCQ7EdZ99HW_EBJPc&jxRj0^~Ceo-3>m z5dgU@g&Bf%sRsi55GMC;5X)-5Jd6ezK~K@}2%K6(z4K`@=P03wrjS`HRdc{u`cNed zthR6VM1Z~QTF;e^ooFLoY3F$~FU_M1XH{w^bwH_sQu^ZxrGG&v{rb59UpO@i1kMLu zF;qc$T6^_s!dE@85~GTv1n2r3RXJ)k-S4<-Y=;Qr8t0X4Qi3GV-J=E2NIrhn_o9Sh}yA=z3)m_uR|#Q<;= zF1U=Ce9OQDR48`sG|`C_lXmT()B;w43B-8idPb)BN2%Qb=8y<{3wB|N5b!iK{~i3~ zcPMoE+|#mX&3pFVyzr$7c)ej@ecbQ7ZF6UeR&ULnfT7ye(et6@-jU_49JhbXMw&tpvpZVF$ z2Q&Hl1It%_b@dllKXILSYCtW$zvqynp1xab+4JGm2UqhgN9QeToq=^79E2zSV8MSV z??1HaKMYjW)dR??YuvCKch}}z?IjoT?9RJ;z}M&eudetm=RB8-?xuUk?j3vY*qX<8 zZ}`38#o)5{S1rG2S$5|57gs&U0dm|Md2i$sPv_GXSpHcna=8D8q_4V8{K+bNmaY)u zmqv2@z^Z3((<)Y21$g9euGwnuoWFfO*R*%(<)y>9#{Q3O2fuoHngJfl#P$B9;k1kW zIR_8&^+LV&|AJ`r7a${q$KerRE(UrOD**LqEE$z$&^_Xv6eB^~aAq+$R|Fp{uFHxO zBB>-?Gy+bMgVFJr`tbq+vCI_QwW^;wIC*L|lSrz--BS+4BBV(U!f}J^T1?Ild|07h zkuX8R3c1LG9tdj`>v&X7#7@wVkWEpP9l>3sBSt5y zY(@RL;U_)h%1&X&B`Ha-9I;619GD%VGxXBf0$*U@E5aKIxHTghr?5$gLNNd$H6oz# zg&H6bkWP=gqECt`g^l9LYjEpHZc*rUXP*k`O#|u$@(_L(dI-bi!@zsE%Vn4~RQoA% zeTp2PqMA>U5$Y*%h%ptA`w?;%Ej4#~ zZujK);iq~Xqk|qxO~LGYWcDo_UNQS`az(`5vc7GdySR#)9wY0Y+t)c{ubKD$@M6K% zmbbOtG{gKldx5FXGxdMa>6xAqg2#p)=^9G3YU7yiE}SeO$QCa>BU!1|$aof^39_Xt z%i|CI%TxKDV;}kRJug2ajU|tc>09&wE@t|c{Le@RP}<#GsO`wtcC6NRZrDxjx~Dee z-aW?^jrLnN-oA0`tqnU|Ew0;8bI;PNg{FR5g@ZkGlKGd>e;O^L>PwdkgI8$R+ZP_& zn-^YPvHOd5_npz(qmP}t7q6{2gU^g)9t%vqPL{c8p6j2t=dG<9$OIhnB5!jJc}g6| zaD+t29tt%3i0+On)t+RS`uTVXk$kCN&6iI;qxJPmx-RC>yt{-TTNIZ1KlnZ^KJ5RJ zw3IF}a7#LuUC}o_Hd^Km+&O&v@VhtWUtckLADe7*!ksI(ugphpk1llF?|#2~@rC#I zer($P*j(=UVar0zirKei~$Z^z6SHt>jtX KN}h+?;rZX{Fk)-~ literal 0 HcmV?d00001 diff --git a/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/__pycache__/contract.cpython-313.pyc b/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/__pycache__/contract.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d235bee3f0bac4a94896c4cff44318080df5914 GIT binary patch literal 7694 zcmd^EU2GKB6`uVc@2^~hGa*DJ&y`?jV9`Zrb0v3!9x z%d6U~7+;ZG`6Et}^q8KGboJDs%1hZ#Eo77<9)TlX!9(bd!{{}3c{GGJ!*n;w!<&!2 z34XASI<%HW`UY6*8hyi+zALMjX_e``LAx)mBE;V4ywN4*q+vXqjF{mO zxs)pkMY*W_=!cn2|9C;p5p<7PpBYw*a{3LaI5nx5zBiO9Gn7%zUp%Mrtjr^k>H*Z+%_{XhEh_G zf*1-`ND?f+m`zJ^u}HFGC8(Apf*xCjf6kPlTjxK2 z`;)itz4T!7SA&``GI!#nF8o4^oVxVl53$7UXTRJ#yZ4*GgR}YzuV~_HbJA&Dl(pE{ zr9)Qjp4mP3N)Jxydxy1C(%k7W{Zv|uWuUfx-R;$1CT0`&lm~~tDrhH8&55J>iC4Ay z*WNuq*0QD4_K9Jr{)l^0J|P51cs!U4nT^!G`qSw`DPJ)&gm5!LwL~{1L#>VQxIWyR zp=5+K;tOIWX+nmmXU2{c($$th&}+ylWUG-qg=_<|HOO!%>!a6UDZv;{ueJb#orrI^ zeez3bR{CbsxApqI5$$w(E~DtD&uH=ScL%J_*w3s3vykYQB)w(;tx7S&aynh47N(dH zbQ(#Xz>hQ=m}^J`-ZN%Ba}Y_+WQZ9ol!}w3V%i4-BOxl|n()2Mf#ZF|i4~;gII$9^ zI4*@2YezF-ydKy_fvLrASZL0-{ zpsSd{G*Q69$#!@_Bvo$$yTtuH((sUv%+yWS%`{9m7<{wFH(%X-bMLji`kGFi-%_cx zDhz(T#;?EnYa_8uOKj5^>kzh19&jkZP>a33;8|st#R0;~s}MYk zWDBgvvQ9J=(uIs7u}cXNDPJf`R(?QsLMv2)iq?mkXuQ()aq`2Y5pUJvtwwya7T>JL z+wbhZ>-pk<7T>N%dglB+Ki3Mz45}3q3Xf?8rIbXpZAvM7KbZu+t5OtkBua}RO0_0L z8xK>p(5e=y*a*Vr0vlVw%8tI7uGx8HBbW3J{fHr9HbOe#L3RK$eaFt{&7i8}GYTQy zD2Y+OEf5-K6jgQa&4LCRpxZ+Bz#wSJ<%7T~{@(CAb>_nKh07OjylEu*v_zkt*r~;K z>in~dIcquKL%;OZ^2`5|J)`(Qc7Y~cnTQ!Gy(nAaN;Wf45 zHs+1s&t}ldFfDw2Jn$qvWFHKyqTN)&c-Tp?33@a_vw8zq#kHF**FWUJzrT+*%?wNr zTpl!{ty;8Ik8ZpZG};eq?T7XDBU2ot!6!97`CZ3uqvN2~anR@(&^iY6j={O%6UMNp4U78ltHy9f8_wv%XS9wp zIzL{n4|ZhdLF zdF!S955tXDHXHF?E#9lgcj)1rAi{G2;K&C(H`;C{uO*Gak&+f8`;+%HY>&{5qXa(DDY2-|#TH+=#B% zqU%2keIENHMqk%y(RDY}n^V`OtW$-ZkU;Is(dnaCh0l6!O?|pYTeodK)sH!h!{w@-qWrEah zR&t*9H#+k_@6@u=;`*tAMMo2uv%1KhtA$n6+_`>M>pW}i0Ncer#6%#LMs1)7OI`*M zIgAXk)eH-1A{W!IkI1CzUeG=*ErGajrB@(|bCx&#xq_Ur@~i=z;|Q`rWJAb~A{$2b z3bJ}+sCL87 zocvh&P}0Mj{!IXGyPv$5yubC{R%6!@ZPyWf*O1mbq{oix{P6eIt{r^`O6n>?7!u?en5-0>U`UOirE*gUATVHhUuFpubsSpY9Wk% z$Ujw`NZx6?Eq}3D=eLztMG?EB73>~Lu7uwk;LcypnZYqdeglrdnDfzHG$Z}ay_Xef z(ZxXjV_7qJ-1;3M)Nke0^kN1qs5Ayqy9mETPzy5s`2rL%T{n5 zGhPX@$B3L);g(1wfZ(F`0H61HJf4T#a`^kn@ApWa zM;!8>q8`udP^LV{h2kGPbL-UH^MhJ&=#uZR9{+pI?=;VQL!RJO-_7u~Fl5i3;NAUy zJpB8^wD_(1=ePd!7A?+=pEbtMY2)Y0*rx2`yiwY5KF)aqILsrj$J1AlL+McvTD*T~ zW^j5?_t45-tXwk>&3fmdGVtEoch)}g`mMnl=CL#%T|z$Z<#DaX>aE)9tz`~ga8T$B arTOJ{=cp}*()?OmzN{jDBrg&g5O;J_bY$3eEHt%L+Z?FAL3+?lbxF$o~rIm~UDj*cW6RnrYAYcv-A|Z zr)w1wpC7YeWenMWXY%KH08E&b;6GPSvoJDavkIt$|C=MT0kSA_1v1;5!u*>GHoqJ7 zGgM-}H?gxoVMvvk3F3SHNfcJ$6kg`v4m5}w^&LC7oA5c6lB@hGG z*;nQV72&n`A%%?rIeeXa z!G*Q~5steJ(}b=E4YjTzt>$9hbhIjt8BjB-mJQY6HXK4wht@ran#mHZSDgpYkZx~& zAnj<4s<+;ZNYcigJL<=!b*VW$@;6GQ%7?N{fRH3@lAOegNHt4G&c@c}?PfxfO5+;q zQiYyPYDOigGOn{O-1Ssan-^3{|gM(Drb2i0C@m^;pg9H&ThDLSTJ&>h=Dn(n#9I&uymb4`3^k@p`e}IJ%g3_coy^Z@jnOfl2WTu~7I7%+`l7-gR6EV>jFCU4Qd*YS8 znC*($o_MWQdLe*ls#O}KuJlv$-PC+9mH+NWH?{V|ayRvEYkM%A{OaMC4_h~13gFTk z-{!x`6J(Ii_S37~^lC4?Hkg_jT*wY)(tn7;C85h-XnpiZdl?4-@r&~3L9c=A?OYfK=_j>p6AOD#`wXH{!_1!3b?H>sC12}N zdxS|JZH<~Dq^h=7gQ|+?4`?X!_W)(6f00Z$bfe}NjVgQx&61G*%-uK1^nzm;<}rvp f1(~Pdt)Ij3emHX!&a~Hh;klOZCx4My7$*4(;x%G@ literal 0 HcmV?d00001 diff --git a/ogc-contracts/projects/ogc-contracts/test_vault.py b/ogc-contracts/projects/ogc-contracts/test_vault.py new file mode 100644 index 0000000..d2272bf --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/test_vault.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +"""Test suite for OGC Vault""" + +from beaker import sandbox, client +from working_vault import app +from algosdk import transaction as tx +from algosdk.atomic_transaction_composer import TransactionWithSigner + +def test_vault_basic(): + print("๐Ÿงช Testing OGC Vault Basic Flow") + + algod_client = sandbox.get_algod_client() + creator = sandbox.get_accounts().pop() + contributor = sandbox.get_accounts().pop() + + goal = 1_000_000 # 1 ALGO + current_round = algod_client.status()["last-round"] + deadline_round = current_round + 100 + + # Deploy + app_client = client.ApplicationClient( + client=algod_client, + app=app, + signer=creator.signer, + ) + + app_id, app_addr, _ = app_client.create( + goal_amount=goal, + deadline_round=deadline_round, + receiver_addr=creator.address, + ) + + print(f"โœ… Deployed: APP_ID {app_id}") + + # Fund app + sp = algod_client.suggested_params() + fund_txn = tx.PaymentTxn(creator.address, sp, app_addr, 2_000_000) + algod_client.send_transaction(fund_txn.sign(creator.private_key)) + + # Test contribution + contrib_amount = 500_000 + pmt = tx.PaymentTxn(contributor.address, sp, app_addr, contrib_amount) + pmt_with_signer = TransactionWithSigner(pmt, contributor.signer) + + app_client_contrib = client.ApplicationClient( + client=algod_client, app=app, app_id=app_id, signer=contributor.signer + ) + app_client_contrib.call("contribute", payment=pmt_with_signer) + + # Verify + total = app_client.call("get_total").return_value + goal_check = app_client.call("get_goal").return_value + + assert total == contrib_amount, f"Expected {contrib_amount}, got {total}" + assert goal_check == goal, f"Expected {goal}, got {goal_check}" + + print(f"โœ… Contribution test passed: {total/1_000_000} ALGO") + return True + +if __name__ == "__main__": + success = test_vault_basic() + print(f"๐Ÿ Tests {'PASSED' if success else 'FAILED'}") \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/working_vault.py b/ogc-contracts/projects/ogc-contracts/working_vault.py new file mode 100644 index 0000000..0f8d0ec --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/working_vault.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +"""Working vault with proper schema""" + +from pyteal import * +from beaker import * + +class VaultState: + goal = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("goal"), default=Int(0)) + deadline = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("deadline"), default=Int(0)) + receiver = GlobalStateValue(stack_type=TealType.bytes, key=Bytes("receiver"), default=Bytes("")) + total = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("total"), default=Int(0)) + +app = Application("WorkingVault", state=VaultState) + +@app.create +def create(goal_amount: abi.Uint64, deadline_round: abi.Uint64, receiver_addr: abi.Address, *, output: abi.Uint64): + return Seq( + app.state.goal.set(goal_amount.get()), + app.state.deadline.set(deadline_round.get()), + app.state.receiver.set(receiver_addr.get()), + app.state.total.set(Int(0)), + output.set(Global.current_application_id()), + ) + +@app.external +def contribute(payment: abi.PaymentTransaction): + return Seq( + Assert(And( + payment.get().receiver() == Global.current_application_address(), + payment.get().amount() > Int(0), + )), + app.state.total.set(app.state.total.get() + payment.get().amount()), + ) + +@app.external +def release(): + now = Global.round() + bal = Balance(Global.current_application_address()) + return Seq( + Assert(And(now >= app.state.deadline.get(), bal >= app.state.goal.get())), + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields({ + TxnField.type_enum: TxnType.Payment, + TxnField.receiver: app.state.receiver.get(), + TxnField.amount: bal - Int(1_000_000), + }), + InnerTxnBuilder.Submit(), + ) + +@app.external(read_only=True) +def get_goal(*, output: abi.Uint64): + return output.set(app.state.goal.get()) + +@app.external(read_only=True) +def get_total(*, output: abi.Uint64): + return output.set(app.state.total.get()) + +if __name__ == "__main__": + app.build().export("./artifacts/working_vault") \ No newline at end of file From 1540a00ecc5275d9f7003cbc8d45000228f159a4 Mon Sep 17 00:00:00 2001 From: Elton Baidoo Date: Sat, 6 Sep 2025 15:38:03 -0400 Subject: [PATCH 05/16] Update CI runner to be more flexible with file structure naming --- .github/workflows/ci.yml | 73 ++++++++++++++++--- ogc-contracts/projects/ogc-contracts/Makefile | 34 +++++++-- 2 files changed, 91 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39f21bf..267c81c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,26 +39,79 @@ jobs: python -m pip install poetry==1.8.3 poetry --version + - name: Find project directory + id: find-project + run: | + # Find directory with pyproject.toml + PROJECT_DIR=$(find . -name "pyproject.toml" -type f | head -1 | xargs dirname) + if [ -z "$PROJECT_DIR" ]; then + echo "No pyproject.toml found, trying common patterns..." + for pattern in "*contracts*/projects/*" "*contracts*" "contracts" "."; do + if find $pattern -name "*.py" -path "*/smart_contracts/*" 2>/dev/null | head -1; then + PROJECT_DIR=$(find $pattern -name "*.py" -path "*/smart_contracts/*" 2>/dev/null | head -1 | xargs dirname | xargs dirname) + break + fi + done + fi + echo "PROJECT_DIR=$PROJECT_DIR" >> $GITHUB_OUTPUT + echo "Found project directory: $PROJECT_DIR" + - name: Install dependencies - working-directory: ogc-contracts/projects/ogc-contracts + working-directory: ${{ steps.find-project.outputs.PROJECT_DIR }} run: | - poetry install --no-interaction --no-root + if [ -f "pyproject.toml" ]; then + poetry install --no-interaction --no-root + else + echo "No pyproject.toml found, installing with pip" + pip install beaker-pyteal pyteal py-algorand-sdk algokit-utils python-dotenv setuptools + fi - name: Build contracts - working-directory: ogc-contracts/projects/ogc-contracts + working-directory: ${{ steps.find-project.outputs.PROJECT_DIR }} run: | - poetry run python working_vault.py - ls -la artifacts/working_vault/ || echo "Artifacts directory not found" + # Find and build any vault/contract files + for contract in *vault*.py *contract*.py; do + if [ -f "$contract" ]; then + echo "Building $contract..." + if [ -f "pyproject.toml" ]; then + poetry run python "$contract" + else + python "$contract" + fi + fi + done + # List any artifacts created + find . -name "artifacts" -type d -exec ls -la {} \; 2>/dev/null || echo "No artifacts directory found" - name: Run tests - working-directory: ogc-contracts/projects/ogc-contracts + working-directory: ${{ steps.find-project.outputs.PROJECT_DIR }} run: | - poetry run python test_vault.py + # Find and run test files + for test in test_*.py *_test.py; do + if [ -f "$test" ]; then + echo "Running $test..." + if [ -f "pyproject.toml" ]; then + poetry run python "$test" + else + python "$test" + fi + fi + done - - name: Run OGC demo - working-directory: ogc-contracts/projects/ogc-contracts + - name: Run demos + working-directory: ${{ steps.find-project.outputs.PROJECT_DIR }} run: | - poetry run python ogc_demo.py + # Find and run demo files + for demo in *demo*.py demo_*.py; do + if [ -f "$demo" ]; then + echo "Running $demo..." + if [ -f "pyproject.toml" ]; then + poetry run python "$demo" + else + python "$demo" + fi + fi + done - name: Stop LocalNet if: always() diff --git a/ogc-contracts/projects/ogc-contracts/Makefile b/ogc-contracts/projects/ogc-contracts/Makefile index 3a16316..4f9b9c6 100644 --- a/ogc-contracts/projects/ogc-contracts/Makefile +++ b/ogc-contracts/projects/ogc-contracts/Makefile @@ -7,17 +7,39 @@ install: ## Install dependencies poetry install --no-interaction build: ## Build smart contracts - poetry run python working_vault.py + @echo "๐Ÿ”จ Building contracts..." + @for contract in *vault*.py *contract*.py; do \ + if [ -f "$$contract" ]; then \ + echo "Building $$contract..."; \ + poetry run python "$$contract"; \ + fi; \ + done @echo "โœ… Contracts built in artifacts/" deploy: ## Deploy to LocalNet - poetry run python deploy_working.py + @for deploy in deploy_*.py *_deploy.py; do \ + if [ -f "$$deploy" ]; then \ + echo "Running $$deploy..."; \ + poetry run python "$$deploy"; \ + break; \ + fi; \ + done test: ## Run test suite - poetry run python test_vault.py - -demo: ## Run OGC demo - poetry run python ogc_demo.py + @for test in test_*.py *_test.py; do \ + if [ -f "$$test" ]; then \ + echo "Running $$test..."; \ + poetry run python "$$test"; \ + fi; \ + done + +demo: ## Run demos + @for demo in *demo*.py demo_*.py; do \ + if [ -f "$$demo" ]; then \ + echo "Running $$demo..."; \ + poetry run python "$$demo"; \ + fi; \ + done clean: ## Clean artifacts rm -rf artifacts/ From 086102e31a7bb61e8431ddd3f7a8841623e23969 Mon Sep 17 00:00:00 2001 From: Elton Baidoo Date: Sat, 6 Sep 2025 15:58:34 -0400 Subject: [PATCH 06/16] Add setup markdown file --- SETUP.md | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 SETUP.md diff --git a/SETUP.md b/SETUP.md new file mode 100644 index 0000000..63663ae --- /dev/null +++ b/SETUP.md @@ -0,0 +1,122 @@ +# OGC Development Setup + +## ๐Ÿš€ Fresh AlgoKit Setup + +If you just installed AlgoKit and want to start: + +### 1. Install AlgoKit +```bash +# Install AlgoKit +pipx install algokit + +# Verify installation +algokit --version +algokit doctor # Check system health +``` + +### 2. Start LocalNet +```bash +# Start Algorand LocalNet +algokit localnet start + +# Check status +algokit localnet status +``` + +### 3. Navigate to Project +```bash +cd ~/OGC/ogc-contracts/projects/ogc-contracts +``` + +### 4. Activate Environment +```bash +# Activate Poetry virtualenv +source "$(poetry env info --path)/bin/activate" + +# Verify Python version +python --version # Should be 3.13.x +``` + +### 5. Install Dependencies +```bash +# Install with Poetry +poetry install + +# Or manually with pip +pip install beaker-pyteal pyteal py-algorand-sdk algokit-utils +``` + +### 6. Build & Test +```bash +# Build contracts +python working_vault.py + +# Run tests +python test_vault.py + +# Run demo +python ogc_demo.py +``` + +### 7. Stop When Done +```bash +# Stop LocalNet +algokit localnet stop +``` + +## ๐Ÿ”„ Quick Reset Script + +If you need to reset your environment: + +```bash +# One-liner to get back into dev mode +cd ~/OGC/ogc-contracts/projects/ogc-contracts && source "$(poetry env info --path)/bin/activate" && python --version +``` + +Or use the Makefile: +```bash +make dev # Activates environment and drops into shell +``` + +## ๐Ÿ› ๏ธ Development Commands + +```bash +make install # Install dependencies +make build # Build contracts +make test # Run tests +make demo # Run demo +make ci # Full CI pipeline +``` + +## ๐Ÿ” Troubleshooting + +**LocalNet not starting?** +```bash +algokit localnet reset # Reset LocalNet +algokit doctor # Check system health +``` + +**Python version issues?** +```bash +poetry env info # Check Poetry environment +poetry shell # Alternative to source activation +``` + +**Dependencies not found?** +```bash +poetry install --no-cache # Reinstall dependencies +``` + +## ๐Ÿ“ Project Structure + +``` +OGC/ +โ”œโ”€โ”€ SETUP.md # This file +โ”œโ”€โ”€ .github/workflows/ci.yml # CI configuration +โ””โ”€โ”€ ogc-contracts/projects/ogc-contracts/ + โ”œโ”€โ”€ pyproject.toml # Dependencies + โ”œโ”€โ”€ Makefile # Build commands + โ”œโ”€โ”€ working_vault.py # Main contract + โ”œโ”€โ”€ test_vault.py # Tests + โ””โ”€โ”€ ogc_demo.py # Demo +``` \ No newline at end of file From 50b639e6dfee4e287ac4b5b7aae92d7e0a531114 Mon Sep 17 00:00:00 2001 From: Elton Baidoo <146414760+eltonbaidoo@users.noreply.github.com> Date: Sat, 6 Sep 2025 16:00:20 -0400 Subject: [PATCH 07/16] Update directory structure instructions in SETUP.md --- SETUP.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SETUP.md b/SETUP.md index 63663ae..b5bfcd2 100644 --- a/SETUP.md +++ b/SETUP.md @@ -27,6 +27,7 @@ algokit localnet status ```bash cd ~/OGC/ogc-contracts/projects/ogc-contracts ``` +any directory name is fine but it should have the \/projects/\ ### 4. Activate Environment ```bash @@ -119,4 +120,4 @@ OGC/ โ”œโ”€โ”€ working_vault.py # Main contract โ”œโ”€โ”€ test_vault.py # Tests โ””โ”€โ”€ ogc_demo.py # Demo -``` \ No newline at end of file +``` From 3a5f32ad17cc3f4d4481440b91be46a02a9f4b50 Mon Sep 17 00:00:00 2001 From: Elton Baidoo <146414760+eltonbaidoo@users.noreply.github.com> Date: Sat, 6 Sep 2025 16:01:43 -0400 Subject: [PATCH 08/16] Update Build & Test section in SETUP.md --- SETUP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SETUP.md b/SETUP.md index b5bfcd2..77603bd 100644 --- a/SETUP.md +++ b/SETUP.md @@ -47,7 +47,7 @@ poetry install pip install beaker-pyteal pyteal py-algorand-sdk algokit-utils ``` -### 6. Build & Test +### 6. Build & Test (Have to write it or pull it) ```bash # Build contracts python working_vault.py From f21de4dd760bd22a1b9a71e681e1c44d05b3be06 Mon Sep 17 00:00:00 2001 From: Elton Baidoo Date: Sat, 6 Sep 2025 16:34:39 -0400 Subject: [PATCH 09/16] ADD tangible set up --- DEMO.md | 127 ++++++++++++++++++ TANGIBLE_DEMO.md | 127 ++++++++++++++++++ .../__pycache__/working_vault.cpython-313.pyc | Bin 5248 -> 5248 bytes .../projects/ogc-contracts/asa_vault.py | 66 +++++++++ .../ogc-contracts/create_ogc_token.py | 48 +++++++ .../projects/ogc-contracts/test_token.py | 54 ++++++++ .../projects/ogc-contracts/verify_token.py | 33 +++++ 7 files changed, 455 insertions(+) create mode 100644 DEMO.md create mode 100644 TANGIBLE_DEMO.md create mode 100644 ogc-contracts/projects/ogc-contracts/asa_vault.py create mode 100644 ogc-contracts/projects/ogc-contracts/create_ogc_token.py create mode 100644 ogc-contracts/projects/ogc-contracts/test_token.py create mode 100644 ogc-contracts/projects/ogc-contracts/verify_token.py diff --git a/DEMO.md b/DEMO.md new file mode 100644 index 0000000..64f3c54 --- /dev/null +++ b/DEMO.md @@ -0,0 +1,127 @@ +# OGC Demo Commands + +## ๐Ÿ“ Where to Run Commands + +**All commands must be run from:** +```bash +/Users/eltonbaidoo/OGC/ogc-contracts/projects/ogc-contracts/ +``` + +## ๐Ÿš€ Quick Start + +### 1. Navigate & Activate +```bash +cd ~/OGC/ogc-contracts/projects/ogc-contracts +source "$(poetry env info --path)/bin/activate" +python --version # Should show 3.13.x +``` + +### 2. Create OGC Token +```bash +python create_ogc_token.py +``` +**Expected Output:** +``` +๐Ÿช™ Creating OGC Token... +โœ… OGC Token Created! + Asset ID: 1033 + Total Supply: 1,000,000,000 OGC + Creator: VYXZFE4BGH... +``` + +### 3. Verify Token +```bash +python verify_token.py +``` +**Expected Output:** +``` +๐Ÿ” Verifying OGC Token... +โœ… OGC Token Found! + Asset ID: 1033 + Name: OGC Token + Unit: OGC + Total: 1,000,000,000 +``` + +### 4. Demo ALGO Vault +```bash +python ogc_demo.py +``` +**Expected Output:** +``` +๐Ÿš€ OGC - Out The Groupchat Demo +๐ŸŽฏ Goal: 2.0 ALGO +โœ… Fund Created: APP_ID 1036 +๐Ÿ’ฐ Total: 1.5 ALGO +โณ Need 0.5 more ALGO +``` + +### 5. Run Tests +```bash +python test_vault.py +``` +**Expected Output:** +``` +๐Ÿงช Testing OGC Vault Basic Flow +โœ… Deployed: APP_ID 1037 +โœ… Contribution test passed: 0.5 ALGO +๐Ÿ Tests PASSED +``` + +## ๐ŸŽฏ One-Liner Commands + +### Full Demo Sequence +```bash +cd ~/OGC/ogc-contracts/projects/ogc-contracts && source "$(poetry env info --path)/bin/activate" && python create_ogc_token.py && python verify_token.py && python ogc_demo.py +``` + +### Quick Token Demo +```bash +python create_ogc_token.py && python verify_token.py +``` + +### Quick Vault Demo +```bash +python ogc_demo.py && python test_vault.py +``` + +## ๐Ÿ› ๏ธ Makefile Commands + +```bash +make demo # Run ogc_demo.py +make test # Run test_vault.py +make build # Build working_vault.py +make ci # Run full pipeline +``` + +## ๐Ÿ“Š What Each Command Does + +| Command | Purpose | Output | +|---------|---------|---------| +| `create_ogc_token.py` | Mints OGC token on LocalNet | Asset ID number | +| `verify_token.py` | Confirms token exists | Token details | +| `ogc_demo.py` | Shows group funding scenario | ALGO vault demo | +| `test_vault.py` | Tests contract functions | Test results | +| `working_vault.py` | Builds smart contract | Artifacts created | + +## ๐ŸŽค For Hackathon Presentation + +**Run in this order:** +1. `python create_ogc_token.py` - "We minted OGC token" +2. `python verify_token.py` - "Token verified on Algorand" +3. `python ogc_demo.py` - "Group funding demo" +4. `python test_vault.py` - "All tests pass" + +## ๐Ÿ”ง Troubleshooting + +**If commands fail:** +```bash +# Check you're in right directory +pwd # Should show: /Users/eltonbaidoo/OGC/ogc-contracts/projects/ogc-contracts + +# Check environment is active +which python # Should show venv path + +# Reactivate if needed +source "$(poetry env info --path)/bin/activate" +``` \ No newline at end of file diff --git a/TANGIBLE_DEMO.md b/TANGIBLE_DEMO.md new file mode 100644 index 0000000..2b7d75f --- /dev/null +++ b/TANGIBLE_DEMO.md @@ -0,0 +1,127 @@ +# OGC Tangible Demo Commands + +## ๐Ÿ“ Where to Run Commands + +**All commands must be run from:** +```bash +/Users/eltonbaidoo/OGC/ogc-contracts/projects/ogc-contracts/ +``` + +## ๐Ÿš€ Quick Start + +### 1. Navigate & Activate +```bash +cd ~/OGC/ogc-contracts/projects/ogc-contracts +source "$(poetry env info --path)/bin/activate" +python --version # Should show 3.13.x +``` + +### 2. Create OGC Token +```bash +python create_ogc_token.py +``` +**Expected Output:** +``` +๐Ÿช™ Creating OGC Token... +โœ… OGC Token Created! + Asset ID: 1033 + Total Supply: 1,000,000,000 OGC + Creator: VYXZFE4BGH... +``` + +### 3. Verify Token +```bash +python verify_token.py +``` +**Expected Output:** +``` +๐Ÿ” Verifying OGC Token... +โœ… OGC Token Found! + Asset ID: 1033 + Name: OGC Token + Unit: OGC + Total: 1,000,000,000 +``` + +### 4. Demo ALGO Vault +```bash +python ogc_demo.py +``` +**Expected Output:** +``` +๐Ÿš€ OGC - Out The Groupchat Demo +๐ŸŽฏ Goal: 2.0 ALGO +โœ… Fund Created: APP_ID 1036 +๐Ÿ’ฐ Total: 1.5 ALGO +โณ Need 0.5 more ALGO +``` + +### 5. Run Tests +```bash +python test_vault.py +``` +**Expected Output:** +``` +๐Ÿงช Testing OGC Vault Basic Flow +โœ… Deployed: APP_ID 1037 +โœ… Contribution test passed: 0.5 ALGO +๐Ÿ Tests PASSED +``` + +## ๐ŸŽฏ One-Liner Commands + +### Full Demo Sequence +```bash +cd ~/OGC/ogc-contracts/projects/ogc-contracts && source "$(poetry env info --path)/bin/activate" && python create_ogc_token.py && python verify_token.py && python ogc_demo.py +``` + +### Quick Token Demo +```bash +python create_ogc_token.py && python verify_token.py +``` + +### Quick Vault Demo +```bash +python ogc_demo.py && python test_vault.py +``` + +## ๐Ÿ› ๏ธ Makefile Commands + +```bash +make demo # Run ogc_demo.py +make test # Run test_vault.py +make build # Build working_vault.py +make ci # Run full pipeline +``` + +## ๐Ÿ“Š What Each Command Does + +| Command | Purpose | Output | +|---------|---------|---------| +| `create_ogc_token.py` | Mints OGC token on LocalNet | Asset ID number | +| `verify_token.py` | Confirms token exists | Token details | +| `ogc_demo.py` | Shows group funding scenario | ALGO vault demo | +| `test_vault.py` | Tests contract functions | Test results | +| `working_vault.py` | Builds smart contract | Artifacts created | + +## ๐ŸŽค For Hackathon Presentation + +**Run in this order:** +1. `python create_ogc_token.py` - "We minted OGC token" +2. `python verify_token.py` - "Token verified on Algorand" +3. `python ogc_demo.py` - "Group funding demo" +4. `python test_vault.py` - "All tests pass" + +## ๐Ÿ”ง Troubleshooting + +**If commands fail:** +```bash +# Check you're in right directory +pwd # Should show: /Users/eltonbaidoo/OGC/ogc-contracts/projects/ogc-contracts + +# Check environment is active +which python # Should show venv path + +# Reactivate if needed +source "$(poetry env info --path)/bin/activate" +``` \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/__pycache__/working_vault.cpython-313.pyc b/ogc-contracts/projects/ogc-contracts/__pycache__/working_vault.cpython-313.pyc index c2cfc1c946002a05ee40fd3e25cf674a3c0a4b2a..ba728485a9fd32d8e13b68303d6b5d3ffa32190c 100644 GIT binary patch delta 19 YcmZqBY|!NT%*)Hg00gGp8@b9v050zYc>n+a delta 19 YcmZqBY|!NT%*)Hg00b_z8@b9v051asdH?_b diff --git a/ogc-contracts/projects/ogc-contracts/asa_vault.py b/ogc-contracts/projects/ogc-contracts/asa_vault.py new file mode 100644 index 0000000..6282eb8 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/asa_vault.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +"""ASA-compatible vault contract""" + +from pyteal import * +from beaker import * + +class ASAVaultState: + goal = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("goal"), default=Int(0)) + deadline = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("deadline"), default=Int(0)) + receiver = GlobalStateValue(stack_type=TealType.bytes, key=Bytes("receiver"), default=Bytes("")) + asset_id = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("asset_id"), default=Int(0)) + total = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("total"), default=Int(0)) + +app = Application("ASAVault", state=ASAVaultState) + +@app.create +def create(goal_amount: abi.Uint64, deadline_round: abi.Uint64, receiver_addr: abi.Address, asset: abi.Asset, *, output: abi.Uint64): + return Seq( + app.state.goal.set(goal_amount.get()), + app.state.deadline.set(deadline_round.get()), + app.state.receiver.set(receiver_addr.get()), + app.state.asset_id.set(asset.asset_id()), + app.state.total.set(Int(0)), + output.set(Global.current_application_id()), + ) + +@app.external +def contribute_asa(*, axfer: abi.AssetTransferTransaction): + return Seq( + Assert(And( + axfer.get().xfer_asset() == app.state.asset_id.get(), + axfer.get().asset_receiver() == Global.current_application_address(), + axfer.get().asset_amount() > Int(0), + )), + app.state.total.set(app.state.total.get() + axfer.get().asset_amount()), + ) + +@app.external +def release_asa(): + now = Global.round() + return Seq( + Assert(And(now >= app.state.deadline.get(), app.state.total.get() >= app.state.goal.get())), + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields({ + TxnField.type_enum: TxnType.AssetTransfer, + TxnField.xfer_asset: app.state.asset_id.get(), + TxnField.asset_receiver: app.state.receiver.get(), + TxnField.asset_amount: app.state.total.get(), + }), + InnerTxnBuilder.Submit(), + ) + +@app.external(read_only=True) +def get_goal(*, output: abi.Uint64): + return output.set(app.state.goal.get()) + +@app.external(read_only=True) +def get_total(*, output: abi.Uint64): + return output.set(app.state.total.get()) + +@app.external(read_only=True) +def get_asset_id(*, output: abi.Uint64): + return output.set(app.state.asset_id.get()) + +if __name__ == "__main__": + app.build().export("./artifacts/asa_vault") \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/create_ogc_token.py b/ogc-contracts/projects/ogc-contracts/create_ogc_token.py new file mode 100644 index 0000000..e51456f --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/create_ogc_token.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +"""Create OGC Token (ASA) for demo""" + +from beaker import sandbox +from algosdk import transaction as tx + +def create_ogc_token(): + print("๐Ÿช™ Creating OGC Token...") + + algod = sandbox.get_algod_client() + creator = sandbox.get_accounts().pop() + sp = algod.suggested_params() + + # Create ASA + txn = tx.AssetCreateTxn( + sender=creator.address, + sp=sp, + total=1_000_000_000, # 1 billion tokens + decimals=6, # 6 decimal places + default_frozen=False, + unit_name="OGC", + asset_name="OGC Token", + url="https://ogc.example.com", + manager=creator.address, + reserve=creator.address, + freeze=None, + clawback=None, + ) + + stx = txn.sign(creator.private_key) + txid = algod.send_transaction(stx) + + # Wait for confirmation + import time + time.sleep(2) # Simple wait + confirmed = algod.pending_transaction_info(txid) + asset_id = confirmed["asset-index"] + + print(f"โœ… OGC Token Created!") + print(f" Asset ID: {asset_id}") + print(f" Total Supply: 1,000,000,000 OGC") + print(f" Creator: {creator.address}") + + return asset_id, creator + +if __name__ == "__main__": + asset_id, creator = create_ogc_token() + print(f"\n๐Ÿ’ก Use Asset ID {asset_id} in your demos!") \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/test_token.py b/ogc-contracts/projects/ogc-contracts/test_token.py new file mode 100644 index 0000000..57706cf --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/test_token.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +"""Test OGC token transfers""" + +from beaker import sandbox +from algosdk import transaction as tx + +def test_token_transfer(): + print("๐Ÿงช Testing OGC Token Transfer...") + + algod = sandbox.get_algod_client() + creator, alice = sandbox.get_accounts()[:2] + sp = algod.suggested_params() + + # Get asset ID from user + asset_id = int(input("Enter Asset ID from create_ogc_token.py: ")) + + # Alice opts into the token + print("๐Ÿ“ Alice opting into OGC token...") + optin = tx.AssetTransferTxn( + sender=alice.address, + sp=sp, + receiver=alice.address, + amt=0, + index=asset_id + ) + algod.send_transaction(optin.sign(alice.private_key)) + import time + time.sleep(2) # Wait for opt-in to confirm + print("โœ… Alice opted in") + + # Creator sends tokens to Alice + print("๐Ÿ’ธ Sending 1000 OGC to Alice...") + transfer = tx.AssetTransferTxn( + sender=creator.address, + sp=sp, + receiver=alice.address, + amt=1000_000_000, # 1000 OGC (6 decimals) + index=asset_id + ) + algod.send_transaction(transfer.sign(creator.private_key)) + print("โœ… Transfer complete!") + + # Check Alice's balance + alice_info = algod.account_info(alice.address) + for asset in alice_info.get("assets", []): + if asset["asset-id"] == asset_id: + balance = asset["amount"] / 1_000_000 # Convert from micro-units + print(f"๐Ÿ’ฐ Alice's OGC balance: {balance} OGC") + break + + print("๐ŸŽ‰ Token test successful!") + +if __name__ == "__main__": + test_token_transfer() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/verify_token.py b/ogc-contracts/projects/ogc-contracts/verify_token.py new file mode 100644 index 0000000..ed983c3 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/verify_token.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +"""Verify OGC token exists""" + +from beaker import sandbox + +def verify_token(): + print("๐Ÿ” Verifying OGC Token...") + + algod = sandbox.get_algod_client() + asset_id = 1033 # From create_ogc_token.py output + + try: + # Get asset info + asset_info = algod.asset_info(asset_id) + params = asset_info["params"] + + print(f"โœ… OGC Token Found!") + print(f" Asset ID: {asset_id}") + print(f" Name: {params['name']}") + print(f" Unit: {params['unit-name']}") + print(f" Total: {params['total']:,}") + print(f" Decimals: {params['decimals']}") + print(f" Creator: {params['creator']}") + + return True + + except Exception as e: + print(f"โŒ Token not found: {e}") + return False + +if __name__ == "__main__": + success = verify_token() + print(f"๐Ÿ Verification {'PASSED' if success else 'FAILED'}") \ No newline at end of file From c08512360d45a67a99f43f91b09219e1d8ff8199 Mon Sep 17 00:00:00 2001 From: Elton Baidoo Date: Sat, 6 Sep 2025 17:00:36 -0400 Subject: [PATCH 10/16] eb-main --- ogc-contracts/projects/ogc-contracts/asa_vault.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ogc-contracts/projects/ogc-contracts/asa_vault.py b/ogc-contracts/projects/ogc-contracts/asa_vault.py index 6282eb8..9ba08b0 100644 --- a/ogc-contracts/projects/ogc-contracts/asa_vault.py +++ b/ogc-contracts/projects/ogc-contracts/asa_vault.py @@ -25,7 +25,7 @@ def create(goal_amount: abi.Uint64, deadline_round: abi.Uint64, receiver_addr: a ) @app.external -def contribute_asa(*, axfer: abi.AssetTransferTransaction): +def contribute_asa(axfer: abi.AssetTransferTransaction): return Seq( Assert(And( axfer.get().xfer_asset() == app.state.asset_id.get(), From f704d4ada72e2d319807c1cd6ec43fbc65d064f6 Mon Sep 17 00:00:00 2001 From: Elton Baidoo Date: Sat, 6 Sep 2025 17:22:36 -0400 Subject: [PATCH 11/16] Update poetry.lock to match pyproject.toml --- .../projects/ogc-contracts/poetry.lock | 635 +++++++++++------- 1 file changed, 382 insertions(+), 253 deletions(-) diff --git a/ogc-contracts/projects/ogc-contracts/poetry.lock b/ogc-contracts/projects/ogc-contracts/poetry.lock index a4fe3de..458c6e5 100644 --- a/ogc-contracts/projects/ogc-contracts/poetry.lock +++ b/ogc-contracts/projects/ogc-contracts/poetry.lock @@ -1,64 +1,20 @@ # This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. -[[package]] -name = "algokit-client-generator" -version = "2.2.0" -description = "Algorand typed client Generator" -optional = false -python-versions = "<4.0,>=3.10" -groups = ["dev"] -files = [ - {file = "algokit_client_generator-2.2.0-py3-none-any.whl", hash = "sha256:f723aa77fb265b38e98836b13b830789c46467ba647815ff4a0a7dee3f64b74a"}, -] - -[package.dependencies] -algokit-utils = ">=4.0.1,<5.0.0" - [[package]] name = "algokit-utils" -version = "4.2.0" +version = "2.4.0" description = "Utilities for Algorand development for use by AlgoKit" optional = false python-versions = "<4.0,>=3.10" -groups = ["main", "dev"] -files = [ - {file = "algokit_utils-4.2.0-py3-none-any.whl", hash = "sha256:e6adb1779971c5748c5f518b9864e4ca20d80e411c393616ea986bc097df4647"}, -] - -[package.dependencies] -httpx = ">=0.23.1,<=0.28.1" -py-algorand-sdk = ">=2.4.0,<3.0.0" -typing-extensions = ">=4.6.0" - -[[package]] -name = "algorand-python" -version = "2.9.0" -description = "API for writing Algorand Python Smart contracts" -optional = false -python-versions = "<4.0,>=3.12" groups = ["main"] files = [ - {file = "algorand_python-2.9.0-py3-none-any.whl", hash = "sha256:37780bcfe8f8fce106a8188d9d643b83d87dd39f5ca3cf3300296b74171af804"}, -] - -[[package]] -name = "algorand-python-testing" -version = "0.5.0" -description = "Algorand Python testing library" -optional = false -python-versions = ">=3.12" -groups = ["main"] -files = [ - {file = "algorand_python_testing-0.5.0-py3-none-any.whl", hash = "sha256:88648812c2faf3a970658b110b4ed0234455b96b3949d32a9981f5066b9dd3e0"}, - {file = "algorand_python_testing-0.5.0.tar.gz", hash = "sha256:d8ecfcfe7e346fcf37bd6d1d5812bc4760395031a1235d215187341cf488ed44"}, + {file = "algokit_utils-2.4.0-py3-none-any.whl", hash = "sha256:acf1f6ea7be59b3bfcc425d54f416d07270ddbd79452c1111f427f43f78fa7dd"}, ] [package.dependencies] -algorand-python = ">=2.0" -coincurve = ">=19.0.1" -ecdsa = ">=0.17.0" -pycryptodomex = ">=3.6.0,<4" -pynacl = ">=1.4.0,<2" +deprecated = ">=1.2.14,<2.0.0" +httpx = ">=0.23.1,<0.24.0" +py-algorand-sdk = ">=2.4.0,<3.0.0" [[package]] name = "anyio" @@ -66,7 +22,7 @@ version = "4.10.0" description = "High-level concurrency and networking framework on top of asyncio or Trio" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1"}, {file = "anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6"}, @@ -75,55 +31,70 @@ files = [ [package.dependencies] idna = ">=2.8" sniffio = ">=1.1" -typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] trio = ["trio (>=0.26.1)"] [[package]] -name = "attrs" -version = "25.3.0" -description = "Classes Without Boilerplate" +name = "beaker-pyteal" +version = "1.1.1" +description = "A Framework for building PyTeal Applications" optional = false -python-versions = ">=3.8" -groups = ["dev"] +python-versions = ">=3.10,<4.0" +groups = ["main"] files = [ - {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, - {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, + {file = "beaker_pyteal-1.1.1-py3-none-any.whl", hash = "sha256:a85a4568213acbd097cd70d8acd71d0e7187b71a4dc5c1fd9760ae8c1433571e"}, ] -[package.extras] -benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] +[package.dependencies] +algokit-utils = ">=2.0.0,<3.0.0" +py-algorand-sdk = ">=2.0.0" +pyteal = ">=0.24,<0.25" [[package]] -name = "cattrs" -version = "24.1.3" -description = "Composable complex class support for attrs and dataclasses." +name = "black" +version = "24.10.0" +description = "The uncompromising code formatter." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "cattrs-24.1.3-py3-none-any.whl", hash = "sha256:adf957dddd26840f27ffbd060a6c4dd3b2192c5b7c2c0525ef1bd8131d8a83f5"}, - {file = "cattrs-24.1.3.tar.gz", hash = "sha256:981a6ef05875b5bb0c7fb68885546186d306f10f0f6718fe9b96c226e68821ff"}, + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, ] [package.dependencies] -attrs = ">=23.1.0" +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" [package.extras] -bson = ["pymongo (>=4.4.0)"] -cbor2 = ["cbor2 (>=5.4.6)"] -msgpack = ["msgpack (>=1.0.5)"] -msgspec = ["msgspec (>=0.18.5) ; implementation_name == \"cpython\""] -orjson = ["orjson (>=3.9.2) ; implementation_name == \"cpython\""] -pyyaml = ["pyyaml (>=6.0)"] -tomlkit = ["tomlkit (>=0.11.8)"] -ujson = ["ujson (>=5.7.0)"] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" @@ -131,7 +102,7 @@ version = "2025.8.3" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"}, {file = "certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407"}, @@ -143,7 +114,7 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -218,66 +189,20 @@ files = [ pycparser = "*" [[package]] -name = "coincurve" -version = "21.0.0" -description = "Safest and fastest Python library for secp256k1 elliptic curve operations" +name = "click" +version = "8.2.1" +description = "Composable command line interface toolkit" optional = false -python-versions = ">=3.9" -groups = ["main"] +python-versions = ">=3.10" +groups = ["dev"] files = [ - {file = "coincurve-21.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:986727bba6cf0c5670990358dc6af9a54f8d3e257979b992a9dbd50dd82fa0dc"}, - {file = "coincurve-21.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1c584059de61ed16c658e7eae87ee488e81438897dae8fabeec55ef408af474"}, - {file = "coincurve-21.0.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4210b35c922b2b36c987a48c0b110ab20e490a2d6a92464ca654cb09e739fcc"}, - {file = "coincurve-21.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf67332cc647ef52ef371679c76000f096843ae266ae6df5e81906eb6463186b"}, - {file = "coincurve-21.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997607a952913c6a4bebe86815f458e77a42467b7a75353ccdc16c3336726880"}, - {file = "coincurve-21.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cfdd0938f284fb147aa1723a69f8794273ec673b10856b6e6f5f63fcc99d0c2e"}, - {file = "coincurve-21.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:88c1e3f6df2f2fbe18152c789a18659ee0429dc604fc77530370c9442395f681"}, - {file = "coincurve-21.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:530b58ed570895612ef510e28df5e8a33204b03baefb5c986e22811fa09622ef"}, - {file = "coincurve-21.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:f920af756a98edd738c0cfa431e81e3109aeec6ffd6dffb5ed4f5b5a37aacba8"}, - {file = "coincurve-21.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:070e060d0d57b496e68e48b39d5e3245681376d122827cb8e09f33669ff8cf1b"}, - {file = "coincurve-21.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:65ec42cab9c60d587fb6275c71f0ebc580625c377a894c4818fb2a2b583a184b"}, - {file = "coincurve-21.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5828cd08eab928db899238874d1aab12fa1236f30fe095a3b7e26a5fc81df0a3"}, - {file = "coincurve-21.0.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54de1cac75182de9f71ce41415faafcaf788303e21cbd0188064e268d61625e5"}, - {file = "coincurve-21.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07cda058d9394bea30d57a92fdc18ee3ca6b5bc8ef776a479a2ffec917105836"}, - {file = "coincurve-21.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9070804d7c71badfe4f0bf19b728cfe7c70c12e733938ead6b1db37920b745c0"}, - {file = "coincurve-21.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:669ab5db393637824b226de058bb7ea0cb9a0236e1842d7b22f74d4a8a1f1ff1"}, - {file = "coincurve-21.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3bcd538af097b3914ec3cb654262e72e224f95f2e9c1eb7fbd75d843ae4e528e"}, - {file = "coincurve-21.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45b6a5e6b5536e1f46f729829d99ce1f8f847308d339e8880fe7fa1646935c10"}, - {file = "coincurve-21.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:87597cf30dfc05fa74218810776efacf8816813ab9fa6ea1490f94e9f8b15e77"}, - {file = "coincurve-21.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:b992d1b1dac85d7f542d9acbcf245667438839484d7f2b032fd032256bcd778e"}, - {file = "coincurve-21.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f60ad56113f08e8c540bb89f4f35f44d434311433195ffff22893ccfa335070c"}, - {file = "coincurve-21.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1cb1cd19fb0be22e68ecb60ad950b41f18b9b02eebeffaac9391dc31f74f08f2"}, - {file = "coincurve-21.0.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05d7e255a697b3475d7ae7640d3bdef3d5bc98ce9ce08dd387f780696606c33b"}, - {file = "coincurve-21.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a366c314df7217e3357bb8c7d2cda540b0bce180705f7a0ce2d1d9e28f62ad4"}, - {file = "coincurve-21.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b04778b75339c6e46deb9ae3bcfc2250fbe48d1324153e4310fc4996e135715"}, - {file = "coincurve-21.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8efcbdcd50cc219989a2662e6c6552f455efc000a15dd6ab3ebf4f9b187f41a3"}, - {file = "coincurve-21.0.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6df44b4e3b7acdc1453ade52a52e3f8a5b53ecdd5a06bd200f1ec4b4e250f7d9"}, - {file = "coincurve-21.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bcc0831f07cb75b91c35c13b1362e7b9dc76c376b27d01ff577bec52005e22a8"}, - {file = "coincurve-21.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:5dd7b66b83b143f3ad3861a68fc0279167a0bae44fe3931547400b7a200e90b1"}, - {file = "coincurve-21.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:78dbe439e8cb22389956a4f2f2312813b4bd0531a0b691d4f8e868c7b366555d"}, - {file = "coincurve-21.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9df5ceb5de603b9caf270629996710cf5ed1d43346887bc3895a11258644b65b"}, - {file = "coincurve-21.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:154467858d23c48f9e5ab380433bc2625027b50617400e2984cc16f5799ab601"}, - {file = "coincurve-21.0.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f57f07c44d14d939bed289cdeaba4acb986bba9f729a796b6a341eab1661eedc"}, - {file = "coincurve-21.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fb03e3a388a93d31ed56a442bdec7983ea404490e21e12af76fb1dbf097082a"}, - {file = "coincurve-21.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d09ba4fd9d26b00b06645fcd768c5ad44832a1fa847ebe8fb44970d3204c3cb7"}, - {file = "coincurve-21.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1a1e7ee73bc1b3bcf14c7b0d1f44e6485785d3b53ef7b16173c36d3cefa57f93"}, - {file = "coincurve-21.0.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ad05952b6edc593a874df61f1bc79db99d716ec48ba4302d699e14a419fe6f51"}, - {file = "coincurve-21.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4d2bf350ced38b73db9efa1ff8fd16a67a1cb35abb2dda50d89661b531f03fd3"}, - {file = "coincurve-21.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:54d9500c56d5499375e579c3917472ffcf804c3584dd79052a79974280985c74"}, - {file = "coincurve-21.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:773917f075ec4b94a7a742637d303a3a082616a115c36568eb6c873a8d950d18"}, - {file = "coincurve-21.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb82ba677fc7600a3bf200edc98f4f9604c317b18c7b3f0a10784b42686e3a53"}, - {file = "coincurve-21.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5001de8324c35eee95f34e011a5c3b4e7d9ae9ca4a862a93b2c89b3f467f511b"}, - {file = "coincurve-21.0.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4d0bb5340bcac695731bef51c3e0126f252453e2d1ae7fa1486d90eff978bf6"}, - {file = "coincurve-21.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a9b49789ff86f3cf86cfc8ff8c6c43bac2607720ec638e8ba471fa7e8765bd2"}, - {file = "coincurve-21.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b85b49e192d2ca1a906a7b978bacb55d4dcb297cc2900fbbd9b9180d50878779"}, - {file = "coincurve-21.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ad6445f0bb61b3a4404d87a857ddb2a74a642cd4d00810237641aab4d6b1a42f"}, - {file = "coincurve-21.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d3f017f1491491f3f2c49e5d2d3a471a872d75117bfcb804d1167061c94bd347"}, - {file = "coincurve-21.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:500e5e38cd4cbc4ea8a5c631ce843b1d52ef19ac41128568214d150f75f1f387"}, - {file = "coincurve-21.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:ef81ca24511a808ad0ebdb8fdaf9c5c87f12f935b3d117acccc6520ad671bcce"}, - {file = "coincurve-21.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:6ec8e859464116a3c90168cd2bd7439527d4b4b5e328b42e3c8e0475f9b0bf71"}, - {file = "coincurve-21.0.0.tar.gz", hash = "sha256:8b37ce4265a82bebf0e796e21a769e56fdbf8420411ccbe3fafee4ed75b6a6e5"}, + {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, + {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, ] +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -285,106 +210,114 @@ description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["dev"] -markers = "sys_platform == \"win32\"" +markers = "sys_platform == \"win32\" or platform_system == \"Windows\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] -name = "docstring-parser" -version = "0.17.0" -description = "Parse Python docstrings in reST, Google and Numpydoc format" +name = "deprecated" +version = "1.2.18" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." optional = false -python-versions = ">=3.8" -groups = ["dev"] +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +groups = ["main"] files = [ - {file = "docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708"}, - {file = "docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912"}, + {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"}, + {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"}, ] +[package.dependencies] +wrapt = ">=1.10,<2" + [package.extras] -dev = ["pre-commit (>=2.16.0) ; python_version >= \"3.9\"", "pydoctor (>=25.4.0)", "pytest"] -docs = ["pydoctor (>=25.4.0)"] -test = ["pytest"] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version >= \"3.12\"", "tox"] [[package]] -name = "ecdsa" -version = "0.19.1" -description = "ECDSA cryptographic signature library (pure python)" +name = "docstring-parser" +version = "0.14.1" +description = "Parse Python docstrings in reST, Google and Numpydoc format" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.6" +python-versions = ">=3.6,<4.0" groups = ["main"] files = [ - {file = "ecdsa-0.19.1-py2.py3-none-any.whl", hash = "sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3"}, - {file = "ecdsa-0.19.1.tar.gz", hash = "sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61"}, + {file = "docstring_parser-0.14.1-py3-none-any.whl", hash = "sha256:14ac6ec1f1ba6905c4d8cb90fd0bc55394f5678183752c90e44812bf28d7a515"}, + {file = "docstring_parser-0.14.1.tar.gz", hash = "sha256:2c77522e31b7c88b1ab457a1f3c9ae38947ad719732260ba77ee8a3deb58622a"}, ] -[package.dependencies] -six = ">=1.9.0" +[[package]] +name = "executing" +version = "1.2.0" +description = "Get the currently executing AST node of a frame, and other information" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "executing-1.2.0-py2.py3-none-any.whl", hash = "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc"}, + {file = "executing-1.2.0.tar.gz", hash = "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107"}, +] [package.extras] -gmpy = ["gmpy"] -gmpy2 = ["gmpy2"] +tests = ["asttokens", "littleutils", "pytest", "rich ; python_version >= \"3.11\""] [[package]] name = "h11" -version = "0.16.0" +version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] +python-versions = ">=3.7" +groups = ["main"] files = [ - {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, - {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] [[package]] name = "httpcore" -version = "1.0.9" +version = "0.16.3" description = "A minimal low-level HTTP client." optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] +python-versions = ">=3.7" +groups = ["main"] files = [ - {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, - {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, + {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, + {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, ] [package.dependencies] +anyio = ">=3.0,<5.0" certifi = "*" -h11 = ">=0.16" +h11 = ">=0.13,<0.15" +sniffio = "==1.*" [package.extras] -asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<1.0)"] [[package]] name = "httpx" -version = "0.28.1" +version = "0.23.3" description = "The next generation HTTP client." optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] +python-versions = ">=3.7" +groups = ["main"] files = [ - {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, - {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, + {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"}, + {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"}, ] [package.dependencies] -anyio = "*" certifi = "*" -httpcore = "==1.*" -idna = "*" +httpcore = ">=0.15.0,<0.17.0" +rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} +sniffio = "*" [package.extras] brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] -cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<13)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] -zstd = ["zstandard (>=0.18.0)"] [[package]] name = "idna" @@ -392,7 +325,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -402,24 +335,39 @@ files = [ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] [[package]] -name = "immutabledict" -version = "4.2.1" -description = "Immutable wrapper around dictionaries (a fork of frozendict)" +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "immutabledict-4.2.1-py3-none-any.whl", hash = "sha256:c56a26ced38c236f79e74af3ccce53772827cef5c3bce7cab33ff2060f756373"}, - {file = "immutabledict-4.2.1.tar.gz", hash = "sha256:d91017248981c72eb66c8ff9834e99c2f53562346f23e7f51e7a5ebcf66a3bcc"}, + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +groups = ["dev"] +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + [[package]] name = "msgpack" version = "1.1.1" description = "MessagePack serializer" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "msgpack-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:353b6fc0c36fde68b661a12949d7d49f8f51ff5fa019c1e47c87c4ff34b080ed"}, {file = "msgpack-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:79c408fcf76a958491b4e3b103d1c417044544b68e96d06432a189b43d1215c8"}, @@ -495,61 +443,61 @@ files = [ ] [[package]] -name = "networkx" -version = "3.5" -description = "Python package for creating and manipulating graphs and networks" +name = "packaging" +version = "24.2" +description = "Core utilities for Python packages" optional = false -python-versions = ">=3.11" +python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec"}, - {file = "networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] -[package.extras] -default = ["matplotlib (>=3.8)", "numpy (>=1.25)", "pandas (>=2.0)", "scipy (>=1.11.2)"] -developer = ["mypy (>=1.15)", "pre-commit (>=4.1)"] -doc = ["intersphinx-registry", "myst-nb (>=1.1)", "numpydoc (>=1.8.0)", "pillow (>=10)", "pydata-sphinx-theme (>=0.16)", "sphinx (>=8.0)", "sphinx-gallery (>=0.18)", "texext (>=0.6.7)"] -example = ["cairocffi (>=1.7)", "contextily (>=1.6)", "igraph (>=0.11)", "momepy (>=0.7.2)", "osmnx (>=2.0.0)", "scikit-learn (>=1.5)", "seaborn (>=0.13)"] -extra = ["lxml (>=4.6)", "pydot (>=3.0.1)", "pygraphviz (>=1.14)", "sympy (>=1.10)"] -test = ["pytest (>=7.2)", "pytest-cov (>=4.0)", "pytest-xdist (>=3.0)"] -test-extras = ["pytest-mpl", "pytest-randomly"] - [[package]] -name = "packaging" -version = "24.2" -description = "Core utilities for Python packages" +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] -name = "puyapy" -version = "4.10.0" -description = "An optimising compiler for Algorand Python" +name = "platformdirs" +version = "4.4.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false -python-versions = "<4.0,>=3.12" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "puyapy-4.10.0-py3-none-any.whl", hash = "sha256:7f243c784272568870c759e69b867260f76583b5fb78f6aec133c82bd96e3c78"}, + {file = "platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85"}, + {file = "platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf"}, ] -[package.dependencies] -attrs = ">=25.3.0,<26.0.0" -cattrs = ">=24.1,<25.0" -colorama = {version = ">=0.4.6,<0.5.0", markers = "sys_platform == \"win32\""} -docstring-parser = ">=0.14.1" -immutabledict = ">=4.2.0,<5.0.0" -mypy_extensions = ">=1.0.0,<2.0.0" -networkx = ">=3.4.2,<4.0.0" -packaging = ">=24.0,<25.0" -pycryptodomex = ">=3.6.0,<4" -structlog = ">=25.2.0,<26.0.0" -typing-extensions = ">=4.11.0,<5.0.0" +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.14.1)"] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] [[package]] name = "py-algorand-sdk" @@ -557,7 +505,7 @@ version = "2.10.0" description = "Algorand SDK in Python" optional = false python-versions = ">=3.10" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "py_algorand_sdk-2.10.0-py3-none-any.whl", hash = "sha256:511a8f172977b4d6940dceb036abb040d70f2ed2c7b090251818f0ba6f1b3af8"}, {file = "py_algorand_sdk-2.10.0.tar.gz", hash = "sha256:521f997a53219210feac519e86d0fb36d18be7e36d528162cf328b4b058e423f"}, @@ -574,7 +522,7 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -586,7 +534,7 @@ version = "3.23.0" description = "Cryptographic library for Python" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "pycryptodomex-3.23.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:add243d204e125f189819db65eed55e6b4713f70a7e9576c043178656529cec7"}, {file = "pycryptodomex-3.23.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1c6d919fc8429e5cb228ba8c0d4d03d202a560b421c14867a65f6042990adc8e"}, @@ -631,13 +579,28 @@ files = [ {file = "pycryptodomex-3.23.0.tar.gz", hash = "sha256:71909758f010c82bc99b0abf4ea12012c98962fbf0583c2164f8b84533c2e4da"}, ] +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + [[package]] name = "pynacl" version = "1.5.0" description = "Python binding to the Networking and Cryptography (NaCl) library" optional = false python-versions = ">=3.6" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, @@ -658,6 +621,47 @@ cffi = ">=1.4.1" docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] +[[package]] +name = "pyteal" +version = "0.24.1" +description = "Algorand Smart Contracts in Python" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "pyteal-0.24.1-py3-none-any.whl", hash = "sha256:19c601f0ea4d1a0be41a3fe48cd3807558a0e907cd47d0dca5df60977d78f2c4"}, + {file = "pyteal-0.24.1.tar.gz", hash = "sha256:172d796981f8f9d3a9a8fbe71a71a49cf185509780f46d82e29aaa692386d1fa"}, +] + +[package.dependencies] +docstring-parser = "0.14.1" +executing = "1.2.0" +py-algorand-sdk = ">=2.0.0,<3.0.0" +semantic-version = ">=2.9.0,<3.0.0" +tabulate = ">=0.9.0,<0.10.0" + +[[package]] +name = "pytest" +version = "8.4.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, + {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1" +packaging = ">=20" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + [[package]] name = "python-dotenv" version = "1.1.1" @@ -674,54 +678,179 @@ files = [ cli = ["click (>=5.0)"] [[package]] -name = "six" -version = "1.17.0" -description = "Python 2 and 3 compatibility utilities" +name = "rfc3986" +version = "1.5.0" +description = "Validating URI References per RFC 3986" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, +] + +[package.dependencies] +idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} + +[package.extras] +idna2008 = ["idna"] + +[[package]] +name = "semantic-version" +version = "2.10.0" +description = "A library implementing the 'SemVer' scheme." +optional = false +python-versions = ">=2.7" +groups = ["main"] +files = [ + {file = "semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177"}, + {file = "semantic_version-2.10.0.tar.gz", hash = "sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c"}, +] + +[package.extras] +dev = ["Django (>=1.11)", "check-manifest", "colorama (<=0.4.1) ; python_version == \"3.4\"", "coverage", "flake8", "nose2", "readme-renderer (<25.0) ; python_version == \"3.4\"", "tox", "wheel", "zest.releaser[recommended]"] +doc = ["Sphinx", "sphinx-rtd-theme"] + +[[package]] +name = "setuptools" +version = "80.9.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, - {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, + {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"}, + {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"}, ] +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] + [[package]] name = "sniffio" version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] [[package]] -name = "structlog" -version = "25.4.0" -description = "Structured Logging for Python" +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" optional = false -python-versions = ">=3.8" -groups = ["dev"] +python-versions = ">=3.7" +groups = ["main"] files = [ - {file = "structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c"}, - {file = "structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4"}, + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, ] +[package.extras] +widechars = ["wcwidth"] + [[package]] -name = "typing-extensions" -version = "4.15.0" -description = "Backported and Experimental Type Hints for Python 3.9+" +name = "wrapt" +version = "1.17.3" +description = "Module for decorators, wrappers and monkey patching." optional = false -python-versions = ">=3.9" -groups = ["main", "dev"] +python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, - {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, + {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04"}, + {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2"}, + {file = "wrapt-1.17.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c"}, + {file = "wrapt-1.17.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775"}, + {file = "wrapt-1.17.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd"}, + {file = "wrapt-1.17.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05"}, + {file = "wrapt-1.17.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418"}, + {file = "wrapt-1.17.3-cp310-cp310-win32.whl", hash = "sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390"}, + {file = "wrapt-1.17.3-cp310-cp310-win_amd64.whl", hash = "sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6"}, + {file = "wrapt-1.17.3-cp310-cp310-win_arm64.whl", hash = "sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18"}, + {file = "wrapt-1.17.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7"}, + {file = "wrapt-1.17.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85"}, + {file = "wrapt-1.17.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f"}, + {file = "wrapt-1.17.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311"}, + {file = "wrapt-1.17.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1"}, + {file = "wrapt-1.17.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5"}, + {file = "wrapt-1.17.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2"}, + {file = "wrapt-1.17.3-cp311-cp311-win32.whl", hash = "sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89"}, + {file = "wrapt-1.17.3-cp311-cp311-win_amd64.whl", hash = "sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77"}, + {file = "wrapt-1.17.3-cp311-cp311-win_arm64.whl", hash = "sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a"}, + {file = "wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0"}, + {file = "wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba"}, + {file = "wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd"}, + {file = "wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828"}, + {file = "wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9"}, + {file = "wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396"}, + {file = "wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc"}, + {file = "wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe"}, + {file = "wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c"}, + {file = "wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6"}, + {file = "wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0"}, + {file = "wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77"}, + {file = "wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7"}, + {file = "wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277"}, + {file = "wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d"}, + {file = "wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa"}, + {file = "wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050"}, + {file = "wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8"}, + {file = "wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb"}, + {file = "wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16"}, + {file = "wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39"}, + {file = "wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235"}, + {file = "wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c"}, + {file = "wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b"}, + {file = "wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa"}, + {file = "wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7"}, + {file = "wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4"}, + {file = "wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10"}, + {file = "wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6"}, + {file = "wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58"}, + {file = "wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a"}, + {file = "wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067"}, + {file = "wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454"}, + {file = "wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e"}, + {file = "wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f"}, + {file = "wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056"}, + {file = "wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804"}, + {file = "wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977"}, + {file = "wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116"}, + {file = "wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6"}, + {file = "wrapt-1.17.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:70d86fa5197b8947a2fa70260b48e400bf2ccacdcab97bb7de47e3d1e6312225"}, + {file = "wrapt-1.17.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:df7d30371a2accfe4013e90445f6388c570f103d61019b6b7c57e0265250072a"}, + {file = "wrapt-1.17.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:caea3e9c79d5f0d2c6d9ab96111601797ea5da8e6d0723f77eabb0d4068d2b2f"}, + {file = "wrapt-1.17.3-cp38-cp38-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:758895b01d546812d1f42204bd443b8c433c44d090248bf22689df673ccafe00"}, + {file = "wrapt-1.17.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02b551d101f31694fc785e58e0720ef7d9a10c4e62c1c9358ce6f63f23e30a56"}, + {file = "wrapt-1.17.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:656873859b3b50eeebe6db8b1455e99d90c26ab058db8e427046dbc35c3140a5"}, + {file = "wrapt-1.17.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a9a2203361a6e6404f80b99234fe7fb37d1fc73487b5a78dc1aa5b97201e0f22"}, + {file = "wrapt-1.17.3-cp38-cp38-win32.whl", hash = "sha256:55cbbc356c2842f39bcc553cf695932e8b30e30e797f961860afb308e6b1bb7c"}, + {file = "wrapt-1.17.3-cp38-cp38-win_amd64.whl", hash = "sha256:ad85e269fe54d506b240d2d7b9f5f2057c2aa9a2ea5b32c66f8902f768117ed2"}, + {file = "wrapt-1.17.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:30ce38e66630599e1193798285706903110d4f057aab3168a34b7fdc85569afc"}, + {file = "wrapt-1.17.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:65d1d00fbfb3ea5f20add88bbc0f815150dbbde3b026e6c24759466c8b5a9ef9"}, + {file = "wrapt-1.17.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7c06742645f914f26c7f1fa47b8bc4c91d222f76ee20116c43d5ef0912bba2d"}, + {file = "wrapt-1.17.3-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7e18f01b0c3e4a07fe6dfdb00e29049ba17eadbc5e7609a2a3a4af83ab7d710a"}, + {file = "wrapt-1.17.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f5f51a6466667a5a356e6381d362d259125b57f059103dd9fdc8c0cf1d14139"}, + {file = "wrapt-1.17.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:59923aa12d0157f6b82d686c3fd8e1166fa8cdfb3e17b42ce3b6147ff81528df"}, + {file = "wrapt-1.17.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:46acc57b331e0b3bcb3e1ca3b421d65637915cfcd65eb783cb2f78a511193f9b"}, + {file = "wrapt-1.17.3-cp39-cp39-win32.whl", hash = "sha256:3e62d15d3cfa26e3d0788094de7b64efa75f3a53875cdbccdf78547aed547a81"}, + {file = "wrapt-1.17.3-cp39-cp39-win_amd64.whl", hash = "sha256:1f23fa283f51c890eda8e34e4937079114c74b4c81d2b2f1f1d94948f5cc3d7f"}, + {file = "wrapt-1.17.3-cp39-cp39-win_arm64.whl", hash = "sha256:24c2ed34dc222ed754247a2702b1e1e89fdbaa4016f324b4b8f1a802d4ffe87f"}, + {file = "wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22"}, + {file = "wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0"}, ] [metadata] lock-version = "2.1" -python-versions = "^3.12" -content-hash = "2f3d0bf4e0cf86fb3a41818da3708e5f1b26c7b6ea6117df9a64a15778d55c06" +python-versions = "^3.13" +content-hash = "182f59be05605d03705a5d4debb2e3bfe4e5a3094f937ba98e7e9a4955b398be" From 97706dadb32ec4b4d8fb0e0bdda3bede4257db76 Mon Sep 17 00:00:00 2001 From: Elton Baidoo Date: Sat, 6 Sep 2025 17:30:13 -0400 Subject: [PATCH 12/16] Fix contract.py Application initialization and add comprehensive demo --- .../projects/ogc-contracts/full_demo.py | 144 ++++++++++++++++++ .../smart_contracts/ogc_vault/contract.py | 62 ++------ 2 files changed, 158 insertions(+), 48 deletions(-) create mode 100644 ogc-contracts/projects/ogc-contracts/full_demo.py diff --git a/ogc-contracts/projects/ogc-contracts/full_demo.py b/ogc-contracts/projects/ogc-contracts/full_demo.py new file mode 100644 index 0000000..08baae4 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/full_demo.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +"""Full OGC Demo - ALGO Vault + Token Creation""" + +from beaker import sandbox, client +from working_vault import app +from algosdk import transaction as tx +from algosdk.atomic_transaction_composer import TransactionWithSigner +import time + +def full_ogc_demo(): + print("๐Ÿš€ OGC - Complete Demo") + print("=" * 50) + + algod_client = sandbox.get_algod_client() + organizer = sandbox.get_accounts().pop() + alice = sandbox.get_accounts().pop() + bob = sandbox.get_accounts().pop() + + # Part 1: Create OGC Token + print("\n๐Ÿช™ Part 1: Creating OGC Token") + sp = algod_client.suggested_params() + + token_txn = tx.AssetCreateTxn( + sender=organizer.address, + sp=sp, + total=1_000_000_000, + decimals=6, + default_frozen=False, + unit_name="OGC", + asset_name="OGC Token", + manager=organizer.address, + reserve=organizer.address, + freeze=None, + clawback=None, + ) + + stx = token_txn.sign(organizer.private_key) + txid = algod_client.send_transaction(stx) + time.sleep(2) + confirmed = algod_client.pending_transaction_info(txid) + asset_id = confirmed["asset-index"] + + print(f"โœ… OGC Token Created: Asset ID {asset_id}") + + # Part 2: ALGO Vault Demo + print(f"\n๐Ÿ’ฐ Part 2: ALGO Vault Demo") + goal = 5_000_000 # 5 ALGO + current_round = algod_client.status()["last-round"] + deadline_round = current_round + 100 + + print(f"๐ŸŽฏ Goal: {goal/1_000_000} ALGO") + + # Deploy vault + app_client = client.ApplicationClient( + client=algod_client, + app=app, + signer=organizer.signer, + ) + + app_id, app_addr, _ = app_client.create( + goal_amount=goal, + deadline_round=deadline_round, + receiver_addr=organizer.address, + ) + + print(f"โœ… Vault Deployed: APP_ID {app_id}") + + # Fund app for operations + fund_txn = tx.PaymentTxn(organizer.address, sp, app_addr, 3_000_000) + algod_client.send_transaction(fund_txn.sign(organizer.private_key)) + + # Alice contributes 2 ALGO + alice_amount = 2_000_000 + print(f"๐Ÿ‘ค Alice contributes {alice_amount/1_000_000} ALGO") + + pmt_alice = tx.PaymentTxn(alice.address, sp, app_addr, alice_amount) + pmt_alice_signed = TransactionWithSigner(pmt_alice, alice.signer) + + app_client_alice = client.ApplicationClient( + client=algod_client, app=app, app_id=app_id, signer=alice.signer + ) + app_client_alice.call("contribute", payment=pmt_alice_signed) + + total_after_alice = app_client.call("get_total").return_value + print(f"๐Ÿ’ณ Total after Alice: {total_after_alice/1_000_000} ALGO") + + # Bob contributes 3 ALGO + bob_amount = 3_000_000 + print(f"๐Ÿ‘ค Bob contributes {bob_amount/1_000_000} ALGO") + + pmt_bob = tx.PaymentTxn(bob.address, sp, app_addr, bob_amount) + pmt_bob_signed = TransactionWithSigner(pmt_bob, bob.signer) + + app_client_bob = client.ApplicationClient( + client=algod_client, app=app, app_id=app_id, signer=bob.signer + ) + app_client_bob.call("contribute", payment=pmt_bob_signed) + + final_total = app_client.call("get_total").return_value + goal_amount = app_client.call("get_goal").return_value + + print(f"๐Ÿ’ณ Final Total: {final_total/1_000_000} ALGO") + print(f"๐ŸŽฏ Goal: {goal_amount/1_000_000} ALGO") + + if final_total >= goal_amount: + print(f"๐ŸŽ‰ SUCCESS! Goal Reached!") + print(f" Ready to release funds after deadline") + else: + needed = goal_amount - final_total + print(f"โณ Need {needed/1_000_000} more ALGO") + + # Part 3: Token Info + print(f"\n๐Ÿช™ Part 3: Token Summary") + try: + asset_info = algod_client.asset_info(asset_id) + params = asset_info["params"] + print(f"โœ… Token Details:") + print(f" Name: {params['name']}") + print(f" Symbol: {params['unit-name']}") + print(f" Total Supply: {params['total']:,}") + print(f" Decimals: {params['decimals']}") + except Exception as e: + print(f"โŒ Token info error: {e}") + + print(f"\n๐Ÿ† Demo Complete!") + print(f" ALGO Vault: APP_ID {app_id}") + print(f" OGC Token: Asset ID {asset_id}") + print(f" Total Raised: {final_total/1_000_000} ALGO") + print(f" Goal Status: {'โœ… Reached' if final_total >= goal_amount else 'โณ Pending'}") + + return { + "vault_app_id": app_id, + "token_asset_id": asset_id, + "total_raised": final_total, + "goal": goal_amount, + "success": final_total >= goal_amount + } + +if __name__ == "__main__": + result = full_ogc_demo() + print(f"\n๐Ÿ“Š Final Results:") + print(f" Vault: {result['vault_app_id']}") + print(f" Token: {result['token_asset_id']}") + print(f" Success: {'๐ŸŽ‰' if result['success'] else 'โณ'}") \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/contract.py b/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/contract.py index 8a51713..345c600 100644 --- a/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/contract.py +++ b/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/contract.py @@ -5,12 +5,9 @@ class VaultState: goal = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("goal"), default=Int(0)) deadline = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("deadline"), default=Int(0)) receiver = GlobalStateValue(stack_type=TealType.bytes, key=Bytes("receiver"), default=Bytes("")) - paused = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("paused"), default=Int(0)) + total = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("total"), default=Int(0)) -class VaultAccountState: - contributed = LocalStateValue(stack_type=TealType.uint64, key=Bytes("contributed"), default=Int(0)) - -app = Application("OGC_Vault", state=VaultState, acct_state=VaultAccountState) +app = Application("OGC_Vault", state=VaultState) @app.create def create(goal_amount: abi.Uint64, deadline_round: abi.Uint64, receiver_addr: abi.Address, *, output: abi.Uint64): @@ -18,41 +15,18 @@ def create(goal_amount: abi.Uint64, deadline_round: abi.Uint64, receiver_addr: a app.state.goal.set(goal_amount.get()), app.state.deadline.set(deadline_round.get()), app.state.receiver.set(receiver_addr.get()), - app.state.paused.set(Int(0)), + app.state.total.set(Int(0)), output.set(Global.current_application_id()), ) -@app.opt_in -def opt_in(): - return VaultAccountState.contributed.set(Int(0)) - -@Subroutine(TealType.none) -def _not_paused(): - return Assert(app.state.paused.get() == Int(0)) - -@app.external -def pause(): - return Seq( - Assert(Txn.sender() == Global.creator_address()), - app.state.paused.set(Int(1)) - ) - -@app.external -def unpause(): - return Seq( - Assert(Txn.sender() == Global.creator_address()), - app.state.paused.set(Int(0)) - ) - @app.external def contribute(payment: abi.PaymentTransaction): return Seq( - _not_paused(), Assert(And( payment.get().receiver() == Global.current_application_address(), payment.get().amount() > Int(0), )), - VaultAccountState.contributed.set(VaultAccountState.contributed.get() + payment.get().amount()), + app.state.total.set(app.state.total.get() + payment.get().amount()), ) @app.external @@ -70,21 +44,13 @@ def release(): InnerTxnBuilder.Submit(), ) -@app.external -def refund(): - now = Global.round() - bal = Balance(Global.current_application_address()) - amt = ScratchVar(TealType.uint64) - return Seq( - Assert(And(now >= app.state.deadline.get(), bal < app.state.goal.get())), - amt.store(VaultAccountState.contributed.get()), - Assert(amt.load() > Int(0)), - InnerTxnBuilder.Begin(), - InnerTxnBuilder.SetFields({ - TxnField.type_enum: TxnType.Payment, - TxnField.receiver: Txn.sender(), - TxnField.amount: amt.load(), - }), - InnerTxnBuilder.Submit(), - VaultAccountState.contributed.set(Int(0)), - ) +@app.external(read_only=True) +def get_goal(*, output: abi.Uint64): + return output.set(app.state.goal.get()) + +@app.external(read_only=True) +def get_total(*, output: abi.Uint64): + return output.set(app.state.total.get()) + +if __name__ == "__main__": + app.build().export("./artifacts/ogc_vault") \ No newline at end of file From 17ac9fb41d2ca9ac0218a76017c9eef46713c877 Mon Sep 17 00:00:00 2001 From: Elton Baidoo Date: Sat, 6 Sep 2025 18:00:40 -0400 Subject: [PATCH 13/16] Fix vault.py Application initialization - remove acct_state parameter --- ogc-contracts/projects/ogc-contracts/vault.py | 60 +++++-------------- 1 file changed, 14 insertions(+), 46 deletions(-) diff --git a/ogc-contracts/projects/ogc-contracts/vault.py b/ogc-contracts/projects/ogc-contracts/vault.py index 3d901ba..1cfe513 100644 --- a/ogc-contracts/projects/ogc-contracts/vault.py +++ b/ogc-contracts/projects/ogc-contracts/vault.py @@ -6,12 +6,9 @@ class G: goal = GlobalStateValue(stack_type=TealType.uint64, default=Int(0)) deadline = GlobalStateValue(stack_type=TealType.uint64, default=Int(0)) # round # receiver = GlobalStateValue(stack_type=TealType.bytes, default=Bytes("")) - paused = GlobalStateValue(stack_type=TealType.uint64, default=Int(0)) + total = GlobalStateValue(stack_type=TealType.uint64, default=Int(0)) -class L: - contributed = LocalStateValue(stack_type=TealType.uint64, default=Int(0)) - -app = Application("OGC_Vault", state=G(), acct_state=L()) +app = Application("OGC_Vault", state=G()) @app.create def create(goal: abi.Uint64, deadline_round: abi.Uint64, receiver: abi.Address, *, output: abi.Uint64): @@ -19,36 +16,18 @@ def create(goal: abi.Uint64, deadline_round: abi.Uint64, receiver: abi.Address, app.state.goal.set(goal.get()), app.state.deadline.set(deadline_round.get()), app.state.receiver.set(receiver.get()), - app.state.paused.set(Int(0)), + app.state.total.set(Int(0)), output.set(Global.current_application_id()), ) -@app.opt_in -def opt_in(): - return app.acct_state.contributed.set(Int(0)) - -@Subroutine(TealType.none) -def _not_paused(): - return Assert(app.state.paused.get() == Int(0)) - -@app.external -def pause(): - return Assert(Txn.sender() == Global.creator_address(), app.state.paused.set(Int(1))) - @app.external -def unpause(): - return Assert(Txn.sender() == Global.creator_address(), app.state.paused.set(Int(0))) - -@app.external -def contribute(*, pay: abi.PaymentTransaction): - """Expect group: [ Payment->app , AppCall(contribute, pay=Gtxn[0]) ]""" +def contribute(payment: abi.PaymentTransaction): return Seq( - _not_paused(), Assert(And( - pay.get().receiver() == Global.current_application_address(), - pay.get().amount() > Int(0), + payment.get().receiver() == Global.current_application_address(), + payment.get().amount() > Int(0), )), - app.acct_state.contributed.set(app.acct_state.contributed.get() + pay.get().amount()), + app.state.total.set(app.state.total.get() + payment.get().amount()), ) @app.external @@ -66,24 +45,13 @@ def release(): InnerTxnBuilder.Submit(), ) -@app.external -def refund(): - now = Global.round() - bal = Balance(Global.current_application_address()) - amt = ScratchVar(TealType.uint64) - return Seq( - Assert(And(now >= app.state.deadline.get(), bal < app.state.goal.get())), - amt.store(app.acct_state.contributed.get()), - Assert(amt.load() > Int(0)), - InnerTxnBuilder.Begin(), - InnerTxnBuilder.SetFields({ - TxnField.type_enum: TxnType.Payment, - TxnField.receiver: Txn.sender(), - TxnField.amount: amt.load(), - }), - InnerTxnBuilder.Submit(), - app.acct_state.contributed.set(Int(0)), - ) +@app.external(read_only=True) +def get_goal(*, output: abi.Uint64): + return output.set(app.state.goal.get()) + +@app.external(read_only=True) +def get_total(*, output: abi.Uint64): + return output.set(app.state.total.get()) if __name__ == "__main__": app.build().export("./artifacts") From 93fdb2560fb3cbdf1cc0b2eee35afb118f5aebb7 Mon Sep 17 00:00:00 2001 From: Elton Baidoo Date: Sat, 6 Sep 2025 18:05:30 -0400 Subject: [PATCH 14/16] feat: working OGC demo (ALGO vault + ASA mint) + scripts --- .../__pycache__/working_vault.cpython-313.pyc | Bin 5248 -> 5248 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/ogc-contracts/projects/ogc-contracts/__pycache__/working_vault.cpython-313.pyc b/ogc-contracts/projects/ogc-contracts/__pycache__/working_vault.cpython-313.pyc index ba728485a9fd32d8e13b68303d6b5d3ffa32190c..6a90219f3d6166c85dee29a7ae2191cff3625524 100644 GIT binary patch delta 19 ZcmZqBY|!NT%*)Hg00f4MH*%GU001vf1dRXy delta 19 YcmZqBY|!NT%*)Hg00gGp8@b9v050zYc>n+a From 161e1ad89de405416769c78e02a85682b498eaf4 Mon Sep 17 00:00:00 2001 From: Elton Baidoo Date: Sat, 6 Sep 2025 18:17:41 -0400 Subject: [PATCH 15/16] Add TestNet demo scripts for live ALGO transactions --- .../projects/ogc-contracts/TESTNET_DEMO.md | 52 ++++++++ .../ogc-contracts/call_release_testnet.py | 41 +++++++ .../ogc-contracts/check_balance_testnet.py | 23 ++++ .../projects/ogc-contracts/deploy_testnet.py | 100 ++++++++++++++++ .../ogc-contracts/deploy_with_address.py | 111 ++++++++++++++++++ .../projects/ogc-contracts/get_app_address.py | 9 ++ 6 files changed, 336 insertions(+) create mode 100644 ogc-contracts/projects/ogc-contracts/TESTNET_DEMO.md create mode 100644 ogc-contracts/projects/ogc-contracts/call_release_testnet.py create mode 100644 ogc-contracts/projects/ogc-contracts/check_balance_testnet.py create mode 100644 ogc-contracts/projects/ogc-contracts/deploy_testnet.py create mode 100644 ogc-contracts/projects/ogc-contracts/deploy_with_address.py create mode 100644 ogc-contracts/projects/ogc-contracts/get_app_address.py diff --git a/ogc-contracts/projects/ogc-contracts/TESTNET_DEMO.md b/ogc-contracts/projects/ogc-contracts/TESTNET_DEMO.md new file mode 100644 index 0000000..7fb1da4 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/TESTNET_DEMO.md @@ -0,0 +1,52 @@ +# OGC TestNet Demo Guide + +## ๐Ÿš€ Quick Demo Steps + +### 1. Deploy Vault +```bash +python deploy_with_address.py +``` +- Enter your 25-word mnemonic +- Note the APP_ID and APP_ADDRESS + +### 2. Send ALGO via Pera +- Open Pera Wallet โ†’ TestNet +- Send 2+ ALGO to APP_ADDRESS +- Confirm transaction + +### 3. Check Balance +```bash +python check_balance_testnet.py +# Enter APP_ID when prompted +``` + +### 4. Get App Address (if needed) +```bash +python get_app_address.py +# Enter APP_ID when prompted +``` + +### 5. Trigger Release (after goal met) +```bash +python call_release_testnet.py +# Enter APP_ID and mnemonic when prompted +``` + +## ๐Ÿ“‹ Your TestNet Address +``` +SXIEIE2D7FOKUNQXUFUZIRYKE75RYD5KBN5BOYZFXLIL7LOTFX4VK3U7CE +``` + +## ๐Ÿ”— Useful Links +- **TestNet Dispenser**: https://testnet.algoexplorer.io/dispenser +- **TestNet Explorer**: https://testnet.algoexplorer.io/ +- **Pera Wallet**: https://perawallet.app/ + +## ๐ŸŽฏ Demo Flow +1. **Deploy**: `python deploy_with_address.py` +2. **Fund**: Send ALGO via Pera to app address +3. **Verify**: `python check_balance_testnet.py` +4. **Release**: `python call_release_testnet.py` +5. **Show**: All transactions on TestNet explorer + +**Total demo time: 3-5 minutes** โšก \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/call_release_testnet.py b/ogc-contracts/projects/ogc-contracts/call_release_testnet.py new file mode 100644 index 0000000..96d5e12 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/call_release_testnet.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +"""Call release function on TestNet""" + +import algosdk +from algosdk.v2client import algod +from algosdk import mnemonic +from algosdk.future import transaction as tx + +ALGOD_URL = "https://testnet-api.algonode.cloud" + +APP_ID = int(input("APP_ID: ")) +mnemo = input("25-word TestNet mnemonic: ").strip() + +try: + sk = mnemonic.to_private_key(mnemo) + addr = algosdk.account.address_from_private_key(sk) + print(f"Calling from: {addr}") + + client = algod.AlgodClient("", ALGOD_URL, "") + sp = client.suggested_params() + + # Call release method + app_args = [b"release"] + txn = tx.ApplicationNoOpTxn(addr, sp, APP_ID, app_args=app_args) + stx = txn.sign(sk) + txid = client.send_transaction(stx) + + print(f"Transaction sent: {txid}") + print("Waiting for confirmation...") + + algosdk.transaction.wait_for_confirmation(client, txid, 4) + print("โœ… Release called successfully!") + print(f"Explorer: https://testnet.algoexplorer.io/tx/{txid}") + +except Exception as e: + print(f"โŒ Error: {e}") + print("Make sure:") + print("- APP_ID is correct") + print("- Mnemonic is valid TestNet account") + print("- Account has some ALGO for fees") + print("- Deadline has passed and goal was met") \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/check_balance_testnet.py b/ogc-contracts/projects/ogc-contracts/check_balance_testnet.py new file mode 100644 index 0000000..26154fc --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/check_balance_testnet.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +"""Check app balance on TestNet""" + +from algosdk.v2client import algod +from algosdk.logic import get_application_address + +ALGOD_URL = "https://testnet-api.algonode.cloud" + +APP_ID = int(input("APP_ID: ")) +app_addr = get_application_address(APP_ID) + +client = algod.AlgodClient("", ALGOD_URL, "") +info = client.account_info(app_addr) + +print(f"APP_ADDRESS: {app_addr}") +print(f"Balance: {info['amount']/1_000_000} ALGO ({info['amount']} microALGO)") +print(f"Explorer: https://testnet.algoexplorer.io/address/{app_addr}") + +# Check if it's an app account +if info.get("apps-total-schema"): + print("โœ… This is an application account") +else: + print("โš ๏ธ This might not be an app account") \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/deploy_testnet.py b/ogc-contracts/projects/ogc-contracts/deploy_testnet.py new file mode 100644 index 0000000..02b9bc5 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/deploy_testnet.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +"""Deploy OGC Vault to TestNet""" + +from algosdk.v2client import algod +from algosdk import mnemonic +from beaker import client +from working_vault import app +import algosdk + +ALGOD_URL = "https://testnet-api.algonode.cloud" + +def deploy_to_testnet(): + print("๐Ÿš€ Deploying OGC Vault to TestNet") + + # Get deployer account + mnemo = input("Enter your 25-word TestNet mnemonic: ").strip() + try: + sk = mnemonic.to_private_key(mnemo) + addr = algosdk.account.address_from_private_key(sk) + print(f"Deploying from: {addr}") + except Exception as e: + print(f"โŒ Invalid mnemonic: {e}") + return + + # Connect to TestNet + algod_client = algod.AlgodClient("", ALGOD_URL, "") + + # Check balance + info = algod_client.account_info(addr) + balance = info["amount"] / 1_000_000 + print(f"Account balance: {balance} ALGO") + + if balance < 0.5: + print("โŒ Need at least 0.5 ALGO for deployment") + print("Get TestNet ALGO from: https://testnet.algoexplorer.io/dispenser") + return + + # Deployment parameters + goal = int(input("Goal amount (ALGO): ")) * 1_000_000 + deadline_rounds = int(input("Deadline (rounds from now): ")) + receiver = input("Receiver address (or press Enter for deployer): ").strip() + + if not receiver: + receiver = addr + + current_round = algod_client.status()["last-round"] + deadline_round = current_round + deadline_rounds + + print(f"\n๐Ÿ“‹ Deployment Config:") + print(f" Goal: {goal/1_000_000} ALGO") + print(f" Deadline: Round {deadline_round} (in {deadline_rounds} rounds)") + print(f" Receiver: {receiver}") + + confirm = input("\nDeploy? (y/N): ").strip().lower() + if confirm != 'y': + print("Cancelled") + return + + try: + # Create signer + signer = algosdk.atomic_transaction_composer.AccountTransactionSigner(sk) + + # Deploy + app_client = client.ApplicationClient( + client=algod_client, + app=app, + signer=signer, + ) + + print("Deploying...") + app_id, app_addr, _ = app_client.create( + goal_amount=goal, + deadline_round=deadline_round, + receiver_addr=receiver, + ) + + print(f"\nโœ… Deployed Successfully!") + print(f" APP_ID: {app_id}") + print(f" APP_ADDRESS: {app_addr}") + print(f" Explorer: https://testnet.algoexplorer.io/application/{app_id}") + + # Fund the app for inner transactions + print(f"\nFunding app with 0.5 ALGO for operations...") + sp = algod_client.suggested_params() + fund_txn = algosdk.transaction.PaymentTxn(addr, sp, app_addr, 500_000) + stx = fund_txn.sign(sk) + txid = algod_client.send_transaction(stx) + algosdk.transaction.wait_for_confirmation(algod_client, txid, 4) + print(f"โœ… App funded") + + print(f"\n๐ŸŽฏ Next Steps:") + print(f"1. Send ALGO to: {app_addr}") + print(f"2. Check balance: python check_balance_testnet.py") + print(f"3. After deadline: python call_release_testnet.py") + + except Exception as e: + print(f"โŒ Deployment failed: {e}") + +if __name__ == "__main__": + deploy_to_testnet() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/deploy_with_address.py b/ogc-contracts/projects/ogc-contracts/deploy_with_address.py new file mode 100644 index 0000000..999eaf4 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/deploy_with_address.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +"""Deploy OGC Vault to TestNet with provided address""" + +from algosdk.v2client import algod +from algosdk import mnemonic +from beaker import client +from working_vault import app +import algosdk + +ALGOD_URL = "https://testnet-api.algonode.cloud" +YOUR_ADDRESS = "SXIEIE2D7FOKUNQXUFUZIRYKE75RYD5KBN5BOYZFXLIL7LOTFX4VK3U7CE" + +def deploy_to_testnet(): + print("๐Ÿš€ Deploying OGC Vault to TestNet") + print(f"Using address: {YOUR_ADDRESS}") + + # Get private key + mnemo = input("Enter your 25-word mnemonic for this address: ").strip() + try: + sk = mnemonic.to_private_key(mnemo) + addr = algosdk.account.address_from_private_key(sk) + + if addr != YOUR_ADDRESS: + print(f"โŒ Mnemonic doesn't match address!") + print(f"Expected: {YOUR_ADDRESS}") + print(f"Got: {addr}") + return + + print("โœ… Address verified") + except Exception as e: + print(f"โŒ Invalid mnemonic: {e}") + return + + # Connect to TestNet + algod_client = algod.AlgodClient("", ALGOD_URL, "") + + # Check balance + try: + info = algod_client.account_info(addr) + balance = info["amount"] / 1_000_000 + print(f"Account balance: {balance} ALGO") + + if balance < 0.5: + print("โŒ Need at least 0.5 ALGO for deployment") + print("Get TestNet ALGO from: https://testnet.algoexplorer.io/dispenser") + return + except Exception as e: + print(f"โŒ Could not check balance: {e}") + return + + # Simple deployment with defaults + goal = 2_000_000 # 2 ALGO goal + current_round = algod_client.status()["last-round"] + deadline_round = current_round + 1000 # 1000 rounds (~1 hour) + receiver = addr # You are the receiver + + print(f"\n๐Ÿ“‹ Deployment Config:") + print(f" Goal: {goal/1_000_000} ALGO") + print(f" Deadline: Round {deadline_round} (in 1000 rounds)") + print(f" Receiver: {receiver}") + + try: + # Create signer + signer = algosdk.atomic_transaction_composer.AccountTransactionSigner(sk) + + # Deploy + app_client = client.ApplicationClient( + client=algod_client, + app=app, + signer=signer, + ) + + print("Deploying...") + app_id, app_addr, _ = app_client.create( + goal_amount=goal, + deadline_round=deadline_round, + receiver_addr=receiver, + ) + + print(f"\nโœ… Deployed Successfully!") + print(f" APP_ID: {app_id}") + print(f" APP_ADDRESS: {app_addr}") + print(f" Explorer: https://testnet.algoexplorer.io/application/{app_id}") + + # Fund the app for inner transactions + print(f"\nFunding app with 0.5 ALGO for operations...") + sp = algod_client.suggested_params() + fund_txn = algosdk.transaction.PaymentTxn(addr, sp, app_addr, 500_000) + stx = fund_txn.sign(sk) + txid = algod_client.send_transaction(stx) + algosdk.transaction.wait_for_confirmation(algod_client, txid, 4) + print(f"โœ… App funded") + + print(f"\n๐ŸŽฏ SAVE THESE:") + print(f"APP_ID: {app_id}") + print(f"APP_ADDRESS: {app_addr}") + + print(f"\n๐Ÿ“ฑ Next Steps:") + print(f"1. Open Pera Wallet โ†’ TestNet") + print(f"2. Send 2+ ALGO to: {app_addr}") + print(f"3. Check: python check_balance_testnet.py") + print(f"4. Release: python call_release_testnet.py") + + return app_id, app_addr + + except Exception as e: + print(f"โŒ Deployment failed: {e}") + return None, None + +if __name__ == "__main__": + deploy_to_testnet() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/get_app_address.py b/ogc-contracts/projects/ogc-contracts/get_app_address.py new file mode 100644 index 0000000..a8158bb --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/get_app_address.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 +"""Get app address from APP_ID""" + +from algosdk.logic import get_application_address + +APP_ID = int(input("APP_ID: ")) +app_addr = get_application_address(APP_ID) +print(f"APP_ADDRESS: {app_addr}") +print(f"Send ALGO to this address in Pera Wallet (TestNet)") \ No newline at end of file From f2578b74089ce12329e29f589e35b4a45303d323 Mon Sep 17 00:00:00 2001 From: Elton Baidoo Date: Sat, 6 Sep 2025 20:38:14 -0400 Subject: [PATCH 16/16] Secure credentials: Move mnemonic to .env, add environment variable support --- PROJECT_STATUS.md | 62 ++++++++++ .../projects/ogc-contracts/.gitignore | 3 + .../ogc-contracts/call_release_testnet.py | 2 +- .../ogc-contracts/check_any_balance.py | 55 +++++++++ .../ogc-contracts/check_contract_balance.py | 29 +++++ .../ogc-contracts/check_echo_status.py | 62 ++++++++++ .../projects/ogc-contracts/check_receiver.py | 40 +++++++ .../ogc-contracts/deploy_echo_testnet.py | 88 ++++++++++++++ .../projects/ogc-contracts/deploy_receiver.py | 91 +++++++++++++++ .../projects/ogc-contracts/deploy_sender.py | 90 +++++++++++++++ .../ogc-contracts/deploy_with_address.py | 2 +- .../projects/ogc-contracts/echo_contract.py | 54 +++++++++ .../projects/ogc-contracts/flexible_send.py | 108 ++++++++++++++++++ .../ogc-contracts/send_from_contract.py | 67 +++++++++++ .../projects/ogc-contracts/send_to_address.py | 86 ++++++++++++++ .../projects/ogc-contracts/sender_contract.py | 50 ++++++++ .../projects/ogc-contracts/simple_receiver.py | 45 ++++++++ .../projects/ogc-contracts/test_echo_local.py | 71 ++++++++++++ .../ogc-contracts/universal_wallet.py | 67 +++++++++++ .../projects/ogc-contracts/view_wallet.py | 35 ++++++ 20 files changed, 1105 insertions(+), 2 deletions(-) create mode 100644 PROJECT_STATUS.md create mode 100644 ogc-contracts/projects/ogc-contracts/check_any_balance.py create mode 100644 ogc-contracts/projects/ogc-contracts/check_contract_balance.py create mode 100644 ogc-contracts/projects/ogc-contracts/check_echo_status.py create mode 100644 ogc-contracts/projects/ogc-contracts/check_receiver.py create mode 100644 ogc-contracts/projects/ogc-contracts/deploy_echo_testnet.py create mode 100644 ogc-contracts/projects/ogc-contracts/deploy_receiver.py create mode 100644 ogc-contracts/projects/ogc-contracts/deploy_sender.py create mode 100644 ogc-contracts/projects/ogc-contracts/echo_contract.py create mode 100644 ogc-contracts/projects/ogc-contracts/flexible_send.py create mode 100644 ogc-contracts/projects/ogc-contracts/send_from_contract.py create mode 100644 ogc-contracts/projects/ogc-contracts/send_to_address.py create mode 100644 ogc-contracts/projects/ogc-contracts/sender_contract.py create mode 100644 ogc-contracts/projects/ogc-contracts/simple_receiver.py create mode 100644 ogc-contracts/projects/ogc-contracts/test_echo_local.py create mode 100644 ogc-contracts/projects/ogc-contracts/universal_wallet.py create mode 100644 ogc-contracts/projects/ogc-contracts/view_wallet.py diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md new file mode 100644 index 0000000..668f41f --- /dev/null +++ b/PROJECT_STATUS.md @@ -0,0 +1,62 @@ +# OGC Project Status Overview + +## โœ… What's Working + +### Local Development +- ALGO vault contracts (working_vault.py) +- OGC token creation (1B tokens, 6 decimals) +- Full demo scripts (LocalNet) +- CI/CD pipeline (GitHub Actions) + +### TestNet Ready +- Wallet format detection (Universal vs Legacy) +- Balance checking scripts +- ALGO sending scripts +- Contract deployment scripts +- Sender contract (can send ALGO to any wallet) + +## ๐ŸŽฏ Your Current Setup + +### Saved Legacy Wallet +- **Address**: `ZHMX3URT56ZLWQY3Y74CRXVOAVEOELOAVMXJZDT76IFORLPWMWMPJVAEN4` +- **Format**: 25-word mnemonic (Python SDK compatible) + +### Available Scripts +- **Check formats**: `universal_wallet.py` +- **Check balances**: `check_any_balance.py` +- **Send ALGO**: `flexible_send.py` +- **Deploy contracts**: `deploy_sender.py`, `deploy_receiver.py` +- **Contract operations**: `send_from_contract.py` + +## โ“ What We Need to Know + +1. **Do you have contracts deployed?** (Need APP_IDs) +2. **Which wallet are you using?** (Universal 24-word or Legacy 25-word) +3. **What do you want to test?** (Send to contract, send from contract, check balances) + +## ๐Ÿš€ Next Steps + +**Tell me what you want to test and I'll guide you through it!** + +## ๐Ÿ“ Project Structure + +``` +OGC/ +โ”œโ”€โ”€ PROJECT_STATUS.md # This file +โ”œโ”€โ”€ SETUP.md # Development setup +โ”œโ”€โ”€ TANGIBLE_DEMO.md # Demo commands +โ””โ”€โ”€ ogc-contracts/projects/ogc-contracts/ + โ”œโ”€โ”€ working_vault.py # Main ALGO vault + โ”œโ”€โ”€ create_ogc_token.py # Token creation + โ”œโ”€โ”€ full_demo.py # Complete demo + โ”œโ”€โ”€ universal_wallet.py # Wallet format handler + โ”œโ”€โ”€ flexible_send.py # ALGO sender + โ””โ”€โ”€ check_any_balance.py # Balance checker +``` + +## ๐ŸŽฏ Demo Ready + +- **Local Demo**: Full ALGO vault + token creation working +- **TestNet Demo**: Scripts ready for live transactions +- **CI/CD**: Automated testing and building +- **Documentation**: Setup guides and command references \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/.gitignore b/ogc-contracts/projects/ogc-contracts/.gitignore index 070829c..35bd956 100644 --- a/ogc-contracts/projects/ogc-contracts/.gitignore +++ b/ogc-contracts/projects/ogc-contracts/.gitignore @@ -1,3 +1,6 @@ +# Environment variables +.env + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/ogc-contracts/projects/ogc-contracts/call_release_testnet.py b/ogc-contracts/projects/ogc-contracts/call_release_testnet.py index 96d5e12..675a357 100644 --- a/ogc-contracts/projects/ogc-contracts/call_release_testnet.py +++ b/ogc-contracts/projects/ogc-contracts/call_release_testnet.py @@ -9,7 +9,7 @@ ALGOD_URL = "https://testnet-api.algonode.cloud" APP_ID = int(input("APP_ID: ")) -mnemo = input("25-word TestNet mnemonic: ").strip() +mnemo = input("24-word TestNet mnemonic: ").strip() try: sk = mnemonic.to_private_key(mnemo) diff --git a/ogc-contracts/projects/ogc-contracts/check_any_balance.py b/ogc-contracts/projects/ogc-contracts/check_any_balance.py new file mode 100644 index 0000000..afe15ba --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/check_any_balance.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +"""Check balance of any address or contract""" + +from algosdk.v2client import algod +from algosdk.logic import get_application_address + +ALGOD_URL = "https://testnet-api.algonode.cloud" + +def check_any_balance(): + print("๐Ÿ’ฐ Check Balance") + print("1. Check wallet address") + print("2. Check contract (APP_ID)") + + choice = input("Choose (1 or 2): ").strip() + + client = algod.AlgodClient("", ALGOD_URL, "") + + if choice == "1": + # Check wallet address + address = input("Paste wallet address: ").strip() + try: + info = client.account_info(address) + balance = info["amount"] / 1_000_000 + + print(f"\n๐Ÿ’ฐ Wallet Balance") + print(f" Address: {address}") + print(f" Balance: {balance} ALGO") + print(f" Explorer: https://testnet.algoexplorer.io/address/{address}") + + except Exception as e: + print(f"โŒ Error: {e}") + + elif choice == "2": + # Check contract + app_id = int(input("Contract APP_ID: ")) + app_addr = get_application_address(app_id) + + try: + info = client.account_info(app_addr) + balance = info["amount"] / 1_000_000 + + print(f"\n๐Ÿ’ฐ Contract Balance") + print(f" APP_ID: {app_id}") + print(f" Address: {app_addr}") + print(f" Balance: {balance} ALGO") + print(f" Explorer: https://testnet.algoexplorer.io/address/{app_addr}") + + except Exception as e: + print(f"โŒ Error: {e}") + + else: + print("โŒ Invalid choice") + +if __name__ == "__main__": + check_any_balance() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/check_contract_balance.py b/ogc-contracts/projects/ogc-contracts/check_contract_balance.py new file mode 100644 index 0000000..46b9d14 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/check_contract_balance.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +"""Check contract balance""" + +from algosdk.v2client import algod +from algosdk.logic import get_application_address + +ALGOD_URL = "https://testnet-api.algonode.cloud" + +def check_balance(): + APP_ID = int(input("Contract APP_ID: ")) + app_addr = get_application_address(APP_ID) + + client = algod.AlgodClient("", ALGOD_URL, "") + + try: + info = client.account_info(app_addr) + balance = info["amount"] / 1_000_000 + + print(f"๐Ÿ’ฐ Contract Balance") + print(f" APP_ID: {APP_ID}") + print(f" Address: {app_addr}") + print(f" Balance: {balance} ALGO") + print(f" Explorer: https://testnet.algoexplorer.io/address/{app_addr}") + + except Exception as e: + print(f"โŒ Error: {e}") + +if __name__ == "__main__": + check_balance() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/check_echo_status.py b/ogc-contracts/projects/ogc-contracts/check_echo_status.py new file mode 100644 index 0000000..04b816b --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/check_echo_status.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +"""Check Echo Contract Status""" + +from algosdk.v2client import algod +from algosdk.logic import get_application_address +from beaker import client +from echo_contract import app +import algosdk + +ALGOD_URL = "https://testnet-api.algonode.cloud" + +def check_echo_status(): + APP_ID = int(input("Echo Contract APP_ID: ")) + app_addr = get_application_address(APP_ID) + + client_algod = algod.AlgodClient("", ALGOD_URL, "") + + # Check app balance + try: + info = client_algod.account_info(app_addr) + balance = info["amount"] / 1_000_000 + + print(f"๐Ÿ”„ Echo Contract Status") + print(f" APP_ID: {APP_ID}") + print(f" Address: {app_addr}") + print(f" Balance: {balance} ALGO") + print(f" Explorer: https://testnet.algoexplorer.io/address/{app_addr}") + + # Try to read contract state + try: + app_info = client_algod.application_info(APP_ID) + global_state = app_info.get("params", {}).get("global-state", []) + + total_received = 0 + last_sender = "" + + for item in global_state: + key = item["key"] + value = item["value"] + + if key == "dG90YWw=": # "total" in base64 + total_received = value["uint"] / 1_000_000 + elif key == "c2VuZGVy": # "sender" in base64 + last_sender = value.get("bytes", "") + + print(f"\n๐Ÿ“Š Contract Stats:") + print(f" Total Received: {total_received} ALGO") + if last_sender: + print(f" Last Sender: {last_sender}") + + except Exception as e: + print(f" Could not read state: {e}") + + print(f"\n๐Ÿ’ก To Test:") + print(f" Send ALGO to: {app_addr}") + print(f" It will automatically bounce back!") + + except Exception as e: + print(f"โŒ Error: {e}") + +if __name__ == "__main__": + check_echo_status() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/check_receiver.py b/ogc-contracts/projects/ogc-contracts/check_receiver.py new file mode 100644 index 0000000..7aea631 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/check_receiver.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +"""Check Simple Receiver Status""" + +from algosdk.v2client import algod +from algosdk.logic import get_application_address + +ALGOD_URL = "https://testnet-api.algonode.cloud" + +def check_receiver(): + APP_ID = int(input("Receiver APP_ID: ")) + app_addr = get_application_address(APP_ID) + + client = algod.AlgodClient("", ALGOD_URL, "") + + try: + # Check app balance + info = client.account_info(app_addr) + balance = info["amount"] / 1_000_000 + + print(f"๐Ÿ“ฅ Simple Receiver Status") + print(f" APP_ID: {APP_ID}") + print(f" Address: {app_addr}") + print(f" Balance: {balance} ALGO") + print(f" Explorer: https://testnet.algoexplorer.io/address/{app_addr}") + + # Check recent transactions + print(f"\n๐Ÿ” Recent Activity:") + print(f" View all transactions: https://testnet.algoexplorer.io/address/{app_addr}") + + if balance > 0: + print(f"โœ… SUCCESS! Contract has received {balance} ALGO") + else: + print(f"โณ No ALGO received yet") + print(f" Send ALGO to: {app_addr}") + + except Exception as e: + print(f"โŒ Error: {e}") + +if __name__ == "__main__": + check_receiver() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/deploy_echo_testnet.py b/ogc-contracts/projects/ogc-contracts/deploy_echo_testnet.py new file mode 100644 index 0000000..a4f081c --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/deploy_echo_testnet.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +"""Deploy Echo Contract to TestNet""" + +from algosdk.v2client import algod +from algosdk import mnemonic +from beaker import client +from echo_contract import app +import algosdk + +ALGOD_URL = "https://testnet-api.algonode.cloud" + +def deploy_echo_contract(): + print("๐Ÿ”„ Deploying Echo Contract to TestNet") + print("This contract automatically sends ALGO back when it receives it!") + + # Get deployer account + mnemo = input("Enter your 25-word TestNet mnemonic: ").strip() + try: + sk = mnemonic.to_private_key(mnemo) + addr = algosdk.account.address_from_private_key(sk) + print(f"Deploying from: {addr}") + except Exception as e: + print(f"โŒ Invalid mnemonic: {e}") + return + + # Connect to TestNet + algod_client = algod.AlgodClient("", ALGOD_URL, "") + + # Check balance + try: + info = algod_client.account_info(addr) + balance = info["amount"] / 1_000_000 + print(f"Account balance: {balance} ALGO") + + if balance < 1.0: + print("โŒ Need at least 1 ALGO for deployment and funding") + print("Get TestNet ALGO from: https://testnet.algoexplorer.io/dispenser") + return + except Exception as e: + print(f"โŒ Could not check balance: {e}") + return + + try: + # Create signer + signer = algosdk.atomic_transaction_composer.AccountTransactionSigner(sk) + + # Deploy + app_client = client.ApplicationClient( + client=algod_client, + app=app, + signer=signer, + ) + + print("Deploying Echo Contract...") + app_id, app_addr, _ = app_client.create() + + print(f"\nโœ… Echo Contract Deployed!") + print(f" APP_ID: {app_id}") + print(f" APP_ADDRESS: {app_addr}") + print(f" Explorer: https://testnet.algoexplorer.io/application/{app_id}") + + # Fund the app for inner transactions (needs ALGO to send back) + print(f"\nFunding contract with 2 ALGO for operations...") + sp = algod_client.suggested_params() + fund_txn = algosdk.transaction.PaymentTxn(addr, sp, app_addr, 2_000_000) + stx = fund_txn.sign(sk) + txid = algod_client.send_transaction(stx) + algosdk.transaction.wait_for_confirmation(algod_client, txid, 4) + print(f"โœ… Contract funded") + + print(f"\n๐ŸŽฏ YOUR PERMANENT ECHO ADDRESS:") + print(f"๐Ÿ“ {app_addr}") + print(f"\n๐Ÿ“ฑ How to Test:") + print(f"1. Send ANY amount of ALGO to: {app_addr}") + print(f"2. Contract will automatically send it back (minus 0.001 ALGO fee)") + print(f"3. Check transactions on: https://testnet.algoexplorer.io/address/{app_addr}") + + print(f"\n๐Ÿ” Monitor Contract:") + print(f"python check_echo_status.py") + + return app_id, app_addr + + except Exception as e: + print(f"โŒ Deployment failed: {e}") + return None, None + +if __name__ == "__main__": + deploy_echo_contract() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/deploy_receiver.py b/ogc-contracts/projects/ogc-contracts/deploy_receiver.py new file mode 100644 index 0000000..6dde2a8 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/deploy_receiver.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +"""Deploy Simple Receiver to TestNet""" + +from algosdk.v2client import algod +from algosdk import mnemonic +from beaker import client +from simple_receiver import app +import algosdk + +ALGOD_URL = "https://testnet-api.algonode.cloud" + +def deploy_receiver(): + print("๐Ÿ“ฅ Deploying Simple ALGO Receiver to TestNet") + + # Get deployer account + mnemo = input("Enter your mnemonic (24 or 25 words): ").strip() + + # Count words + words = mnemo.split() + print(f"Found {len(words)} words") + + if len(words) == 24: + print("โš ๏ธ You have 24 words, but Algorand needs 25") + print("Check Pera Wallet again - there might be one more word") + return + elif len(words) != 25: + print(f"โŒ Expected 25 words, got {len(words)}") + return + + try: + sk = mnemonic.to_private_key(mnemo) + addr = algosdk.account.address_from_private_key(sk) + print(f"โœ… Valid mnemonic") + print(f"Deploying from: {addr}") + except Exception as e: + print(f"โŒ Invalid mnemonic: {e}") + return + + # Connect to TestNet + algod_client = algod.AlgodClient("", ALGOD_URL, "") + + # Check balance + try: + info = algod_client.account_info(addr) + balance = info["amount"] / 1_000_000 + print(f"Account balance: {balance} ALGO") + + if balance < 0.5: + print("โŒ Need at least 0.5 ALGO for deployment") + print("Get TestNet ALGO from: https://testnet.algoexplorer.io/dispenser") + return + except Exception as e: + print(f"โŒ Could not check balance: {e}") + return + + try: + # Create signer + signer = algosdk.atomic_transaction_composer.AccountTransactionSigner(sk) + + # Deploy + app_client = client.ApplicationClient( + client=algod_client, + app=app, + signer=signer, + ) + + print("Deploying Simple Receiver...") + app_id, app_addr, _ = app_client.create() + + print(f"\nโœ… Simple Receiver Deployed!") + print(f" APP_ID: {app_id}") + print(f" APP_ADDRESS: {app_addr}") + print(f" Explorer: https://testnet.algoexplorer.io/application/{app_id}") + + print(f"\n๐ŸŽฏ YOUR PERMANENT RECEIVER ADDRESS:") + print(f"๐Ÿ“ {app_addr}") + + print(f"\n๐Ÿ“ฑ How to Send ALGO:") + print(f"1. Open Pera Wallet โ†’ TestNet") + print(f"2. Send ANY amount to: {app_addr}") + print(f"3. Contract will receive and log it") + print(f"4. Check status: python check_receiver.py") + + return app_id, app_addr + + except Exception as e: + print(f"โŒ Deployment failed: {e}") + return None, None + +if __name__ == "__main__": + deploy_receiver() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/deploy_sender.py b/ogc-contracts/projects/ogc-contracts/deploy_sender.py new file mode 100644 index 0000000..f763ae9 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/deploy_sender.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +"""Deploy Sender Contract to TestNet""" + +from algosdk.v2client import algod +from algosdk import mnemonic +from beaker import client +from sender_contract import app +import algosdk + +ALGOD_URL = "https://testnet-api.algonode.cloud" + +def deploy_sender(): + print("๐Ÿ“ค Deploying ALGO Sender Contract to TestNet") + + # Load from environment + import os + from dotenv import load_dotenv + load_dotenv() + + mnemo = os.getenv('TESTNET_MNEMONIC') + if not mnemo: + print("โŒ TESTNET_MNEMONIC not found in .env file") + return + + try: + sk = mnemonic.to_private_key(mnemo) + addr = algosdk.account.address_from_private_key(sk) + print(f"Deploying from: {addr}") + except Exception as e: + print(f"โŒ Invalid mnemonic: {e}") + return + + # Connect to TestNet + algod_client = algod.AlgodClient("", ALGOD_URL, "") + + # Check balance + try: + info = algod_client.account_info(addr) + balance = info["amount"] / 1_000_000 + print(f"Account balance: {balance} ALGO") + + if balance < 1.0: + print("โŒ Need at least 1 ALGO for deployment and funding") + print("Get TestNet ALGO from: https://testnet.algoexplorer.io/dispenser") + print(f"Your address: {addr}") + return + except Exception as e: + print(f"โŒ Could not check balance: {e}") + return + + try: + # Create signer + signer = algosdk.atomic_transaction_composer.AccountTransactionSigner(sk) + + # Deploy + app_client = client.ApplicationClient( + client=algod_client, + app=app, + signer=signer, + ) + + print("Deploying Sender Contract...") + app_id, app_addr, _ = app_client.create() + + print(f"\nโœ… Sender Contract Deployed!") + print(f" APP_ID: {app_id}") + print(f" APP_ADDRESS: {app_addr}") + print(f" Explorer: https://testnet.algoexplorer.io/application/{app_id}") + + # Fund the contract so it can send ALGO + print(f"\nFunding contract with 5 ALGO...") + sp = algod_client.suggested_params() + fund_txn = algosdk.transaction.PaymentTxn(addr, sp, app_addr, 5_000_000) + stx = fund_txn.sign(sk) + txid = algod_client.send_transaction(stx) + algosdk.transaction.wait_for_confirmation(algod_client, txid, 4) + print(f"โœ… Contract funded with 5 ALGO") + + print(f"\n๐ŸŽฏ CONTRACT CAN NOW SEND ALGO!") + print(f" Use: python send_from_contract.py") + print(f" APP_ID: {app_id}") + + return app_id, app_addr + + except Exception as e: + print(f"โŒ Deployment failed: {e}") + return None, None + +if __name__ == "__main__": + deploy_sender() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/deploy_with_address.py b/ogc-contracts/projects/ogc-contracts/deploy_with_address.py index 999eaf4..c40a94b 100644 --- a/ogc-contracts/projects/ogc-contracts/deploy_with_address.py +++ b/ogc-contracts/projects/ogc-contracts/deploy_with_address.py @@ -15,7 +15,7 @@ def deploy_to_testnet(): print(f"Using address: {YOUR_ADDRESS}") # Get private key - mnemo = input("Enter your 25-word mnemonic for this address: ").strip() + mnemo = input("Enter your 24-word mnemonic for this address: ").strip() try: sk = mnemonic.to_private_key(mnemo) addr = algosdk.account.address_from_private_key(sk) diff --git a/ogc-contracts/projects/ogc-contracts/echo_contract.py b/ogc-contracts/projects/ogc-contracts/echo_contract.py new file mode 100644 index 0000000..a1f5e04 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/echo_contract.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +"""Echo Contract - Automatically sends ALGO back when received""" + +from pyteal import * +from beaker import * + +class EchoState: + total_received = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("total"), default=Int(0)) + last_sender = GlobalStateValue(stack_type=TealType.bytes, key=Bytes("sender"), default=Bytes("")) + +app = Application("EchoContract", state=EchoState) + +@app.create +def create(*, output: abi.Uint64): + return Seq( + app.state.total_received.set(Int(0)), + app.state.last_sender.set(Bytes("")), + output.set(Global.current_application_id()), + ) + +@app.external +def echo_payment(payment: abi.PaymentTransaction): + """Receives ALGO and immediately sends it back""" + return Seq( + # Verify payment is to this app + Assert(And( + payment.get().receiver() == Global.current_application_address(), + payment.get().amount() > Int(0), + )), + + # Update state + app.state.total_received.set(app.state.total_received.get() + payment.get().amount()), + app.state.last_sender.set(payment.get().sender()), + + # Send ALGO back to sender (minus fees) + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields({ + TxnField.type_enum: TxnType.Payment, + TxnField.receiver: payment.get().sender(), + TxnField.amount: payment.get().amount() - Int(1000), # Subtract 0.001 ALGO fee + }), + InnerTxnBuilder.Submit(), + ) + +@app.external(read_only=True) +def get_total(*, output: abi.Uint64): + return output.set(app.state.total_received.get()) + +@app.external(read_only=True) +def get_last_sender(*, output: abi.String): + return output.set(app.state.last_sender.get()) + +if __name__ == "__main__": + app.build().export("./artifacts/echo_contract") \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/flexible_send.py b/ogc-contracts/projects/ogc-contracts/flexible_send.py new file mode 100644 index 0000000..b9e084a --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/flexible_send.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +"""Send ALGO - supports both wallet formats""" + +from algosdk.v2client import algod +from algosdk import mnemonic, transaction as tx, account +import algosdk + +ALGOD_URL = "https://testnet-api.algonode.cloud" + +def flexible_send(): + print("๐Ÿ“ค Flexible ALGO Sender") + print("Works with Universal (24-word) and Legacy (25-word) wallets") + + print("\n1. Use saved Legacy wallet (25-word)") + print("2. Enter your mnemonic (24 or 25 words)") + print("3. Create new Legacy wallet") + + choice = input("Choose (1, 2, or 3): ").strip() + + if choice == "1": + # Use saved Legacy wallet from environment + import os + from dotenv import load_dotenv + load_dotenv() + + mnemo = os.getenv('TESTNET_MNEMONIC') + if not mnemo: + print("โŒ TESTNET_MNEMONIC not found in .env file") + return + sk = mnemonic.to_private_key(mnemo) + sender_addr = algosdk.account.address_from_private_key(sk) + print(f"Using saved wallet: {sender_addr}") + + elif choice == "2": + # Try user's mnemonic + user_mnemonic = input("Paste your mnemonic: ").strip() + words = user_mnemonic.split() + + if len(words) == 24: + print("โŒ Universal (24-word) format detected") + print("Python SDK requires Legacy (25-word) format") + print("Options:") + print("- Use WalletConnect integration") + print("- Create Legacy wallet (option 3)") + print("- Import Legacy wallet to Pera") + return + + elif len(words) == 25: + try: + sk = mnemonic.to_private_key(user_mnemonic) + sender_addr = algosdk.account.address_from_private_key(sk) + print(f"โœ… Legacy wallet detected: {sender_addr}") + except Exception as e: + print(f"โŒ Invalid mnemonic: {e}") + return + else: + print(f"โŒ Invalid: Expected 24 or 25 words, got {len(words)}") + return + + elif choice == "3": + # Create new Legacy wallet + print("Creating new Legacy wallet...") + sk, sender_addr = account.generate_account() + mnemo = mnemonic.from_private_key(sk) + + print(f"โœ… New Legacy Wallet:") + print(f" Address: {sender_addr}") + print(f" Mnemonic: {mnemo}") + print(f" Save this for future use!") + + use_new = input("Use this wallet now? (y/N): ").strip().lower() + if use_new != 'y': + return + else: + print("โŒ Invalid choice") + return + + # Continue with sending + recipient = input("Recipient address: ").strip() + amount_algo = float(input("Amount (ALGO): ")) + amount_micro = int(amount_algo * 1_000_000) + + algod_client = algod.AlgodClient("", ALGOD_URL, "") + + try: + # Check balance + info = algod_client.account_info(sender_addr) + balance = info["amount"] / 1_000_000 + print(f"Balance: {balance} ALGO") + + if balance < amount_algo + 0.001: + print(f"โŒ Insufficient balance") + return + + # Send transaction + sp = algod_client.suggested_params() + txn = tx.PaymentTxn(sender_addr, sp, recipient, amount_micro) + stx = txn.sign(sk) + txid = algod_client.send_transaction(stx) + + print(f"โœ… Sent {amount_algo} ALGO to {recipient}") + print(f"TX: https://testnet.algoexplorer.io/tx/{txid}") + + except Exception as e: + print(f"โŒ Failed: {e}") + +if __name__ == "__main__": + flexible_send() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/send_from_contract.py b/ogc-contracts/projects/ogc-contracts/send_from_contract.py new file mode 100644 index 0000000..e12902d --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/send_from_contract.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +"""Send ALGO from contract to any wallet""" + +from algosdk.v2client import algod +from algosdk import mnemonic +from beaker import client +from sender_contract import app +import algosdk + +ALGOD_URL = "https://testnet-api.algonode.cloud" + +def send_from_contract(): + print("๐Ÿ“ค Send ALGO from Contract to Wallet") + + # Load contract owner mnemonic from environment + import os + from dotenv import load_dotenv + load_dotenv() + + mnemo = os.getenv('TESTNET_MNEMONIC') + if not mnemo: + print("โŒ TESTNET_MNEMONIC not found in .env file") + return + + APP_ID = int(input("Sender Contract APP_ID: ")) + recipient = input("Recipient wallet address: ").strip() + amount_algo = float(input("Amount to send (ALGO): ")) + amount_micro = int(amount_algo * 1_000_000) + + try: + sk = mnemonic.to_private_key(mnemo) + addr = algosdk.account.address_from_private_key(sk) + + # Connect to TestNet + algod_client = algod.AlgodClient("", ALGOD_URL, "") + + # Create app client + signer = algosdk.atomic_transaction_composer.AccountTransactionSigner(sk) + app_client = client.ApplicationClient( + client=algod_client, + app=app, + app_id=APP_ID, + signer=signer, + ) + + print(f"\n๐Ÿ“ค Sending {amount_algo} ALGO to {recipient}...") + + # Call send_algo method + app_client.call("send_algo", recipient=recipient, amount=amount_micro) + + print(f"โœ… SUCCESS! Sent {amount_algo} ALGO from contract!") + print(f" To: {recipient}") + print(f" Check: https://testnet.algoexplorer.io/address/{recipient}") + + # Check contract balance + balance = app_client.call("get_balance").return_value + total_sent = app_client.call("get_total_sent").return_value + + print(f"\n๐Ÿ“Š Contract Status:") + print(f" Remaining balance: {balance/1_000_000} ALGO") + print(f" Total sent: {total_sent/1_000_000} ALGO") + + except Exception as e: + print(f"โŒ Send failed: {e}") + +if __name__ == "__main__": + send_from_contract() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/send_to_address.py b/ogc-contracts/projects/ogc-contracts/send_to_address.py new file mode 100644 index 0000000..ae32ed2 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/send_to_address.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +"""Send ALGO from your wallet to any address""" + +from algosdk.v2client import algod +from algosdk import mnemonic, transaction as tx +import algosdk + +ALGOD_URL = "https://testnet-api.algonode.cloud" + +def send_to_address(): + print("๐Ÿ“ค Send ALGO to Any Address") + + # Option to use saved wallet or enter mnemonic + print("1. Use saved wallet") + print("2. Enter your own mnemonic") + print("3. View saved wallet details") + + choice = input("Choose (1, 2, or 3): ").strip() + + if choice == "1": + # Use the generated wallet + mnemo = "swear suffer shrimp clinic cause differ nice space update mansion cradle brisk unknown lecture host clarify again faint divide decrease renew choice still abstract tomorrow" + print("Using saved wallet: ZHMX3URT56ZLWQY3Y74CRXVOAVEOELOAVMXJZDT76IFORLPWMWMPJVAEN4") + elif choice == "2": + mnemo = input("Enter your 25-word mnemonic: ").strip() + elif choice == "3": + # View wallet details + address = "ZHMX3URT56ZLWQY3Y74CRXVOAVEOELOAVMXJZDT76IFORLPWMWMPJVAEN4" + mnemonic_saved = "swear suffer shrimp clinic cause differ nice space update mansion cradle brisk unknown lecture host clarify again faint divide decrease renew choice still abstract tomorrow" + print(f"\n๐Ÿ” Saved Wallet Details:") + print(f"Address: {address}") + print(f"Mnemonic: {mnemonic_saved}") + print(f"Explorer: https://testnet.algoexplorer.io/address/{address}") + return + else: + print("โŒ Invalid choice") + return + + try: + sk = mnemonic.to_private_key(mnemo) + sender_addr = algosdk.account.address_from_private_key(sk) + print(f"Sending from: {sender_addr}") + except Exception as e: + print(f"โŒ Invalid mnemonic: {e}") + return + + # Get recipient and amount + recipient = input("Paste recipient address: ").strip() + amount_algo = float(input("Amount to send (ALGO): ")) + amount_micro = int(amount_algo * 1_000_000) + + # Connect to TestNet + algod_client = algod.AlgodClient("", ALGOD_URL, "") + + try: + # Check sender balance + info = algod_client.account_info(sender_addr) + balance = info["amount"] / 1_000_000 + print(f"Your balance: {balance} ALGO") + + if balance < amount_algo + 0.001: # Need extra for fees + print(f"โŒ Insufficient balance. Need {amount_algo + 0.001} ALGO") + return + + # Create and send transaction + sp = algod_client.suggested_params() + txn = tx.PaymentTxn(sender_addr, sp, recipient, amount_micro) + stx = txn.sign(sk) + txid = algod_client.send_transaction(stx) + + print(f"\n๐Ÿ“ค Transaction sent!") + print(f" Amount: {amount_algo} ALGO") + print(f" To: {recipient}") + print(f" TX ID: {txid}") + + # Wait for confirmation + print("Waiting for confirmation...") + algosdk.transaction.wait_for_confirmation(algod_client, txid, 4) + print(f"โœ… SUCCESS! Transaction confirmed!") + print(f" Explorer: https://testnet.algoexplorer.io/tx/{txid}") + + except Exception as e: + print(f"โŒ Send failed: {e}") + +if __name__ == "__main__": + send_to_address() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/sender_contract.py b/ogc-contracts/projects/ogc-contracts/sender_contract.py new file mode 100644 index 0000000..004a831 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/sender_contract.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +"""Contract that can send ALGO to any wallet""" + +from pyteal import * +from beaker import * + +class SenderState: + owner = GlobalStateValue(stack_type=TealType.bytes, key=Bytes("owner"), default=Bytes("")) + total_sent = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("sent"), default=Int(0)) + +app = Application("SenderContract", state=SenderState) + +@app.create +def create(*, output: abi.Uint64): + return Seq( + app.state.owner.set(Txn.sender()), + app.state.total_sent.set(Int(0)), + output.set(Global.current_application_id()), + ) + +@app.external +def send_algo(recipient: abi.Address, amount: abi.Uint64): + """Send ALGO to any address (only owner can call)""" + return Seq( + # Only owner can send + Assert(Txn.sender() == app.state.owner.get()), + + # Send ALGO to recipient + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields({ + TxnField.type_enum: TxnType.Payment, + TxnField.receiver: recipient.get(), + TxnField.amount: amount.get(), + }), + InnerTxnBuilder.Submit(), + + # Update total sent + app.state.total_sent.set(app.state.total_sent.get() + amount.get()), + ) + +@app.external(read_only=True) +def get_balance(*, output: abi.Uint64): + return output.set(Balance(Global.current_application_address())) + +@app.external(read_only=True) +def get_total_sent(*, output: abi.Uint64): + return output.set(app.state.total_sent.get()) + +if __name__ == "__main__": + app.build().export("./artifacts/sender_contract") \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/simple_receiver.py b/ogc-contracts/projects/ogc-contracts/simple_receiver.py new file mode 100644 index 0000000..120606f --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/simple_receiver.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +"""Simple ALGO Receiver Contract""" + +from pyteal import * +from beaker import * + +class ReceiverState: + total_received = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("total"), default=Int(0)) + transaction_count = GlobalStateValue(stack_type=TealType.uint64, key=Bytes("count"), default=Int(0)) + +app = Application("SimpleReceiver", state=ReceiverState) + +@app.create +def create(*, output: abi.Uint64): + return Seq( + app.state.total_received.set(Int(0)), + app.state.transaction_count.set(Int(0)), + output.set(Global.current_application_id()), + ) + +@app.external +def receive_payment(payment: abi.PaymentTransaction): + """Receives ALGO and logs it""" + return Seq( + # Verify payment is to this app + Assert(And( + payment.get().receiver() == Global.current_application_address(), + payment.get().amount() > Int(0), + )), + + # Update counters + app.state.total_received.set(app.state.total_received.get() + payment.get().amount()), + app.state.transaction_count.set(app.state.transaction_count.get() + Int(1)), + ) + +@app.external(read_only=True) +def get_total(*, output: abi.Uint64): + return output.set(app.state.total_received.get()) + +@app.external(read_only=True) +def get_count(*, output: abi.Uint64): + return output.set(app.state.transaction_count.get()) + +if __name__ == "__main__": + app.build().export("./artifacts/simple_receiver") \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/test_echo_local.py b/ogc-contracts/projects/ogc-contracts/test_echo_local.py new file mode 100644 index 0000000..cc85e44 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/test_echo_local.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +"""Test Echo Contract Locally""" + +from beaker import sandbox, client +from echo_contract import app +from algosdk import transaction as tx +from algosdk.atomic_transaction_composer import TransactionWithSigner + +def test_echo_contract(): + print("๐Ÿ”„ Testing Echo Contract Locally") + + algod_client = sandbox.get_algod_client() + deployer = sandbox.get_accounts().pop() + sender = sandbox.get_accounts().pop() + + # Deploy contract + app_client = client.ApplicationClient( + client=algod_client, + app=app, + signer=deployer.signer, + ) + + app_id, app_addr, _ = app_client.create() + print(f"โœ… Echo Contract Deployed: APP_ID {app_id}") + print(f" Address: {app_addr}") + + # Fund the contract so it can send ALGO back + sp = algod_client.suggested_params() + fund_txn = tx.PaymentTxn(deployer.address, sp, app_addr, 5_000_000) # 5 ALGO + algod_client.send_transaction(fund_txn.sign(deployer.private_key)) + print(f"โœ… Contract funded with 5 ALGO") + + # Test: Send 1 ALGO to contract + test_amount = 1_000_000 # 1 ALGO + print(f"\n๐Ÿงช Test: Sending {test_amount/1_000_000} ALGO to contract...") + + # Get sender's balance before + sender_balance_before = algod_client.account_info(sender.address)["amount"] + + # Send payment to contract + payment = tx.PaymentTxn(sender.address, sp, app_addr, test_amount) + payment_signed = TransactionWithSigner(payment, sender.signer) + + # Call echo_payment method + app_client_sender = client.ApplicationClient( + client=algod_client, app=app, app_id=app_id, signer=sender.signer + ) + + try: + app_client_sender.call("echo_payment", payment=payment_signed) + print("โœ… Echo payment successful!") + + # Check sender's balance after + sender_balance_after = algod_client.account_info(sender.address)["amount"] + difference = (sender_balance_after - sender_balance_before) / 1_000_000 + + print(f"๐Ÿ“Š Results:") + print(f" Sent: {test_amount/1_000_000} ALGO") + print(f" Net change: {difference} ALGO (should be ~-0.002 for fees)") + + # Check contract stats + total = app_client.call("get_total").return_value + print(f" Contract total received: {total/1_000_000} ALGO") + + print(f"\n๐ŸŽ‰ SUCCESS! Contract automatically sent ALGO back!") + + except Exception as e: + print(f"โŒ Test failed: {e}") + +if __name__ == "__main__": + test_echo_contract() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/universal_wallet.py b/ogc-contracts/projects/ogc-contracts/universal_wallet.py new file mode 100644 index 0000000..1fb30ac --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/universal_wallet.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +"""Handle both Universal (24-word) and Legacy (25-word) wallets""" + +from algosdk import mnemonic, account +import algosdk + +def detect_wallet_type(): + print("๐Ÿ” Wallet Format Detector") + print("1. Test your mnemonic") + print("2. Create Legacy wallet (25-word)") + print("3. View saved Legacy wallet") + + choice = input("Choose (1, 2, or 3): ").strip() + + if choice == "1": + # Test user's mnemonic + user_mnemonic = input("Paste your mnemonic: ").strip() + words = user_mnemonic.split() + + print(f"\n๐Ÿ“Š Analysis:") + print(f" Word count: {len(words)}") + + if len(words) == 24: + print(" Format: Universal (24-word)") + print(" โš ๏ธ Python SDK needs 25-word Legacy format") + print(" ๐Ÿ’ก Use WalletConnect or create Legacy wallet") + + elif len(words) == 25: + print(" Format: Legacy (25-word)") + try: + sk = mnemonic.to_private_key(user_mnemonic) + addr = algosdk.account.address_from_private_key(sk) + print(f" โœ… Valid! Address: {addr}") + except Exception as e: + print(f" โŒ Invalid: {e}") + + else: + print(f" โŒ Invalid: Expected 24 or 25 words, got {len(words)}") + + elif choice == "2": + # Create new Legacy wallet + print("\n๐Ÿ”‘ Creating Legacy Wallet (25-word)...") + sk, addr = account.generate_account() + mnemo = mnemonic.from_private_key(sk) + + print(f"โœ… Legacy Wallet Created:") + print(f" Address: {addr}") + print(f" Mnemonic: {mnemo}") + print(f" Words: {len(mnemo.split())}") + print(f" Compatible with Python SDK: โœ…") + + elif choice == "3": + # Show saved Legacy wallet + address = "ZHMX3URT56ZLWQY3Y74CRXVOAVEOELOAVMXJZDT76IFORLPWMWMPJVAEN4" + mnemo = "swear suffer shrimp clinic cause differ nice space update mansion cradle brisk unknown lecture host clarify again faint divide decrease renew choice still abstract tomorrow" + + print(f"\n๐Ÿ” Saved Legacy Wallet:") + print(f" Address: {address}") + print(f" Mnemonic: {mnemo}") + print(f" Words: {len(mnemo.split())}") + print(f" Format: Legacy (Python SDK compatible)") + + else: + print("โŒ Invalid choice") + +if __name__ == "__main__": + detect_wallet_type() \ No newline at end of file diff --git a/ogc-contracts/projects/ogc-contracts/view_wallet.py b/ogc-contracts/projects/ogc-contracts/view_wallet.py new file mode 100644 index 0000000..a9cffdf --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/view_wallet.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""View saved wallet details""" + +def view_wallet(): + print("๐Ÿ” Your Saved TestNet Wallet") + print("=" * 50) + + import os + from dotenv import load_dotenv + load_dotenv() + + address = os.getenv('TESTNET_ADDRESS', 'Not found in .env') + mnemonic = os.getenv('TESTNET_MNEMONIC', 'Not found in .env') + + print(f"Address: {address}") + print(f"\nMnemonic (25 words):") + print(f"{mnemonic}") + + print(f"\n๐Ÿ”— Links:") + print(f"TestNet Explorer: https://testnet.algoexplorer.io/address/{address}") + print(f"Get TestNet ALGO: https://testnet.algoexplorer.io/dispenser") + + print(f"\n๐Ÿ“ฑ Import to Pera Wallet:") + print(f"1. Open Pera Wallet") + print(f"2. Add Account โ†’ Import Account") + print(f"3. Enter the 25-word mnemonic above") + print(f"4. Switch to TestNet") + + print(f"\nโš ๏ธ SECURITY:") + print(f"- This is for TestNet only (no real value)") + print(f"- Never share MainNet mnemonics") + print(f"- Keep this safe for your demos") + +if __name__ == "__main__": + view_wallet() \ No newline at end of file