1 unstable release

0.1.0 Mar 4, 2024

#3 in #instantiate

38 downloads per month

MIT/Apache

73KB
1.5K SLoC

Simple NFT contract

This module may serve as a demonstration of importing and implementing CW721 spec. A small subset of base specification have been implementing so as to confirm the proper functioning. For help with rest of the implementation, refer to CW721-base.

Deployment on blockchain

For testing, let's use malaga testnet. Check this page for setting up an environment for testing.

Compile

  1. To compile the smart contract for deployment, use the following(on M1 Mac):
$ docker run --rm -v "$(pwd)":/code \
  --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
  --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
  cosmwasm/rust-optimizer-arm64:0.12.6

This will create a .wasm binary file in artifacts/ folder.

Deploy

  1. To store the binary file on blockchain, use wasmd.
$ RES=$(wasmd tx wasm store artifacts/simple_nft-aarch64.wasm --from wallet $TXFLAG -y --output json -b block)
echo $RES | jq .
{
  "height": "614976",
  "txhash": "F8503588FCBC18F4EF22A3B52D2C3E0A15ED4A839AF92E5934F8A2EF518DA5E8",
  "data": "0A250A1E2F636F736D7761736D2E7761736D2E76312E4D736753746F7265436F6465120308B302",
  "raw_log": "[{\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmwasm.wasm.v1.MsgStoreCode\"},{\"key\":\"module\",\"value\":\"wasm\"},{\"key\":\"sender\",\"value\":\"wasm1g9urk8rj9news03dv7wfckcu49a6yk8z5rldwf\"}]},{\"type\":\"store_code\",\"attributes\":[{\"key\":\"code_id\",\"value\":\"307\"}]}]}]",
  // more text
}
  1. Retrieve code id for the uploaded binary.
$ CODE_ID=$(echo $RES | jq -r '.logs[0].events[-1].attributes[0].value')
$ echo $CODE_ID
307

Interaction on blockchain

Any smart contract consists of 3 actions: Instantiate, Execute and Query. One of each has been implemented in this contract for demonstration.

Instantiate

  1. Create the InstantiateMsg
$ INIT='{"name":"TestNFT","symbol":"TNFT"}'
  1. Instantiate the contract
$ wasmd tx wasm instantiate $CODE_ID $INIT --from wallet --label "Test simple NFT" $TXFLAG -y --no-admin
logs: []
raw_log: '[]'
txhash: A9B8E576F4B3AD2F183AFFB7A2E1593E00594E375B9C522AC5B0710984223C2D
  1. Retrieve the contract address
$ CONTRACT=$(wasmd query wasm list-contract-by-code $CODE_ID $NODE --output json | jq -r '.contracts[-1]')
$ echo $CONTRACT
wasm1ydfppp7qhh5m28zvwy2gk98j2hu8fs4ky3h4h0yt7rhwrymjqdlssh63sg
  1. Query the contract info
$ wasmd query wasm contract $CONTRACT $NODE
address: wasm1ydfppp7qhh5m28zvwy2gk98j2hu8fs4ky3h4h0yt7rhwrymjqdlssh63sg
contract_info:
  code_id: "307"
  creator: wasm1g9urk8rj9news03dv7wfckcu49a6yk8z5rldwf
  label: Test simple NFT
  1. Retrive the state of the contract. Name and symbol should with the INIT msg. Also note the minter address is set to address that instatiated the contract. This is to make things simpler.
$ wasmd query wasm contract-state all $CONTRACT $NODE --output json | jq -r '.models[0].value' | base64 -d | jq .
{
  "name": "TestNFT",
  "symbol": "TNFT",
  "minter": "wasm1g9urk8rj9news03dv7wfckcu49a6yk8z5rldwf",
  "num_tokens": 0
}

Execute

  1. Let's mint a new token
$ MINT='{"mint":{"token_id":1,"owner":"wasm1qka2er800suxsy7y9yz9wqgt8p3ktw5ptpf28s","token_uri":"None","price":[{"amount":"1000","denom":"umlg"}]}}'
$ echo $MINT | jq .
{
  "mint": {
    "token_id": 1,
    "owner": "wasm1qka2er800suxsy7y9yz9wqgt8p3ktw5ptpf28s",
    "token_uri": "None",
    "price": [
      {
        "amount": "1000",
        "denom": "umlg"
      }
    ]
  }
}
  1. execute the mint command in the contract
