Skip to content

Commit

Permalink
feat(minor-interchain-token-service): refactor chain registration
Browse files Browse the repository at this point in the history
* combine SetChainConfig and RegisterItsContract into single message
* add UpdateChain message
  • Loading branch information
cjcobb23 committed Nov 5, 2024
1 parent badaf87 commit 7de2fdb
Show file tree
Hide file tree
Showing 13 changed files with 359 additions and 334 deletions.
50 changes: 23 additions & 27 deletions contracts/interchain-token-service/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use cosmwasm_std::{Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Respons
use error_stack::{Report, ResultExt};
use execute::{freeze_chain, unfreeze_chain};

use crate::events::Event;
use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::state;
use crate::state::Config;
Expand All @@ -26,10 +25,10 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
pub enum Error {
#[error("failed to execute a cross-chain message")]
Execute,
#[error("failed to register an its edge contract")]
RegisterItsContract,
#[error("failed to deregsiter an its edge contract")]
DeregisterItsContract,
#[error("failed to register chain")]
RegisterChain,
#[error("failed to update chain")]
UpdateChain,
#[error("failed to freeze chain")]
FreezeChain,
#[error("failed to unfreeze chain")]
Expand Down Expand Up @@ -79,17 +78,9 @@ pub fn instantiate(

state::save_config(deps.storage, &Config { axelarnet_gateway })?;

for (chain, address) in msg.its_contracts.iter() {
state::save_its_contract(deps.storage, chain, address)?;
}

killswitch::init(deps.storage, killswitch::State::Disengaged)?;

Ok(Response::new().add_events(
msg.its_contracts
.into_iter()
.map(|(chain, address)| Event::ItsContractRegistered { chain, address }.into()),
))
Ok(Response::new())
}

#[cfg_attr(not(feature = "library"), entry_point)]
Expand All @@ -106,13 +97,24 @@ pub fn execute(
payload,
}) => execute::execute_message(deps, cc_id, source_address, payload)
.change_context(Error::Execute),
ExecuteMsg::RegisterItsContract { chain, address } => {
execute::register_its_contract(deps, chain, address)
.change_context(Error::RegisterItsContract)
}
ExecuteMsg::DeregisterItsContract { chain } => {
execute::deregister_its_contract(deps, chain)
.change_context(Error::DeregisterItsContract)
ExecuteMsg::RegisterChain {
chain,
its_edge_contract,
max_uint,
max_target_decimals,
} => execute::register_chain(
deps,
chain,
its_edge_contract,
max_uint,
max_target_decimals,
)
.change_context(Error::RegisterChain),
ExecuteMsg::UpdateChain {
chain,
its_edge_contract,
} => {
execute::update_chain(deps, chain, its_edge_contract).change_context(Error::UpdateChain)
}
ExecuteMsg::FreezeChain { chain } => {
freeze_chain(deps, chain).change_context(Error::FreezeChain)
Expand All @@ -126,12 +128,6 @@ pub fn execute(
ExecuteMsg::EnableExecution => {
execute::enable_execution(deps).change_context(Error::EnableExecution)
}
ExecuteMsg::SetChainConfig {
chain,
max_uint,
max_target_decimals,
} => execute::set_chain_config(deps, chain, max_uint, max_target_decimals)
.change_context(Error::SetChainConfig),
}?
.then(Ok)
}
Expand Down
52 changes: 23 additions & 29 deletions contracts/interchain-token-service/src/contract/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,24 +205,6 @@ fn send_to_destination(
Ok(Response::new().add_message(call_contract_msg))
}

pub fn register_its_contract(
deps: DepsMut,
chain: ChainNameRaw,
address: Address,
) -> Result<Response, Error> {
state::save_its_contract(deps.storage, &chain, &address)
.change_context_lazy(|| Error::FailedItsContractRegistration(chain.clone()))?;

Ok(Response::new().add_event(Event::ItsContractRegistered { chain, address }.into()))
}

pub fn deregister_its_contract(deps: DepsMut, chain: ChainNameRaw) -> Result<Response, Error> {
state::remove_its_contract(deps.storage, &chain)
.change_context_lazy(|| Error::FailedItsContractDeregistration(chain.clone()))?;

Ok(Response::new().add_event(Event::ItsContractDeregistered { chain }.into()))
}

pub fn freeze_chain(deps: DepsMut, chain: ChainNameRaw) -> Result<Response, Error> {
state::freeze_chain(deps.storage, &chain).change_context(Error::State)?;

Expand All @@ -243,20 +225,36 @@ pub fn enable_execution(deps: DepsMut) -> Result<Response, Error> {
killswitch::disengage(deps.storage, Event::ExecutionEnabled).change_context(Error::State)
}

pub fn set_chain_config(
pub fn register_chain(
deps: DepsMut,
chain: ChainNameRaw,
its_address: Address,
max_uint: nonempty::Uint256,
max_target_decimals: u8,
) -> Result<Response, Error> {
match state::may_load_chain_config(deps.storage, &chain).change_context(Error::State)? {
Some(_) => bail!(Error::ChainConfigAlreadySet(chain)),
None => state::save_chain_config(deps.storage, &chain, max_uint, max_target_decimals)
.change_context(Error::State)?
.then(|_| Ok(Response::new())),
None => state::save_chain_config(
deps.storage,
&chain,
its_address,
max_uint,
max_target_decimals,
)
.change_context(Error::State)?
.then(|_| Ok(Response::new())),
}
}

pub fn update_chain(
deps: DepsMut,
chain: ChainNameRaw,
its_address: Address,
) -> Result<Response, Error> {
state::update_its_contract(deps.storage, &chain, its_address).change_context(Error::State)?;
Ok(Response::new())
}

/// Calculates the destination on token transfer amount.
///
/// The amount is calculated based on the token decimals on the source and destination chains.
Expand Down Expand Up @@ -612,8 +610,8 @@ mod tests {
use router_api::{ChainNameRaw, CrossChainId};

use crate::contract::execute::{
disable_execution, enable_execution, execute_message, freeze_chain, register_its_contract,
set_chain_config, unfreeze_chain, Error,
disable_execution, enable_execution, execute_message, freeze_chain, register_chain,
unfreeze_chain, Error,
};
use crate::state::{self, Config};
use crate::{DeployInterchainToken, HubMessage, InterchainTransfer};
Expand Down Expand Up @@ -867,14 +865,10 @@ mod tests {

for chain_name in [SOLANA, ETHEREUM, XRPL] {
let chain = ChainNameRaw::try_from(chain_name).unwrap();
assert_ok!(register_its_contract(
assert_ok!(register_chain(
deps.as_mut(),
chain.clone(),
ITS_ADDRESS.to_string().try_into().unwrap(),
));
assert_ok!(set_chain_config(
deps.as_mut(),
chain,
Uint256::one().try_into().unwrap(),
16u8
));
Expand Down
26 changes: 11 additions & 15 deletions contracts/interchain-token-service/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ pub struct InstantiateMsg {
pub admin_address: String,
/// The address of the axelarnet-gateway contract on Amplifier
pub axelarnet_gateway_address: String,
/// Addresses of the ITS edge contracts on connected chains
pub its_contracts: HashMap<ChainNameRaw, Address>,
}

#[cw_serde]
Expand All @@ -25,18 +23,23 @@ pub enum ExecuteMsg {
/// Execute a cross-chain message received by the axelarnet-gateway from another chain
#[permission(Specific(gateway))]
Execute(AxelarExecutableMsg),

/// Register the ITS contract address of another chain. Each chain's ITS contract has to be whitelisted before
/// ITS Hub can send cross-chain messages to it, or receive messages from it.
/// If an ITS contract is already set for the chain, an error is returned.
#[permission(Governance)]
RegisterItsContract {
RegisterChain {
chain: ChainNameRaw,
address: Address,
its_edge_contract: Address,
max_uint: nonempty::Uint256, // The maximum uint value that is supported by the chain's token standard
max_target_decimals: u8, // The maximum number of decimals that is preserved when deploying a token to another chain where smaller uint values are used
},

#[permission(Governance)]
UpdateChain {
chain: ChainNameRaw,
its_edge_contract: Address,
},
/// Deregister the ITS contract address for the given chain.
/// The admin is allowed to remove the ITS address of a chain for emergencies.
#[permission(Elevated)]
DeregisterItsContract { chain: ChainNameRaw },

/// Freeze execution of ITS messages for a particular chain
#[permission(Elevated)]
Expand All @@ -51,13 +54,6 @@ pub enum ExecuteMsg {

#[permission(Elevated)]
EnableExecution,
/// Set the chain configuration for a chain.
#[permission(Governance)]
SetChainConfig {
chain: ChainNameRaw,
max_uint: nonempty::Uint256, // The maximum uint value that is supported by the chain's token standard
max_target_decimals: u8, // The maximum number of decimals that is preserved when deploying a token to another chain where smaller uint values are used
},
}

#[cw_serde]
Expand Down
74 changes: 41 additions & 33 deletions contracts/interchain-token-service/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::HashMap;

use axelar_wasm_std::{nonempty, FnExt, IntoContractError};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{ensure, Addr, OverflowError, StdError, Storage, Uint256};
use cosmwasm_std::{Addr, OverflowError, StdError, Storage, Uint256};
use cw_storage_plus::{Item, Map};
use error_stack::{report, Result, ResultExt};
use router_api::{Address, ChainNameRaw};
Expand Down Expand Up @@ -36,6 +36,7 @@ pub struct Config {
pub struct ChainConfig {
pub max_uint: nonempty::Uint256,
pub max_target_decimals: u8,
pub its_address: Address,
frozen: bool,
}

Expand Down Expand Up @@ -108,7 +109,6 @@ pub struct TokenConfig {
}

const CONFIG: Item<Config> = Item::new("config");
const ITS_CONTRACTS: Map<&ChainNameRaw, Address> = Map::new("its_contracts");
const CHAIN_CONFIGS: Map<&ChainNameRaw, ChainConfig> = Map::new("chain_configs");
const TOKEN_INSTANCE: Map<&(ChainNameRaw, TokenId), TokenInstance> = Map::new("token_instance");
const TOKEN_CONFIGS: Map<&TokenId, TokenConfig> = Map::new("token_configs");
Expand Down Expand Up @@ -144,6 +144,7 @@ pub fn load_chain_config(
pub fn save_chain_config(
storage: &mut dyn Storage,
chain: &ChainNameRaw,
its_contract: Address,
max_uint: nonempty::Uint256,
max_target_decimals: u8,
) -> Result<(), Error> {
Expand All @@ -154,18 +155,36 @@ pub fn save_chain_config(
&ChainConfig {
max_uint,
max_target_decimals,
its_address: its_contract,
frozen: false,
},
)
.change_context(Error::Storage)
}

pub fn update_its_contract(
storage: &mut dyn Storage,
chain: &ChainNameRaw,
its_address: Address,
) -> Result<ChainConfig, Error> {
CHAIN_CONFIGS
.update(storage, chain, |config| match config {
Some(config) => Ok(ChainConfig {
its_address,
..config
}),
None => Err(StdError::not_found("config not found")),
})
.change_context(Error::ChainNotFound(chain.to_owned()))
}

pub fn may_load_its_contract(
storage: &dyn Storage,
chain: &ChainNameRaw,
) -> Result<Option<Address>, Error> {
ITS_CONTRACTS
CHAIN_CONFIGS
.may_load(storage, chain)
.map(|res| res.map(|config| config.its_address))
.change_context(Error::Storage)
}

Expand All @@ -175,38 +194,15 @@ pub fn load_its_contract(storage: &dyn Storage, chain: &ChainNameRaw) -> Result<
.ok_or_else(|| report!(Error::ItsContractNotFound(chain.clone())))
}

pub fn save_its_contract(
storage: &mut dyn Storage,
chain: &ChainNameRaw,
address: &Address,
) -> Result<(), Error> {
ensure!(
may_load_its_contract(storage, chain)?.is_none(),
Error::ItsContractAlreadyRegistered(chain.clone())
);

ITS_CONTRACTS
.save(storage, chain, address)
.change_context(Error::Storage)
}

pub fn remove_its_contract(storage: &mut dyn Storage, chain: &ChainNameRaw) -> Result<(), Error> {
ensure!(
may_load_its_contract(storage, chain)?.is_some(),
Error::ItsContractNotFound(chain.clone())
);

ITS_CONTRACTS.remove(storage, chain);

Ok(())
}

pub fn load_all_its_contracts(
storage: &dyn Storage,
) -> Result<HashMap<ChainNameRaw, Address>, Error> {
ITS_CONTRACTS
CHAIN_CONFIGS
.range(storage, None, None, cosmwasm_std::Order::Ascending)
.map(|res| res.change_context(Error::Storage))
.map(|res| {
res.map(|(chain, config)| (chain, config.its_address))
.change_context(Error::Storage)
})
.collect::<Result<HashMap<_, _>, _>>()
}

Expand Down Expand Up @@ -324,8 +320,20 @@ mod tests {
HashMap::new()
);

assert_ok!(save_its_contract(deps.as_mut().storage, &chain1, &address1));
assert_ok!(save_its_contract(deps.as_mut().storage, &chain2, &address2));
assert_ok!(save_chain_config(
deps.as_mut().storage,
&chain1,
address1.clone(),
Uint256::MAX.try_into().unwrap(),
16u8
));
assert_ok!(save_chain_config(
deps.as_mut().storage,
&chain2,
address2.clone(),
Uint256::MAX.try_into().unwrap(),
16u8
));
assert_eq!(
assert_ok!(load_its_contract(deps.as_ref().storage, &chain1)),
address1
Expand Down
Loading

0 comments on commit 7de2fdb

Please sign in to comment.