stellar-interchain-token-service 0.2.0

Contract related to Interchain Token Service.
Documentation
mod utils;

use soroban_sdk::testutils::Address as _;
use soroban_sdk::{Address, BytesN, Env, IntoVal};
use soroban_token_sdk::metadata::TokenMetadata;
use stellar_axelar_std::address::AddressExt;
use stellar_axelar_std::{assert_auth, assert_auth_err, assert_contract_err, events};
use stellar_interchain_token::InterchainTokenClient;
use stellar_interchain_token_service::error::ContractError;
use stellar_interchain_token_service::event::InterchainTokenDeployedEvent;
use stellar_interchain_token_service::types::TokenManagerType;
use utils::{setup_env, TokenMetadataExt};

fn dummy_token_params(env: &Env) -> (Address, BytesN<32>, TokenMetadata) {
    let sender = Address::generate(env);
    let salt = BytesN::<32>::from_array(env, &[1; 32]);
    let token_metadata = TokenMetadata::new(env, "Test", "TEST", 6);

    (sender, salt, token_metadata)
}

#[test]
fn deploy_interchain_token_succeeds() {
    let (env, client, _, _, _) = setup_env();

    let (sender, salt, token_metadata) = dummy_token_params(&env);
    let minter: Option<Address> = None;
    let initial_supply = 100;

    assert_auth!(
        &sender,
        client.deploy_interchain_token(&sender, &salt, &token_metadata, &initial_supply, &minter,)
    );

    goldie::assert!(events::fmt_emitted_event_at_idx::<
        InterchainTokenDeployedEvent,
    >(&env, -2));
}

#[test]
fn deploy_interchain_token_fails_when_paused() {
    let (env, client, _, _, _) = setup_env();

    client.mock_all_auths().set_pause_status(&true);

    assert_contract_err!(
        client.try_deploy_interchain_token(
            &Address::generate(&env),
            &BytesN::from_array(&env, &[1; 32]),
            &TokenMetadata::new(&env, "Test", "TEST", 6),
            &1,
            &None
        ),
        ContractError::ContractPaused
    );
}

#[test]
fn deploy_interchain_token_with_initial_supply_no_minter() {
    let (env, client, _, _, _) = setup_env();

    let (sender, salt, token_metadata) = dummy_token_params(&env);
    let minter: Option<Address> = None;
    let initial_supply = 100;

    let token_id = assert_auth!(
        &sender,
        client.deploy_interchain_token(&sender, &salt, &token_metadata, &initial_supply, &minter,)
    );

    goldie::assert!(events::fmt_emitted_event_at_idx::<
        InterchainTokenDeployedEvent,
    >(&env, -2));

    let token_address = client.token_address(&token_id);
    let token = InterchainTokenClient::new(&env, &token_address);

    assert_eq!(token.owner(), client.address);
    assert!(token.is_minter(&client.address));
    assert!(!token.is_minter(&sender));
    assert_eq!(token.balance(&sender), initial_supply);
}

#[test]
fn deploy_interchain_token_with_initial_supply_valid_minter() {
    let (env, client, _, _, _) = setup_env();

    let (sender, salt, token_metadata) = dummy_token_params(&env);
    let minter = Address::generate(&env);
    let initial_supply = 100;

    let token_id = assert_auth!(
        &sender,
        client.deploy_interchain_token(
            &sender,
            &salt,
            &token_metadata,
            &initial_supply,
            &Some(minter.clone()),
        )
    );

    goldie::assert!(events::fmt_emitted_event_at_idx::<
        InterchainTokenDeployedEvent,
    >(&env, -4));

    let token_address = client.token_address(&token_id);
    let token = InterchainTokenClient::new(&env, &token_address);

    assert_eq!(token.owner(), client.address);
    assert!(!token.is_minter(&client.address));
    assert!(token.is_minter(&minter));
    assert_eq!(token.balance(&sender), initial_supply);
}

#[test]
fn deploy_interchain_token_check_token_id_and_token_manager_type() {
    let (env, client, _, _, _) = setup_env();

    let (sender, salt, token_metadata) = dummy_token_params(&env);
    let minter = Address::generate(&env);
    let initial_supply = 100;

    let deploy_salt = client.interchain_token_deploy_salt(&sender, &salt);
    let expected_token_id = client.interchain_token_id(&Address::zero(&env), &deploy_salt);

    let token_id = assert_auth!(
        &sender,
        client.deploy_interchain_token(
            &sender,
            &salt,
            &token_metadata,
            &initial_supply,
            &Some(minter.clone()),
        )
    );

    goldie::assert!(events::fmt_emitted_event_at_idx::<
        InterchainTokenDeployedEvent,
    >(&env, -4));

    assert_eq!(token_id, expected_token_id);
    assert_eq!(
        client.token_manager_type(&token_id),
        TokenManagerType::NativeInterchainToken
    );
}

