This site requires Javascript to be enabled.

Sending Tokens

This section shows a smart contract designed to send a blockchain's native tokens to a recipient specified by the original sender in the execute message.

Explanation

send_tokens

Once the main use case of this function is executed (which in this context is void), a bank message is appended for the contract to act upon. It's worth noting that the contract becomes the signer of the transaction, not the initiating sender.

// contract.rspub fn send_tokens(    _deps: DepsMut,    amount: Uint128,    denom: String,    to: Addr) -> Result<Response, ContractError> {        /* Sending tokens is managed via the response of this function.       A developer crafts a BankMsg to transmit tokens to a specified address using the native token.       The function will fail if the smart contract lacks sufficient tokens.       If any error surfaces prior to the response's generation, funds won't be transmitted. */        Ok(Response::new()        .add_attribute("action", "send")        .add_message(BankMsg::Send {            to_address: to.into_string(),            amount: vec![Coin{denom, amount}]        })    )}

Integration Testing

// integration_tests.rsfn balance() {    let (mut app, cw_template_contract) = proper_instantiate();    let msg = ExecuteMsg::SendTokens {        amount: Uint128::new(10),        denom: "token".to_string(),        to: Addr::unchecked("receiver")    };    let funds_sent = Coin::new(10u128, "token".to_string());    let cosmos_msg = cw_template_contract.call(msg, funds_sent).unwrap();    app.execute(Addr::unchecked(USER), cosmos_msg).unwrap();    let balance = app.wrap().query_balance("receiver", "token").unwrap();    assert_eq!(balance.amount, Uint128::new(10));    assert_eq!(balance.denom, "token");}

Example

To send tokens with CosmWasm, you can create the following files: lib.rs contract.rs msg.rs error.rs state.rs helpers.rs integration_tests.rs

lib.rs

pub mod contract;mod error;pub mod msg;pub mod state;pub mod integration_tests;pub mod helpers;pub use crate::error::ContractError;

contract.rs

#[cfg(not(feature = "library"))]use cosmwasm_std::entry_point;use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult};use crate::error::ContractError;use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};#[cfg_attr(not(feature = "library"), entry_point)]pub fn instantiate(    _deps: DepsMut,    _env: Env,    _info: MessageInfo,    _msg: InstantiateMsg,) -> Result<Response, ContractError> {    Ok(Response::new()        .add_attribute("method", "instantiate"))}#[cfg_attr(not(feature = "library"), entry_point)]pub fn execute(    deps: DepsMut,    _env: Env,    _info: MessageInfo,    msg: ExecuteMsg,) -> Result<Response, ContractError> {    match msg {        ExecuteMsg::SendTokens {amount, denom, to} => execute::send_tokens(deps, amount, denom, to),    }}pub mod execute {    use cosmwasm_std::{Uint128, Addr, BankMsg, Coin};    use super::*;    pub fn send_tokens(_deps: DepsMut, amount: Uint128, denom: String, to: Addr) -> Result<Response, ContractError> {        Ok(Response::new().add_attribute("action", "increment")        /* Sending tokens is part of the response of a function        Developer creates a BankMsg to send tokens to an address with a specific native token        Will fail if smart contract does not have this much tokens initially  */        .add_message(BankMsg::Send { to_address: to.into_string(), amount: vec![Coin{denom, amount}] }))    }   }#[cfg_attr(not(feature = "library"), entry_point)]pub fn query(_deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {    match msg {    }}pub mod query {   }#[cfg(test)]mod tests {}

msg.rs

use cosmwasm_schema::{cw_serde, QueryResponses};use cosmwasm_std::{Uint128, Addr, Coin};#[cw_serde]pub struct InstantiateMsg {}#[cw_serde]pub enum ExecuteMsg {    SendTokens {amount: Uint128, denom: String, to: Addr}}#[cw_serde]#[derive(QueryResponses)]pub enum QueryMsg {}#[cw_serde]pub struct BalanceResponse {    pub amount: Coin,}

error.rs

use cosmwasm_std::StdError;use thiserror::Error;#[derive(Error, Debug)]pub enum ContractError {    #[error("{0}")]    Std(#[from] StdError),    #[error("Unauthorized")]    Unauthorized {},    // Add any other custom errors you like here.    // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details.}

state.rs

use schemars::JsonSchema;use serde::{Deserialize, Serialize};use cosmwasm_std::Addr;use cw_storage_plus::Item;#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]pub struct State {    pub count: i32,    pub owner: Addr,}pub const STATE: Item<State> = Item::new("state");

helpers.rs

use schemars::JsonSchema;use serde::{Deserialize, Serialize};use cosmwasm_std::{    to_binary, Addr, CosmosMsg, StdResult, WasmMsg, Coin,};use crate::msg::{ExecuteMsg};/// CwTemplateContract is a wrapper around Addr that provides a lot of helpers/// for working with this.#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]pub struct CwTemplateContract(pub Addr);impl CwTemplateContract {    pub fn addr(&self) -> Addr {        self.0.clone()    }    pub fn call<T: Into<ExecuteMsg>>(&self, msg: T, funds: Coin) -> StdResult<CosmosMsg> {        let msg = to_binary(&msg.into())?;        Ok(WasmMsg::Execute {            contract_addr: self.addr().into(),            msg,            funds: vec![funds],        }        .into())    }   }

integration_tests.rs

#[cfg(test)]mod tests {    use crate::helpers::CwTemplateContract;    use crate::msg::InstantiateMsg;    use cosmwasm_std::{Addr, Coin, Empty, Uint128};    use cw_multi_test::{App, AppBuilder, Contract, ContractWrapper, Executor};    pub fn contract_template() -> Box<dyn Contract<Empty>> {        let contract = ContractWrapper::new(            crate::contract::execute,            crate::contract::instantiate,            crate::contract::query,        );        Box::new(contract)    }    const USER: &str = "USER";    const ADMIN: &str = "ADMIN";    const NATIVE_DENOM: &str = "token";    fn mock_app() -> App {        AppBuilder::new().build(|router, _, storage| {            router                .bank                .init_balance(                    storage,                    &Addr::unchecked(USER),                    vec![Coin {                        denom: NATIVE_DENOM.to_string(),                        amount: Uint128::new(10),                    }],                )                .unwrap();        })    }    fn proper_instantiate() -> (App, CwTemplateContract) {        let mut app = mock_app();        let cw_template_id = app.store_code(contract_template());        let msg = InstantiateMsg {};        let cw_template_contract_addr = app            .instantiate_contract(                cw_template_id,                Addr::unchecked(ADMIN),                &msg,                &[],                "test",                None,            )            .unwrap();        let cw_template_contract = CwTemplateContract(cw_template_contract_addr);        (app, cw_template_contract)    }    mod count {        use super::*;        use crate::msg::ExecuteMsg;        #[test]        fn balance() {            let (mut app, cw_template_contract) = proper_instantiate();            let msg = ExecuteMsg::SendTokens { amount: Uint128::new(10), denom: "token".to_string(), to: Addr::unchecked("receiver") } ;            let funds_sent = Coin::new(10u128, "token".to_string());            let cosmos_msg = cw_template_contract.call(msg, funds_sent).unwrap();            app.execute(Addr::unchecked(USER), cosmos_msg).unwrap();             let balance = app.wrap().query_balance("receiver", "token").unwrap();            assert_eq!(balance.amount, Uint128::new(10));            assert_eq!(balance.denom, "token");                    }    }}

Credits: CosmWasm by example. You can check the code on Github or open it with VS code.

Drop Camp is here!

Join the queue and be one of the first to get in!.

Go Camping ↗