- Docs
- Developers
- Sending Tokens
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.