Skip to content

Commit

Permalink
feat(interchain-token-service): add message type SetChainConfig (#669)
Browse files Browse the repository at this point in the history
  • Loading branch information
fish-sammy authored Oct 29, 2024
1 parent 4adfcef commit dc20405
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 14 deletions.
10 changes: 8 additions & 2 deletions contracts/interchain-token-service/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ pub enum Error {
RegisterItsContract,
#[error("failed to deregsiter an its edge contract")]
DeregisterItsContract,
#[error("too many coins attached. Execute accepts zero or one coins")]
TooManyCoins,
#[error("failed to set chain config")]
SetChainConfig,
#[error("failed to query its address")]
QueryItsContract,
#[error("failed to query all its addresses")]
Expand Down Expand Up @@ -99,6 +99,12 @@ pub fn execute(
execute::deregister_its_contract(deps, chain)
.change_context(Error::DeregisterItsContract)
}
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
30 changes: 23 additions & 7 deletions contracts/interchain-token-service/src/contract/execute.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use axelar_wasm_std::IntoContractError;
use axelar_wasm_std::{nonempty, FnExt, IntoContractError};
use cosmwasm_std::{DepsMut, HexBinary, QuerierWrapper, Response, Storage};
use error_stack::{bail, ensure, report, Result, ResultExt};
use router_api::{Address, ChainName, ChainNameRaw, CrossChainId};
Expand All @@ -21,12 +21,12 @@ pub enum Error {
FailedItsContractRegistration(ChainNameRaw),
#[error("failed to deregister its contract for chain {0}")]
FailedItsContractDeregistration(ChainNameRaw),
#[error("failed to execute message")]
FailedExecuteMessage,
#[error("failed to query nexus")]
NexusQueryError,
#[error("storage error")]
StorageError,
#[error("chain config for {0} already set")]
ChainConfigAlreadySet(ChainNameRaw),
#[error("invalid chain max uint")]
LoadChainConfig(ChainNameRaw),
#[error("failed to save chain config for chain {0}")]
SaveChainConfig(ChainNameRaw),
}

/// Executes an incoming ITS message.
Expand Down Expand Up @@ -132,3 +132,19 @@ pub fn deregister_its_contract(deps: DepsMut, chain: ChainNameRaw) -> Result<Res

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

pub fn set_chain_config(
deps: DepsMut,
chain: ChainNameRaw,
max_uint: nonempty::Uint256,
max_target_decimals: u8,
) -> Result<Response, Error> {
match state::may_load_chain_config(deps.storage, &chain)
.change_context_lazy(|| Error::LoadChainConfig(chain.clone()))?
{
Some(_) => bail!(Error::ChainConfigAlreadySet(chain)),
None => state::save_chain_config(deps.storage, &chain, max_uint, max_target_decimals)
.change_context_lazy(|| Error::SaveChainConfig(chain))?
.then(|_| Ok(Response::new())),
}
}
8 changes: 8 additions & 0 deletions contracts/interchain-token-service/src/msg.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::collections::HashMap;

use axelar_wasm_std::nonempty;
use axelarnet_gateway::AxelarExecutableMsg;
use cosmwasm_schema::{cw_serde, QueryResponses};
use msgs_derive::EnsurePermissions;
Expand Down Expand Up @@ -33,6 +34,13 @@ pub enum ExecuteMsg {
/// The admin is allowed to remove the ITS address of a chain for emergencies.
#[permission(Elevated)]
DeregisterItsContract { chain: ChainNameRaw },
/// 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 anothe chain where smaller uint values are used
},
}

#[cw_serde]
Expand Down
34 changes: 30 additions & 4 deletions contracts/interchain-token-service/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,26 @@ use router_api::{Address, ChainNameRaw};
pub enum Error {
#[error(transparent)]
Std(#[from] StdError),
#[error("ITS contract got into an invalid state, its config is missing")]
MissingConfig,
#[error("its address for chain {0} not found")]
ItsContractNotFound(ChainNameRaw),
#[error("its address for chain {0} already registered")]
ItsContractAlreadyRegistered(ChainNameRaw),
#[error("gateway token already registered {0}")]
GatewayTokenAlreadyRegistered(nonempty::String),
}

#[cw_serde]
pub struct Config {
pub axelarnet_gateway: Addr,
}

#[cw_serde]
pub struct ChainConfig {
max_uint: nonempty::Uint256,
max_target_decimals: u8,
}

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");

pub fn load_config(storage: &dyn Storage) -> Config {
CONFIG
Expand All @@ -38,6 +41,29 @@ pub fn save_config(storage: &mut dyn Storage, config: &Config) -> Result<(), Err
Ok(CONFIG.save(storage, config)?)
}

pub fn may_load_chain_config(
storage: &dyn Storage,
chain: &ChainNameRaw,
) -> Result<Option<ChainConfig>, Error> {
Ok(CHAIN_CONFIGS.may_load(storage, chain)?)
}

pub fn save_chain_config(
storage: &mut dyn Storage,
chain: &ChainNameRaw,
max_uint: nonempty::Uint256,
max_target_decimals: u8,
) -> Result<(), Error> {
Ok(CHAIN_CONFIGS.save(
storage,
chain,
&ChainConfig {
max_uint,
max_target_decimals,
},
)?)
}

pub fn may_load_its_contract(
storage: &dyn Storage,
chain: &ChainNameRaw,
Expand Down
49 changes: 48 additions & 1 deletion contracts/interchain-token-service/tests/execute.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::str::FromStr;

use assert_ok::assert_ok;
use axelar_wasm_std::response::inspect_response_msg;
use axelar_wasm_std::{assert_err_contains, permission_control};
use axelarnet_gateway::msg::ExecuteMsg as AxelarnetGatewayExecuteMsg;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::HexBinary;
use cosmwasm_std::{HexBinary, Uint256};
use interchain_token_service::contract::{self, ExecuteError};
use interchain_token_service::events::Event;
use interchain_token_service::msg::ExecuteMsg;
Expand Down Expand Up @@ -314,3 +316,48 @@ fn execute_message_when_invalid_message_type_fails() {
);
assert_err_contains!(result, ExecuteError, ExecuteError::InvalidMessageType);
}

#[test]
fn set_chain_config_should_succeed() {
let chain = "ethereum".parse().unwrap();
let max_uint = Uint256::from_str("120000000000000000000000000")
.unwrap()
.try_into()
.unwrap();
let decimals = 18;

let mut deps = mock_dependencies();
utils::instantiate_contract(deps.as_mut()).unwrap();

assert_ok!(utils::set_chain_config(
deps.as_mut(),
chain,
max_uint,
decimals
));
}

#[test]
fn set_chain_config_should_fail_if_chain_config_is_already_set() {
let chain: ChainNameRaw = "ethereum".parse().unwrap();
let max_uint = Uint256::from_str("120000000000000000000000000")
.unwrap()
.try_into()
.unwrap();
let decimals = 18;

let mut deps = mock_dependencies();
utils::instantiate_contract(deps.as_mut()).unwrap();

assert_ok!(utils::set_chain_config(
deps.as_mut(),
chain.clone(),
max_uint,
decimals
));
assert_err_contains!(
utils::set_chain_config(deps.as_mut(), chain, max_uint, decimals),
ExecuteError,
ExecuteError::ChainConfigAlreadySet(_)
)
}
19 changes: 19 additions & 0 deletions contracts/interchain-token-service/tests/utils/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use axelar_core_std::nexus;
use axelar_core_std::nexus::query::IsChainRegisteredResponse;
use axelar_core_std::query::AxelarQueryMsg;
use axelar_wasm_std::error::ContractError;
use axelar_wasm_std::nonempty;
use cosmwasm_std::testing::{mock_env, mock_info, MockApi, MockQuerier, MockStorage};
use cosmwasm_std::{
from_json, to_json_binary, Addr, DepsMut, HexBinary, MemoryStorage, OwnedDeps, Response,
Expand Down Expand Up @@ -58,6 +59,24 @@ pub fn deregister_its_contract(
)
}

pub fn set_chain_config(
deps: DepsMut,
chain: ChainNameRaw,
max_uint: nonempty::Uint256,
max_target_decimals: u8,
) -> Result<Response, ContractError> {
contract::execute(
deps,
mock_env(),
mock_info(params::GOVERNANCE, &[]),
ExecuteMsg::SetChainConfig {
chain,
max_uint,
max_target_decimals,
},
)
}

pub fn make_deps() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<AxelarQueryMsg>> {
let addr = Addr::unchecked(params::GATEWAY);
let mut deps = OwnedDeps {
Expand Down
1 change: 1 addition & 0 deletions packages/axelar-wasm-std/src/nonempty/uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ impl fmt::Display for Uint64 {
// TODO: consider using macro for these types
#[cw_serde]
#[derive(Copy, PartialOrd, Eq, IntoInner)]
#[serde(try_from = "cosmwasm_std::Uint256")]
pub struct Uint256(cosmwasm_std::Uint256);

impl TryFrom<cosmwasm_std::Uint256> for Uint256 {
Expand Down

0 comments on commit dc20405

Please sign in to comment.