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..901e38b --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/README.md @@ -0,0 +1,110 @@ +# OGC - Out The Groupchat ๐Ÿš€ + +**Decentralized Group Funding on Algorand** + +A hackathon project that solves the classic group coordination problem: "Who's actually going to pay?" + +## ๐Ÿ’ก The Problem + +We've all been there: +- Group chat: "Let's book that Airbnb!" +- Everyone: "I'm in!" +- Reality: Only 3 people actually pay, trip cancelled ๐Ÿ˜” + +## โœจ The Solution + +**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 + +## ๐Ÿ—๏ธ How It Works + +``` +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 +``` + +## ๐Ÿงช Testing + +Run the complete demo: +```bash +# Activate environment +source venv/bin/activate + +# Run hackathon demo +python ogc_demo.py + +# Run comprehensive tests +python test_vault.py +``` + +## ๐Ÿ“Š Demo Results + +``` +๐ŸŽซ 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 +``` + +## ๐Ÿ”ง Technical Stack + +- **Blockchain**: Algorand +- **Smart Contracts**: PyTeal + Beaker +- **Language**: Python +- **Testing**: LocalNet sandbox + +## ๐Ÿš€ Features + +- โœ… **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 + +## ๐Ÿ“ Project Structure + +``` +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 +``` + +## ๐ŸŽฏ Use Cases + +- **Group Travel**: Airbnb, flights, activities +- **Event Planning**: Concert tickets, restaurant bookings +- **Shared Purchases**: Group gifts, bulk orders +- **Community Projects**: Local initiatives, fundraising + +## ๐Ÿ† Hackathon Highlights + +- **Problem**: Group coordination & payment trust +- **Solution**: Algorand smart contract escrow +- **Innovation**: Automatic goal-based fund management +- **Impact**: Eliminates payment coordination friction + +## ๐Ÿ”ฎ Future Roadmap + +- [ ] Web frontend with Pera Wallet integration +- [ ] Mobile app for easy group creation +- [ ] Multi-token support (ASAs) +- [ ] Recurring payment schedules +- [ ] Integration with group chat platforms + +--- + +**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/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/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/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/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/__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..8a51713 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/contract.py @@ -0,0 +1,90 @@ +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 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 new file mode 100644 index 0000000..f545be1 --- /dev/null +++ b/ogc-contracts/projects/ogc-contracts/smart_contracts/ogc_vault/deploy_config.py @@ -0,0 +1,31 @@ +import logging +from beaker import sandbox +from smart_contracts.ogc_vault.contract import app + +logger = logging.getLogger(__name__) + +def deploy() -> None: + # 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/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 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