Project initialised with AlgoKit CLI using template: https://github.com/algorandfoundation/algokit-fullstack-template.git
This commit is contained in:
17
projects/TokenizeRWATemplate-frontend/.algokit.toml
Normal file
17
projects/TokenizeRWATemplate-frontend/.algokit.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[algokit]
|
||||
min_version = "v2.0.0"
|
||||
|
||||
[project]
|
||||
type = "frontend"
|
||||
name = 'TokenizeRWATemplate-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-vercel = { commands = [
|
||||
'npm install --global vercel@latest',
|
||||
'npm run ci:vercel:pull',
|
||||
'npm run ci:vercel:deploy',
|
||||
], description = 'Deploy to Vercel' }
|
||||
@ -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: sarajanedeveloper@gmail.com
|
||||
author_name: SaraJane
|
||||
cloud_provider: vercel
|
||||
preset_name: production
|
||||
project_name: TokenizeRWATemplate-frontend
|
||||
|
||||
9
projects/TokenizeRWATemplate-frontend/.editorconfig
Normal file
9
projects/TokenizeRWATemplate-frontend/.editorconfig
Normal file
@ -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
|
||||
67
projects/TokenizeRWATemplate-frontend/.env.template
Normal file
67
projects/TokenizeRWATemplate-frontend/.env.template
Normal file
@ -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=""
|
||||
|
||||
27
projects/TokenizeRWATemplate-frontend/.eslintrc
Normal file
27
projects/TokenizeRWATemplate-frontend/.eslintrc
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
38
projects/TokenizeRWATemplate-frontend/.gitignore
vendored
Normal file
38
projects/TokenizeRWATemplate-frontend/.gitignore
vendored
Normal file
@ -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
|
||||
1
projects/TokenizeRWATemplate-frontend/.npmrc
Normal file
1
projects/TokenizeRWATemplate-frontend/.npmrc
Normal file
@ -0,0 +1 @@
|
||||
engine-strict = true
|
||||
12
projects/TokenizeRWATemplate-frontend/.prettierignore
Normal file
12
projects/TokenizeRWATemplate-frontend/.prettierignore
Normal file
@ -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
|
||||
10
projects/TokenizeRWATemplate-frontend/.prettierrc.cjs
Normal file
10
projects/TokenizeRWATemplate-frontend/.prettierrc.cjs
Normal file
@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
singleQuote: true,
|
||||
jsxSingleQuote: false,
|
||||
semi: false,
|
||||
tabWidth: 2,
|
||||
trailingComma: 'all',
|
||||
printWidth: 140,
|
||||
endOfLine: 'lf',
|
||||
arrowParens: 'always',
|
||||
}
|
||||
14
projects/TokenizeRWATemplate-frontend/.vscode/extensions.json
vendored
Normal file
14
projects/TokenizeRWATemplate-frontend/.vscode/extensions.json
vendored
Normal file
@ -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",
|
||||
]
|
||||
}
|
||||
|
||||
68
projects/TokenizeRWATemplate-frontend/.vscode/launch.json
vendored
Normal file
68
projects/TokenizeRWATemplate-frontend/.vscode/launch.json
vendored
Normal file
@ -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_internals>/**", "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_internals>/**", "node_modules/**"],
|
||||
"preLaunchTask": "Start AlgoKit LocalNet",
|
||||
"presentation": {
|
||||
"hidden": false,
|
||||
"group": "1. Run Project",
|
||||
"order": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
13
projects/TokenizeRWATemplate-frontend/.vscode/settings.json
vendored
Normal file
13
projects/TokenizeRWATemplate-frontend/.vscode/settings.json
vendored
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
15
projects/TokenizeRWATemplate-frontend/.vscode/tasks.json
vendored
Normal file
15
projects/TokenizeRWATemplate-frontend/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Start AlgoKit LocalNet",
|
||||
"command": "algokit",
|
||||
"args": ["localnet", "start"],
|
||||
"type": "shell",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
124
projects/TokenizeRWATemplate-frontend/README.md
Normal file
124
projects/TokenizeRWATemplate-frontend/README.md
Normal file
@ -0,0 +1,124 @@
|
||||
# TokenizeRWATemplate-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.
|
||||
12
projects/TokenizeRWATemplate-frontend/index.html
Normal file
12
projects/TokenizeRWATemplate-frontend/index.html
Normal file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>AlgoKit React Template</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
20
projects/TokenizeRWATemplate-frontend/jest.config.ts
Normal file
20
projects/TokenizeRWATemplate-frontend/jest.config.ts
Normal file
@ -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: {
|
||||
'<regex_match_files>': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: 'tsconfig.test.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
coveragePathIgnorePatterns: ['tests'],
|
||||
testPathIgnorePatterns: ['/tests/'],
|
||||
}
|
||||
|
||||
export default config
|
||||
12083
projects/TokenizeRWATemplate-frontend/package-lock.json
generated
Normal file
12083
projects/TokenizeRWATemplate-frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
84
projects/TokenizeRWATemplate-frontend/package.json
Normal file
84
projects/TokenizeRWATemplate-frontend/package.json
Normal file
@ -0,0 +1,84 @@
|
||||
{
|
||||
"name": "TokenizeRWATemplate-frontend",
|
||||
"version": "0.1.0",
|
||||
"author": {
|
||||
"name": "SaraJane",
|
||||
"email": "sarajanedeveloper@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",
|
||||
"ci:vercel:build": "vercel build --prod --token=$VERCEL_TOKEN",
|
||||
"ci:vercel:pull": "vercel pull --yes --environment=production --token=$VERCEL_TOKEN",
|
||||
"ci:vercel:deploy": "npm run ci:vercel:build && vercel deploy --prebuilt --prod --token=$VERCEL_TOKEN",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
73
projects/TokenizeRWATemplate-frontend/playwright.config.ts
Normal file
73
projects/TokenizeRWATemplate-frontend/playwright.config.ts
Normal file
@ -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,
|
||||
},
|
||||
})
|
||||
6
projects/TokenizeRWATemplate-frontend/postcss.config.cjs
Normal file
6
projects/TokenizeRWATemplate-frontend/postcss.config.cjs
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
37
projects/TokenizeRWATemplate-frontend/public/index.html
Normal file
37
projects/TokenizeRWATemplate-frontend/public/index.html
Normal file
@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
3
projects/TokenizeRWATemplate-frontend/public/robots.txt
Normal file
3
projects/TokenizeRWATemplate-frontend/public/robots.txt
Normal file
@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
56
projects/TokenizeRWATemplate-frontend/src/App.tsx
Normal file
56
projects/TokenizeRWATemplate-frontend/src/App.tsx
Normal file
@ -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 (
|
||||
<SnackbarProvider maxSnack={3}>
|
||||
<WalletProvider manager={walletManager}>
|
||||
<Home />
|
||||
</WalletProvider>
|
||||
</SnackbarProvider>
|
||||
)
|
||||
}
|
||||
76
projects/TokenizeRWATemplate-frontend/src/Home.tsx
Normal file
76
projects/TokenizeRWATemplate-frontend/src/Home.tsx
Normal file
@ -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<HomeProps> = () => {
|
||||
const [openWalletModal, setOpenWalletModal] = useState<boolean>(false)
|
||||
const [openDemoModal, setOpenDemoModal] = useState<boolean>(false)
|
||||
const [appCallsDemoModal, setAppCallsDemoModal] = useState<boolean>(false)
|
||||
const { activeAddress } = useWallet()
|
||||
|
||||
const toggleWalletModal = () => {
|
||||
setOpenWalletModal(!openWalletModal)
|
||||
}
|
||||
|
||||
const toggleDemoModal = () => {
|
||||
setOpenDemoModal(!openDemoModal)
|
||||
}
|
||||
|
||||
const toggleAppCallsModal = () => {
|
||||
setAppCallsDemoModal(!appCallsDemoModal)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="hero min-h-screen bg-teal-400">
|
||||
<div className="hero-content text-center rounded-lg p-6 max-w-md bg-white mx-auto">
|
||||
<div className="max-w-md">
|
||||
<h1 className="text-4xl">
|
||||
Welcome to <div className="font-bold">AlgoKit 🙂</div>
|
||||
</h1>
|
||||
<p className="py-6">
|
||||
This starter has been generated using official AlgoKit React template. Refer to the resource below for next steps.
|
||||
</p>
|
||||
|
||||
<div className="grid">
|
||||
<a
|
||||
data-test-id="getting-started"
|
||||
className="btn btn-primary m-2"
|
||||
target="_blank"
|
||||
href="https://github.com/algorandfoundation/algokit-cli"
|
||||
>
|
||||
Getting started
|
||||
</a>
|
||||
|
||||
<div className="divider" />
|
||||
<button data-test-id="connect-wallet" className="btn m-2" onClick={toggleWalletModal}>
|
||||
Wallet Connection
|
||||
</button>
|
||||
|
||||
{activeAddress && (
|
||||
<button data-test-id="transactions-demo" className="btn m-2" onClick={toggleDemoModal}>
|
||||
Transactions Demo
|
||||
</button>
|
||||
)}
|
||||
|
||||
{activeAddress && (
|
||||
<button data-test-id="appcalls-demo" className="btn m-2" onClick={toggleAppCallsModal}>
|
||||
Contract Interactions Demo
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ConnectWallet openModal={openWalletModal} closeModal={toggleWalletModal} />
|
||||
<Transact openModal={openDemoModal} setModalState={setOpenDemoModal} />
|
||||
<AppCalls openModal={appCallsDemoModal} setModalState={setAppCallsDemoModal} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
||||
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@ -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 (
|
||||
<div>
|
||||
<a className="text-xl" target="_blank" href={`https://lora.algokit.io/${networkName}/account/${activeAddress}/`}>
|
||||
Address: {ellipseAddress(activeAddress)}
|
||||
</a>
|
||||
<div className="text-xl">Network: {networkName}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Account
|
||||
@ -0,0 +1,98 @@
|
||||
import { useWallet } from '@txnlab/use-wallet-react'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { useState } from 'react'
|
||||
import { HelloWorldFactory } from '../contracts/HelloWorld'
|
||||
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<boolean>(false)
|
||||
const [contractInput, setContractInput] = useState<string>('')
|
||||
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 HelloWorldFactory({
|
||||
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 (
|
||||
<dialog id="appcalls_modal" className={`modal ${openModal ? 'modal-open' : ''} bg-slate-200`}>
|
||||
<form method="dialog" className="modal-box">
|
||||
<h3 className="font-bold text-lg">Say hello to your Algorand smart contract</h3>
|
||||
<br />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Provide input to hello function"
|
||||
className="input input-bordered w-full"
|
||||
value={contractInput}
|
||||
onChange={(e) => {
|
||||
setContractInput(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<div className="modal-action ">
|
||||
<button className="btn" onClick={() => setModalState(!openModal)}>
|
||||
Close
|
||||
</button>
|
||||
<button className={`btn`} onClick={sendAppCall}>
|
||||
{loading ? <span className="loading loading-spinner" /> : 'Send application call'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppCalls
|
||||
@ -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 (
|
||||
<dialog id="connect_wallet_modal" className={`modal ${openModal ? 'modal-open' : ''}`}>
|
||||
<form method="dialog" className="modal-box">
|
||||
<h3 className="font-bold text-2xl">Select wallet provider</h3>
|
||||
|
||||
<div className="grid m-2 pt-5">
|
||||
{activeAddress && (
|
||||
<>
|
||||
<Account />
|
||||
<div className="divider" />
|
||||
</>
|
||||
)}
|
||||
|
||||
{!activeAddress &&
|
||||
wallets?.map((wallet) => (
|
||||
<button
|
||||
data-test-id={`${wallet.id}-connect`}
|
||||
className="btn border-teal-800 border-1 m-2"
|
||||
key={`provider-${wallet.id}`}
|
||||
onClick={() => {
|
||||
return wallet.connect()
|
||||
}}
|
||||
>
|
||||
{!isKmd(wallet) && (
|
||||
<img
|
||||
alt={`wallet_icon_${wallet.id}`}
|
||||
src={wallet.metadata.icon}
|
||||
style={{ objectFit: 'contain', width: '30px', height: 'auto' }}
|
||||
/>
|
||||
)}
|
||||
<span>{isKmd(wallet) ? 'LocalNet Wallet' : wallet.metadata.name}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="modal-action ">
|
||||
<button
|
||||
data-test-id="close-wallet-modal"
|
||||
className="btn"
|
||||
onClick={() => {
|
||||
closeModal()
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
{activeAddress && (
|
||||
<button
|
||||
className="btn btn-warning"
|
||||
data-test-id="logout"
|
||||
onClick={async () => {
|
||||
if (wallets) {
|
||||
const activeWallet = wallets.find((w) => w.isActive)
|
||||
if (activeWallet) {
|
||||
await activeWallet.disconnect()
|
||||
} else {
|
||||
// Required for logout/cleanup of inactive providers
|
||||
// For instance, when you login to localnet wallet and switch network
|
||||
// to testnet/mainnet or vice verse.
|
||||
localStorage.removeItem('@txnlab/use-wallet:v3')
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
)
|
||||
}
|
||||
export default ConnectWallet
|
||||
@ -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<ErrorBoundaryProps, ErrorBoundaryState> {
|
||||
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 (
|
||||
<div className="hero min-h-screen bg-teal-400">
|
||||
<div className="hero-content text-center rounded-lg p-6 max-w-md bg-white mx-auto">
|
||||
<div className="max-w-md">
|
||||
<h1 className="text-4xl">Error occured</h1>
|
||||
<p className="py-6">
|
||||
{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}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorBoundary
|
||||
@ -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<boolean>(false)
|
||||
const [receiverAddress, setReceiverAddress] = useState<string>('')
|
||||
|
||||
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 (
|
||||
<dialog id="transact_modal" className={`modal ${openModal ? 'modal-open' : ''} bg-slate-200`}>
|
||||
<form method="dialog" className="modal-box">
|
||||
<h3 className="font-bold text-lg">Send payment transaction</h3>
|
||||
<br />
|
||||
<input
|
||||
type="text"
|
||||
data-test-id="receiver-address"
|
||||
placeholder="Provide wallet address"
|
||||
className="input input-bordered w-full"
|
||||
value={receiverAddress}
|
||||
onChange={(e) => {
|
||||
setReceiverAddress(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<div className="modal-action ">
|
||||
<button className="btn" onClick={() => setModalState(!openModal)}>
|
||||
Close
|
||||
</button>
|
||||
<button
|
||||
data-test-id="send-algo"
|
||||
className={`btn ${receiverAddress.length === 58 ? '' : 'btn-disabled'} lo`}
|
||||
onClick={handleSubmitAlgo}
|
||||
>
|
||||
{loading ? <span className="loading loading-spinner" /> : 'Send 1 Algo'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default Transact
|
||||
@ -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.
|
||||
@ -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
|
||||
}
|
||||
13
projects/TokenizeRWATemplate-frontend/src/main.tsx
Normal file
13
projects/TokenizeRWATemplate-frontend/src/main.tsx
Normal file
@ -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(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<App />
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>,
|
||||
)
|
||||
@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@ -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('')
|
||||
})
|
||||
})
|
||||
@ -0,0 +1,3 @@
|
||||
export function ellipseAddress(address: string | null, width = 6): string {
|
||||
return address ? `${address.slice(0, width)}...${address.slice(-width)}` : (address ?? '')
|
||||
}
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
24
projects/TokenizeRWATemplate-frontend/src/vite-env.d.ts
vendored
Normal file
24
projects/TokenizeRWATemplate-frontend/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
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
|
||||
}
|
||||
12
projects/TokenizeRWATemplate-frontend/tailwind.config.cjs
Normal file
12
projects/TokenizeRWATemplate-frontend/tailwind.config.cjs
Normal file
@ -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')],
|
||||
}
|
||||
40
projects/TokenizeRWATemplate-frontend/tests/example.spec.ts
Normal file
40
projects/TokenizeRWATemplate-frontend/tests/example.spec.ts
Normal file
@ -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()
|
||||
})
|
||||
38
projects/TokenizeRWATemplate-frontend/tsconfig.json
Normal file
38
projects/TokenizeRWATemplate-frontend/tsconfig.json
Normal file
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
9
projects/TokenizeRWATemplate-frontend/tsconfig.node.json
Normal file
9
projects/TokenizeRWATemplate-frontend/tsconfig.node.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
15
projects/TokenizeRWATemplate-frontend/vite.config.ts
Normal file
15
projects/TokenizeRWATemplate-frontend/vite.config.ts
Normal file
@ -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,
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
||||
Reference in New Issue
Block a user