diff --git a/.algokit.toml b/.algokit.toml new file mode 100644 index 0000000..db5e773 --- /dev/null +++ b/.algokit.toml @@ -0,0 +1,9 @@ +[algokit] +min_version = "v2.0.0" + +[project] +type = 'workspace' +projects_root_path = 'projects' + +[project.run] +build = ['AlgoFire-contracts', 'AlgoFire-frontend'] diff --git a/.algokit/.copier-answers.yml b/.algokit/.copier-answers.yml new file mode 100644 index 0000000..97e4f9c --- /dev/null +++ b/.algokit/.copier-answers.yml @@ -0,0 +1,10 @@ +# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY +_commit: 2.1.6 +_src_path: gh:algorandfoundation/algokit-fullstack-template +author_email: cordesir@gmail.com +author_name: Corey Stedman +cloud_provider: netlify +contract_name: algofire +preset_name: production +project_name: AlgoFire + diff --git a/.algokit/generators/create-devcontainer/copier.yaml b/.algokit/generators/create-devcontainer/copier.yaml new file mode 100644 index 0000000..e98f334 --- /dev/null +++ b/.algokit/generators/create-devcontainer/copier.yaml @@ -0,0 +1,4 @@ +_tasks: + - "echo '==== Successfully generated new .devcontainer.json file 🚀 ===='" + +_templates_suffix: ".j2" diff --git a/.algokit/generators/create-devcontainer/devcontainer.json b/.algokit/generators/create-devcontainer/devcontainer.json new file mode 100644 index 0000000..6452c65 --- /dev/null +++ b/.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/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5e550a1 --- /dev/null +++ b/.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/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.github/workflows/algofire-contracts-cd.yaml b/.github/workflows/algofire-contracts-cd.yaml new file mode 100644 index 0000000..744c62c --- /dev/null +++ b/.github/workflows/algofire-contracts-cd.yaml @@ -0,0 +1,45 @@ +name: Release AlgoFire-contracts + +on: + workflow_call: + +jobs: + deploy-testnet: + runs-on: "ubuntu-latest" + + environment: contract-testnet + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Use Node.js 22.x + uses: actions/setup-node@v4 + with: + node-version: '22.x' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install algokit + run: pipx install algokit + + - name: Bootstrap dependencies + run: algokit project bootstrap all --project-name 'AlgoFire-contracts' + + - name: Configure git + shell: bash + run: | + # set git user and email as test invoke git + git config --global user.email "actions@github.com" && git config --global user.name "github-actions" + + - name: Deploy to testnet + run: algokit project deploy testnet --project-name 'AlgoFire-contracts' + env: + # This is the account that becomes the creator of the contract + DEPLOYER_MNEMONIC: ${{ secrets.DEPLOYER_MNEMONIC }} + # The dispenser account is used to ensure the deployer account is funded + DISPENSER_MNEMONIC: ${{ secrets.DISPENSER_MNEMONIC }} diff --git a/.github/workflows/algofire-contracts-ci.yaml b/.github/workflows/algofire-contracts-ci.yaml new file mode 100644 index 0000000..adb4772 --- /dev/null +++ b/.github/workflows/algofire-contracts-ci.yaml @@ -0,0 +1,72 @@ +name: Validate AlgoFire-contracts + +on: + workflow_call: + +jobs: + validate: + runs-on: "ubuntu-latest" + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Use Node.js 22.x + uses: actions/setup-node@v4 + with: + node-version: '22.x' + cache: 'npm' + cache-dependency-path: "**/package-lock.json" + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install algokit + run: pipx install algokit + + - name: Start LocalNet + run: algokit localnet start + + - name: Bootstrap dependencies + run: algokit project bootstrap all --project-name 'AlgoFire-contracts' + + - name: Configure git + shell: bash + run: | + # set git user and email as test invoke git + git config --global user.email "actions@github.com" && git config --global user.name "github-actions" + + + - name: Audit dependencies + run: algokit project run audit --project-name 'AlgoFire-contracts' + + + + - name: Lint and format + run: algokit project run lint --project-name 'AlgoFire-contracts' + + + + - name: Run tests + shell: bash + run: | + set -o pipefail + algokit project run test --project-name 'AlgoFire-contracts' + + + - name: Build smart contracts + run: algokit project run build --project-name 'AlgoFire-contracts' + + - name: Scan TEAL files for issues + run: algokit project run audit-teal --project-name 'AlgoFire-contracts' + + # # Uncomment to enable TEAL static analysis with snapshoting using Tealer package + # # Would first require running locally using audit-teal command and committing the snapshots into source control + # # Please note, tealer has a known bug that may result in large snapshot file sizes, track resolution here: + # # https://github.com/crytic/tealer/issues/101 + # - name: Check output stability of the smart contracts + # run: algokit project run ci-teal-diff --project-name 'AlgoFire-contracts' + + - name: Run deployer against LocalNet + run: algokit project deploy localnet --project-name 'AlgoFire-contracts' diff --git a/.github/workflows/algofire-frontend-cd.yaml b/.github/workflows/algofire-frontend-cd.yaml new file mode 100644 index 0000000..2ce284c --- /dev/null +++ b/.github/workflows/algofire-frontend-cd.yaml @@ -0,0 +1,36 @@ +name: Release AlgoFire-frontend + +on: + workflow_call: +permissions: + contents: read + packages: read + +jobs: + + deploy: + runs-on: ubuntu-latest + name: Deploy to Netlify + environment: frontend-prod + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 18 + + - name: Install algokit + run: pipx install algokit + + - name: Bootstrap dependencies + run: algokit project bootstrap all --project-name 'AlgoFire-frontend' + + - name: Publish to Netlify + env: + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + run: algokit project run ci-deploy-netlify + \ No newline at end of file diff --git a/.github/workflows/algofire-frontend-ci.yaml b/.github/workflows/algofire-frontend-ci.yaml new file mode 100644 index 0000000..7e9b89d --- /dev/null +++ b/.github/workflows/algofire-frontend-ci.yaml @@ -0,0 +1,47 @@ +name: Validate AlgoFire-frontend + +on: + + workflow_call: + + +jobs: + validate: + runs-on: 'ubuntu-latest' + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 18 + + - name: Install poetry + run: pipx install poetry + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: "poetry" + + - name: Install algokit + run: pipx install algokit + + - name: Install dependencies + run: algokit project bootstrap all --project-name 'AlgoFire-frontend' + + + - name: Run linters + run: algokit project run lint --project-name 'AlgoFire-frontend' + + + - name: Run unit tests + run: algokit project run test --project-name 'AlgoFire-frontend' + + + - name: Build + run: algokit project run build --project-name 'AlgoFire-frontend' diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..b144b5f --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,38 @@ +name: Release + +on: + push: + branches: + - main + paths-ignore: + - 'docs/**' + - '**.md' + - '.vscode/**' + - '.idea/**' + +permissions: + contents: read + packages: read + +jobs: + AlgoFire-contracts-validate: + name: Run AlgoFire-contracts release + secrets: inherit + uses: ./.github/workflows/AlgoFire-contracts-ci.yaml + + AlgoFire-contracts-release: + name: Run AlgoFire-contracts release + secrets: inherit + uses: ./.github/workflows/AlgoFire-contracts-cd.yaml + needs: AlgoFire-contracts-validate + + AlgoFire-frontend-validate: + name: Run AlgoFire-frontend release + secrets: inherit + uses: ./.github/workflows/AlgoFire-frontend-ci.yaml + + AlgoFire-frontend-release: + name: Run AlgoFire-frontend release + secrets: inherit + uses: ./.github/workflows/AlgoFire-frontend-cd.yaml + needs: AlgoFire-frontend-validate diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml new file mode 100644 index 0000000..ff18dca --- /dev/null +++ b/.github/workflows/validate.yaml @@ -0,0 +1,14 @@ +name: Pull Request validation + +on: [pull_request] + + +jobs: + AlgoFire-contracts-ci: + name: Run AlgoFire-contracts validation + uses: ./.github/workflows/AlgoFire-contracts-ci.yaml + + AlgoFire-frontend-ci: + name: Run AlgoFire-frontend validation + uses: ./.github/workflows/AlgoFire-frontend-ci.yaml + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4105eb2 --- /dev/null +++ b/.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/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..1b8aa88 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,7 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..033fd10 --- /dev/null +++ b/.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/AlgoFire.code-workspace b/AlgoFire.code-workspace new file mode 100644 index 0000000..dbdf2c3 --- /dev/null +++ b/AlgoFire.code-workspace @@ -0,0 +1,70 @@ +{ + "folders": [ + { + "path": "./", + "name": "ROOT" + }, + { + "path": "./projects/AlgoFire-contracts" + }, + { + "path": "./projects/AlgoFire-frontend" + } + ], + "settings": { + "files.exclude": { + "projects/": true + }, + "jest.disabledWorkspaceFolders": ["ROOT", "projects"], + "dotenv.enableAutocloaking": false + }, + "extensions": { + "recommendations": ["joshx.workspace-terminals"] + }, + + "tasks": { + "version": "2.0.0", + "tasks": [ + { + "label": "Build artifacts (+ LocalNet)", + "command": "algokit", + "args": ["project", "run", "build"], + "options": { + "cwd": "${workspaceFolder}" + }, + "dependsOn": "Start AlgoKit LocalNet", + "problemMatcher": [] + }, + { + "label": "Start AlgoKit LocalNet", + "command": "algokit", + "args": ["localnet", "start"], + "type": "shell", + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [] + } + ] + }, + "launch": { + "configurations": [], + "compounds": [ + { + "preLaunchTask": "Build artifacts (+ LocalNet)", + "name": "Run Frontend (+ LocalNet and Smart Contract)", + "configurations": [ + { + "name": "Deploy contracts", + "folder": "AlgoFire-contracts" + }, + { "name": "Run dApp", "folder": "AlgoFire-frontend" } + ], + "presentation": { + "hidden": false, + "group": "0. Run workspace" + } + } + ] + } + } diff --git a/README (copy).md b/README (copy).md new file mode 100644 index 0000000..7e0bb41 --- /dev/null +++ b/README (copy).md @@ -0,0 +1,55 @@ +# AlgoFire + +This starter full stack project has been generated using AlgoKit. See below for default getting started instructions. + +## Setup + +### Initial setup +1. Clone this repository to your local machine. +2. Ensure [Docker](https://www.docker.com/) is installed and operational. Then, install `AlgoKit` following this [guide](https://github.com/algorandfoundation/algokit-cli#install). +3. Run `algokit project bootstrap all` in the project directory. This command sets up your environment by installing necessary dependencies, setting up a Python virtual environment, and preparing your `.env` file. +4. In the case of a smart contract project, execute `algokit generate env-file -a target_network localnet` from the `AlgoFire-contracts` directory to create a `.env.localnet` file with default configuration for `localnet`. +5. To build your project, execute `algokit project run build`. This compiles your project and prepares it for running. +6. For project-specific instructions, refer to the READMEs of the child projects: + - Smart Contracts: [AlgoFire-contracts](projects/AlgoFire-contracts/README.md) + - Frontend Application: [AlgoFire-frontend](projects/AlgoFire-frontend/README.md) + +> This project is structured as a monorepo, refer to the [documentation](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/project/run.md) to learn more about custom command orchestration via `algokit project run`. + +### Subsequently + +1. If you update to the latest source code and there are new dependencies, you will need to run `algokit project bootstrap all` again. +2. Follow step 3 above. + +### Continuous Integration / Continuous Deployment (CI/CD) + +This project uses [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to define CI/CD workflows, which are located in the [`.github/workflows`](./.github/workflows) folder. You can configure these actions to suit your project's needs, including CI checks, audits, linting, type checking, testing, and deployments to TestNet. + +For pushes to `main` branch, after the above checks pass, the following deployment actions are performed: + - The smart contract(s) are deployed to TestNet using [AlgoNode](https://algonode.io). + - The frontend application is deployed to a provider of your choice (Netlify, Vercel, etc.). See [frontend README](frontend/README.md) for more information. + +> Please note deployment of smart contracts is done via `algokit deploy` command which can be invoked both via CI as seen on this project, or locally. For more information on how to use `algokit deploy` please see [AlgoKit documentation](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/deploy.md). + +## Tools + +This project makes use of Python and React to build Algorand smart contracts and to provide a base project configuration to develop frontends for your Algorand dApps and interactions with smart contracts. The following tools are in use: + +- Algorand, AlgoKit, and AlgoKit Utils +- Python dependencies including Poetry, Black, Ruff or Flake8, mypy, pytest, and pip-audit +- React and related dependencies including AlgoKit Utils, Tailwind CSS, daisyUI, use-wallet, npm, jest, playwright, Prettier, ESLint, and Github Actions workflows for build validation + +### VS Code + +It has also been configured to have a productive dev experience out of the box in [VS Code](https://code.visualstudio.com/), see the [backend .vscode](./backend/.vscode) and [frontend .vscode](./frontend/.vscode) folders for more details. + +## Integrating with smart contracts and application clients + +Refer to the [AlgoFire-contracts](projects/AlgoFire-contracts/README.md) folder for overview of working with smart contracts, [projects/AlgoFire-frontend](projects/AlgoFire-frontend/README.md) for overview of the React project and the [projects/AlgoFire-frontend/contracts](projects/AlgoFire-frontend/src/contracts/README.md) folder for README on adding new smart contracts from backend as application clients on your frontend. The templates provided in these folders will help you get started. +When you compile and generate smart contract artifacts, your frontend component will automatically generate typescript application clients from smart contract artifacts and move them to `frontend/src/contracts` folder, see [`generate:app-clients` in package.json](projects/AlgoFire-frontend/package.json). Afterwards, you are free to import and use them in your frontend application. + +The frontend starter also provides an example of interactions with your AlgofireClient in [`AppCalls.tsx`](projects/AlgoFire-frontend/src/components/AppCalls.tsx) component by default. + +## Next Steps + +You can take this project and customize it to build your own decentralized applications on Algorand. Make sure to understand how to use AlgoKit and how to write smart contracts for Algorand before you start. diff --git a/projects/.gitkeep b/projects/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/projects/AlgoFire-contracts/.algokit.toml b/projects/AlgoFire-contracts/.algokit.toml new file mode 100644 index 0000000..655dbd8 --- /dev/null +++ b/projects/AlgoFire-contracts/.algokit.toml @@ -0,0 +1,56 @@ +[algokit] +min_version = "v2.6.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 = 'AlgoFire-contracts' +artifacts = 'smart_contracts/artifacts' + +[project.deploy] +command = "npm run deploy:ci" + +[project.deploy.testnet] +environment_secrets = [ + "DEPLOYER_MNEMONIC", + "DISPENSER_MNEMONIC", +] + +[project.deploy.mainnet] +environment_secrets = [ + "DEPLOYER_MNEMONIC", + "DISPENSER_MNEMONIC", +] + +[project.run] +# Commands intended for use locally and in CI +build = { commands = [ + 'npm run build', +], description = 'Build all smart contracts in the project' } +test = { commands = [ + 'npm run test', +], description = 'Run smart contract tests using vitest' } +audit = { commands = [ + 'npm run audit', +], description = 'Audit with better-npm-audit' } +lint = { commands = [ + 'npm run lint', + 'npm run format', +], 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/projects/AlgoFire-contracts/.algokit/.copier-answers.yml b/projects/AlgoFire-contracts/.algokit/.copier-answers.yml new file mode 100644 index 0000000..bc0cade --- /dev/null +++ b/projects/AlgoFire-contracts/.algokit/.copier-answers.yml @@ -0,0 +1,9 @@ +# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY +_commit: 0.3.4 +_src_path: gh:algorandfoundation/algokit-typescript-template +author_email: cordesir@gmail.com +author_name: Corey Stedman +contract_name: algofire +preset_name: production +project_name: AlgoFire-contracts + diff --git a/projects/AlgoFire-contracts/.algokit/generators/create_contract/copier.yaml b/projects/AlgoFire-contracts/.algokit/generators/create_contract/copier.yaml new file mode 100644 index 0000000..82f2c4a --- /dev/null +++ b/projects/AlgoFire-contracts/.algokit/generators/create_contract/copier.yaml @@ -0,0 +1,15 @@ +_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" + +include_tests: + type: bool + help: Should we include testing files? + default: 'yes' + +_templates_suffix: ".j2" diff --git a/projects/AlgoFire-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.algo.ts.j2 b/projects/AlgoFire-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.algo.ts.j2 new file mode 100644 index 0000000..344c1a1 --- /dev/null +++ b/projects/AlgoFire-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/contract.algo.ts.j2 @@ -0,0 +1,7 @@ +import { Contract } from '@algorandfoundation/algorand-typescript' + +export class {{ contract_name.split('_')|map('capitalize')|join }} extends Contract { + {% if preset_name != 'starter' %}public {% endif %}hello(name: string): string { + return `Hello, ${name}` + } +} diff --git a/projects/AlgoFire-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 b/projects/AlgoFire-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 new file mode 100644 index 0000000..4626980 --- /dev/null +++ b/projects/AlgoFire-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/deploy-config.ts.j2 @@ -0,0 +1,33 @@ +import { AlgorandClient } from '@algorandfoundation/algokit-utils' +import { {{ contract_name.split('_')|map('capitalize')|join }}Factory } from '../artifacts/{{ contract_name }}/{{ contract_name.split('_')|map('capitalize')|join }}Client' + +// Below is a showcase of various deployment options you can use in TypeScript Client +export async function deploy() { + console.log('=== Deploying {{ contract_name.split('_')|map('capitalize')|join }} ===') + + const algorand = AlgorandClient.fromEnvironment() + const deployer = await algorand.account.fromEnvironment('DEPLOYER') + + const factory = algorand.client.getTypedAppFactory({{ contract_name.split('_')|map('capitalize')|join }}Factory, { + defaultSender: deployer.addr, + }) + + const { appClient, result } = await factory.deploy({ onUpdate: 'append', onSchemaBreak: 'append' }) + + // If app was just created fund the app account + if (['create', 'replace'].includes(result.operationPerformed)) { + await algorand.send.payment({ + amount: (1).algo(), + sender: deployer.addr, + receiver: appClient.appAddress, + }) + } + + const method = 'hello' + const response = await appClient.send.hello({ + args: { name: 'world' }, + }) + console.log( + `Called ${method} on ${appClient.appClient.appName} (${appClient.appClient.appId}) with name = world, received: ${response.return}`, + ) +} diff --git a/projects/AlgoFire-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/{% if include_tests %}contract.e2e.spec.ts{% endif %}.j2 b/projects/AlgoFire-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/{% if include_tests %}contract.e2e.spec.ts{% endif %}.j2 new file mode 100644 index 0000000..146e270 --- /dev/null +++ b/projects/AlgoFire-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/{% if include_tests %}contract.e2e.spec.ts{% endif %}.j2 @@ -0,0 +1,36 @@ +import { Config } from '@algorandfoundation/algokit-utils' +import { algorandFixture } from '@algorandfoundation/algokit-utils/testing' +import { Address } from 'algosdk' +import { beforeAll, beforeEach, describe, expect, test } from 'vitest' +import { {{ contract_name.split('_')|map('capitalize')|join }}Factory } from '../artifacts/{{ contract_name }}/{{ contract_name.split('_')|map('capitalize')|join }}Client' + +describe('{{ contract_name.split('_')|map('capitalize')|join }} contract', () => { + const localnet = algorandFixture() + beforeAll(() => { + Config.configure({ + debug: true, + // traceAll: true, + }) + }) + beforeEach(localnet.newScope) + + const deploy = async (account: Address) => { + const factory = localnet.algorand.client.getTypedAppFactory({{ contract_name.split('_')|map('capitalize')|join }}Factory, { + defaultSender: account, + }) + + const { appClient } = await factory.deploy({ onUpdate: 'append', onSchemaBreak: 'append' }) + return { client: appClient } + } + + test('says hello', async () => { + const { testAccount } = localnet.context + const { client } = await deploy(testAccount) + + const result = await client.send.hello({ args: { name: 'World' } }) + + expect(result.return).toBe('Hello World') + }) + +}) + diff --git a/projects/AlgoFire-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/{% if include_tests %}contract.spec.ts{% endif %}.j2 b/projects/AlgoFire-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/{% if include_tests %}contract.spec.ts{% endif %}.j2 new file mode 100644 index 0000000..e7a7414 --- /dev/null +++ b/projects/AlgoFire-contracts/.algokit/generators/create_contract/smart_contracts/{{ contract_name }}/{% if include_tests %}contract.spec.ts{% endif %}.j2 @@ -0,0 +1,14 @@ +import { TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing' +import { describe, expect, it } from 'vitest' +import { {{ contract_name.split('_')|map('capitalize')|join }} } from './contract.algo' + +describe('{{ contract_name.split('_')|map('capitalize')|join }} contract', () => { + const ctx = new TestExecutionContext() + it('Logs the returned value when sayHello is called', () => { + const contract = ctx.contract.create({{ contract_name.split('_')|map('capitalize')|join }}) + + const result = contract.hello('Sally') + + expect(result).toBe('Hello Sally') + }) +}) diff --git a/projects/AlgoFire-contracts/.algokit/generators/create_env_file/copier.yaml b/projects/AlgoFire-contracts/.algokit/generators/create_env_file/copier.yaml new file mode 100644 index 0000000..afa2cac --- /dev/null +++ b/projects/AlgoFire-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/projects/AlgoFire-contracts/.algokit/generators/create_env_file/{% if is_customnet %}.env.{{custom_network_name}}{% endif %} b/projects/AlgoFire-contracts/.algokit/generators/create_env_file/{% if is_customnet %}.env.{{custom_network_name}}{% endif %} new file mode 100644 index 0000000..cfc9f21 --- /dev/null +++ b/projects/AlgoFire-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/projects/AlgoFire-contracts/.algokit/generators/create_env_file/{% if is_localnet %}.env.localnet{% endif %} b/projects/AlgoFire-contracts/.algokit/generators/create_env_file/{% if is_localnet %}.env.localnet{% endif %} new file mode 100644 index 0000000..fcbf442 --- /dev/null +++ b/projects/AlgoFire-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/projects/AlgoFire-contracts/.algokit/generators/create_env_file/{% if is_mainnet %}.env.mainnet{% endif %} b/projects/AlgoFire-contracts/.algokit/generators/create_env_file/{% if is_mainnet %}.env.mainnet{% endif %} new file mode 100644 index 0000000..bb9a787 --- /dev/null +++ b/projects/AlgoFire-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/projects/AlgoFire-contracts/.algokit/generators/create_env_file/{% if is_testnet %}.env.testnet{% endif %} b/projects/AlgoFire-contracts/.algokit/generators/create_env_file/{% if is_testnet %}.env.testnet{% endif %} new file mode 100644 index 0000000..eeea43d --- /dev/null +++ b/projects/AlgoFire-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/projects/AlgoFire-contracts/.editorconfig b/projects/AlgoFire-contracts/.editorconfig new file mode 100644 index 0000000..0a71b07 --- /dev/null +++ b/projects/AlgoFire-contracts/.editorconfig @@ -0,0 +1,7 @@ +root=true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true diff --git a/projects/AlgoFire-contracts/.gitignore b/projects/AlgoFire-contracts/.gitignore new file mode 100644 index 0000000..25494bf --- /dev/null +++ b/projects/AlgoFire-contracts/.gitignore @@ -0,0 +1,62 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# Dependency directories +node_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# Editor/OS directories and files +.DS_Store +*.suo + +# IntelliJ family +.idea +!.idea/ +.idea/* +!.idea/runConfigurations/ + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# Compiled code +dist/ +build/ + +# Coverage report +coverage + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# Received approval test files +*.received.* + +# AlgoKit +debug_traces/ +.algokit/static-analysis/ # Replace with .algokit/static-analysis/tealer/ to enable snapshot checks in CI +.algokit/sources diff --git a/projects/AlgoFire-contracts/.npmrc b/projects/AlgoFire-contracts/.npmrc new file mode 100644 index 0000000..c42da84 --- /dev/null +++ b/projects/AlgoFire-contracts/.npmrc @@ -0,0 +1 @@ +engine-strict = true diff --git a/projects/AlgoFire-contracts/.prettierignore b/projects/AlgoFire-contracts/.prettierignore new file mode 100644 index 0000000..8ca71b1 --- /dev/null +++ b/projects/AlgoFire-contracts/.prettierignore @@ -0,0 +1,13 @@ +# don't ever format node_modules +node_modules +# don't lint format output (make sure it's set to your correct build folder name) +dist +build +# don't format nyc coverage output +coverage +# don't format generated types +**/generated/types.d.ts +**/generated/types.ts +# don't format ide files +.idea +smart_contracts/artifacts diff --git a/projects/AlgoFire-contracts/.prettierrc.js b/projects/AlgoFire-contracts/.prettierrc.js new file mode 100644 index 0000000..c484d0e --- /dev/null +++ b/projects/AlgoFire-contracts/.prettierrc.js @@ -0,0 +1,10 @@ +module.exports = { + singleQuote: true, + jsxSingleQuote: false, + semi: false, + tabWidth: 2, + trailingComma: 'all', + printWidth: 120, + endOfLine: 'lf', + arrowParens: 'always', +} diff --git a/projects/AlgoFire-contracts/.tours/getting-started-with-your-algokit-project.tour b/projects/AlgoFire-contracts/.tours/getting-started-with-your-algokit-project.tour new file mode 100644 index 0000000..3ad36a6 --- /dev/null +++ b/projects/AlgoFire-contracts/.tours/getting-started-with-your-algokit-project.tour @@ -0,0 +1,56 @@ +{ + "$schema": "https://aka.ms/codetour-schema", + "title": "Getting Started with Your AlgoKit Project", + "steps": [ + { + "file": "README.md", + "description": "Welcome to your brand new AlgoKit template-based project. In this tour, we will guide you through the main features and capabilities included in the template.", + "line": 3 + }, + { + "file": "README.md", + "description": "Start by ensuring you have followed the setup of pre-requisites.", + "line": 9 + }, + { + "file": ".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. 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.algo.ts` file, which is the entry point for your contract implementation, and `deployConfig.ts` file, 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.ts", + "description": "The default deployment scripts invoke a sample method on the starter contract that demonstrates how to interact with your deployed Algorand on-chain applications using the [`AlgoKit Typed Clients`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/generate.md#1-typed-clients) feature. The invocation if deploy is aliased in `.algokit.toml` file, allowing simple deployments via `algokit project deploy` command.", + "line": 32 + }, + { + "file": "smart_contracts/algofire/contact.spec.ts", + "description": "The default tests provided demonstrate an example of setting up in-memory fast unit tests with the Algorand TypeScript testing library that mocks AVM functionality.", + "line": 5 + }, + { + "file": "smart_contracts/algofire/contact.e2e.spec.ts", + "description": "The default tests provided demonstrate an example of setting up an end-to-end test with fixtures, and testing smart contract calls against a LocalNet network via an AlgoKit typed client.", + "line": 7 + }, + { + "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/index.ts", + "description": "Uncomment the following line to enable artifacts required for the [AlgoKit AVM Debugger](https://github.com/algorandfoundation/algokit-avm-vscode-debugger) that run for every single call rather than just failures. This VSCode plugin is available on the [VSCode Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=algorandfoundation.algokit-avm-vscode-debugger) for every call. A new folder will be automatically created in the `.algokit` directory with source maps of all TEAL contracts in this workspace, as well as traces that will appear in a folder at the root of the workspace. You can then use the traces as entry points to trigger the debug extension. Make sure to have the `.algokit.toml` file available at the root of the workspace.", + "line": 13 + } + ] +} diff --git a/projects/AlgoFire-contracts/.vscode/extensions.json b/projects/AlgoFire-contracts/.vscode/extensions.json new file mode 100644 index 0000000..cb71192 --- /dev/null +++ b/projects/AlgoFire-contracts/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "vitest.explorer", + "editorconfig.editorconfig", + "vsls-contrib.codetour", + "algorandfoundation.algokit-avm-vscode-debugger" + ] +} diff --git a/projects/AlgoFire-contracts/.vscode/launch.json b/projects/AlgoFire-contracts/.vscode/launch.json new file mode 100644 index 0000000..093ebb1 --- /dev/null +++ b/projects/AlgoFire-contracts/.vscode/launch.json @@ -0,0 +1,58 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Build & Deploy contracts", + "type": "node", + "request": "launch", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "deploy"], + "cwd": "${workspaceFolder}/smart_contracts", + "console": "integratedTerminal", + "skipFiles": ["/**", "node_modules/**"], + "preLaunchTask": "Build contracts (+ 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": "node", + "request": "launch", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "deploy"], + "cwd": "${workspaceFolder}/smart_contracts", + "console": "integratedTerminal", + "skipFiles": ["/**", "node_modules/**"], + "env": { + "ALGOD_TOKEN": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ALGOD_SERVER": "http://localhost", + "ALGOD_PORT": "4001", + "INDEXER_TOKEN": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "INDEXER_SERVER": "http://localhost", + "INDEXER_PORT": "8980" + } + }, + { + "name": "Build contracts", + "request": "launch", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "build"], + "cwd": "${workspaceFolder}", + "console": "integratedTerminal", + "skipFiles": ["/**", "node_modules/**"], + }, + { + "type": "avm", + "request": "launch", + "name": "Debug AVM executions", + "simulateTraceFile": "${workspaceFolder}/${command:PickSimulateTraceFile}", + "stopOnEntry": true + } + ] +} diff --git a/projects/AlgoFire-contracts/.vscode/settings.json b/projects/AlgoFire-contracts/.vscode/settings.json new file mode 100644 index 0000000..b829961 --- /dev/null +++ b/projects/AlgoFire-contracts/.vscode/settings.json @@ -0,0 +1,18 @@ +{ + "files.eol": "\n", + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + }, + "jest.enable": false, + "files.associations": { + ".github/**/*.yml": "github-actions-workflow" + }, + "eslint.enable": true, + "eslint.validate": ["typescript"], + "eslint.options": { + "extensions": [".ts"] + }, + "eslint.workingDirectories": ["./smart_contracts"] + } diff --git a/projects/AlgoFire-contracts/.vscode/tasks.json b/projects/AlgoFire-contracts/.vscode/tasks.json new file mode 100644 index 0000000..55310ab --- /dev/null +++ b/projects/AlgoFire-contracts/.vscode/tasks.json @@ -0,0 +1,69 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build contracts", + "type": "npm", + "script": "build", + "path": "${workspaceFolder}", + "options": { + "cwd": "${workspaceFolder}" + }, + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [] + }, + { + "label": "Build contracts (+ LocalNet)", + "type": "npm", + "script": "build", + "path": "${workspaceFolder}", + "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/projects/AlgoFire-contracts/README.md b/projects/AlgoFire-contracts/README.md new file mode 100644 index 0000000..a0d698c --- /dev/null +++ b/projects/AlgoFire-contracts/README.md @@ -0,0 +1,157 @@ +# AlgoFire-contracts + +This project has been generated using AlgoKit. See below for default getting started instructions. + +# Setup + +### Pre-requisites + +- [Nodejs 22](https://nodejs.org/en/download) or later +- [AlgoKit CLI 2.5](https://github.com/algorandfoundation/algokit-cli?tab=readme-ov-file#install) or later +- [Docker](https://www.docker.com/) (only required for LocalNet) +- [Puya Compiler 4.4.4](https://pypi.org/project/puyapy/) or later + +> For interactive tour over the codebase, download [vsls-contrib.codetour](https://marketplace.visualstudio.com/items?itemName=vsls-contrib.codetour) extension for VS Code, then open the [`.codetour.json`](./.tours/getting-started-with-your-algokit-project.tour) file in code tour extension. + +### Initial Setup + +#### 1. Clone the Repository +Start by cloning this repository to your local machine. + +#### 2. Install Pre-requisites +Ensure the following pre-requisites are installed and properly configured: + +- **Docker**: Required for running a local Algorand network. +- **AlgoKit CLI**: Essential for project setup and operations. Verify installation with `algokit --version`, expecting `2.6.0` or later. + +#### 3. Bootstrap Your Local Environment +Run the following commands within the project folder: + +- **Setup Project**: Execute `algokit project bootstrap all` to install dependencies and setup npm dependencies. +- **Configure environment**: Execute `algokit generate env-file -a target_network localnet` to create a `.env.localnet` file with default configuration for `localnet`. +- **Start LocalNet**: Use `algokit localnet start` to initiate a local Algorand network. + +### Development Workflow + +#### Terminal +Directly manage and interact with your project using AlgoKit commands: + +1. **Build Contracts**: `algokit project run build` compiles all smart contracts. You can also specify a specific contract by passing the name of the contract folder as an extra argument. +For example: `algokit project run build -- hello_world` will only build the `hello_world` contract. +2. **Deploy**: Use `algokit project deploy localnet` to deploy contracts to the local network. You can also specify a specific contract by passing the name of the contract folder as an extra argument. +For example: `algokit project deploy localnet -- hello_world` will only deploy the `hello_world` contract. + +#### VS Code +For a seamless experience with breakpoint debugging and other features: + +1. **Open Project**: In VS Code, open the repository root. +2. **Install Extensions**: Follow prompts to install recommended extensions. +3. **Debugging**: + - Use `F5` to start debugging. + +#### JetBrains IDEs +While primarily optimized for VS Code, JetBrains IDEs are supported: + +1. **Open Project**: In your JetBrains IDE, open the repository root. +2. **Automatic Setup**: The IDE should configure the Node.js environment. +3. **Debugging**: Use `Shift+F10` or `Ctrl+R` to start debugging. Note: Windows users may encounter issues with pre-launch tasks due to a known bug. See [JetBrains forums](https://youtrack.jetbrains.com/issue/IDEA-277486/Shell-script-configuration-cannot-run-as-before-launch-task) for workarounds. + +## AlgoKit Workspaces and Project Management +This project supports both standalone and monorepo setups through AlgoKit workspaces. Leverage [`algokit project run`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/project/run.md) commands for efficient monorepo project orchestration and management across multiple projects within a workspace. + +## AlgoKit Generators + +This template provides a set of [algokit generators](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/generate.md) that allow you to further modify the project instantiated from the template to fit your needs, as well as giving you a base to build your own extensions to invoke via the `algokit generate` command. + +### Generate Smart Contract + +By default the template creates a single `HelloWorld` contract under algofire folder in the `smart_contracts` directory. To add a new contract: + +1. From the root of the project (`../`) execute `algokit generate smart-contract`. This will create a new starter smart contract and deployment configuration file under `{your_contract_name}` subfolder in the `smart_contracts` directory. +2. Each contract potentially has different creation parameters and deployment steps. Hence, you need to define your deployment logic in `deploy-config.ts` file. +3. Technically, you need to reference your contract deployment logic in the `index.ts` file. However, by default, `index.ts` will auto import all TypeScript deployment files under `smart_contracts` directory. If you want to manually import specific contracts, modify the default code provided by the template in `index.ts` file. + +> Please note, above is just a suggested convention tailored for the base configuration and structure of this template. The default code supplied by the template in the `index.ts` file is tailored for the suggested convention. You are free to modify the structure and naming conventions as you see fit. + +### Generate '.env' files + +By default the template instance does not contain any env files to deploy to different networks. Using [`algokit project deploy`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/project/deploy.md) against `localnet` | `testnet` | `mainnet` will use default values for `algod` and `indexer` unless overwritten via `.env` or `.env.{target_network}`. + +To generate a new `.env` or `.env.{target_network}` file, run `algokit generate env-file` + +### Debugging Smart Contracts + +This project is optimized to work with AlgoKit AVM Debugger extension. To activate it: + +Refer to the commented header in the `index.ts` file in the `smart_contracts` folder.Since you have opted in to include VSCode launch configurations in your project, you can also use the `Debug TEAL via AlgoKit AVM Debugger` launch configuration to interactively select an available trace file and launch the debug session for your smart contract. + + +For information on using and setting up the `AlgoKit AVM Debugger` VSCode extension refer [here](https://github.com/algorandfoundation/algokit-avm-vscode-debugger). To install the extension from the VSCode Marketplace, use the following link: [AlgoKit AVM Debugger extension](https://marketplace.visualstudio.com/items?itemName=algorandfoundation.algokit-avm-vscode-debugger).### Continuous Integration / Continuous Deployment (CI/CD) + +This project uses [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to define CI/CD workflows, which are located in the [.github/workflows](`../../.github/workflows`) folder. + +> Please note, if you instantiated the project with --workspace flag in `algokit init` it will automatically attempt to move the contents of the `.github` folder to the root of the workspace. + +### AlgoKit Workspaces + +To define custom `algokit project run` commands refer to [documentation](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/project/run.md). This allows orchestration of commands spanning across multiple projects within an algokit workspace based project (monorepo). + +#### Setting up GitHub for CI/CD workflow and TestNet deployment + + 1. Every time you have a change to your smart contract, and when you first initialize the project you need to [build the contract](#initial-setup) and then commit the `smart_contracts/artifacts` folder so the [output stability](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/articles/output_stability.md) tests pass + 2. Decide what values you want to use for the `allowUpdate` and `allowDelete` parameters specified in [`deploy-config.ts`](./smart_contracts/algofire/deploy-config.ts). + When deploying to LocalNet these values are both set to `true` for convenience. But for non-LocalNet networks + they are more conservative and use `false` + These default values will allow the smart contract to be deployed initially, but will not allow the app to be updated or deleted if is changed and the build will instead fail. + To help you decide it may be helpful to read the [AlgoKit Utils app deployment documentation](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/docs/capabilities/app-deploy.md) or the [AlgoKit smart contract deployment architecture](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/architecture-decisions/2023-01-12_smart-contract-deployment.md#upgradeable-and-deletable-contracts). + 3. Create a [Github Environment](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#creating-an-environment) named `Test`. + Note: If you have a private repository and don't have GitHub Enterprise then Environments won't work and you'll need to convert the GitHub Action to use a different approach. Ignore this step if you picked `Starter` preset. + 4. Create or obtain a mnemonic for an Algorand account for use on TestNet to deploy apps, referred to as the `DEPLOYER` account. + 5. Store the mnemonic as a [secret](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#environment-secrets) `DEPLOYER_MNEMONIC` + in the Test environment created in step 3. + 6. The account used to deploy the smart contract will require enough funds to create the app, and also fund it. There are two approaches available here: + * Either, ensure the account is funded outside of CI/CD. + In Testnet, funds can be obtained by using the [Algorand TestNet dispenser](https://bank.testnet.algorand.network/) and we recommend provisioning 50 ALGOs. + * Or, fund the account as part of the CI/CD process by using a `DISPENSER_MNEMONIC` GitHub Environment secret to point to a separate `DISPENSER` account that you maintain ALGOs in (similarly, you need to provision ALGOs into this account using the [TestNet dispenser](https://bank.testnet.algorand.network/)). + +#### Continuous Integration + +For pull requests and pushes to `main` branch against this repository the following checks are automatically performed by GitHub Actions: + - NPM dependencies are audited using [better-npm-audit](https://github.com/jeemok/better-npm-audit#readme) + - Code formatting is performed using [Prettier](https://prettier.io/) + - Linting is checked using [ESLint](https://eslint.org/) +- The base framework for testing is [vitest](https://vitest.dev/), and the project includes two separate kinds of tests: +- - `Algorand TypeScript` smart contract unit tests, that are run using [`algorand-typescript-testing`](https://github.com/algorandfoundation/algorand-typescript-testing), which are executed in a Node.js intepreter emulating major AVM behaviour +- - End-to-end (e2e) `AppClient` tests that are run against `algokit localnet` and test the behaviour in a real network environment + - Smart contract artifacts are built + - Smart contract artifacts are checked for [output stability](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/articles/output_stability.md). + - Smart contract is deployed to a AlgoKit LocalNet instance + +> NOTE: By default smart contract artifacts are compiled with `--debug-level` set to 0, to change this, modify the compiler invocation under the `build` script in `package.json` + +#### Continuous Deployment + +For pushes to `main` branch, after the above checks pass, the following deployment actions are performed: + - The smart contract(s) are deployed to TestNet using [AlgoNode](https://algonode.io). + +> Please note deployment is also performed via `algokit deploy` command which can be invoked both via CI as seen on this project, or locally. For more information on how to use `algokit deploy` please see [AlgoKit documentation](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/deploy.md). + +# Tools + +This project makes use of Algorand TypeScript to build Algorand smart contracts. The following tools are in use: + +- [Algorand](https://www.algorand.com/) - Layer 1 Blockchain; [Developer portal](https://dev.algorand.co/), [Why Algorand?](https://dev.algorand.co/getting-started/why-algorand/) +- [AlgoKit](https://github.com/algorandfoundation/algokit-cli) - One-stop shop tool for developers building on the Algorand network; [docs](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/algokit.md), [intro tutorial](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/tutorials/intro.md) +- [Algorand TypeScript](https://github.com/algorandfoundation/puya-ts/) - A semantically and syntactically compatible, typed TypeScript language that works with standard TypeScript tooling and allows you to express smart contracts (apps) and smart signatures (logic signatures) for deployment on the Algorand Virtual Machine (AVM); [docs](https://github.com/algorandfoundation/puya-ts/), [examples](https://github.com/algorandfoundation/puya-ts/tree/main/examples) +- [AlgoKit Utils](https://github.com/algorandfoundation/algokit-utils-ts) - A set of core Algorand utilities that make it easier to build solutions on Algorand. +- [NPM](https://www.npmjs.com/): TypeScript packaging and dependency management. +- [TypeScript](https://www.typescriptlang.org/): Strongly typed programming language that builds on JavaScript +- [ts-node-dev](https://github.com/wclr/ts-node-dev): TypeScript development execution environment- [Prettier](https://prettier.io/): A code formatter.- [ESLint](https://eslint.org/): A JavaScript / TypeScript linter. +- [vitest](https://vitest.dev/): Automated testing. +- [better-npm-audit](https://github.com/jeemok/better-npm-audit#readme): Tool for scanning JavaScript / TypeScript environments for packages with known vulnerabilities. + + +It has also been configured to have a productive dev experience out of the box in [VS Code](https://code.visualstudio.com/), see the [.vscode](./.vscode) folder. + + + diff --git a/projects/AlgoFire-contracts/eslint.config.mjs b/projects/AlgoFire-contracts/eslint.config.mjs new file mode 100644 index 0000000..4d2428a --- /dev/null +++ b/projects/AlgoFire-contracts/eslint.config.mjs @@ -0,0 +1,18 @@ +import eslint from '@eslint/js' +import tseslint from 'typescript-eslint' +import globals from 'globals' + +export default tseslint.config( + eslint.configs.recommended, + tseslint.configs.recommended, + { + languageOptions: { + globals: { + ...globals.node, + }, + }, + rules: { + '@typescript-eslint/explicit-member-accessibility': 'warn', + }, + }, +); diff --git a/projects/AlgoFire-contracts/package.json b/projects/AlgoFire-contracts/package.json new file mode 100644 index 0000000..265330c --- /dev/null +++ b/projects/AlgoFire-contracts/package.json @@ -0,0 +1,48 @@ +{ + "name": "smart_contracts", + "version": "1.0.0", + "description": "Smart contract deployer", + "main": "smart_contracts/index.ts", + "scripts": { + "build": "algokit compile ts smart_contracts --output-source-map --out-dir artifacts && algokit generate client smart_contracts/artifacts --output {app_spec_dir}/{contract_name}Client.ts", + "deploy": "ts-node-dev --transpile-only --watch .env -r dotenv/config smart_contracts/index.ts", + "deploy:ci": "ts-node --transpile-only -r dotenv/config smart_contracts/index.ts", + "lint": "eslint smart_contracts", + "lint:fix": "eslint smart_contracts --fix", + "audit": "better-npm-audit audit", + "format": "prettier --write .", + "test": "vitest run --coverage", + "test:watch": "vitest watch", + "check-types": "tsc --noEmit" + }, + "engines": { + "node": ">=22.0", + "npm": ">=9.0" + }, + "dependencies": { + "@algorandfoundation/algorand-typescript": "~1.0.0-beta.25 <1.0.0" + }, + "devDependencies": { + "@algorandfoundation/algokit-client-generator": "^6.0.0", + "@algorandfoundation/algokit-utils": "^9.0.0", + "@algorandfoundation/algokit-utils-debug": "^1.0.4", + "@algorandfoundation/puya-ts": "~1.0.0-beta.50 <1.0.0", + "@rollup/plugin-typescript": "^12.1.2", + "@tsconfig/node22": "^22.0.0", + "algosdk": "^3.0.0", + "better-npm-audit": "^3.11.0", + "dotenv": "^16.4.7", + "eslint": "^9.18.0", + "@eslint/js": "^9.18.0", + "typescript-eslint": "^8.19.1", + "prettier": "^3.4.2", + "ts-node-dev": "^2.0.0", + "@algorandfoundation/algorand-typescript-testing": "~1.0.0-beta.30 <1.0.0", + "vitest": "^2.1.8", + "@vitest/coverage-v8": "^2.1.8", + "typescript": "^5.7.3" + }, + "overrides": { + "esbuild": "0.25.0" + } +} diff --git a/projects/AlgoFire-contracts/smart_contracts/algofire/contract.algo.ts b/projects/AlgoFire-contracts/smart_contracts/algofire/contract.algo.ts new file mode 100644 index 0000000..11fd81e --- /dev/null +++ b/projects/AlgoFire-contracts/smart_contracts/algofire/contract.algo.ts @@ -0,0 +1,7 @@ +import { Contract } from '@algorandfoundation/algorand-typescript' + +export class Algofire extends Contract { + public hello(name: string): string { + return `Hello, ${name}` + } +} diff --git a/projects/AlgoFire-contracts/smart_contracts/algofire/contract.e2e.spec.ts b/projects/AlgoFire-contracts/smart_contracts/algofire/contract.e2e.spec.ts new file mode 100644 index 0000000..e83b67c --- /dev/null +++ b/projects/AlgoFire-contracts/smart_contracts/algofire/contract.e2e.spec.ts @@ -0,0 +1,53 @@ +import { Config } from '@algorandfoundation/algokit-utils' +import { registerDebugEventHandlers } from '@algorandfoundation/algokit-utils-debug' +import { algorandFixture } from '@algorandfoundation/algokit-utils/testing' +import { Address } from 'algosdk' +import { beforeAll, beforeEach, describe, expect, test } from 'vitest' +import { AlgofireFactory } from '../artifacts/algofire/AlgofireClient' + +describe('Algofire contract', () => { + const localnet = algorandFixture() + beforeAll(() => { + Config.configure({ + debug: true, + // traceAll: true, + }) + registerDebugEventHandlers() + }) + beforeEach(localnet.newScope) + + const deploy = async (account: Address) => { + const factory = localnet.algorand.client.getTypedAppFactory(AlgofireFactory, { + defaultSender: account, + }) + + const { appClient } = await factory.deploy({ + onUpdate: 'append', + onSchemaBreak: 'append', + }) + return { client: appClient } + } + + test('says hello', async () => { + const { testAccount } = localnet.context + const { client } = await deploy(testAccount) + + const result = await client.send.hello({ args: { name: 'World' } }) + + expect(result.return).toBe('Hello, World') + }) + + test('simulate says hello with correct budget consumed', async () => { + const { testAccount } = localnet.context + const { client } = await deploy(testAccount) + const result = await client + .newGroup() + .hello({ args: { name: 'World' } }) + .hello({ args: { name: 'Jane' } }) + .simulate() + + expect(result.returns[0]).toBe('Hello, World') + expect(result.returns[1]).toBe('Hello, Jane') + expect(result.simulateResponse.txnGroups[0].appBudgetConsumed).toBeLessThan(100) + }) +}) diff --git a/projects/AlgoFire-contracts/smart_contracts/algofire/contract.spec.ts b/projects/AlgoFire-contracts/smart_contracts/algofire/contract.spec.ts new file mode 100644 index 0000000..3408310 --- /dev/null +++ b/projects/AlgoFire-contracts/smart_contracts/algofire/contract.spec.ts @@ -0,0 +1,14 @@ +import { TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing' +import { describe, expect, it } from 'vitest' +import { Algofire } from './contract.algo' + +describe('Algofire contract', () => { + const ctx = new TestExecutionContext() + it('Logs the returned value when sayHello is called', () => { + const contract = ctx.contract.create(Algofire) + + const result = contract.hello('Sally') + + expect(result).toBe('Hello, Sally') + }) +}) diff --git a/projects/AlgoFire-contracts/smart_contracts/algofire/deploy-config.ts b/projects/AlgoFire-contracts/smart_contracts/algofire/deploy-config.ts new file mode 100644 index 0000000..24a1a1e --- /dev/null +++ b/projects/AlgoFire-contracts/smart_contracts/algofire/deploy-config.ts @@ -0,0 +1,33 @@ +import { AlgorandClient } from '@algorandfoundation/algokit-utils' +import { AlgofireFactory } from '../artifacts/algofire/AlgofireClient' + +// Below is a showcase of various deployment options you can use in TypeScript Client +export async function deploy() { + console.log('=== Deploying Algofire ===') + + const algorand = AlgorandClient.fromEnvironment() + const deployer = await algorand.account.fromEnvironment('DEPLOYER') + + const factory = algorand.client.getTypedAppFactory(AlgofireFactory, { + defaultSender: deployer.addr, + }) + + const { appClient, result } = await factory.deploy({ onUpdate: 'append', onSchemaBreak: 'append' }) + + // If app was just created fund the app account + if (['create', 'replace'].includes(result.operationPerformed)) { + await algorand.send.payment({ + amount: (1).algo(), + sender: deployer.addr, + receiver: appClient.appAddress, + }) + } + + const method = 'hello' + const response = await appClient.send.hello({ + args: { name: 'world' }, + }) + console.log( + `Called ${method} on ${appClient.appClient.appName} (${appClient.appClient.appId}) with name = world, received: ${response.return}`, + ) +} diff --git a/projects/AlgoFire-contracts/smart_contracts/index.ts b/projects/AlgoFire-contracts/smart_contracts/index.ts new file mode 100644 index 0000000..6030d0e --- /dev/null +++ b/projects/AlgoFire-contracts/smart_contracts/index.ts @@ -0,0 +1,62 @@ +import { Config } from '@algorandfoundation/algokit-utils' +import { registerDebugEventHandlers } from '@algorandfoundation/algokit-utils-debug' +import { consoleLogger } from '@algorandfoundation/algokit-utils/types/logging' +import * as fs from 'node:fs' +import * as path from 'node:path' + +// Uncomment the traceAll option to enable auto generation of AVM Debugger compliant sourceMap and simulation trace file for all AVM calls. +// 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({ + logger: consoleLogger, + debug: true, + // traceAll: true, +}) +registerDebugEventHandlers() + +// base directory +const baseDir = path.resolve(__dirname) + +// function to validate and dynamically import a module +async function importDeployerIfExists(dir: string) { + const deployerPath = path.resolve(dir, 'deploy-config') + if (fs.existsSync(deployerPath + '.ts') || fs.existsSync(deployerPath + '.js')) { + const deployer = await import(deployerPath) + return { ...deployer, name: path.basename(dir) } + } + return null +} + +// get a list of all deployers from the subdirectories +async function getDeployers() { + const directories = fs + .readdirSync(baseDir, { withFileTypes: true }) + .filter((dirent) => dirent.isDirectory()) + .map((dirent) => path.resolve(baseDir, dirent.name)) + + const deployers = await Promise.all(directories.map(importDeployerIfExists)) + return deployers.filter((deployer) => deployer !== null) // Filter out null values +} + +// execute all the deployers +(async () => { + const contractName = process.argv.length > 2 ? process.argv[2] : undefined + const contractDeployers = await getDeployers() + + const filteredDeployers = contractName + ? contractDeployers.filter(deployer => deployer.name === contractName) + : contractDeployers + + if (contractName && filteredDeployers.length === 0) { + console.warn(`No deployer found for contract name: ${contractName}`) + return + } + + for (const deployer of filteredDeployers) { + try { + await deployer.deploy() + } catch (e) { + console.error(`Error deploying ${deployer.name}:`, e) + } + } +})() diff --git a/projects/AlgoFire-contracts/tsconfig.json b/projects/AlgoFire-contracts/tsconfig.json new file mode 100644 index 0000000..323e15c --- /dev/null +++ b/projects/AlgoFire-contracts/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "strict": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "allowJs": false, + "allowSyntheticDefaultImports": true, + "module": "CommonJS", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true + }, + "include": ["smart_contracts/**/*.ts"], + "exclude": ["node_modules", "dist", "coverage"] +} diff --git a/projects/AlgoFire-contracts/tsconfig.test.json b/projects/AlgoFire-contracts/tsconfig.test.json new file mode 100644 index 0000000..6cc09d9 --- /dev/null +++ b/projects/AlgoFire-contracts/tsconfig.test.json @@ -0,0 +1,14 @@ +{ + "extends": "@tsconfig/node22/tsconfig.json", + "compilerOptions": { + "noEmit": true, + "target": "ES2023", + "module": "ESNext", + "lib": ["ES2023"], + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "esModuleInterop": true + }, + "include": ["**/*.ts", "**/*.mts"], + "exclude": ["node_modules"] +} diff --git a/projects/AlgoFire-contracts/vitest.config.mts b/projects/AlgoFire-contracts/vitest.config.mts new file mode 100644 index 0000000..abb2d2a --- /dev/null +++ b/projects/AlgoFire-contracts/vitest.config.mts @@ -0,0 +1,21 @@ +import { puyaTsTransformer } from '@algorandfoundation/algorand-typescript-testing/vitest-transformer' +import typescript from '@rollup/plugin-typescript' +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + esbuild: {}, + test: { + testTimeout: 10000, + coverage: { + provider: 'v8', + }, + }, + plugins: [ + typescript({ + tsconfig: './tsconfig.test.json', + transformers: { + before: [puyaTsTransformer], + }, + }), + ], +}) diff --git a/projects/AlgoFire-frontend/.algokit.toml b/projects/AlgoFire-frontend/.algokit.toml new file mode 100644 index 0000000..14d75bc --- /dev/null +++ b/projects/AlgoFire-frontend/.algokit.toml @@ -0,0 +1,17 @@ +[algokit] +min_version = "v2.0.0" + +[project] +type = "frontend" +name = 'AlgoFire-frontend' +artifacts = "src/contracts" + +[project.run] +build = { commands = ['npm run build'], description = 'Build frontend' } +test = { commands = ['npm run test'], description = 'Run frontend tests' } +lint = { commands = ['npm run lint'], description = 'Lint frontend code' } +ci-deploy-netlify = { commands = [ + 'npm install --global netlify-cli@latest', + 'netlify login', + 'netlify deploy --build --prod' + ], description = 'Deploy to Netlify' } diff --git a/projects/AlgoFire-frontend/.algokit/.copier-answers.yml b/projects/AlgoFire-frontend/.algokit/.copier-answers.yml new file mode 100644 index 0000000..164869e --- /dev/null +++ b/projects/AlgoFire-frontend/.algokit/.copier-answers.yml @@ -0,0 +1,9 @@ +# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY +_commit: 1.1.1 +_src_path: gh:algorandfoundation/algokit-react-frontend-template +author_email: cordesir@gmail.com +author_name: Corey Stedman +cloud_provider: netlify +preset_name: production +project_name: AlgoFire-frontend + diff --git a/projects/AlgoFire-frontend/.editorconfig b/projects/AlgoFire-frontend/.editorconfig new file mode 100644 index 0000000..a83b72c --- /dev/null +++ b/projects/AlgoFire-frontend/.editorconfig @@ -0,0 +1,9 @@ +[*] +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 diff --git a/projects/AlgoFire-frontend/.env.template b/projects/AlgoFire-frontend/.env.template new file mode 100644 index 0000000..eb4b229 --- /dev/null +++ b/projects/AlgoFire-frontend/.env.template @@ -0,0 +1,67 @@ +# ====================== +# LocalNet configuration +# uncomment below to use +# ====================== + +VITE_ENVIRONMENT=local + +# Algod +VITE_ALGOD_TOKEN=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +VITE_ALGOD_SERVER=http://localhost +VITE_ALGOD_PORT=4001 +VITE_ALGOD_NETWORK=localnet + +# Indexer +VITE_INDEXER_TOKEN=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +VITE_INDEXER_SERVER=http://localhost +VITE_INDEXER_PORT=8980 + +# KMD +# Please note: +# 1. This is only needed for LocalNet since +# by default KMD provider is ignored on other networks. +# 2. AlgoKit LocalNet starts with a single wallet called 'unencrypted-default-wallet', +# with heaps of tokens available for testing. +VITE_KMD_TOKEN=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +VITE_KMD_SERVER=http://localhost +VITE_KMD_PORT=4002 +VITE_KMD_WALLET="unencrypted-default-wallet" +VITE_KMD_PASSWORD="" + +# # ====================== +# # TestNet configuration: +# # uncomment below to use +# # ====================== + +# VITE_ENVIRONMENT=local + +# # Algod +# VITE_ALGOD_TOKEN="" +# VITE_ALGOD_SERVER="https://testnet-api.algonode.cloud" +# VITE_ALGOD_PORT="" +# VITE_ALGOD_NETWORK="testnet" + +# # Indexer +# VITE_INDEXER_TOKEN="" +# VITE_INDEXER_SERVER="https://testnet-idx.algonode.cloud" +# VITE_INDEXER_PORT="" + + +# # ====================== +# # MainNet configuration: +# # uncomment below to use +# # ====================== + +# VITE_ENVIRONMENT=production + +# # Algod +# VITE_ALGOD_TOKEN="" +# VITE_ALGOD_SERVER="https://mainnet-api.algonode.cloud" +# VITE_ALGOD_PORT="" +# VITE_ALGOD_NETWORK="mainnet" + +# # Indexer +# VITE_INDEXER_TOKEN="" +# VITE_INDEXER_SERVER="https://mainnet-idx.algonode.cloud" +# VITE_INDEXER_PORT="" + diff --git a/projects/AlgoFire-frontend/.eslintrc b/projects/AlgoFire-frontend/.eslintrc new file mode 100644 index 0000000..868fe37 --- /dev/null +++ b/projects/AlgoFire-frontend/.eslintrc @@ -0,0 +1,27 @@ +{ + "root": true, + "env": { + "node": true + }, + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint", "prettier"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" + ], + "rules": { + "prettier/prettier": "warn", + "no-console": "warn", + "@typescript-eslint/no-unused-vars": [ + "warn", + { + "ignoreRestSiblings": true, + "argsIgnorePattern": "^_", + "destructuredArrayIgnorePattern": "^_" + } + ], + "prefer-template": "error" + } +} diff --git a/projects/AlgoFire-frontend/.gitignore b/projects/AlgoFire-frontend/.gitignore new file mode 100644 index 0000000..b1ce837 --- /dev/null +++ b/projects/AlgoFire-frontend/.gitignore @@ -0,0 +1,38 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + + +# dotenv environment variable files +.env +env/ + +# misc +/dist +.DS_Store + + +npm-debug.log* +yarn-debug.log* +yarn-error.log* +/test-results/ +/playwright-report/ +/playwright/.cache/ + +# PyCharm +.idea +!.idea/ +.idea/* +!.idea/runConfigurations/ + +.vercel +.netlify diff --git a/projects/AlgoFire-frontend/.npmrc b/projects/AlgoFire-frontend/.npmrc new file mode 100644 index 0000000..c42da84 --- /dev/null +++ b/projects/AlgoFire-frontend/.npmrc @@ -0,0 +1 @@ +engine-strict = true diff --git a/projects/AlgoFire-frontend/.prettierignore b/projects/AlgoFire-frontend/.prettierignore new file mode 100644 index 0000000..dbda6ae --- /dev/null +++ b/projects/AlgoFire-frontend/.prettierignore @@ -0,0 +1,12 @@ +# don't ever format node_modules +node_modules +# don't lint format output (make sure it's set to your correct build folder name) +dist +build +# don't format nyc coverage output +coverage +# don't format generated types +**/generated/types.d.ts +**/generated/types.ts +# don't format ide files +.idea diff --git a/projects/AlgoFire-frontend/.prettierrc.cjs b/projects/AlgoFire-frontend/.prettierrc.cjs new file mode 100644 index 0000000..066b8e5 --- /dev/null +++ b/projects/AlgoFire-frontend/.prettierrc.cjs @@ -0,0 +1,10 @@ +module.exports = { + singleQuote: true, + jsxSingleQuote: false, + semi: false, + tabWidth: 2, + trailingComma: 'all', + printWidth: 140, + endOfLine: 'lf', + arrowParens: 'always', +} diff --git a/projects/AlgoFire-frontend/.vscode/extensions.json b/projects/AlgoFire-frontend/.vscode/extensions.json new file mode 100644 index 0000000..5c94122 --- /dev/null +++ b/projects/AlgoFire-frontend/.vscode/extensions.json @@ -0,0 +1,14 @@ +{ + "recommendations": [ + "EditorConfig.EditorConfig", + "dotenv.dotenv-vscode", + "esbenp.prettier-vscode", + "dbaeumer.vscode-eslint", + "krysenlo.vite-plugin-eslint-problemmatcher", + "ms-playwright.playwright", + "Orta.vscode-jest", + "bradlc.vscode-tailwindcss", + "csstools.postcss", + ] +} + diff --git a/projects/AlgoFire-frontend/.vscode/launch.json b/projects/AlgoFire-frontend/.vscode/launch.json new file mode 100644 index 0000000..7edaf04 --- /dev/null +++ b/projects/AlgoFire-frontend/.vscode/launch.json @@ -0,0 +1,68 @@ +{ + "configurations": [ + { + "type": "msedge", + "request": "launch", + "name": "Run (Edge)", + "url": "http://localhost:5173", + "webRoot": "${workspaceFolder}", + "presentation": { + "hidden": false, + "group": "2. Web" + } + }, + { + "type": "chrome", + "request": "launch", + "name": "Run (Chrome)", + "url": "http://localhost:5173", + "webRoot": "${workspaceFolder}", + "presentation": { + "hidden": false, + "group": "2. Web" + } + }, + { + "type": "firefox", + "request": "launch", + "name": "Run (Firefox)", + "url": "http://localhost:5173", + "webRoot": "${workspaceFolder}", + "presentation": { + "hidden": false, + "group": "2. Web" + } + }, + { + "name": "Run dApp", + "type": "node", + "request": "launch", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "dev"], + "cwd": "${workspaceRoot}", + "console": "integratedTerminal", + "skipFiles": ["/**", "node_modules/**"], + "presentation": { + "hidden": false, + "group": "1. Run Project", + "order": 1 + } + }, + { + "name": "Run dApp (+ LocalNet)", + "type": "node", + "request": "launch", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "dev"], + "cwd": "${workspaceRoot}", + "console": "integratedTerminal", + "skipFiles": ["/**", "node_modules/**"], + "preLaunchTask": "Start AlgoKit LocalNet", + "presentation": { + "hidden": false, + "group": "1. Run Project", + "order": 1 + } + } + ] +} diff --git a/projects/AlgoFire-frontend/.vscode/settings.json b/projects/AlgoFire-frontend/.vscode/settings.json new file mode 100644 index 0000000..f1d7017 --- /dev/null +++ b/projects/AlgoFire-frontend/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit", + "source.organizeImports": "explicit" + }, + "dotenv.enableAutocloaking": false, + "jest.autoRun": { + "watch": false, + "onSave": "test-file" + } +} diff --git a/projects/AlgoFire-frontend/.vscode/tasks.json b/projects/AlgoFire-frontend/.vscode/tasks.json new file mode 100644 index 0000000..d611c4f --- /dev/null +++ b/projects/AlgoFire-frontend/.vscode/tasks.json @@ -0,0 +1,15 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Start AlgoKit LocalNet", + "command": "algokit", + "args": ["localnet", "start"], + "type": "shell", + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [] + } + ] +} diff --git a/projects/AlgoFire-frontend/README.md b/projects/AlgoFire-frontend/README.md new file mode 100644 index 0000000..801a145 --- /dev/null +++ b/projects/AlgoFire-frontend/README.md @@ -0,0 +1,124 @@ +# AlgoFire-frontend + +This starter React project has been generated using AlgoKit. See below for default getting started instructions. + +# Setup + +### Initial Setup + +#### 1. Clone the Repository +Start by cloning this repository to your local machine. + +#### 2. Install Pre-requisites +Ensure the following pre-requisites are installed and properly configured: + +- **npm**: Node package manager. Install from [Node.js Installation Guide](https://nodejs.org/en/download/). Verify with `npm -v` to see version `18.12`+. +- **AlgoKit CLI**: Essential for project setup and operations. Install the latest version from [AlgoKit CLI Installation Guide](https://github.com/algorandfoundation/algokit-cli#install). Verify installation with `algokit --version`, expecting `2.0.0` or later. + +#### 3. Bootstrap Your Local Environment +Run the following commands within the project folder: + +- **Install Project Dependencies**: With `algokit project bootstrap all`, ensure all dependencies are ready. + +### Development Workflow + +#### Terminal +Directly manage and interact with your project using AlgoKit commands: + +1. **Build Contracts**: `algokit project run build` builds react web app and links with smart contracts in workspace, if any. +2. Remaining set of command for linting, testing and deployment can be found in respective [package.json](./package.json) file and [.algokit.toml](./.algokit.toml) files. + +#### VS Code +For a seamless experience with breakpoint debugging and other features: + +1. **Open Project**: In VS Code, open the repository root. +2. **Install Extensions**: Follow prompts to install recommended extensions. +3. **Debugging**: + - Use `F5` to start debugging. + - **Windows Users**: Select the Python interpreter at `./.venv/Scripts/python.exe` via `Ctrl/Cmd + Shift + P` > `Python: Select Interpreter` before the first run. + +#### Other IDEs +While primarily optimized for VS Code, Jetbrains WebStorm has base support for this project: + +1. **Open Project**: In your JetBrains IDE, open the repository root. +2. **Automatic Setup**: The IDE should configure the Python interpreter and virtual environment. +3. **Debugging**: Use `Shift+F10` or `Ctrl+R` to start debugging. Note: Windows users may encounter issues with pre-launch tasks due to a known bug. See [JetBrains forums](https://youtrack.jetbrains.com/issue/IDEA-277486/Shell-script-configuration-cannot-run-as-before-launch-task) for workarounds. + +## AlgoKit Workspaces and Project Management +This project supports both standalone and monorepo setups through AlgoKit workspaces. Leverage [`algokit project run`](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/project/run.md) commands for efficient monorepo project orchestration and management across multiple projects within a workspace. + +> Please note, by default frontend is pre configured to run against Algorand LocalNet. If you want to run against TestNet or MainNet, comment out the current environment variable and uncomment the relevant one in [`.env`](.env) file that is created after running bootstrap command and based on [`.env.template`](.env.template). + +### Continuous Integration + +This project uses [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to define CI workflows, which are located in the [.github/workflows](`../../.github/workflows`) folder. + +For pull requests and pushes to `main` branch against this repository the following checks are automatically performed by GitHub Actions: + +- `install`: Installs dependencies using `npm` +- `lint`: Lints the codebase using `ESLint` +- `build`: Builds the codebase using `vite` + +> Please note, if you instantiated the project via `algokit init` without explicitly specifying the `--no-workspace` flag, we will automatically attempt to move the contents of the `.github` folder to the root of the workspace. + +### Continuous Deployment + +The project template provides base Github Actions workflows for continuous deployment to [Netlify](https://www.netlify.com/) or [Vercel](https://vercel.com/). These workflows are located in the [`.github/workflows`](./.github/workflows) folder. + +**Please note**: when configuring the github repository for the first time. Depending on selected provider you will need to set the provider secrets in the repository settings. Default setup provided by the template allows you to manage the secrets via environment variables and secrets on your github repository. + + +#### Setting up environment variables and secrets for webapp deployment + +For Vercel: +1. Retrieve your [Vercel Access Token](https://vercel.com/support/articles/how-do-i-use-a-vercel-api-access-token) +2. Install the [Vercel CLI](https://vercel.com/cli) and run `vercel login` +3. Inside your folder, run `vercel link` to create a new Vercel project +4. Inside the generated `.vercel` folder, save the `projectId` and `orgId` from the `project.json` +5. Inside GitHub, add `VERCEL_TOKEN`, `VERCEL_ORG_ID`, and `VERCEL_PROJECT_ID` as [secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets). +6. Create an .env file containing ENV vars for the project (pointing to testnet or mainnet), drag and drop the .env file to upload initial batch of default environment variables to your vercel project. +7. Upon invocation, CD pipeline will pull the VITE_ prefixed environment variables, build the project and deploy to the specified environment. + +For Netlify: +1. Retrieve your [Netlify Access Token](https://docs.netlify.com/cli/get-started/#obtain-a-token-in-the-netlify-ui) +2. Inside your folder run `netlify login` +3. Inside your folder run `netlify sites:create` to create a new site, obtain NETLIFY_SITE_ID from the output +4. Inside GitHub, add `NETLIFY_AUTH_TOKEN` and `NETLIFY_SITE_ID` as [secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets). +5. Define the VITE_ prefixed environment variables in netlify environment variables under site settings. +6. Upon invocation, CD pipeline will build the project and deploy to the specified environment. + +> If you prefer alternative deployment methods, you can modify the relevant workflow files from the [`.github/workflows`](./.github/workflows) folder or modify deploy scripts in `.algokit.toml`. + + +# Algorand Wallet integrations + +The template comes with [`use-wallet`](https://github.com/txnlab/use-wallet) integration, which provides a React hook for connecting to an Algorand wallet providers. The following wallet providers are included by default: +- LocalNet: +- - [KMD/Local Wallet](https://github.com/TxnLab/use-wallet#kmd-algorand-key-management-daemon) - Algorand's Key Management Daemon (KMD) is a service that manages Algorand private keys and signs transactions. Works best with AlgoKit LocalNet and allows you to easily test and interact with your dApps locally. +- TestNet and others: +- - [Pera Wallet](https://perawallet.app). +- - [Defly Wallet](https://defly.app). +- - [Exodus Wallet](https://www.exodus.com). +- - [Daffi Wallet](https://www.daffi.me). + +Refer to official [`use-wallet`](https://github.com/txnlab/use-wallet) documentation for detailed guidelines on how to integrate with other wallet providers (such as WalletConnect v2). Too see implementation details on the use wallet hook and initialization of extra wallet providers refer to [`App.tsx`](./src/App.tsx). + +# Tools + +This project makes use of React and Tailwind to provider a base project configuration to develop frontends for your Algorand dApps and interactions with smart contracts. The following tools are in use: + +- [AlgoKit Utils](https://github.com/algorandfoundation/algokit-utils-ts) - Various TypeScript utilities to simplify interactions with Algorand and AlgoKit. +- [React](https://reactjs.org/) - A JavaScript library for building user interfaces. +- [Tailwind CSS](https://tailwindcss.com/) - A utility-first CSS framework for rapidly building custom designs. +- [daisyUI](https://daisyui.com/) - A component library for Tailwind CSS. +- [use-wallet](https://github.com/txnlab/use-wallet) - A React hook for connecting to an Algorand wallet providers. +- [npm](https://www.npmjs.com/): Node.js package manager +- [jest](https://jestjs.io/): JavaScript testing framework +- [playwright](https://playwright.dev/): Browser automation library +- [Prettier](https://prettier.io/): Opinionated code formatter +- [ESLint](https://eslint.org/): Tool for identifying and reporting on patterns in JavaScript +- Github Actions workflows for build validation +It has also been configured to have a productive dev experience out of the box in [VS Code](https://code.visualstudio.com/), see the [.vscode](./.vscode) folder. +# Integrating with smart contracts and application clients + +Refer to the detailed guidance on [integrating with smart contracts and application clients](./src/contracts/README.md). In essence, for any smart contract codebase generated with AlgoKit or other tools that produce compile contracts into ARC34 compliant app specifications, you can use the `algokit generate` command to generate TypeScript or Python typed client. Once generated simply drag and drop the generated client into `./src/contracts` and import it into your React components as you see fit. diff --git a/projects/AlgoFire-frontend/index.html b/projects/AlgoFire-frontend/index.html new file mode 100644 index 0000000..5a2ef4f --- /dev/null +++ b/projects/AlgoFire-frontend/index.html @@ -0,0 +1,12 @@ + + + + + + AlgoKit React Template + + +
+ + + diff --git a/projects/AlgoFire-frontend/jest.config.ts b/projects/AlgoFire-frontend/jest.config.ts new file mode 100644 index 0000000..28a8c3d --- /dev/null +++ b/projects/AlgoFire-frontend/jest.config.ts @@ -0,0 +1,20 @@ +import type { Config } from '@jest/types' + +const config: Config.InitialOptions = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/*.spec.ts', '**/*.spec.tsx'], + moduleDirectories: ['node_modules', 'src'], + transform: { + '': [ + 'ts-jest', + { + tsconfig: 'tsconfig.test.json', + }, + ], + }, + coveragePathIgnorePatterns: ['tests'], + testPathIgnorePatterns: ['/tests/'], + } + +export default config diff --git a/projects/AlgoFire-frontend/package.json b/projects/AlgoFire-frontend/package.json new file mode 100644 index 0000000..5a8d28b --- /dev/null +++ b/projects/AlgoFire-frontend/package.json @@ -0,0 +1,81 @@ +{ + "name": "AlgoFire-frontend", + "version": "0.1.0", + "author": { + "name": "Corey Stedman", + "email": "cordesir@gmail.com" + }, + "private": true, + "type": "module", + "engines": { + "node": ">=20.0", + "npm": ">=9.0" + }, + "devDependencies": { + "@algorandfoundation/algokit-client-generator": "^5.0.0", + "@types/node": "^18.17.14", + "@types/react": "^18.2.11", + "@types/react-dom": "^18.2.4", + "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.14", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "@typescript-eslint/eslint-plugin": "^7.0.2", + "@typescript-eslint/parser": "^7.0.2", + "postcss": "^8.4.24", + "tailwindcss": "3.3.2", + "ts-jest": "^29.1.1", + "@types/jest": "29.5.2", + "ts-node": "^10.9.1", + "typescript": "^5.1.6", + "@playwright/test": "^1.35.0", + "playwright": "^1.35.0", + "vite": "^5.0.0", + "vite-plugin-node-polyfills": "^0.22.0" + }, + "dependencies": { + "@algorandfoundation/algokit-utils": "^9.0.0", + "@blockshake/defly-connect": "^1.2.1", + "@perawallet/connect": "^1.4.1", + "@txnlab/use-wallet": "^4.0.0", + "@txnlab/use-wallet-react": "^4.0.0", + "algosdk": "^3.0.0", + "daisyui": "^4.0.0", + "notistack": "^3.0.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "tslib": "^2.6.2" + }, + "scripts": { + "generate:app-clients": "algokit project link --all", + "dev": "npm run generate:app-clients && vite", + "build": "npm run generate:app-clients && tsc && vite build", + "test": "jest --coverage --passWithNoTests", + "playwright:test": "playwright test", + "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "lint:fix": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0 --fix", + "preview": "vite preview" + }, + "eslintConfig": { + "extends": [ + "react-app/jest", + "react-app" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "overrides": { + "ws@>7.0.0 <7.5.9": "7.5.10" + } +} diff --git a/projects/AlgoFire-frontend/playwright.config.ts b/projects/AlgoFire-frontend/playwright.config.ts new file mode 100644 index 0000000..d7cbca6 --- /dev/null +++ b/projects/AlgoFire-frontend/playwright.config.ts @@ -0,0 +1,73 @@ +import { defineConfig, devices } from '@playwright/test' + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + testIdAttribute: 'data-test-id', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ..devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + webServer: { + command: 'npm run dev', + url: 'http://localhost:5173', + reuseExistingServer: !process.env.CI, + }, +}) diff --git a/projects/AlgoFire-frontend/postcss.config.cjs b/projects/AlgoFire-frontend/postcss.config.cjs new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/projects/AlgoFire-frontend/postcss.config.cjs @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/projects/AlgoFire-frontend/public/index.html b/projects/AlgoFire-frontend/public/index.html new file mode 100644 index 0000000..0d3a3a5 --- /dev/null +++ b/projects/AlgoFire-frontend/public/index.html @@ -0,0 +1,37 @@ + + + + + + + + + + React App + + + +
+ + + diff --git a/projects/AlgoFire-frontend/public/robots.txt b/projects/AlgoFire-frontend/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/projects/AlgoFire-frontend/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/projects/AlgoFire-frontend/src/App.tsx b/projects/AlgoFire-frontend/src/App.tsx new file mode 100644 index 0000000..f346006 --- /dev/null +++ b/projects/AlgoFire-frontend/src/App.tsx @@ -0,0 +1,56 @@ +import { SupportedWallet, WalletId, WalletManager, WalletProvider } from '@txnlab/use-wallet-react' +import { SnackbarProvider } from 'notistack' +import Home from './Home' +import { getAlgodConfigFromViteEnvironment, getKmdConfigFromViteEnvironment } from './utils/network/getAlgoClientConfigs' + +let supportedWallets: SupportedWallet[] +if (import.meta.env.VITE_ALGOD_NETWORK === 'localnet') { + const kmdConfig = getKmdConfigFromViteEnvironment() + supportedWallets = [ + { + id: WalletId.KMD, + options: { + baseServer: kmdConfig.server, + token: String(kmdConfig.token), + port: String(kmdConfig.port), + }, + }, + ] +} else { + supportedWallets = [ + { id: WalletId.DEFLY }, + { id: WalletId.PERA }, + { id: WalletId.EXODUS }, + // If you are interested in WalletConnect v2 provider + // refer to https://github.com/TxnLab/use-wallet for detailed integration instructions + ] +} + +export default function App() { + const algodConfig = getAlgodConfigFromViteEnvironment() + + const walletManager = new WalletManager({ + wallets: supportedWallets, + defaultNetwork: algodConfig.network, + networks: { + [algodConfig.network]: { + algod: { + baseServer: algodConfig.server, + port: algodConfig.port, + token: String(algodConfig.token), + }, + }, + }, + options: { + resetNetwork: true, + }, + }) + + return ( + + + + + + ) +} diff --git a/projects/AlgoFire-frontend/src/Home.tsx b/projects/AlgoFire-frontend/src/Home.tsx new file mode 100644 index 0000000..68313ee --- /dev/null +++ b/projects/AlgoFire-frontend/src/Home.tsx @@ -0,0 +1,76 @@ +// src/components/Home.tsx +import { useWallet } from '@txnlab/use-wallet-react' +import React, { useState } from 'react' +import ConnectWallet from './components/ConnectWallet' +import Transact from './components/Transact' +import AppCalls from './components/AppCalls' + +interface HomeProps {} + +const Home: React.FC = () => { + const [openWalletModal, setOpenWalletModal] = useState(false) + const [openDemoModal, setOpenDemoModal] = useState(false) + const [appCallsDemoModal, setAppCallsDemoModal] = useState(false) + const { activeAddress } = useWallet() + + const toggleWalletModal = () => { + setOpenWalletModal(!openWalletModal) + } + + const toggleDemoModal = () => { + setOpenDemoModal(!openDemoModal) + } + + const toggleAppCallsModal = () => { + setAppCallsDemoModal(!appCallsDemoModal) + } + + return ( +
+
+
+

+ Welcome to
AlgoKit 🙂
+

+

+ This starter has been generated using official AlgoKit React template. Refer to the resource below for next steps. +

+ +
+ + Getting started + + +
+ + + {activeAddress && ( + + )} + + {activeAddress && ( + + )} +
+ + + + +
+
+
+ ) +} + +export default Home diff --git a/projects/AlgoFire-frontend/src/assets/logo.svg b/projects/AlgoFire-frontend/src/assets/logo.svg new file mode 100644 index 0000000..7169476 --- /dev/null +++ b/projects/AlgoFire-frontend/src/assets/logo.svg @@ -0,0 +1 @@ + diff --git a/projects/AlgoFire-frontend/src/components/Account.tsx b/projects/AlgoFire-frontend/src/components/Account.tsx new file mode 100644 index 0000000..081ce47 --- /dev/null +++ b/projects/AlgoFire-frontend/src/components/Account.tsx @@ -0,0 +1,24 @@ +import { useWallet } from '@txnlab/use-wallet-react' +import { useMemo } from 'react' +import { ellipseAddress } from '../utils/ellipseAddress' +import { getAlgodConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' + +const Account = () => { + const { activeAddress } = useWallet() + const algoConfig = getAlgodConfigFromViteEnvironment() + + const networkName = useMemo(() => { + return algoConfig.network === '' ? 'localnet' : algoConfig.network.toLocaleLowerCase() + }, [algoConfig.network]) + + return ( + + ) +} + +export default Account diff --git a/projects/AlgoFire-frontend/src/components/AppCalls.tsx b/projects/AlgoFire-frontend/src/components/AppCalls.tsx new file mode 100644 index 0000000..87e19e3 --- /dev/null +++ b/projects/AlgoFire-frontend/src/components/AppCalls.tsx @@ -0,0 +1,98 @@ +import { useWallet } from '@txnlab/use-wallet-react' +import { useSnackbar } from 'notistack' +import { useState } from 'react' +import { AlgofireFactory } from '../contracts/Algofire' +import { OnSchemaBreak, OnUpdate } from '@algorandfoundation/algokit-utils/types/app' +import { getAlgodConfigFromViteEnvironment, getIndexerConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' +import { AlgorandClient } from '@algorandfoundation/algokit-utils' + +interface AppCallsInterface { + openModal: boolean + setModalState: (value: boolean) => void +} + +const AppCalls = ({ openModal, setModalState }: AppCallsInterface) => { + const [loading, setLoading] = useState(false) + const [contractInput, setContractInput] = useState('') + const { enqueueSnackbar } = useSnackbar() + const { transactionSigner, activeAddress } = useWallet() + + const algodConfig = getAlgodConfigFromViteEnvironment() + const indexerConfig = getIndexerConfigFromViteEnvironment() + const algorand = AlgorandClient.fromConfig({ + algodConfig, + indexerConfig, + }) + algorand.setDefaultSigner(transactionSigner) + + const sendAppCall = async () => { + setLoading(true) + + // Please note, in typical production scenarios, + // you wouldn't want to use deploy directly from your frontend. + // Instead, you would deploy your contract on your backend and reference it by id. + // Given the simplicity of the starter contract, we are deploying it on the frontend + // for demonstration purposes. + const factory = new AlgofireFactory({ + defaultSender: activeAddress ?? undefined, + algorand, + }) + const deployResult = await factory + .deploy({ + onSchemaBreak: OnSchemaBreak.AppendApp, + onUpdate: OnUpdate.AppendApp, + }) + .catch((e: Error) => { + enqueueSnackbar(`Error deploying the contract: ${e.message}`, { variant: 'error' }) + setLoading(false) + return undefined + }) + + if (!deployResult) { + return + } + + const { appClient } = deployResult + + const response = await appClient.send.hello({ args: { name: contractInput } }).catch((e: Error) => { + enqueueSnackbar(`Error calling the contract: ${e.message}`, { variant: 'error' }) + setLoading(false) + return undefined + }) + + if (!response) { + return + } + + enqueueSnackbar(`Response from the contract: ${response.return}`, { variant: 'success' }) + setLoading(false) + } + + return ( + +
+

Say hello to your Algorand smart contract

+
+ { + setContractInput(e.target.value) + }} + /> +
+ + +
+
+
+ ) +} + +export default AppCalls diff --git a/projects/AlgoFire-frontend/src/components/ConnectWallet.tsx b/projects/AlgoFire-frontend/src/components/ConnectWallet.tsx new file mode 100644 index 0000000..8ff01dc --- /dev/null +++ b/projects/AlgoFire-frontend/src/components/ConnectWallet.tsx @@ -0,0 +1,86 @@ +import { useWallet, Wallet, WalletId } from '@txnlab/use-wallet-react' +import Account from './Account' + +interface ConnectWalletInterface { + openModal: boolean + closeModal: () => void +} + +const ConnectWallet = ({ openModal, closeModal }: ConnectWalletInterface) => { + const { wallets, activeAddress } = useWallet() + + const isKmd = (wallet: Wallet) => wallet.id === WalletId.KMD + + return ( + +
+

Select wallet provider

+ +
+ {activeAddress && ( + <> + +
+ + )} + + {!activeAddress && + wallets?.map((wallet) => ( + + ))} +
+ +
+ + {activeAddress && ( + + )} +
+ +
+ ) +} +export default ConnectWallet diff --git a/projects/AlgoFire-frontend/src/components/ErrorBoundary.tsx b/projects/AlgoFire-frontend/src/components/ErrorBoundary.tsx new file mode 100644 index 0000000..435bf61 --- /dev/null +++ b/projects/AlgoFire-frontend/src/components/ErrorBoundary.tsx @@ -0,0 +1,46 @@ +import React, { ReactNode } from 'react' + +interface ErrorBoundaryProps { + children: ReactNode +} + +interface ErrorBoundaryState { + hasError: boolean + error: Error | null +} + +class ErrorBoundary extends React.Component { + constructor(props: ErrorBoundaryProps) { + super(props) + this.state = { hasError: false, error: null } + } + + static getDerivedStateFromError(error: Error): ErrorBoundaryState { + // Update state so the next render will show the fallback UI. + return { hasError: true, error: error } + } + + render(): ReactNode { + if (this.state.hasError) { + // You can render any custom fallback UI + return ( +
+
+
+

Error occured

+

+ {this.state.error?.message.includes('Attempt to get default algod configuration') + ? 'Please make sure to set up your environment variables correctly. Create a .env file based on .env.template and fill in the required values. This controls the network and credentials for connections with Algod and Indexer.' + : this.state.error?.message} +

+
+
+
+ ) + } + + return this.props.children + } +} + +export default ErrorBoundary diff --git a/projects/AlgoFire-frontend/src/components/Transact.tsx b/projects/AlgoFire-frontend/src/components/Transact.tsx new file mode 100644 index 0000000..2ba3e96 --- /dev/null +++ b/projects/AlgoFire-frontend/src/components/Transact.tsx @@ -0,0 +1,80 @@ +import { algo, AlgorandClient } from '@algorandfoundation/algokit-utils' +import { useWallet } from '@txnlab/use-wallet-react' +import { useSnackbar } from 'notistack' +import { useState } from 'react' +import { getAlgodConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' + +interface TransactInterface { + openModal: boolean + setModalState: (value: boolean) => void +} + +const Transact = ({ openModal, setModalState }: TransactInterface) => { + const [loading, setLoading] = useState(false) + const [receiverAddress, setReceiverAddress] = useState('') + + const algodConfig = getAlgodConfigFromViteEnvironment() + const algorand = AlgorandClient.fromConfig({ algodConfig }) + + const { enqueueSnackbar } = useSnackbar() + + const { transactionSigner, activeAddress } = useWallet() + + const handleSubmitAlgo = async () => { + setLoading(true) + + if (!transactionSigner || !activeAddress) { + enqueueSnackbar('Please connect wallet first', { variant: 'warning' }) + return + } + + try { + enqueueSnackbar('Sending transaction...', { variant: 'info' }) + const result = await algorand.send.payment({ + signer: transactionSigner, + sender: activeAddress, + receiver: receiverAddress, + amount: algo(1), + }) + enqueueSnackbar(`Transaction sent: ${result.txIds[0]}`, { variant: 'success' }) + setReceiverAddress('') + } catch (e) { + enqueueSnackbar('Failed to send transaction', { variant: 'error' }) + } + + setLoading(false) + } + + return ( + +
+

Send payment transaction

+
+ { + setReceiverAddress(e.target.value) + }} + /> +
+ + +
+
+
+ ) +} + +export default Transact diff --git a/projects/AlgoFire-frontend/src/contracts/README.md b/projects/AlgoFire-frontend/src/contracts/README.md new file mode 100644 index 0000000..04629b1 --- /dev/null +++ b/projects/AlgoFire-frontend/src/contracts/README.md @@ -0,0 +1,14 @@ +## How to connect my web app with Algorand smart contracts? + +The following folder is reserved for the Algorand Application Clients. The clients are used to interact with instances of Algorand Smart Contracts (ASC1s) deployed on-chain. + +To integrate this react frontend template with your smart contracts codebase, perform the following steps: + +1. Generate the typed client using `algokit generate client -l typescript -o {path/to/this/folder}` or using the dedicated `link` command `algokit project link` (ensure to invoke it from the root of this react project). Using the `link` command is especially useful within workspaces that have multiple contract projects. +2. The generated typescript client should be ready to be imported and used in this react frontend template, making it a full fledged dApp. + +> Please note, by default this template defines `"generate:app-clients": "algokit project link --all"` which is a shortcut to automatically link TEAL code from all `contract` projects in the workspace as typed clients into the `frontend` project that is invoking the `link` command. Refer to [documentation](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/project/link.md) to read more about `link` command. + +## **How to interact with the smart contract?** + +The generated client provides a set of functions that can be used to interact with the ABI (Application Binary Interface) compliant Algorand smart contract. For example, if the smart contract has a function called `hello`, the generated client will have a function called `hello` that can be used to interact with the smart contract. Refer to a [full-stack end-to-end starter template](https://github.com/algorandfoundation/algokit-fullstack-template) for a reference example on invoking and interacting with typescript typed clients generated. diff --git a/projects/AlgoFire-frontend/src/interfaces/network.ts b/projects/AlgoFire-frontend/src/interfaces/network.ts new file mode 100644 index 0000000..a458edc --- /dev/null +++ b/projects/AlgoFire-frontend/src/interfaces/network.ts @@ -0,0 +1,26 @@ +import { AlgoClientConfig } from '@algorandfoundation/algokit-utils/types/network-client' +import type { TokenHeader } from 'algosdk/dist/types/client/urlTokenBaseHTTPClient' + +export interface AlgoViteClientConfig extends AlgoClientConfig { + /** Base URL of the server e.g. http://localhost, https://testnet-api.algonode.cloud/, etc. */ + server: string + /** The port to use e.g. 4001, 443, etc. */ + port: string | number + /** The token to use for API authentication (or undefined if none needed) - can be a string, or an object with the header key => value */ + token: string | TokenHeader + /** String representing current Algorand Network type (testnet/mainnet and etc) */ + network: string +} + +export interface AlgoViteKMDConfig extends AlgoClientConfig { + /** Base URL of the server e.g. http://localhost, https://testnet-api.algonode.cloud/, etc. */ + server: string + /** The port to use e.g. 4001, 443, etc. */ + port: string | number + /** The token to use for API authentication (or undefined if none needed) - can be a string, or an object with the header key => value */ + token: string | TokenHeader + /** KMD wallet name */ + wallet: string + /** KMD wallet password */ + password: string +} diff --git a/projects/AlgoFire-frontend/src/main.tsx b/projects/AlgoFire-frontend/src/main.tsx new file mode 100644 index 0000000..adf72ec --- /dev/null +++ b/projects/AlgoFire-frontend/src/main.tsx @@ -0,0 +1,13 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App' +import './styles/main.css' +import ErrorBoundary from './components/ErrorBoundary' + +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + + + + , +) diff --git a/projects/AlgoFire-frontend/src/styles/main.css b/projects/AlgoFire-frontend/src/styles/main.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/projects/AlgoFire-frontend/src/styles/main.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/projects/AlgoFire-frontend/src/utils/ellipseAddress.spec.tsx b/projects/AlgoFire-frontend/src/utils/ellipseAddress.spec.tsx new file mode 100644 index 0000000..2cbff10 --- /dev/null +++ b/projects/AlgoFire-frontend/src/utils/ellipseAddress.spec.tsx @@ -0,0 +1,15 @@ +import { ellipseAddress } from './ellipseAddress' + +describe('ellipseAddress', () => { + it('should return ellipsed address with specified width', () => { + const address = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + const result = ellipseAddress(address, 4) + expect(result).toBe('aaaa...aaaa') + }) + + it('should return empty string when address is empty', () => { + const address = '' + const result = ellipseAddress(address) + expect(result).toBe('') + }) +}) diff --git a/projects/AlgoFire-frontend/src/utils/ellipseAddress.ts b/projects/AlgoFire-frontend/src/utils/ellipseAddress.ts new file mode 100644 index 0000000..db97e48 --- /dev/null +++ b/projects/AlgoFire-frontend/src/utils/ellipseAddress.ts @@ -0,0 +1,3 @@ +export function ellipseAddress(address: string | null, width = 6): string { + return address ? `${address.slice(0, width)}...${address.slice(-width)}` : (address ?? '') +} diff --git a/projects/AlgoFire-frontend/src/utils/network/getAlgoClientConfigs.ts b/projects/AlgoFire-frontend/src/utils/network/getAlgoClientConfigs.ts new file mode 100644 index 0000000..b5121f8 --- /dev/null +++ b/projects/AlgoFire-frontend/src/utils/network/getAlgoClientConfigs.ts @@ -0,0 +1,41 @@ +import { AlgoViteClientConfig, AlgoViteKMDConfig } from '../../interfaces/network' + +export function getAlgodConfigFromViteEnvironment(): AlgoViteClientConfig { + if (!import.meta.env.VITE_ALGOD_SERVER) { + throw new Error('Attempt to get default algod configuration without specifying VITE_ALGOD_SERVER in the environment variables') + } + + return { + server: import.meta.env.VITE_ALGOD_SERVER, + port: import.meta.env.VITE_ALGOD_PORT, + token: import.meta.env.VITE_ALGOD_TOKEN, + network: import.meta.env.VITE_ALGOD_NETWORK, + } +} + +export function getIndexerConfigFromViteEnvironment(): AlgoViteClientConfig { + if (!import.meta.env.VITE_INDEXER_SERVER) { + throw new Error('Attempt to get default algod configuration without specifying VITE_INDEXER_SERVER in the environment variables') + } + + return { + server: import.meta.env.VITE_INDEXER_SERVER, + port: import.meta.env.VITE_INDEXER_PORT, + token: import.meta.env.VITE_INDEXER_TOKEN, + network: import.meta.env.VITE_ALGOD_NETWORK, + } +} + +export function getKmdConfigFromViteEnvironment(): AlgoViteKMDConfig { + if (!import.meta.env.VITE_KMD_SERVER) { + throw new Error('Attempt to get default kmd configuration without specifying VITE_KMD_SERVER in the environment variables') + } + + return { + server: import.meta.env.VITE_KMD_SERVER, + port: import.meta.env.VITE_KMD_PORT, + token: import.meta.env.VITE_KMD_TOKEN, + wallet: import.meta.env.VITE_KMD_WALLET, + password: import.meta.env.VITE_KMD_PASSWORD, + } +} diff --git a/projects/AlgoFire-frontend/src/vite-env.d.ts b/projects/AlgoFire-frontend/src/vite-env.d.ts new file mode 100644 index 0000000..67c2d30 --- /dev/null +++ b/projects/AlgoFire-frontend/src/vite-env.d.ts @@ -0,0 +1,24 @@ +/// + +interface ImportMetaEnv { + readonly VITE_ENVIRONMENT: string + + readonly VITE_ALGOD_TOKEN: string + readonly VITE_ALGOD_SERVER: string + readonly VITE_ALGOD_PORT: string + readonly VITE_ALGOD_NETWORK: string + + readonly VITE_INDEXER_TOKEN: string + readonly VITE_INDEXER_SERVER: string + readonly VITE_INDEXER_PORT: string + + readonly VITE_KMD_TOKEN: string + readonly VITE_KMD_SERVER: string + readonly VITE_KMD_PORT: string + readonly VITE_KMD_PASSWORD: string + readonly VITE_KMD_WALLET: string +} + +interface ImportMeta { + readonly env: ImportMetaEnv +} diff --git a/projects/AlgoFire-frontend/tailwind.config.cjs b/projects/AlgoFire-frontend/tailwind.config.cjs new file mode 100644 index 0000000..eaaba6c --- /dev/null +++ b/projects/AlgoFire-frontend/tailwind.config.cjs @@ -0,0 +1,12 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['./src/**/*.{js,ts,jsx,tsx}'], + theme: { + extend: {}, + }, + daisyui: { + themes: ['lofi'], + logs: false, + }, + plugins: [require('daisyui')], +} diff --git a/projects/AlgoFire-frontend/tests/example.spec.ts b/projects/AlgoFire-frontend/tests/example.spec.ts new file mode 100644 index 0000000..a62aae4 --- /dev/null +++ b/projects/AlgoFire-frontend/tests/example.spec.ts @@ -0,0 +1,40 @@ +import { algorandFixture } from '@algorandfoundation/algokit-utils/testing' +import { expect, test } from '@playwright/test' + +const localnet = algorandFixture() + +test.beforeEach(async ({ page }) => { + await localnet.newScope() + await page.goto('http://localhost:5173/') +}) + +test('has title', async ({ page }) => { + // Expect a title "to contain" a substring. + await expect(page).toHaveTitle('AlgoKit React Template') +}) + +test('get started link', async ({ page }) => { + await expect(page.getByTestId('getting-started')).toHaveText('Getting started') +}) + +test('authentication and dummy payment transaction', async ({ page }) => { + page.on('dialog', async (dialog) => { + dialog.message() === 'KMD password' ? await dialog.accept() : await dialog.dismiss() + }) + + // 1. Must be able to connect to a KMD wallet provider + await page.getByTestId('connect-wallet').click() + await page.getByTestId('kmd-connect').click() + await page.getByTestId('close-wallet-modal').click() + + // 2. Must be able to send a dummy payment transaction + await page.getByTestId('transactions-demo').click() + + await page.getByTestId('receiver-address').fill(localnet.context.testAccount.toString()) + await page.getByTestId('send-algo').click() + + // 3. Must be able to see a notification that the transaction was sent + const notification = await page.getByText('Transaction sent:') + await notification.waitFor() + expect(notification).toBeTruthy() +}) diff --git a/projects/AlgoFire-frontend/tsconfig.json b/projects/AlgoFire-frontend/tsconfig.json new file mode 100644 index 0000000..5cde02a --- /dev/null +++ b/projects/AlgoFire-frontend/tsconfig.json @@ -0,0 +1,38 @@ +{ + "compilerOptions": { + "target": "ES2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + "module": "ES2022" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, + "declaration": true /* Generates corresponding '.d.ts' file. */, + "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */, + "sourceMap": true /* Generates corresponding '.map' file. */, + "strict": true /* Enable all strict type-checking options. */, + "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, + "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, + "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, + "skipLibCheck": true /* Skip type checking of declaration files. */, + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, + "allowJs": false, + "allowSyntheticDefaultImports": true, + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "outDir": "./dist/" + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx", + "vite.config.js", + "src/utils/ellipseAddress.spec.tsx", + "src/utils/ellipseAddress.spec.tsx", + "src/main.tsx", + ], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} diff --git a/projects/AlgoFire-frontend/tsconfig.node.json b/projects/AlgoFire-frontend/tsconfig.node.json new file mode 100644 index 0000000..9d31e2a --- /dev/null +++ b/projects/AlgoFire-frontend/tsconfig.node.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/projects/AlgoFire-frontend/vite.config.ts b/projects/AlgoFire-frontend/vite.config.ts new file mode 100644 index 0000000..e42dc7c --- /dev/null +++ b/projects/AlgoFire-frontend/vite.config.ts @@ -0,0 +1,15 @@ +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' +import { nodePolyfills } from 'vite-plugin-node-polyfills' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + react(), + nodePolyfills({ + globals: { + Buffer: true, + }, + }), + ], +})