Skip to content

Commit

Permalink
fix(interchain-token): unimplemented notice for SAC methods (#225)
Browse files Browse the repository at this point in the history
Co-authored-by: ahramy <ahram@interoplabs.io>
  • Loading branch information
milapsheth and ahramy authored Jan 28, 2025
1 parent 7bfe787 commit 8c31f8e
Show file tree
Hide file tree
Showing 15 changed files with 288 additions and 153 deletions.
Binary file not shown.
62 changes: 27 additions & 35 deletions contracts/stellar-interchain-token-service/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,19 +231,17 @@ impl InterchainTokenServiceInterface for InterchainTokenService {
) -> Result<BytesN<32>, ContractError> {
caller.require_auth();

ensure!(initial_supply >= 0, ContractError::InvalidSupply);
ensure!(initial_supply >= 0, ContractError::InvalidInitialSupply);

let token_id = Self::interchain_token_id(env, caller.clone(), salt);

token_metadata.validate()?;

Self::deploy_token(
env,
token_id.clone(),
token_metadata,
minter,
Some((initial_supply, caller)),
);
let token_address = Self::deploy_token(env, token_id.clone(), token_metadata, minter)?;

if initial_supply > 0 {
StellarAssetClient::new(env, &token_address).mint(&caller, &initial_supply);
}

Ok(token_id)
}
Expand Down Expand Up @@ -272,14 +270,9 @@ impl InterchainTokenServiceInterface for InterchainTokenService {
) -> Result<BytesN<32>, ContractError> {
let token_id = Self::canonical_interchain_token_id(env, token_address.clone());

ensure!(
!env.storage()
.persistent()
.has(&DataKey::TokenIdConfig(token_id.clone())),
ContractError::TokenAlreadyRegistered
);
Self::ensure_token_not_registered(env, token_id.clone())?;

let _ = Self::deploy_token_manager(
let _: Address = Self::deploy_token_manager(
env,
token_id.clone(),
token_address,
Expand Down Expand Up @@ -696,17 +689,12 @@ impl InterchainTokenService {
minter,
}: DeployInterchainToken,
) -> Result<(), ContractError> {
ensure!(
Self::token_id_config(env, token_id.clone()).is_err(),
ContractError::TokenAlreadyDeployed
);

let token_metadata = TokenMetadata::new(name, symbol, decimals as u32)?;

// Note: attempt to convert a byte string which doesn't represent a valid Soroban address fails at the Host level
let minter = minter.map(|m| Address::from_string_bytes(&m));

Self::deploy_token(env, token_id, token_metadata, minter, None);
let _: Address = Self::deploy_token(env, token_id, token_metadata, minter)?;

Ok(())
}
Expand Down Expand Up @@ -744,14 +732,14 @@ impl InterchainTokenService {
/// * `token_id` - The token ID for the interchain token being deployed.
/// * `token_metadata` - The metadata for the interchain token being deployed.
/// * `minter` - An optional address of an additional minter for the interchain token being deployed.
/// * `initial_mint` - The initial mint amount and recipient for the interchain token being deployed.
fn deploy_token(
env: &Env,
token_id: BytesN<32>,
token_metadata: TokenMetadata,
minter: Option<Address>,
initial_mint: Option<(i128, Address)>,
) {
) -> Result<Address, ContractError> {
Self::ensure_token_not_registered(env, token_id.clone())?;

let token_address = deployer::deploy_interchain_token(
env,
Self::interchain_token_wasm_hash(env),
Expand All @@ -764,20 +752,24 @@ impl InterchainTokenService {
let token_manager = Self::deploy_token_manager(
env,
token_id,
token_address,
token_address.clone(),
TokenManagerType::NativeInterchainToken,
);

match initial_mint {
Some((initial_supply, recipient)) if initial_supply > 0 => {
StellarAssetClient::new(env, &interchain_token_client.address)
.mint(&recipient, &initial_supply);
}
_ => {}
}

// Transfer minter role to the token manager
// Give minter role to the token manager
interchain_token_client.add_minter(&token_manager);
interchain_token_client.remove_minter(&env.current_contract_address());

Ok(token_address)
}

fn ensure_token_not_registered(env: &Env, token_id: BytesN<32>) -> Result<(), ContractError> {
ensure!(
!env.storage()
.persistent()
.has(&DataKey::TokenIdConfig(token_id)),
ContractError::TokenAlreadyRegistered
);

Ok(())
}
}
7 changes: 3 additions & 4 deletions contracts/stellar-interchain-token-service/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub enum ContractError {
NotHubAddress = 15,
InvalidTokenMetaData = 16,
InvalidTokenId = 17,
TokenAlreadyDeployed = 18,
TokenAlreadyRegistered = 18,
InvalidFlowLimit = 19,
FlowLimitExceeded = 20,
FlowAmountOverflow = 21,
Expand All @@ -33,9 +33,8 @@ pub enum ContractError {
InvalidTokenName = 25,
InvalidTokenSymbol = 26,
InvalidTokenDecimals = 27,
TokenAlreadyRegistered = 28,
ContractPaused = 29,
InvalidSupply = 30,
ContractPaused = 28,
InvalidInitialSupply = 29,
}

impl_not_approved_error!(ContractError);
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ use crate::error::ContractError;
use crate::event::{InterchainTokenDeployedEvent, TokenManagerDeployedEvent};
use crate::types::TokenManagerType;

const INTERCHAIN_TOKEN_DEPLOYED_EVENT_IDX: i32 = -4;
const INTERCHAIN_TOKEN_DEPLOYED_NO_SUPPLY_EVENT_IDX: i32 = INTERCHAIN_TOKEN_DEPLOYED_EVENT_IDX + 1;
const TOKEN_MANAGER_DEPLOYED_EVENT_IDX: i32 = INTERCHAIN_TOKEN_DEPLOYED_EVENT_IDX + 1;

fn dummy_token_params(env: &Env) -> (Address, BytesN<32>, TokenMetadata) {
let sender = Address::generate(env);
let salt = BytesN::<32>::from_array(env, &[1; 32]);
Expand All @@ -29,10 +33,13 @@ fn deploy_interchain_token_succeeds() {
&sender,
client.deploy_interchain_token(&sender, &salt, &token_metadata, &initial_supply, &minter)
);
let interchain_token_deployed_event =
events::fmt_emitted_event_at_idx::<InterchainTokenDeployedEvent>(&env, -5);
let token_manager_deployed_event =
events::fmt_emitted_event_at_idx::<TokenManagerDeployedEvent>(&env, -4);
let interchain_token_deployed_event = events::fmt_emitted_event_at_idx::<
InterchainTokenDeployedEvent,
>(&env, INTERCHAIN_TOKEN_DEPLOYED_EVENT_IDX);
let token_manager_deployed_event = events::fmt_emitted_event_at_idx::<TokenManagerDeployedEvent>(
&env,
TOKEN_MANAGER_DEPLOYED_EVENT_IDX,
);

goldie::assert!([
interchain_token_deployed_event,
Expand All @@ -59,6 +66,34 @@ fn deploy_interchain_token_fails_when_paused() {
);
}

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

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

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

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

#[test]
fn deploy_interchain_token_with_initial_supply_no_minter() {
let (env, client, _, _, _) = setup_env();
Expand All @@ -74,7 +109,7 @@ fn deploy_interchain_token_with_initial_supply_no_minter() {

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

let token_address = client.token_address(&token_id);
let token_manager = client.token_manager(&token_id);
Expand Down Expand Up @@ -108,7 +143,7 @@ fn deploy_interchain_token_with_initial_supply_valid_minter() {

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

let token_address = client.token_address(&token_id);
let token_manager = client.token_manager(&token_id);
Expand Down Expand Up @@ -138,7 +173,7 @@ fn deploy_interchain_token_check_token_id_and_token_manager_type() {

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

assert_eq!(token_id, expected_token_id);
assert_eq!(
Expand Down Expand Up @@ -168,7 +203,7 @@ fn deploy_interchain_token_zero_initial_supply_and_valid_minter() {

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

let token_address = client.token_address(&token_id);
let token_manager = client.token_manager(&token_id);
Expand Down Expand Up @@ -199,7 +234,7 @@ fn deploy_interchain_token_fails_with_zero_initial_supply_and_no_minter() {

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

let token_address = client.token_address(&token_id);
let token_manager = client.token_manager(&token_id);
Expand Down Expand Up @@ -292,6 +327,6 @@ fn deploy_interchain_token_fails_with_negative_supply() {
&invalid_supply,
&None
),
ContractError::InvalidSupply
ContractError::InvalidInitialSupply
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,9 @@ fn deploy_interchain_token_message_execute_succeeds() {
client.execute(&source_chain, &message_id, &source_address, &payload);

let interchain_token_deployed_event =
events::fmt_emitted_event_at_idx::<InterchainTokenDeployedEvent>(&env, -4);
events::fmt_emitted_event_at_idx::<InterchainTokenDeployedEvent>(&env, -3);
let token_manager_deployed_event =
events::fmt_emitted_event_at_idx::<TokenManagerDeployedEvent>(&env, -3);
events::fmt_emitted_event_at_idx::<TokenManagerDeployedEvent>(&env, -2);

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

Expand Down Expand Up @@ -690,6 +690,6 @@ fn deploy_interchain_token_message_execute_fails_token_already_deployed() {

assert_contract_err!(
client.try_execute(&source_chain, &second_message_id, &source_address, &payload),
ContractError::TokenAlreadyDeployed
ContractError::TokenAlreadyRegistered
);
}
6 changes: 4 additions & 2 deletions contracts/stellar-interchain-token-service/src/testutils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ use crate::{InterchainTokenService, InterchainTokenServiceClient};

// Note: On changes to `interchain-token` and `token-manager` crates, recompile via `stellar contract build && ./optimize.sh`
// and copy the built `target/wasm32-unknown-unknown/release/stellar_*.optimized.wasm` to ../testdata.
pub const INTERCHAIN_TOKEN_WASM: &[u8] = include_bytes!("./testdata/interchain_token.wasm");
pub const TOKEN_MANAGER_WASM: &[u8] = include_bytes!("./testdata/token_manager.wasm");
pub const INTERCHAIN_TOKEN_WASM: &[u8] =
include_bytes!("./testdata/stellar_interchain_token.optimized.wasm");
pub const TOKEN_MANAGER_WASM: &[u8] =
include_bytes!("./testdata/stellar_token_manager.optimized.wasm");

pub fn setup_its<'a>(
env: &Env,
Expand Down
60 changes: 34 additions & 26 deletions contracts/stellar-interchain-token/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
use soroban_sdk::token::{StellarAssetInterface, TokenInterface};
use soroban_sdk::{
assert_with_error, contract, contractimpl, panic_with_error, token, Address, BytesN, Env,
String,
};
use soroban_sdk::{assert_with_error, contract, contractimpl, token, Address, BytesN, Env, String};
use soroban_token_sdk::event::Events as TokenEvents;
use soroban_token_sdk::metadata::TokenMetadata;
use soroban_token_sdk::TokenUtils;
Expand Down Expand Up @@ -35,14 +32,31 @@ impl InterchainToken {

env.storage().instance().set(&DataKey::TokenId, &token_id);

env.storage().instance().set(&DataKey::Minter(owner), &());

if let Some(minter) = minter {
env.storage().instance().set(&DataKey::Minter(minter), &());
env.storage()
.instance()
.set(&DataKey::Minter(minter.clone()), &());

MinterAddedEvent { minter }.emit(&env);
}
}
}

#[contractimpl]
impl OwnableInterface for InterchainToken {
fn owner(env: &Env) -> Address {
interfaces::owner(env)
}

fn transfer_ownership(env: &Env, new_owner: Address) {
interfaces::transfer_ownership::<Self>(env, new_owner.clone());

// Emit the standard soroban event for setting admin
TokenEvents::new(env).set_admin(Self::owner(env), new_owner);
}
}

// Note: Some methods below are intentionally unimplemented as they are not supported by this token
#[contractimpl]
impl StellarAssetInterface for InterchainToken {
fn set_admin(env: Env, admin: Address) {
Expand All @@ -54,21 +68,28 @@ impl StellarAssetInterface for InterchainToken {
}

fn set_authorized(_env: Env, _id: Address, _authorize: bool) {
todo!()
unimplemented!()
}

fn authorized(_env: Env, _id: Address) -> bool {
todo!()
unimplemented!()
}

fn mint(env: Env, to: Address, amount: i128) {
if let Err(err) = Self::mint_from(&env, Self::owner(&env), to, amount) {
panic_with_error!(env, err);
}
let owner = Self::owner(&env);
owner.require_auth();

Self::validate_amount(&env, amount);

Self::receive_balance(&env, to.clone(), amount);

extend_instance_ttl(&env);

TokenUtils::new(&env).events().mint(owner, to, amount);
}

fn clawback(_env: Env, _from: Address, _amount: i128) {
todo!()
unimplemented!()
}
}

Expand Down Expand Up @@ -355,16 +376,3 @@ impl InterchainToken {
extend_persistent_ttl(env, &key);
}
}

#[contractimpl]
impl OwnableInterface for InterchainToken {
fn owner(env: &Env) -> Address {
interfaces::owner(env)
}

fn transfer_ownership(env: &Env, new_owner: Address) {
interfaces::transfer_ownership::<Self>(env, new_owner.clone());
// adhere to reference implementation for tokens and emit predefined soroban event
TokenEvents::new(env).set_admin(Self::owner(env), new_owner);
}
}
Loading

0 comments on commit 8c31f8e

Please sign in to comment.