$ wasmd tx wasm execute $CONTRACT "$MINT" --from wallet $TXFLAG -y
logs: []
raw_log: '[]'
txhash: 79C89F6CC934C2921B284458D7B0DC33AA9EFCD2A95BB5C54C217E8EF019FA3E
  1. View the newly minted token info
$ wasmd query wasm contract-state all $CONTRACT $NODE --output json | jq -r '.models[0].value' | base64 -d | jq .
{
  "owner": "wasm1qka2er800suxsy7y9yz9wqgt8p3ktw5ptpf28s",
  "approvals": null,
  "base_price": [
    {
      "denom": "umlg",
      "amount": "1000"
    }
  ],
  "token_uri": null,
  "token_id": 1
}
  1. Let's mint another token with a different owner
$ MINT='{"mint":{"token_id":2,"owner":"wasm1g9urk8rj9news03dv7wfckcu49a6yk8z5rldwf","token_uri":"None","price":[{"amount":"1000","denom":"umlg"}]}}'
$ echo $MINT | jq .
{
  "mint": {
    "token_id": 2,
    "owner": "wasm1g9urk8rj9news03dv7wfckcu49a6yk8z5rldwf",
    "token_uri": "None",
    "price": [
      {
        "amount": "1000",
        "denom": "umlg"
      }
    ]
  }
}
  1. Execute mint command
$ wasmd tx wasm execute $CONTRACT "$MINT" --from wallet $TXFLAG -y
gas estimate: 160602
logs: []
raw_log: '[]'
txhash: 5AE13707AD8C944607FEF23B43648F983CFBE298724A3EA4966934254BABB984
  1. View another newly minted token
$ wasmd query wasm contract-state all $CONTRACT $NODE --output json | jq -r '.models[1].value' | base64 -d | jq .
{
  "owner": "wasm1g9urk8rj9news03dv7wfckcu49a6yk8z5rldwf",
  "approvals": null,
  "base_price": [
    {
      "denom": "umlg",
      "amount": "1000"
    }
  ],
  "token_uri": null,
  "token_id": 2
}
  1. Transfer token 2 to another owner
$ EXECUTE='{"transfer_nft":{"recipient":"wasm1qka2er800suxsy7y9yz9wqgt8p3ktw5ptpf28s","token_id":2}}'
$ echo $EXECUTE | jq .
{
  "transfer_nft": {
    "recipient": "wasm1qka2er800suxsy7y9yz9wqgt8p3ktw5ptpf28s",
    "token_id": 2
  }
}
  1. Execute transfer command
$ wasmd tx wasm execute $CONTRACT "$EXECUTE" --from wallet $TXFLAG -y --output json | jq .
{
  "txhash": "5CD670F4EF8A836E8AFDC28E84CFCEC4046174AA63C6899409856EF9C756B500",
  "raw_log": "[]",
  "logs": []
}
  1. Both the tokens should have the same owner now.
$ wasmd query wasm contract-state all $CONTRACT $NODE --output json | jq -r '.models[0].value' | base64 -d | jq .
{
  "owner": "wasm1qka2er800suxsy7y9yz9wqgt8p3ktw5ptpf28s",
  "approvals": null,
  "base_price": [
    {
      "denom": "umlg",
      "amount": "1000"
    }
  ],
  "token_uri": null,
  "token_id": 1
}
$ wasmd query wasm contract-state all $CONTRACT $NODE --output json | jq -r '.models[1].value' | base64 -d | jq .
{
  "owner": "wasm1qka2er800suxsy7y9yz9wqgt8p3ktw5ptpf28s",
  "approvals": null,
  "base_price": [
    {
      "denom": "umlg",
      "amount": "1000"
    }
  ],
  "token_uri": null,
  "token_id": 2
}

Query

A simple query has been implemented in this smart contract which returns the price of the NFT.

  1. Create the query message
$ QUERY='{"asking_price":{"token_id":1}}'
  1. Query the smart contract for the price
$ wasmd query wasm contract-state smart $CONTRACT "$QUERY" $NODE --output json | jq .
{
  "data": {
    "price": {
      "denom": "umlg",
      "amount": "1000"
    }
  }
}

Smart queries are a better option than using the command: wasmd query wasm contract-state all $CONTRACT $NODE --output json | jq -r '.models[0].value' | base64 -d | jq .. This is because .models[0].value might not always refer to the same value after state changes are made during execution.

Dependencies

~4–5.5MB
~122K SLoC