XION
DiscordGithub
  • Welcome to XION
  • XION'S Core
    • Concepts
      • Generalized Chain Abstraction
      • Intro to Account Abstraction
      • XION's Meta Accounts
      • Meta Accounts Design
      • Architecture & Tech Glossary
      • Use Cases
  • developers
    • Xion Quick Start
      • Zero to Dapp in 5 Minutes
        • Launch a User Map Dapp on XION in 5 Minutes
        • React Native Mobile Dapp on XION in 5 Minutes
    • Mobile dapp Development on XION
      • Set up your XION Mobile Development Environment
      • Create Mobile Dapp and Integrate Meta Account Authentication
    • Getting Started (Advanced)
      • Set Up Local Environment
        • Setting up your Local Smart Contract Development Environment for XION
          • Setting up your XION Smart Contract Development Environment on Windows (WSL2 + Ubuntu)
        • Set Up an Integrated Development Environment (IDE)
        • Interacting with Xion Chain using Xion Daemon
      • Your First Contract
        • Deploying Your First Smart Contract on XION
      • Gasless UX & Permission Grants
        • Enabling Gasless Transactions with Treasury Contracts
      • Dapp Development
        • Account Abstraction with Gasless Transactions
        • Interact with XION via your Backend Service
    • Re-using Existing Contracts
      • Deployed Contracts on XION
      • Non-Fungible Tokens (NFTs)
      • Fungible Tokens
      • Marketplace
      • Multisig
      • Proxy Contracts
      • Membership Management
      • Governance
      • Valuts
      • SCV Audited Contracts
    • Web3 for Web2 Developers
      • Web2 vs Web3 App Architecture: A Comparison
      • Misconceptions and Misapplied Web2 Patterns
      • Recommended Architecture for Dapps on XION
    • Building for Mainnet
      • Xion Testnet: Your Development Playground
      • Building with Audited & Battle-Tested Contracts
      • Community Engagement: Building Support for Your dApp
      • Deploying to Xion Mainnet
        • Smart Contract Audits: Process, Costs & Support
        • Governance Process to Deploying Smart Contracts to Mainnet
    • Learn & Build
      • Token Factory
        • Creating, Minting, and Interacting with a Token Factory Token
        • Building a React dApp to Interact with Token Factory Tokens
        • Integrating a Token Factory Token in a Smart Contract
      • Websockets
        • WebSockets with Xion: Real-Time Communication
      • Mobile Development
        • Building a React Native Mobile App with Abstraxion (Xion.js)
      • Oracles
        • Creating a Smart Contract with Pyth Oracle Integration
      • Indexers: Optimized Data Retrieval
        • SubQuery
      • Use Cases
        • Building a Per-User Data Storage Dapp
    • Reference and Resources
      • Requesting XION Testnet Tokens
      • Public Endpoints & Resources
      • Block Explorers
      • Governance
        • Deploying Smart Contracts to Mainnet
      • Developer Tools: Abstract
      • IBC Denoms on XION Networks
      • Frequently Asked Questions
      • XION Token Contract Addresses on EVM Chains
  • Nodes & Validators
    • Run a Node
      • System Specifications
      • Build the Xion Daemon
      • Download the Xion Daemon
      • Configure the Xion Daemon
        • app.toml
        • client.toml
        • config.toml
      • Join the XION Network
        • xion-testnet-1
      • Confirm node is running
    • Become a Validator
      • Initial Setup
      • Obtain a XION Wallet Address
      • Obtain Funds
        • Testnet
      • Create Validator
    • IBC Relayers and Tokens
  • Others
    • Resources
Powered by GitBook
On this page
  • Prerequisites
  • Creating the Smart Contract
  • Initialize a New Contract
  • Implementing Core Logic
  • Optimize Contract
  • Upload Optimized Contract On-chain
  • Retrieve the Code ID
  • Instantiate the Contract
  • Retrieve the Contract Address
  • Querying the Contract
  • Execute Transactions

Was this helpful?

Edit on GitHub
  1. developers
  2. Learn & Build
  3. Token Factory

Integrating a Token Factory Token in a Smart Contract

PreviousBuilding a React dApp to Interact with Token Factory TokensNextWebsockets

Last updated 27 days ago

Was this helpful?

This guide will walk you through integrating a Token Factory token into a smart contract. The contract will allow users to:

  • Retrieve their Token Factory token balance.

  • Send Token Factory tokens to another user.

Prerequisites

Before deploying your smart contract on-chain, ensure you have completed the following setup steps:

  • Set up your local environment: Follow the to configure your development environment.

  • Install the XION daemon: Set up the XION CLI by following the to interact with the blockchain.

  • Make sure you have installed and running, as it is required to compile your contract.

