Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: update boilerplate code #3

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
{
"name": "ICP Azle 201",
"build": {
"dockerfile": "Dockerfile"
},
"image": "ghcr.io/dfinity/icp-dev-env-azle:3",
"forwardPorts": [4943],
"portsAttributes": {
"4943": {
Expand Down
2 changes: 1 addition & 1 deletion .gitpod.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
tasks:
- before: |
export PATH="$HOME/bin:$PATH"
export DFX_VERSION=0.16.1
export DFX_VERSION=0.17.0
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc
sudo apt-get install -q -y libunwind-dev build-essential clang libssl-dev pkg-config
sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)"
Expand Down
31 changes: 23 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ ICP Azle 201 Boilerplate is a comprehensive project setup designed to streamline
- **React.js Setup:** The boilerplate comes with a well-structured React.js setup, making it easy to manage your frontend infrastructure.
- **ICP Canister:** ICP Canister integration is included, offering a powerful way to manage data and interactions on the Internet Computer.

**[Read the Getting Started Guide](link-to-your-tutorial)**

## Things to be explained in the course:
1. What is Ledger? More details here: https://internetcomputer.org/docs/current/developer-docs/integrations/ledger/
2. What is Internet Identity? More details here: https://internetcomputer.org/internet-identity
Expand All @@ -17,6 +15,8 @@ ICP Azle 201 Boilerplate is a comprehensive project setup designed to streamline

## Getting started

To get started developing in the browser, click this button:

[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/dacadeorg/icp-azle-201)

If you rather want to use GitHub Codespaces, click this button instead:
Expand All @@ -25,6 +25,12 @@ If you rather want to use GitHub Codespaces, click this button instead:

**NOTE**: After deploying your canisters in GitHub Codespaces, run `./canister_urls.py` and click the links that are shown there.

If you prefer running VS Code locally and not in the browser, click "Codespaces: ..." or "Gitpod" in the bottom left corner and select "Open in VS Code" in the menu that appears.
If prompted, proceed by installing the recommended plugins for VS Code.

To develop fully locally, first install [Docker](https://www.docker.com/get-started/) and [VS Code](https://code.visualstudio.com/) and start them on your machine.
Next, click the following button to open the dev container locally:

[![Open locally in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/dacadeorg/icp-azle-201)

## How to deploy canisters implemented in the course
Expand Down Expand Up @@ -58,26 +64,35 @@ Switch to the minter identity:
`dfx identity use minter`

Transfer ICP:
`dfx ledger transfer <ADDRESS> --memo 0 --icp 100 --fee 0`
`dfx ledger transfer <ADDRESS> --memo 0 --icp 100 --fee 0`
where:
- `--memo` is some correlation id that can be set to identify some particular transactions (we use that in the marketplace canister).
- `--icp` is the transfer amount
- `--fee` is the transaction fee. In this case it's 0 because we make this transfer as the minter idenity thus this transaction is of type MINT, not TRANSFER.
- `<ADDRESS>` is the address of the recipient. To get the address from the principal, you can use the helper function from the marketplace canister - `getAddressFromPrincipal(principal: Principal)`, it can be called via the Candid UI.

### ICRC2 ledger canister

`deploy-local-icrc-ledger.sh` - deploys an ICRC2 canister.

Transfer ICRC token:
`dfx canister call icrc1_ledger_canister icrc1_transfer '(record { to = record { owner = principal "<PRINCIPAL>";}; amount = <AMOUNT>;})'`
where:
- `<PRINCIPAL>` is the principal string of the receiver
- `<AMOUNT>` is the amount of token to be transferred

### Internet identity canister

`dfx deploy internet_identity` - that is the canister that handles the authentication flow. Once it's deployed, the `js-agent` library will be talking to it to register identities. There is UI that acts as a wallet where you can select existing identities
`deploy-local-identity.sh` - deploys an identity canister and outputs the canister id to `.env` as the `IDENTITY_CANISTER_ID` variable. Once it's deployed, the `js-agent` library will be talking to it to register identities. There is UI that acts as a wallet where you can select existing identities
or create a new one.

### Marketplace canister

`dfx deploy dfinity_js_backend` - deploys the marketplace canister where the business logic is implemented.
Basically, it implements functions like add, view, update, delete, and buy products + a set of helper functions.
Switch to the default identity:
`dfx identity use default`

Do not forget to run `dfx generate dfinity_js_backend` anytime you add/remove functions in the canister or when you change the signatures.
Otherwise, these changes won't be reflected in IDL's and won't work when called using the JS agent.
`deploy-local-backend-canister.sh` - deploys the marketplace canister where the business logic is implemented and outputs the canister id to `.env` as the `BACKEND_CANISTER_ID` variable.
Basically, it implements functions like add, view, update, delete, and buy products + a set of helper functions.

### Marketplace frontend canister
`dfx deploy dfinity_js_frontend` - deployes the frontend app for the `dfinity_js_backend` canister on IC.
9 changes: 9 additions & 0 deletions deploy-local-backend-canister.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash

dfx identity use default

dfx generate dfinity_js_backend
dfx deploy dfinity_js_backend

sed -i '' /^BACKEND_CANISTER_ID/d .env
dfx canister id dfinity_js_backend | awk '{print "BACKEND_CANISTER_ID="$1}' >> .env
45 changes: 45 additions & 0 deletions deploy-local-icrc-ledger.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env bash

dfx identity new minter
dfx identity use minter
export MINTER=$(dfx identity get-principal)

export TOKEN_NAME="Dacade Token"
export TOKEN_SYMBOL="DCDT"

dfx identity use default
export DEFAULT=$(dfx identity get-principal)

export PRE_MINTED_TOKENS=10_000
export TRANSFER_FEE=0

dfx identity new archive_controller
dfx identity use archive_controller
export ARCHIVE_CONTROLLER=$(dfx identity get-principal)
export TRIGGER_THRESHOLD=2000
export NUM_OF_BLOCK_TO_ARCHIVE=1000
export CYCLE_FOR_ARCHIVE_CREATION=10000000000000

export FEATURE_FLAGS=true

dfx identity use default

dfx generate icrc1_ledger_canister

dfx deploy icrc1_ledger_canister --specified-id mxzaz-hqaaa-aaaar-qaada-cai --argument "(variant {Init =
record {
token_symbol = \"${TOKEN_SYMBOL}\";
token_name = \"${TOKEN_NAME}\";
minting_account = record { owner = principal \"${MINTER}\" };
transfer_fee = ${TRANSFER_FEE};
metadata = vec {};
feature_flags = opt record{icrc2 = ${FEATURE_FLAGS}};
initial_balances = vec { record { record { owner = principal \"${DEFAULT}\"; }; ${PRE_MINTED_TOKENS}; }; };
archive_options = record {
num_blocks_to_archive = ${NUM_OF_BLOCK_TO_ARCHIVE};
trigger_threshold = ${TRIGGER_THRESHOLD};
controller_id = principal \"${ARCHIVE_CONTROLLER}\";
cycles_for_archive_creation = opt ${CYCLE_FOR_ARCHIVE_CREATION};
};
}
})"
10 changes: 10 additions & 0 deletions deploy-local-identity.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash

dfx identity use default

dfx deploy internet_identity

dfx generate internet_identity

sed -i '' /^IDENTITY_CANISTER_ID/d .env
dfx canister id internet_identity | awk '{print "IDENTITY_CANISTER_ID="$1}' >> .env
7 changes: 4 additions & 3 deletions deploy-local-ledger.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
# Change the variable to "ic" to deploy the ledger on the mainnet.
export NETWORK=local

dfx identity new minter --storage-mode=plaintext
dfx identity use minter
export MINTER_ACCOUNT_ID=$(dfx ledger account-id)
dfx identity use default
export DEFAULT_ACCOUNT_ID=$(dfx ledger account-id)
dfx identity new minter --storage-mode=plaintext
dfx identity use minter
export MINTER_ACCOUNT_ID=$(dfx ledger account-id)

dfx generate ledger_canister

dfx deploy --specified-id ryjl3-tyaaa-aaaaa-aaaba-cai ledger_canister --argument '(variant {
Init = record {
Expand Down
15 changes: 11 additions & 4 deletions dfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,28 @@
},
"ledger_canister": {
"type": "custom",
"candid": "https://raw.githubusercontent.com/dfinity/ic/ca5e5052886de781021506814d2c6502e375da48/rs/rosetta-api/icp_ledger/ledger.did",
"wasm": "https://download.dfinity.systems/ic/ca5e5052886de781021506814d2c6502e375da48/canisters/ledger-canister.wasm.gz",
"candid": "https://raw.githubusercontent.com/dfinity/ic/d87954601e4b22972899e9957e800406a0a6b929/rs/rosetta-api/icp_ledger/ledger.did",
"wasm": "https://download.dfinity.systems/ic/d87954601e4b22972899e9957e800406a0a6b929/canisters/ledger-canister.wasm.gz",
"remote": {
"id": {
"ic": "ryjl3-tyaaa-aaaaa-aaaba-cai"
}
}
},
"icrc1_ledger_canister": {
"type": "custom",
"candid": "https://raw.githubusercontent.com/dfinity/ic/d87954601e4b22972899e9957e800406a0a6b929/rs/rosetta-api/icrc1/ledger/ledger.did",
"wasm": "https://download.dfinity.systems/ic/d87954601e4b22972899e9957e800406a0a6b929/canisters/ic-icrc1-ledger.wasm.gz"
},
"dfinity_js_backend": {
"type": "custom",
"candid_gen": "http",
"main": "src/dfinity_js_backend/src/index.ts",
"candid": "src/dfinity_js_backend/src/index.did",
"build": "npx azle dfinity_js_backend",
"wasm": ".azle/dfinity_js_backend/dfinity_js_backend.wasm",
"gzip": true
"gzip": true,
"assets": [["src/declarations/icrc1-ledger/icrc1-ledger.did", "src/icrc1-ledger.did"]]
},
"dfinity_js_frontend": {
"dependencies": [
Expand All @@ -47,4 +54,4 @@
},
"output_env_file": ".env",
"version": 1
}
}
36 changes: 20 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,56 +18,60 @@
"gen-deploy:local": "dfx generate dfinity_js_backend && dfx deploy --network=local"
},
"dependencies": {
"@dfinity/agent": "^0.19.2",
"@dfinity/candid": "^0.19.2",
"@dfinity/principal": "^0.19.2",
"@dfinity/agent": "^0.19.3",
"@dfinity/auth-client": "^0.19.2",
"@dfinity/candid": "^0.19.3",
"@dfinity/ledger": "^0.0.15",
"@dfinity/nns": "^0.16.7",
"@dfinity/principal": "^0.19.3",
"@dfinity/utils": "^0.0.22",
"@dfinity/ledger": "^0.0.15",
"@popperjs/core": "^2.10.2",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"azle": "^0.20.2",
"big-number": "^2.0.0",
"bootstrap": "^5.1.3",
"bootstrap-icons": "^1.7.1",
"cors": "^2.8.5",
"express": "^4.18.2",
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-bootstrap": "^2.0.3",
"react-confirm-alert": "^2.7.0",
"react-dom": "^17.0.2",
"react-toastify": "^8.1.0",
"web-vitals": "^1.0.1",
"secp256k1": "^5.0.0",
"azle": "^0.19.0",
"uuid": "^9.0.0",
"hashcode": "^1.0.3"
"web-vitals": "^1.0.1"
},
"devDependencies": {
"@babel/core": "^7.21.8",
"@babel/preset-env": "^7.21.5",
"@babel/preset-react": "^7.18.6",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/react-dom": "^18.2.19",
"assert": "2.0.0",
"babel-loader": "^8.0.0",
"buffer": "6.0.3",
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.3",
"dotenv": "^16.0.3",
"events": "3.3.0",
"file-loader": "^6.2.0",
"html-webpack-plugin": "5.5.0",
"process": "0.11.10",
"stream-browserify": "3.0.0",
"style-loader": "^3.3.2",
"terser-webpack-plugin": "^5.3.3",
"util": "0.12.4",
"webpack": "^5.73.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.8.1",
"babel-loader": "^8.0.0",
"@babel/core": "^7.21.8",
"@babel/preset-react": "^7.18.6",
"@babel/preset-env": "^7.21.5",
"file-loader": "^6.2.0",
"css-loader": "^6.7.3",
"style-loader": "^3.3.2"
"webpack-dev-server": "^4.8.1"
},
"engines": {
"node": "^12 || ^14 || ^16 || ^18 || ^20"
"node": "^20"
},
"browserslist": [
"last 2 chrome version",
Expand Down
4 changes: 2 additions & 2 deletions src/dfinity_js_frontend/.babelrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
88 changes: 54 additions & 34 deletions src/dfinity_js_frontend/src/components/Wallet.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,63 @@
import React from "react";
import { Dropdown, Stack } from "react-bootstrap";
import { NotificationSuccess } from "./utils/Notifications";
import { toast } from "react-toastify";

const Wallet = ({ principal, balance, symbol, isAuthenticated, destroy }) => {
const Wallet = ({ address, principal, icpBalance, icrcBalance, symbol, isAuthenticated, destroy }) => {
if (isAuthenticated) {
return (
<>
<Dropdown>
<Dropdown.Toggle
variant="light"
align="end"
id="dropdown-basic"
className="d-flex align-items-center border rounded-pill py-1"
>
{balance} <span className="ms-1"> {symbol}</span>
</Dropdown.Toggle>

<Dropdown.Menu className="shadow-lg border-0">
<Dropdown.Item>
<Stack direction="horizontal" gap={2}>
<i className="bi bi-person-circle fs-4" />
<span className="font-monospace">{principal}</span>
</Stack>
</Dropdown.Item>

<Dropdown.Divider />

<Dropdown.Item
as="button"
className="d-flex align-items-center"
onClick={() => {
destroy();
}}
<>
<Dropdown>
<Dropdown.Toggle
variant="light"
align="end"
id="dropdown-basic"
className="d-flex align-items-center border rounded-pill py-1"
>
<i className="bi bi-box-arrow-right me-2 fs-4" />
Logout
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</>
{icrcBalance} <span className="ms-1"> {symbol}</span>
</Dropdown.Toggle>

<Dropdown.Menu className="shadow-lg border-0">
<Dropdown.Item>
<Stack direction="horizontal" gap={2}>
<i className="bi bi-currency-dollar fs-4" />
<span className="font-monospace">ICP Balance: {icpBalance}</span>
</Stack>
</Dropdown.Item>

<Dropdown.Divider />

<Dropdown.Item onClick={() => { navigator.clipboard.writeText(principal); toast(<NotificationSuccess text="Copied principal" />) }}>
<Stack direction="horizontal" gap={2}>
<i className="bi bi-person-circle fs-4" />
<span className="font-monospace">Principal: {principal}</span>
</Stack>
</Dropdown.Item>

<Dropdown.Divider />

<Dropdown.Item onClick={() => { navigator.clipboard.writeText(address); toast(<NotificationSuccess text="Copied address" />) }}>
<Stack direction="horizontal" gap={2}>
<i className="bi bi-wallet2 fs-4" />
<span className="font-monospace">Address: {address}</span>
</Stack>
</Dropdown.Item>

<Dropdown.Divider />

<Dropdown.Item
as="button"
className="d-flex align-items-center"
onClick={() => {
destroy();
}}
>
<i className="bi bi-box-arrow-right me-2 fs-4" />
Logout
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</>
);
}

Expand Down
Loading