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.