Creating the Smart Contract

Initialize a New Contract

We are going to use the contract as a base for our contract. Run the following command to create a new contract:

cargo generate --git https://github.com/burnt-labs/cw-counter --name token_factory_contract
cd token-factory-contract

Implementing Core Logic

Remove some files

We need to remove the src/helpers.rs and src/integration_test.rs files. Then update the lib.rs file with the following:

pub mod contract;
mod error;
pub mod msg;
pub mod state;

pub use crate::error::ContractError;

Store the Token Address in Contract State

Modify state.rs to store the token denomination in the contract’s persistent storage.

use cw_storage_plus::Item;

pub const TOKEN_DENOM: Item<String> = Item::new("token_denom");

Define Contract Messages

Delete the contents of src/msg.rs and replace with the following:

use cosmwasm_schema::{cw_serde, QueryResponses};

#[cw_serde]
pub struct InstantiateMsg {
    pub token_denom: String, // Token denom (e.g., factory/xion.../mytoken)
}

#[cw_serde]
pub enum ExecuteMsg {
    SendToken {
        recipient: String,
    },
}

#[cw_serde]
#[derive(QueryResponses)]
pub enum QueryMsg {
    #[returns(BalanceResponse)]
    GetBalance { address: String },
}

#[cw_serde]
pub struct BalanceResponse {
    pub balance: String,
}

Contract Logic

Modify src/contract.rs to implement the core logic of the contract:

use cosmwasm_std::{
    entry_point, to_binary, Addr, Binary, Coin, Deps, DepsMut, Env, MessageInfo, QueryRequest,
    Response, StdResult, BankMsg, BankQuery, Uint128,
};
use crate::msg::{BalanceResponse, ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::state::TOKEN_DENOM;

#[entry_point]
pub fn instantiate(
    deps: DepsMut,
    _env: Env,
    _info: MessageInfo,
    msg: InstantiateMsg,
) -> StdResult<Response> {
    TOKEN_DENOM.save(deps.storage, &msg.token_denom)?;

    Ok(Response::new()
        .add_attribute("method", "instantiate")
        .add_attribute("token_denom", msg.token_denom))
}

#[entry_point]
pub fn execute(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> StdResult<Response> {
    match msg {
        ExecuteMsg::SendToken { recipient } => execute_send_token(deps, info, recipient),
    }
}

fn execute_send_token(
    deps: DepsMut,
    info: MessageInfo,
    recipient: String,
) -> StdResult<Response> {
    if info.funds.len() != 1 {
        return Err(cosmwasm_std::StdError::generic_err(
            "Must send exactly one token type",
        ));
    }

    // Extract the token denomination and amount
    let sent_coin = &info.funds[0];
    let amount = sent_coin.amount;
    let token_denom = sent_coin.denom.clone();

    let transfer_msg = BankMsg::Send {
        to_address: recipient.clone(),
        amount: vec![Coin {
            denom: token_denom.clone(),
            amount,
        }],
    };

    Ok(Response::new()
        .add_message(transfer_msg)
        .add_attribute("action", "send_token")
        .add_attribute("sender", info.sender.to_string())
        .add_attribute("recipient", recipient)
        .add_attribute("amount", amount.to_string())
        .add_attribute("token_denom", token_denom))
}

#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
    match msg {
        QueryMsg::GetBalance { address } => to_binary(&query_balance(deps, address)?),
    }
}

fn query_balance(deps: Deps, address: String) -> StdResult<BalanceResponse> {
    let token_denom = TOKEN_DENOM.load(deps.storage)?;

    let balance_response: cosmwasm_std::BalanceResponse = deps.querier.query(&QueryRequest::Bank(
        BankQuery::Balance {
            address,
            denom: token_denom.clone(),
        },
    ))?;

    Ok(BalanceResponse {
        balance: balance_response.amount.amount.to_string(),
    })
}

Optimize Contract

docker run --rm -v "$(pwd)":/code \
  --mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \
  --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
  cosmwasm/optimizer:0.16.0

Upload Optimized Contract On-chain

First, set your wallet address or key name by executing the following in your terminal:

WALLET="your-wallet-address-or-key-name-here"

Now, upload the contract to the blockchain:

RES=$(xiond tx wasm store ./artifacts/cw_counter.wasm \
      --chain-id xion-testnet-2 \
      --gas-adjustment 1.3 \
      --gas-prices 0.001uxion \
      --gas auto \
      -y --output json \
      --node https://rpc.xion-testnet-2.burnt.com:443 \
      --from $WALLET)

After running the command, extract the transaction hash by executing:

echo $RES

Example output:

{
  "height": "0",
  "txhash": "B557242F3BBF2E68D228EBF6A792C3C617C8C8C984440405A578FBBB8A385035",
  ...
}

Copy the txhash value for the next step.

Retrieve the Code ID

The Code ID is required for creating an instance of your contract.

Set your transaction hash you retrieved above by executing:

TXHASH="your-txhash-here"

Query the blockchain to get the Code ID:

CODE_ID=$(xiond query tx $TXHASH \
  --node https://rpc.xion-testnet-2.burnt.com:443 \
  --output json | jq -r '.events[-1].attributes[1].value')

Now, display the retrieved Code ID:

echo $CODE_ID

Example output:

1213

Instantiate the Contract

To initialize the contract, set up the initialization message:

MSG='{ "token_denom": "factory/xion1xyz.../denom-name" }'

Replace "factory/xion1xyz.../denom-name" with the actual token factory token you created.

Now, instantiate the contract using the CODE_ID obtained from the previous step:

xiond tx wasm instantiate $CODE_ID "$MSG" \
  --from $WALLET \
  --label "token-factory-token" \
  --gas-prices 0.025uxion \
  --gas auto \
  --gas-adjustment 1.3 \
  -y --no-admin \
  --chain-id xion-testnet-2 \
  --node https://rpc.xion-testnet-2.burnt.com:443

Example output:

gas estimate: 217976
code: 0
txhash: 09D48FE11BE8D8BD4FCE11D236D80D180E7ED7707186B1659F5BADC4EC116F30

Copy the new transaction hash for the next step.

Retrieve the Contract Address

Once the contract has been instantiated, it is assigned a unique contract address. Follow these steps to retrieve it:

Set the transaction hash (TXHASH) from the contract instantiation step:

TXHASH="your-txhash-here"

Replace "your-txhash-here" with the actual transaction hash from the previous step.

Query the blockchain to get the contract address:

CONTRACT=$(xiond query tx $TXHASH \
  --node https://rpc.xion-testnet-2.burnt.com:443 \
  --output json | jq -r '.events[] | select(.type == "instantiate") | .attributes[] | select(.key == "_contract_address") | .value')

Display the contract address:

echo $CONTRACT

Example output:

xion1v6476wrjmw8fhsh20rl4h6jadeh5sdvlhrt8jyk2szrl3pdj4musyxj6gl

Querying the Contract

After deploying the contract, you can interact with it using queries.

The contract exposes a method to fetch the balance of a specific address. Define the query message in the following format:

QUERY='{"get_balance":{ "address": "add-address-here" }}'

Replace "add-address-here" with the actual address you want to check.

Run the following command to query the contract for the specified address's token balance:

xiond query wasm contract-state smart $CONTRACT "$QUERY" --output json --node https://rpc.xion-testnet-2.burnt.com:443

Where:

  • $CONTRACT is the smart contract address retrieved in the previous step.

  • $QUERY contains the request payload.

If the query is successful, you will receive a JSON response containing the balance of the specified address. Example output:

{
  "data": {
    "balance": "1000000"
  }
}

This indicates that the queried address holds 1,000,000 units of the token.

Execute Transactions

Now that you have an instance of the contract, you can interact with it by executing transactions. The contract accepts a message that allows token transfers from the sender to a specified recipient.

Define the message as follows:

SEND_TOKEN='{"send_token": { "recipient": "recipient-address-here" }}'

Replace "recipient-address-here" with the actual wallet address you want to send tokens to.

Run the following command to execute the token transfer:

xiond tx wasm execute $CONTRACT "$SEND_TOKEN" \
  --from $WALLET \
  --gas-prices 0.025uxion \
  --gas auto \
  --gas-adjustment 1.5 \
  --amount "10factory/xion1ka5gdcv4m7kfzxkllapqdflenwe0fv8ftm357r/emp" \
  -y \
  --node https://rpc.xion-testnet-2.burnt.com:443 \
  --chain-id xion-testnet-2

Where:

  • $CONTRACT: The deployed contract address.

  • $SEND_TOKEN: The execution message to send tokens.

  • --amount "10factory/xion1ka5gdcv4m7kfzxkllapqdflenwe0fv8ftm357r/emp": Specifies the token amount and denom being sent.

After executing this transaction, you can run the get_balance query again to verify that the amount was deducted from your account and also check the recipient's address to see if they received the tokens.

Next, compile and optimize the smart contract using the . You need to have Docker running to execute the command below. Make sure you are in the root folder of your project and then executes the following:

installation and setup guide
installation instructions
Docker
cw-counter
CosmWasm Optimizing Compiler