#[test]
fn deploy_interchain_token_zero_initial_supply_and_valid_minter() {
    let (env, client, _, _, _) = setup_env();

    let (sender, salt, token_metadata) = dummy_token_params(&env);
    let minter = Address::generate(&env);
    let initial_supply = 0;

    let token_id = assert_auth!(
        &sender,
        client.deploy_interchain_token(
            &sender,
            &salt,
            &token_metadata,
            &initial_supply,
            &Some(minter.clone()),
        )
    );

    goldie::assert!(events::fmt_last_emitted_event::<InterchainTokenDeployedEvent>(&env));

    let token_address = client.token_address(&token_id);
    let token = InterchainTokenClient::new(&env, &token_address);

    assert_eq!(token.owner(), client.address);
    assert!(token.is_minter(&client.address));
    assert!(!token.is_minter(&sender));
    assert!(token.is_minter(&minter));
    assert_eq!(token.balance(&sender), initial_supply);
}

#[test]
fn deploy_interchain_token_fails_zero_initial_supply_and_invalid_minter() {
    let (env, client, _, _, _) = setup_env();

    let (sender, salt, token_metadata) = dummy_token_params(&env);
    let minter: Option<Address> = Some(client.address.clone());
    let initial_supply = 0;

    assert_contract_err!(
        client.mock_all_auths().try_deploy_interchain_token(
            &sender,
            &salt,
            &token_metadata,
            &initial_supply,
            &minter
        ),
        ContractError::InvalidMinter
    );
}

#[test]
fn deploy_interchain_token_zero_initial_supply_no_minter() {
    let (env, client, _, _, _) = setup_env();

    let (sender, salt, token_metadata) = dummy_token_params(&env);
    let minter: Option<Address> = None;
    let initial_supply = 0;

    let token_id = assert_auth!(
        &sender,
        client.deploy_interchain_token(&sender, &salt, &token_metadata, &initial_supply, &minter,)
    );

    goldie::assert!(events::fmt_last_emitted_event::<InterchainTokenDeployedEvent>(&env));

    let token_address = client.token_address(&token_id);
    let token = InterchainTokenClient::new(&env, &token_address);

    assert_eq!(token.owner(), client.address);
    assert!(token.is_minter(&client.address));
    assert!(!token.is_minter(&sender));
    assert_eq!(token.balance(&sender), initial_supply);
}

#[test]
fn deploy_interchain_token_fails_with_invalid_token_metadata() {
    let (env, client, _, _, _) = setup_env();

    let sender = Address::generate(&env);
    let minter: Option<Address> = None;
    let salt = BytesN::<32>::from_array(&env, &[1; 32]);
    let initial_supply = 0;

    let cases = [
        (
            TokenMetadata::new(&env, "", "symbol", 6),
            ContractError::InvalidTokenName,
        ),
        (
            TokenMetadata::new(&env, "A".repeat(33).as_str(), "symbol", 6),
            ContractError::InvalidTokenName,
        ),
        (
            TokenMetadata::new(&env, "name", "", 6),
            ContractError::InvalidTokenSymbol,
        ),
        (
            TokenMetadata::new(&env, "name", "A".repeat(33).as_str(), 6),
            ContractError::InvalidTokenSymbol,
        ),
        (
            TokenMetadata::new(&env, "name", "symbol", (u8::MAX) as u32 + 1),
            ContractError::InvalidTokenDecimals,
        ),
        (
            TokenMetadata::new(&env, "name", "symbol", u32::MAX),
            ContractError::InvalidTokenDecimals,
        ),
    ];

    for (token_metadata, expected_error) in cases {
        assert_contract_err!(
            client.mock_all_auths().try_deploy_interchain_token(
                &sender,
                &salt,
                &token_metadata,
                &initial_supply,
                &minter
            ),
            expected_error
        );
    }
}

#[test]
fn deploy_interchain_token_fails_with_invalid_auth() {
    let (env, client, _, _, _) = setup_env();

    let (sender, salt, token_metadata) = dummy_token_params(&env);
    let user = Address::generate(&env);
    let minter: Option<Address> = None;

    let initial_supply = 100;

    assert_auth_err!(
        user,
        client.deploy_interchain_token(&sender, &salt, &token_metadata, &initial_supply, &minter)
    );
}