How to deploy and test a src20 contract

  • The contract code is as follows
contract;

use asset::base::{
    _total_assets, 
    _total_supply,
    _name,
    _symbol,
    _decimals
};
use asset::mint::{_mint, _burn};
use src20::SRC20;
use src3::SRC3;
use std::{hash::{Hash, sha256}, string::String, storage::storage_string::*};
storage {
    total_assets: u64 = 0,
    total_supply: StorageMap<AssetId, u64> = StorageMap {},
    name: StorageMap<AssetId, StorageString> = StorageMap {},
    symbol: StorageMap<AssetId, StorageString> = StorageMap {},
    decimals: StorageMap<AssetId, u8> = StorageMap {},
}

impl SRC20 for Contract {
    #[storage(read)]
    fn total_assets() -> u64 {
        _total_assets(storage.total_assets)
    }

    #[storage(read)]
    fn total_supply(asset: AssetId) -> Option<u64> {
        _total_supply(storage.total_supply, asset)
    }

    #[storage(read)]
    fn name(asset: AssetId) -> Option<String> {
        _name(storage.name, asset)
    }

    #[storage(read)]
    fn symbol(asset: AssetId) -> Option<String> {
        _symbol(storage.symbol, asset)
    }

    #[storage(read)]
    fn decimals(asset: AssetId) -> Option<u8> {
        _decimals(storage.decimals, asset)
    }
}

impl SRC3 for Contract {
    #[storage(read, write)]
    fn mint(recipient: Identity, sub_id: SubId, amount: u64) {
        // č®°å½•å‚ę•°äæ”ęÆ
        let _asset_id = _mint(storage.total_assets, storage.total_supply, recipient, sub_id, amount);
       
    }

    #[storage(read, write)]
    fn burn(sub_id: SubId, amount: u64) {
        _burn(storage.total_supply, sub_id, amount);
    }
}
  • The test code is as follows
use fuels::{prelude::*, types::{ContractId,Identity,Bits256}};


// Load abi from json
abigen!(Contract(
    name = "MyContract",
    abi = "out/debug/erc20_token-abi.json"
));

async fn get_contract_instance() -> (MyContract<WalletUnlocked>,MyContract<WalletUnlocked>,ContractId,WalletUnlocked,WalletUnlocked) {
    // Launch a local network and deploy the contract
    let mut wallets = launch_custom_provider_and_get_wallets(
        WalletsConfig::new(
            Some(2),             /* Single wallet */
            Some(1),             /* Single coin (UTXO) */
            Some(1_000_000_000), /* Amount per coin */
        ),
        None,
        None,
    )
    .await
    .unwrap();
    let wallet = wallets.pop().unwrap();
    let wallet1 = wallets.pop().unwrap();
    let id = Contract::load_from(
        "./out/debug/erc20_token.bin",
        LoadConfiguration::default(),
    )
    .unwrap()
    .deploy(&wallet, TxPolicies::default())
    .await
    .unwrap();

    let instance = MyContract::new(id.clone(), wallet.clone());
    let instance1 = MyContract::new(id.clone(), wallet1.clone());

    (instance,instance1, id.into(),wallet.clone(),wallet1.clone())
}



#[tokio::test]
async fn should_check_mint() {
    
    let (instance, instance1, _id, wallet, wallet1) = get_contract_instance().await;

    // Check initial total assets
    let total_assets = instance.methods().total_assets().call().await.unwrap();
    println!("Initial total assets: {:?}", total_assets.value);

    let my_asset = AssetId::default();

    // Check initial name (should be None or a default value)
    let name: fuels::programs::call_response::FuelCallResponse<Option<String>> = instance.methods().name(my_asset).call().await.unwrap();
    println!("Initial name: {:?}", name.value);

    // Define sub_id and recipient
    let sub_id_array = [1u8; 32];
    let sub_id = Bits256(sub_id_array);
    let recipient = Identity::Address(wallet.address().into());
     println!("Minting: recipient = {:?}, sub_id = {:?}, amount = {:?}", recipient, sub_id, 10);
    // Mint new assets
    let mint_result = instance.methods().mint(recipient.clone(), sub_id, 10).call().await;
    match mint_result {
        Ok(response) => {
            println!("Mint successful: {:?}", response);
        }
        Err(e) => {
            println!("Mint failed: {:?}", e);
        }
    }
    // Check total supply after minting
    let total_supply = instance.methods().total_supply(my_asset).call().await.unwrap();
    println!("Total supply after minting: {:?}", total_supply.value);

    // Additional checks and assertions can be added here
    assert_eq!(total_supply.value.unwrap(), 10);

   
}

[dependencies]

asset = { git = ā€œGitHub - FuelLabs/sway-libs: Miscellaneous Sway libraries.ā€, tag = ā€œv0.19.0ā€ }

src20 = { git = ā€œGitHub - FuelLabs/sway-standards: SRC Standards set for the Sway languageā€, tag = ā€œv0.3.3ā€ }

src3 = { git = ā€œGitHub - FuelLabs/sway-standards: SRC Standards set for the Sway languageā€, tag = ā€œv0.3.3ā€ }

  • Error
Mint failed: RevertTransactionError { reason: "failed transfer to address.", revert_id: 18446744073709486081, receipts: [Call { id: 0000000000000000000000000000000000000000000000000000000000000000, to: e928fcd5fce1b52983d628e36a5332d84077fc9b77d1d7448b26401b2ce110ca, amount: 0, asset_id: 0000000000000000000000000000000000000000000000000000000000000000, gas: 3039, param1: 3596839292, param2: 10448, pc: 11712, is: 11712 }, Mint { sub_id: 0101010101010101010101010101010101010101010101010101010101010101, contract_id: e928fcd5fce1b52983d628e36a5332d84077fc9b77d1d7448b26401b2ce110ca, val: 10, pc: 18332, is: 11712 }, Revert { id: e928fcd5fce1b52983d628e36a5332d84077fc9b77d1d7448b26401b2ce110ca, ra: 18446744073709486081, pc: 18620, is: 11712 }, ScriptResult { result: Revert, gas_used: 3076 }] }

Hey @lopo1, I was able to deploy your contract locally. You can find the repo here. Also, you can refer to the example src20 contracts here, if you have any doubt. Lmk if you have any more queries :slight_smile:

Deployment, there is no problem on my side, but there is a problem when the test calls mint.

Perform an operation

// Define sub_id and recipient
    let sub_id_array = [1u8; 32];
    let sub_id = Bits256(sub_id_array);
    let recipient = Identity::Address(wallet.address().into());
     println!("Minting: recipient = {:?}, sub_id = {:?}, amount = {:?}", recipient, sub_id, 10);
    // Mint new assets
    let mint_result = instance.methods().mint(recipient.clone(), sub_id, 10).call().await;
    match mint_result {
        Ok(response) => {
            println!("Mint successful: {:?}", response);
        }
        Err(e) => {
            println!("Mint failed: {:?}", e);
        }
    }

Hey @lopo1, I am not able to deploy your contract anymore. you can see the error here.

I get same error, how to fix it, sir?

@duyenhtm93 are you having a problem with running the test itself or with the build as well?

No, the tests were successful, Iā€™m not sure but maybe the problem is in the contract code, Thank you!

1 Like