Skip to main content

Integrate Stellar Assets Contracts

When interacting with assets in a smart contract, the Stellar Asset Contract is not different from any other token that implements the Stellar SEP-41 Token Interface.

Contract Code

The Rust SDK contains a pre-generated client for any contract that implements the token interface:

use soroban_sdk::{contract, contractimpl}
use soroban_sdk::token;

#[contract]
pub struct MyContract;

#[contractimpl]
impl MyContract {
pub fn token_fn(e: Env, id: Address) {
// Create a client instance for the provided token identifier. If the id
// value corresponds to an SAC contract, then SAC implementation is used.
let client = token::TokenClient::new(&env, &id);
// Call token functions part of the Stellar SEP-41 token interface
client.transfer(...);
}
}

The asset parameter is not the address of the issuer of an asset, it corresponds to the deployed contract address for this asset.

stellar contract id asset \
--source G... \
--network testnet \
--asset [asset:issuer]

E.g. for USDC, it would be --asset USDC:G... For the native asset, XLM, --asset native. See the deploy SAC guide for more details.

Clients

A client created by token::TokenClient implements the functions defined by any contract that implements the SEP-41 Token Interface. But with CAP-46-6 smart contract standardized asset, the Stellar Asset Contract exposes additional functions such as mint. To access the additional functions, another client needs to be used: token::StellarAssetClient. This client only implements the functions from CAP-46-6, which are not part of the SEP-41 interface.

let client = token::StellarAssetClient::new(&env, &id);
// Call token functions which are not part of the SEP-41 token interface
// but part of the CAP-46-6 Smart Contract Standardized Asset
client.mint(...);

Testing

Soroban Rust SDK provides an easy way to instantiate a Stellar Asset Contract tokens using register_stellar_asset_contract_v2. This function can be seen as the deployment of a generic token. It also allows you to manipulate flags on the issuer account like AUTH_REVOCABLE and AUTH_REQUIRED. In the following example, we are following the best practices outlined in the Issuing and Distribution Accounts section:

#![cfg(test)]

use soroban_sdk::testutils::Address as _;
use soroban_sdk::{token, Address, Env};
use token::{StellarAssetClient, TokenClient};

#[test]
fn test() {
let e = Env::default();
e.mock_all_auths();

let issuer = Address::generate(&e);
let distributor = Address::generate(&e);

let sac = e.register_stellar_asset_contract_v2(issuer.clone());
let token_address = sac.address();

// client for SEP-41 functions
let token = TokenClient::new(&e, &token_address);
// client for Stellar Asset Contract functions
let token_sac = StellarAssetClient::new(&e, &token_address);

// note that you need to account for the difference between the minimal
// unit and the unit itself when working with amounts.
// E.g. to mint 1 TOKEN, we need to use 1*1e7 in the mint function.
let genesis_amount: i128 = 1_000_000_000 * 10_000_000;

token_sac.mint(&distributor, &genesis_amount);

assert_eq!(token.balance(&distributor), genesis_amount);

// Make issuer AuthRequired and AuthRevocable
sac.issuer().set_flag(IssuerFlags::RevocableFlag);
sac.issuer().set_flag(IssuerFlags::RequiredFlag);
}

Examples

See the full examples that utilize the token contract in various ways for more details: