Walkthrough: cw20_base
In this walkthrough, we will upload and interact with cw20_base
contract. We will do:
- Fetch the contract binary from a running Cosmos chain and upload it to our chain.
- Instantiate the contract.
- Execute a transfer.
Installing ccw
Clone the repository, then run:
# Clone the repo
git clone https://github.com/ComposableFi/cw-toolkit
# Install the ccw binary
cargo install --path ./cw-toolkit/cli
Installing nix
(for running the chain locally)
We are using Nix to set up and start our local development environment. So check out our Nix installation page to install Nix.
Then start the development environment by running:
nix run "github:ComposableFi/composable#dali-devnet"
This will take time at first but since it is cached, it will be almost instant afterward. But note your node will be rebuilt if the commit hash changes so if you don't want that, you can always use a specific commit hash:
nix run "github:ComposableFi/composable/d2845fc731bc3ee418a17cf528336d50f4b39924#dali-devnet"
Running pallet-cosmwasm
in local
You can run our devnet locally following this guide: https://docs.composable.finance/nix.
Uploading the contract
Let's say that we want to upload the v1.0.1
release of cw20_base
. We can directly use the download link
from the release page.
ccw substrate -c ws://127.0.0.1:9988 -n alice tx upload --url https://github.com/CosmWasm/cw-plus/releases/download/v1.0.1/cw20_base.wasm
Output:
[ + ] Contract uploaded.
- Code Hash: 0x12f7…1f73
- Code ID: 1
Getting JSON output instead of plain text
Sometimes it is easy to get the output in JSON for to automize the process. You can do that by using --output-type
parameter.
ccw substrate --output-type json COMMAND
Instantiating the contract
The next step is to instantiate the contract so that we have an instance of the contract that we can execute and query. The upload command returned a code ID. This code ID is used to identify the wasm binary (compiled CosmWasm contract). We will use this code ID to instantiate the contract from.
We want to use the following configurations to instantiate the contract:
- Code ID:
1
which is returned from the previous upload command. - Salt: Just a random salt.
- Label: Let's say that it is "our-fancy-cw20base-contract".
- Maximum gas: We don't care, let's set it to
10000000000
- Instantiate message: Let's instantiate a PICA token and give some initial balance to Bob's account
{
"name": "Picasso",
"symbol": "PICA",
"decimals": 6,
"initial_balances": [
{
"amount": "1000000000000",
"address": "0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"
}
]
}
Note that in the instantiate message, we used a hexadecimal address. That is the hexadecimal representation of the ordinary SS58 public key. We will soon make use of SS58 representations in the contracts as well, but for now, this is how we do it.
So the command will be:
ccw substrate -c ws://127.0.0.1:9988 -n alice \
tx instantiate \
-c 1 \
-s random-salt \
-l our-fancy-cw20base-contract \
-g 10000000000 \
-m '{"name":"Picasso","symbol":"PICA","decimals":6,"initial_balances":[{"amount":"1000000000000","address":"0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"}]}'
Note that apart from the address of the contract that is instantiated, we also get the raw CosmWasm events.
Output:
[ + ] Contract instantiated.
- Contract address: 5EszMeNDPmy4orcLEHRLiJawAt5xAvfK5VH7REV8bpB1jtjX
- Event: instantiate
- Contract: 5EszMeNDPmy4orcLEHRLiJawAt5xAvfK5VH7REV8bpB1jtjX
- Attributes:
- _contract_address: 0x7c888b21f31f0cec5149830bb0f8f20b949e2b589c240d223625f53df694ca49
- code_id: 1
Execute a transfer
Let's transfer some amount from Bob
to Charlie
.
Note that as the signer, we need to use bob
instead of alice
because the signer here will be both the signer
of the extrinsic, and the sender
of the contract message. Since we want to transfer from bob
, bob
needs to be the caller of the execute
call.
Also, the contract address used here is 5EszMeNDPmy4orcLEHRLiJawAt5xAvfK5VH7REV8bpB1jtjX
which should be the same for you as well since the algorithm for the address
generation that we use is based on the instantiate parameters that we provide, not on some random values or chain state. But if for some reason, you get a different
address, use that address to execute the contract.
ccw substrate -c ws://127.0.0.1:9988 -n bob \
tx execute \
-c 5EszMeNDPmy4orcLEHRLiJawAt5xAvfK5VH7REV8bpB1jtjX \
-g 10000000000 \
-m '{"transfer":{"amount":"1000","recipient":"0xd64439add16b49b6b68ac74e1b28a73a8491501ab7e0e829716f580947a4bd7e"}}'
Output:
[ + ] Contract executed.
- Event: execute
- Contract: 5EszMeNDPmy4orcLEHRLiJawAt5xAvfK5VH7REV8bpB1jtjX
- Attributes:
- _contract_address: 0x7c888b21f31f0cec5149830bb0f8f20b949e2b589c240d223625f53df694ca49
- Event: wasm
- Contract: 5EszMeNDPmy4orcLEHRLiJawAt5xAvfK5VH7REV8bpB1jtjX
- Attributes:
- action: transfer
- from: 0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48
- to: 0xd64439add16b49b6b68ac74e1b28a73a8491501ab7e0e829716f580947a4bd7e
- amount: 1000
- _contract_address: 0x7c888b21f31f0cec5149830bb0f8f20b949e2b589c240d223625f53df694ca49
Query the balance
Although you can see that the events clearly show the transfer happened. Let's query the contract to check out our balance to make sure. Since the query is not a transaction
but an RPC call, we'll use the subcommand rpc
instead of tx
.
Note that we are using a different protocol and port for the RPC endpoint.
ccw substrate -c http://127.0.0.1:32200 \
rpc query \
-c 5EszMeNDPmy4orcLEHRLiJawAt5xAvfK5VH7REV8bpB1jtjX \
-g 10000000000 \
-q '{"balance":{"address":"0xd64439add16b49b6b68ac74e1b28a73a8491501ab7e0e829716f580947a4bd7e"}}'
Output:
ccw substrate -c http://127.0.0.1:32200 \
rpc query \
-c 5EszMeNDPmy4orcLEHRLiJawAt5xAvfK5VH7REV8bpB1jtjX \
-g 100000000 \
-q '{"balance":{"address":"0xd64439add16b49b6b68ac74e1b28a73a8491501ab7e0e829716f580947a4bd7e"}}'