- Setup
- Build & Upload
- Quickstart Build & Upload "Flipper" ink! Rust Smart Contract to Local Testnet (using Cargo Contract)
- Quickstart Build & Upload "Basic Contract Caller" ink! Rust Smart Contract to Local Testnet (using Cargo Contract)
- Quickstart Build & Upload "IPSP22" ink! Rust Smart Contract to Local Testnet (using Cargo Contract)
- Quickstart Build & Upload "Unnamed" ink! Rust Smart Contract to Local Testnet (using Cargo Contract)
- Build & Upload Moonbeam VRF Randomness Precompile Solidity Smart Contract to Moonbase Alpha Testnet (using Truffle)
- Build & Upload Chainlink VRFD20 Randomness Solidity Smart Contract to Ethereum Sepolia Testnet (using Truffle)
- Build & Upload "Flipper" ink! Rust Smart Contract to Local Testnet (using Cargo Contract)
- UNSUPPORTED Build & Upload "Flipper" ink! Rust Smart Contract to Local Testnet (using Swanky CLI)
- Interact
- Tips
-
Note:
- The docker/Dockerfile and its dependencies in docker/utility are modified copies of files in https://github.com/paritytech/scripts/blob/master/dockerfiles to have the flexibility to develop locally using ink! in a Docker container that uses Linux without any dependency issues, and where changes replicated on the host machine.
- This is useful if you're on an old macOS Catalina and Apple won't allow you to do further software updates, so you cannot install
brew install protobuf
to install necessary dependencies.
-
Install and run Docker
-
Generate .env file from sample file with
cp .env.example .env
- Generate a Substrate-based account on an air-gapped machine using Subkey or by installing Subkey in Docker on an air-gapped machine
- Add the mnemonic phrase of the Substrate-based account to the value of
LS_CONTRACTS
in the .env file. - Obtain testnet tokens from faucet at https://use.ink/faucet/
- Obtain testnet Shibuya from faucet at https://portal.astar.network/shibuya-testnet/assets
-
Check versions used in Dockerfile:
- Rust nightly version
- Node.js version
- Cargo Contract
- Substrate Contracts Node
-
Configure amount of CPUs and memory of the host machine the Docker container should use in ./docker/docker.sh. Use
docker update
to change the configuration https://docs.docker.com/engine/reference/commandline/container_update/ -
Run Docker container and follow the terminal log instructions.
- Note: Optionally exclude installing substrate-contracts-node by running
time ./docker/docker.sh "without_contracts_node"
since including it will increase build time substantially and may not be necessary if you are deploying to remote testnets. - Important Note: If you are on a Mac OS with Rosetta (e.g. M1, M2, M3) then run with:
time ./docker/docker.sh "" "linux/amd64"
(where 2nd argument is "linux/amm64" x86_64 and 1st argument could be"use_contracts_node"
or"without_contracts_node"
) (otherwise on macOS it may use"linux/arm64"
by default). Check what you are using by runninguname -a
on your host machine.- Note: If you are using it this way on Rosetta (Apple Silicon) it will work but it will run much slower than directly on your host without Docker or with Docker container on an x86 machine, if that is possible to you.
touch .env && cp .env.example .env time ./docker/docker.sh "use_contracts_node"
- Note: Optionally exclude installing substrate-contracts-node by running
-
Check Memory & CPU usage. Update memory limits in docker-compose.yml
docker stats
-
Enter Docker container
docker exec -it ink /bin/bash
-
Optional Attach to Container in Visual Studio Code (VSCode)
- Open folder /app with
cd /app
- Open folder /app with
-
Check versions. Note:
rustup toolchain list
rustup update
rustup show
cargo-contract --version
- Check if substrate-contracts-node was installed if you used the "with-node" argument
substrate-contracts-node --version
- Optionally update dependencies in ./dapps/ink-rust/wasm-flipper/package.json https://stackoverflow.com/a/70588930/3208553
cd ./dapps/ink-rust/wasm-flipper/ yarn upgrade
- Optionally update dependencies in ./dapps/ink-rust/wasm-flipper/contract/flipper/Cargo.toml
cd ./dapps/ink-rust/wasm-flipper/contract/flipper/ cargo update
- Important This is only available if you did not run ./docker/run.sh using "without_contracts_node" argument
- Run Cargo Contract Node
- Note: Use either
--tmp
or--base-path "/tmp/ink"
- Note: Delete chain database
rm -rf /tmp/ink
. - Note: Check disk space used by database
du /tmp/ink
- Note: Use either
- Note: Refer to debugging docs https://use.ink/basics/contract-debugging
./docker/run-scn.sh
-
Leave that terminal tab running the node. Enter the terminal again in a new tab with
docker exec -it ink /bin/bash
-
Attach to the running terminal with VSCode if necessary. See here
-
Restart the node and delete the chain database by running
./docker/reset.sh
inside the Docker container ordocker exec -it ink /app/docker/reset.sh
from outside the Docker container and waiting 15 seconds
- Verify that you are able to connect from websites like:
- Note: It is necessary to use
--unsafe-rpc-external
and--unsafe-ws-external
instead of just--rpc-external
and--ws-external
, otherwise you will get an errorAPI-WS: disconnected from ws://127.0.0.1:9944: 1006:: Abnormal Closure
if you try to connect to the local node at https://contracts-ui.substrate.io/?rpc=ws://127.0.0.1:9944 - Note: It is also necessary if using the Brave browser to disable Advertisement blocker shields to avoid that error as mentioned in my response here https://substrate.stackexchange.com/a/8648/83
Demo Quickstart Build & Upload ink! Rust Flipper Smart Contract to Local Testnet (using Cargo Contract)
- First run a Cargo Contracts Node, by following the steps in the section "Run Cargo Contracts Node in Docker Container"
SCN_PORT=$(docker exec -it ink lsof -ti:30333) && \
docker exec -it ink echo $(kill -9 $SCN_PORT) && \
docker exec -it ink /app/docker/reset.sh && \
docker exec -it ink /app/docker/quickstart.sh
* Enter shell of Docker container
```bash
docker exec -it ink /bin/bash
```
* Run quickstart
```bash
./docker/reset.sh
./docker/quickstart.sh
```
- Note: This may be run repeatedly since it automatically:
- Kills any existing substrate-contracts-node on port 30333
- Empties the chain database with
rm -rf /tmp/ink
so we can redeploy the Flipper contract to the same address - Run substrate-contracts-node again
- Redeploys the Flipper contract
- Interacts with the Flipper contract
Demo Quickstart Build & Upload ink! Rust "Basic Contract Caller" Smart Contract to Local Testnet (using Cargo Contract)
- First run a Cargo Contracts Node, by following the steps in the section "Run Cargo Contracts Node in Docker Container"
* Enter shell of Docker container
```bash
docker exec -it ink /bin/bash
```
* Run in terminal tab 1
```bash
./docker/reset.sh
```
* Run in terminal tab 2
```
./docker/quickstart-basic-contract-caller.sh
```
Demo Quickstart Build & Upload ink! Rust "IPSP22" Smart Contract to Local Testnet (using Cargo Contract)
- First run a Cargo Contracts Node, by following the steps in the section "Run Cargo Contracts Node in Docker Container"
* Enter shell of Docker container
```bash
docker exec -it ink /bin/bash
```
* Run in terminal tab 1
```bash
./docker/reset.sh
```
* Run in terminal tab 2
```bash
cd /app
./docker/quickstart-ipsp22.sh
```
Demo Quickstart Build & Upload ink! Rust "Unnamed" Smart Contract to Local Testnet (using Cargo Contract)
* First run a Cargo Contracts Node, by following the steps in the section "Run Cargo Contracts Node in Docker Container"
* Enter shell of Docker container
```bash
docker exec -it ink /bin/bash
```
* Run in terminal tab 1
```bash
./docker/reset.sh
```
* Run in terminal tab 2
````
./docker/quickstart-unnamed.sh
```
Build & Upload Moonbeam VRF Randomness Precompile Solidity Smart Contract to Moonbase Alpha Testnet (using Truffle)
- Follow the instructions in the VRF example README and install necessary dependencies then run the following from this directory
./docker/quickstart-moonbeam-vrf-precompile.sh
Build & Upload Chainlink VRFD20 Randomness Solidity Smart Contract to Ethereum Sepolia Testnet (using Truffle)
- Follow the instructions in the VRF20 example README
-
First run a Cargo Contracts Node, by following the steps in the section "Run Cargo Contracts Node in Docker Container"
-
Create Rust project with template. Note that the flipper folder may already exist
cd dapps/ink-rust/wasm-flipper/contract
cargo contract new flipper
cd flipper
-
Optionally build with VSCode by adding the project
"dapps/ink-rust/wasm-flipper/contract/flipper"
to the list of members in the Cargo.toml file in the project root, and running "Terminal > Run Task > Build Contract" to build all the contract using the configuration in ./.vscode/launch.json -
Generate .contract, .wasm, and metadata.json code. Note: Use
--release
to deploy in "release" mode (without debug logs) instead of "debug" mode- Note: If you get error
ERROR: Cannot read /app/target/ink/flipper/.target
then runrm -rf /app/target
cargo contract build --manifest-path /app/dapps/ink-rust/wasm-flipper/contract/flipper/Cargo.toml
- Note: If you get error
-
Copy ./target/ink/flipper/flipper.json
- Paste this as the ABI value of
const abi =
./dapps/ink-rust/wasm-flipper/ui/components/abi.ts - Note: Refer to ./docker/quickstart.sh that shows how to do this programmatically
- Paste this as the ABI value of
-
Upload Contract (note: prefer to use contracts-ui to avoid exposing private key)
cargo contract upload --suri //Alice
-
Actual output:
-
Terminal #1
Result Success! Code hash "0xec3a66a8f99674ecf25d180fc39ee8e620d45e8de459277e353ece20753d6c53" Deposit 434925000000 Your upload call has not been executed. To submit the transaction and execute the call on chain, add -x/--execute flag to the command
-
Terminal #2
2023-05-11 05:49:47.389 INFO tokio-runtime-worker substrate: 💤 Idle (0 peers), best: #0 (0x18c5…59af), finalized #0 (0x18c5…59af), ⬇ 0 ⬆ 0 2023-05-11 05:49:48.124 DEBUG tokio-runtime-worker sync: Propagating transactions 2023-05-11 05:49:48.437 INFO tokio-runtime-worker jsonrpsee_ws_server::server: Accepting new connection 1/100
-
-
Upload and Execute it. Optionally
--skip-dry-run
cargo contract upload --suri //Alice \
--execute \
--skip-confirm
-
Copy the "Code hash" that is output since you can query the contract by pasting it at https://contracts-ui.substrate.io/hash-lookup?rpc=ws://127.0.0.1:9944 and pasting its ABI as the Metadata using the contents of ./target/ink/flipper/flipper.json
-
Note: The output format should be:
CodeStored event code_hash: 0x......
- Note: only one copy of the code is stored, but there can be many instance of one code blob, differs from other EVM chains where each node has a copy
-
Actual output:
-
Terminal #1
Events ... Code hash "0xec3a66a8f99674ecf25d180fc39ee8e620d45e8de459277e353ece20753d6c53"
-
Terminal #2
2023-05-11 05:56:14.102 INFO tokio-runtime-worker substrate: ✨ Imported #1 (0x9bc4…be22) 2023-05-11 05:56:14.699 DEBUG tokio-runtime-worker sync: Propagating transactions 2023-05-11 05:56:15.591 INFO tokio-runtime-worker substrate: 💤 Idle (0 peers), best: #1 (0x9bc4…be22), finalized #0 (0x18c5…59af), ⬇ 0 ⬆ 0
-
WARNING: Swanky CLI is not supported by this repository using Docker until this issue is resolved inkdevhub/swanky-cli#222 (comment) WARNING: Swanky CLI only supports older versions of ink!, cargo-contract, and rustc, so you might need to downgrade to the following versions to get it to work, as mentioned here inkdevhub/swanky-cli#222 (comment)
- ink v4.2.1 or v5 to ink 4.0.0
- cargo-contract v4.1.0 to v2.1.0
- rustc 1.77.2 (25ef9e3d8 2024-04-09) to rustc to 1.69 (2023-03-21)
The versions that are used in the Docker container are specified in ./docker/Dockerfile.
-
Enter the Docker container shell in a new terminal window if necessary:
docker exec -it ink /bin/bash
-
Install NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
Install Swanky CLI https://github.com/AstarNetwork/swanky-cli
cd dapps/ink-rust
nvm install
nvm use
yarn global add @astar-network/[email protected]
Show Swanky help https://docs.astar.network/docs/build/wasm/swanky-suite/cli/
swanky help --nested-commands
Add Flipper smart contract template (only if that folder does not exist yet) to generate https://github.com/AstarNetwork/wasm-flipper.
swanky init flipper
Note: Choose ink
as a contract language and flipper
as template and a chosen contract name.
Optionally choose from Y/N
when asking to download the Swanky Node (NOT required if already using Substrate Contracts Node).
If you chose to download the Swanky Node then run it in your local environment:
cd ./flipper
yarn
yarn run run-node
Note: The command yarn run run-node
runs swanky node start
Warning: It is old and outdated and may not work
- Init
cd ./wasm-flipper
cd contract
- Start the local node
If you chose to download the Swanky Node then run it in your local environment:
cd flipper
swanky node start
- Build the contract
Build the contract in a new tab
swanky contract compile flipper
Note: Try rustup update
if you face error
- Deploy the contract
Local Testnet
swanky contract deploy flipper --account alice -g 1000000000000 -a true
Shibuya Testnet
swanky contract deploy flipper --account alice --gas 1000000000000 --args true --network shibuya
Copy paste the contract address.
-
Update
WS_PROVIDER
to check if it connects to Shibuya or localhost in ./dapps/ink-rust/wasm-flipper/ui/components/app.tsx -
View in block explorer if deploy on Astar https://astar.subscan.io/wasm_contract_dashboard?tab=contract Note: If you deployed on Shibuya then go to https://shibuya.subscan.io/
- Run the steps in the Setup section (if you want to connect to a local node)
- Enter the Docker container with
docker exec -it ink /bin/bash
- Run the following inside the Docker container
cd ./dapps/ink-python/example
pip3 install --no-cache-dir -r requirements.txt
python3 ./src/app.py
-
Note: If you get error
DuplicateContract', 'docs': ['A contract with the same AccountId already exists
then restart the substrate-contracts-node and reset the database by simply running the following on the host machine from outside the Docker container and waiting for approx. 15 seconds before running your commands again. Or run/app/docker/reset.sh
from within the Docker container.docker exec -it ink /app/docker/reset.sh
-
Note: If you get error
ValueError: Invalid mnemonic: invalid word in phrase
then you needed to set account mnemonic phrase as the value ofLS_CONTRACT
in the .env file and obtain testnet tokens for it from faucet at https://use.ink/faucet/
Obtain Shibuya from faucet at https://portal.astar.network/shibuya-testnet/assets
-
Enter the Docker container shell in a new terminal window if necessary:
docker exec -it ink /bin/bash
-
Install dependencies and run the Flipper DApp
cd dapps/ink-rust/wasm-flipper yarn yarn dev
-
Go to this address in web browser http://localhost:3000
-
Reference https://polkadot.js.org/docs/api-contract/start/basics
- Instantiate Contract
cargo contract instantiate \
--suri //Bob \
--constructor new \
--args true \
--execute \
--skip-confirm
- Wait for response
...
Event System => NewAccount
account: 5G...
...
Event Contracts + Instantiated
deployer: 5F...
contract: 5G.... (new contract account address to interact with contract)
...
- Store the response in an environment variable for reuse. Replace the example value below of
5G....
with the actual contract account address provided in the event response above.
CONTRACT_ADDR=5G....
echo "stored in variable CONTRACT_ADDR the contract address value ${CONTRACT_ADDR"
- Interact to flip the boolean value, not a dry run so no response but we get a gas limit response
cargo contract call \
--suri //Charlie \
--contract $CONTRACT_ADDR \
--message flip \
--execute \
--skip-confirm
- Check it flipped the boolean value (dry run only)
cargo contract call \
--suri //Charlie \
--contract $CONTRACT_ADDR \
--message get \
--execute \
--skip-confirm
- Check the outputs:
- Emitted events in the terminal where you run
cargo contract ...
comments - Debug logs in the substrate-contracts-node terminal
- Optionally go to https://contracts-ui.substrate.io/hash-lookup?rpc=ws://127.0.0.1:9944 and paste the "Code hash" from when you initially uploaded the contract, and pasting its ABI as the Metadata using the contents of ./target/ink/flipper/flipper.json
- Emitted events in the terminal where you run
- Note: If you don't build in "debug" mode with
cargo contract build ...
instead ofcargo contract build --release ...
and you run it using dry run by running extra options like the following, or if you execute as a transaction, then you won't be able to see node terminal debug logs liketokio-runtime-worker runtime::contracts Execution finished with debug buffer...
from your use ofink::env::debug_println!
in the smart contract
--skip-dry-run \
--gas 1000000000000 \
--proof-size 1000000000000
- List Docker containers
docker ps -a
- List Docker images
docker images -a
docker buildx ls
- Enter Docker container shell
docker exec -it $CONTAINER_ID /bin/sh
- View Docker container logs
docker logs -f $CONTAINER_ID
- Remove Docker container
docker stop $CONTAINER_ID; docker rm $CONTAINER_ID;
- Remove Docker image
docker rmi $IMAGE_ID
docker buildx rm --all-inactive
- Reduce space used by Docker Desktop
- Docker Preferences -> Resources -> Advanced -> Virtual Disk Limit
- e.g. 64Gb reduce to 32Gb
- Note: This deletes all Docker images similar to
docker system prune -a --volumes
- Docker Preferences -> Resources -> Advanced -> Virtual Disk Limit
-
Strategy:
- Why use smart contract instead of blockchain?
- Faster iterations of design, development, testing, and release of applications to market
- Provide core functionality for the base layer of a general purpose blockchain that is being built
- Allow smart contracts to interact with an application-specific blockchain pallet logic and use them to expose some logic to users since smart contracts treat all user input as untrusted and potentially adversarial
- Example:
- Building an application where most logic in Substrate pallets
- Allow users to upload their own trading algorithms using smart contracts
- Smart contracts require gas fees to execute so users would pay for the execution time of those trading algorithms
- Expose relevant primitives similar to the Chain extension primitive of the Contracts pallet
- Building an application where most logic in Substrate pallets
- What types of smart contracts may be deployed on Substrate runtime?
- WebAssembly
- EVM-compatible
- What is a smart contract?
- Instructions that are instantiated and executed on a host platform using a specific smart contract chain account address
- Instructions written in a language
- What Substrate pallet is best to use when building a runtime to host smart contracts that are being built?
- Contracts pallet allows deployment and execution of WebAssembly-based smart contracts
- What trait do smart contract accounts used in the Contracts pallet of Substrate extend?
Currency
trait
- How to resolve
ERROR: This contract has already been uploaded with code hash
- It may be because you ran a Substrate contract node on your host machine and then tried running another one in your Docker container. So it may be necessary to run
kill -9 $(lsof -ti:30333)
on both the host machine and inside the Docker container. Or just restart Docker.
- It may be because you ran a Substrate contract node on your host machine and then tried running another one in your Docker container. So it may be necessary to run
- Why use smart contract instead of blockchain?
-
Link
TODO - continue summarising from "Smart contract accounts" section