terra-asset

Helpers for interacting with Terra assets, including native coins and CW20 tokens

Usage

This crate contains two struct types:

Instances of AssetInfo and Asset can be created as follows:

```rust use terra_asset::{AssetInfo, Asset};

// native coin let coin_info = AssetInfo::native("uusd");

let coin = Asset::new(coin_info, 69420 as u128); // or let coin = Asset::native("uusd", 69420 as u128);

// CW20 token let tokenaddr = deps.api.addrvalidate("mocktoken")?; let tokeninfo = AssetInfo::cw20(token_addr.clone());

let token = Asset::new(tokeninfo, 12345 as u128); // or let token = Asset::cw20(tokenaddr, 12345 as u128); ```

Checked and unchecked types

AssetInfo and Asset contain contract addresses of cosmwasm_std::Addr type. Additionally, they each comes with an "unchecked" counterpart where the addresses are in String type. Both the unchecked and chekced types can be serialized to / deserialized from JSON format. The checked type is intended to be saved in contract storage, while the unchecked type is intended to be passed between contracts in messages.

The following code snippets show common usage of the AssetInfo type. However, the same methods are also implemented for Asset type.

Save the checked type in storage

```rust use cwstorageplus::Item;

const TOKENINFO: Item = Item::new("tokeninfo");

let tokeninfo = AssetInfo::cw20(tokenaddr); TOKENINFO.save(deps.storage, &tokeninfo)?; ```

Using the unchecked type in messages

```rust use terra_asset::AssetInfoUnchecked;

pub struct InstantiateMsg { token_info: AssetInfoUnchecked, } ```

Conversions between checked and unchecked types

```rust // cast checked to unchecked type let tokeninfounchecked: AssetInfoUnchecked = token_info.into();

// cast unchecked to checked type let tokeninfo = tokeninfo_unchecked.check(deps.api)?; ```

Tax handling

Stability fee (a.k.a. "tax") is a fees charged on Terra stablecoin transfers and considered by many developers to be tricky to work with.

Tax works as follows. Suppose Alice sends Bob 100 UST when the tax rate is 0.1%. The tax amount is 100 * 0.1% = 0.1 UST. After the transfer is executed, Bob's balance increases by 100 UST, while Alice's balance is deducted by 100.1 UST.

Note that tax is paid by whoever sends the BankMsg::Send message, not the transaction's initiator. If Alice holds some funds in a smart contract, and invokes a functions on the contract to send 100 UST. The resulting 0.1 UST tax is deducted from the contract's balance, not Alice's.

An implication of this is that if the contract only has 100 UST balance, it is impossible for it to send all 100 UST out, because it needs to reserve some funds to pay tax. In fact, at 0.1% tax rate, the maximum amount the contract can send is 99900099uusd, with 99900uusd needed for tax. After this transfer, the contract will have exactly 1uusd left, which cannot be transferred out.

The Asset type implements two helper functions for handling taxes:

deduct_tax

Calculates the deliverable amount (tax deducted) when sending an asset:

rust let coin = Asset::native("uusd", 100000000 as u128); let coin_after_tax = coin.deduct_tax(&deps.querier)?; // at 0.1% tax rate, `coin_after_tax.amount` should be 99900099

add_tax

Calculates the total cost (including tax) for sending an asset:

rust let coin = Asset::native("uusd", 99900099 as u128); let coin_with_tax = coin.add_tax(&deps.querier)?; // at 0.1% tax rate, `coin_with_tax.amount` should be 99999999

Message generation

The Asset type also comes with helper functions for generating messages:

transfer_msg

The following example creates a message for transferring 100 UST to Bob. Note that we first deduct tax before generating the message:

rust let coin = Asset::native("uusd", 100000000 as u128); let msg = coin.deduct_tax(&deps.querier)?.transfer_msg("bob_address")?; let res = Response::new().add_message(msg);

transfer_from_msg

The following example creates a message that draws 100 MIR tokens from Alice's wallet to Bob's. Note that:

rust let token = Asset::cw20(deps.api.addr_validate("mock_token")?, 100000000 as u128); let msg = token.transfer_from_msg("alice", "bob")?; let res = Response::new().add_message(msg);

Stringify

The std::fmt::Display trait is implemented for AssetInfo and Asset, so you can easily invoke to_string method to generate a string representation of the asset. This may be useful when creating logging outputs:

rust let res = Response::new() .add_message(token.transfer_msg("alice")?) .add_attribute("asset_sent", token.to_string());

The string representation of the asset is label:amount where label is the denom for native coins, or the contract address for CW20 tokens.

License

Contents of this repository are open source under MIT License.