Skip to main content

Bonus: Token Assets

In this optional lesson, we will move some USDC on Solana. This introduces the asset concept in more depth. You can skip this lesson and go to the next chapter if you are more interested in security and the access policy.

Set up USDC

By default, Treasury is loaded with the supported chains and their corresponding native assets. You can list all of each with the commands chains and assets, or specifically look at Solana:

chain SOL
Output
native chain SOL { confirmations = 4, priority = "market" }
assets for SOL
Output
native asset SOL for SOL { decimals = 9 }

Note that we use the symbol of the native asset also as symbol for the underlying chain. This is currently not configurable.

Token assets exists on most chains, and are identified by the address of a smart contract. To use these, they need to be registered:

USDC = create token asset 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU for SOL { decimals = 6 }
Output
token asset 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU for SOL { version = 1, decimals = 6 }

This command requires the token address as an argument, but also the number of decimals of the asset.

You can list all token assets registered so far:

assets | variant = "token"
Output
token asset 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU for SOL { version = 1, decimals = 6 }

Load Funds

Similar to the earlier lesson, you can visit https://faucet.circle.com in your browser, select "Solana Devnet" as network, enter the the "funded" Solana address, and receive 10 USDC shortly after. Confirm this by visiting https://explorer.solana.com/?cluster=devnet for the address and inspecting the "Tokens" tab.

Move USDC

After the above setup, moving USDC works the same way as moving SOL:

create transfer { from = $funded, to = $unfunded, asset = $USDC, amount = "1" }
Output
Permission Denied: 7: transfer policy did not pass

However, our transfer rule from the previous lesson only allowed moving SOL, but not tokens such as USDC.

Run:

transfer policy
Output
allow transfer-rule 8 { version = 1, asset = "chains/SOL/assets/SOL", from = "chains/SOL/addresses/CirHy2hKuR3Yajy5K6qXo4xEkobftHCFNJKQswswdc1A", to = "chains/SOL/addresses/Vihj2YEBxvbAJM6iSxDAGC1ANESmGsiStdk8cJquJfW" }

to find the name of the transfer rule, and modify it to allow any asset:

update transfer rule 8 { asset = "any/asset" }
Output
allow transfer-rule 8 { version = 2, asset = "any/asset", from = "chains/SOL/addresses/479NqsSroFxiEw85P8pt7nxW3M9G7xdnJ9dWA2jUSz7o", to = "chains/SOL/addresses/689Zowcs7z4EQYRAdEhqcL2tmedoabAQg1eWqsV3kDA1" }

Now, the transfer is allowed and will work:

create transfer { from = $funded, to = $unfunded, asset = $USDC, amount = "1" }
Output
preparing transfer 12 { version = 1, from = "chains/SOL/addresses/CirHy2hKuR3Yajy5K6qXo4xEkobftHCFNJKQswswdc1A", to = "chains/SOL/addresses/Vihj2YEBxvbAJM6iSxDAGC1ANESmGsiStdk8cJquJfW", asset = "chains/SOL/assets/4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU", amount = "1", symbol = "chains/SOL/symbols/USDC", last_transaction = "transfers/12/transactions/13" }