Skip to content

Commit dc20405

Browse files
authored
feat(interchain-token-service): add message type SetChainConfig (#669)
1 parent 4adfcef commit dc20405

File tree

7 files changed

+137
-14
lines changed

7 files changed

+137
-14
lines changed

contracts/interchain-token-service/src/contract.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ pub enum Error {
2929
RegisterItsContract,
3030
#[error("failed to deregsiter an its edge contract")]
3131
DeregisterItsContract,
32-
#[error("too many coins attached. Execute accepts zero or one coins")]
33-
TooManyCoins,
32+
#[error("failed to set chain config")]
33+
SetChainConfig,
3434
#[error("failed to query its address")]
3535
QueryItsContract,
3636
#[error("failed to query all its addresses")]
@@ -99,6 +99,12 @@ pub fn execute(
9999
execute::deregister_its_contract(deps, chain)
100100
.change_context(Error::DeregisterItsContract)
101101
}
102+
ExecuteMsg::SetChainConfig {
103+
chain,
104+
max_uint,
105+
max_target_decimals,
106+
} => execute::set_chain_config(deps, chain, max_uint, max_target_decimals)
107+
.change_context(Error::SetChainConfig),
102108
}?
103109
.then(Ok)
104110
}

contracts/interchain-token-service/src/contract/execute.rs

+23-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use axelar_wasm_std::IntoContractError;
1+
use axelar_wasm_std::{nonempty, FnExt, IntoContractError};
22
use cosmwasm_std::{DepsMut, HexBinary, QuerierWrapper, Response, Storage};
33
use error_stack::{bail, ensure, report, Result, ResultExt};
44
use router_api::{Address, ChainName, ChainNameRaw, CrossChainId};
@@ -21,12 +21,12 @@ pub enum Error {
2121
FailedItsContractRegistration(ChainNameRaw),
2222
#[error("failed to deregister its contract for chain {0}")]
2323
FailedItsContractDeregistration(ChainNameRaw),
24-
#[error("failed to execute message")]
25-
FailedExecuteMessage,
26-
#[error("failed to query nexus")]
27-
NexusQueryError,
28-
#[error("storage error")]
29-
StorageError,
24+
#[error("chain config for {0} already set")]
25+
ChainConfigAlreadySet(ChainNameRaw),
26+
#[error("invalid chain max uint")]
27+
LoadChainConfig(ChainNameRaw),
28+
#[error("failed to save chain config for chain {0}")]
29+
SaveChainConfig(ChainNameRaw),
3030
}
3131

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

133133
Ok(Response::new().add_event(Event::ItsContractDeregistered { chain }.into()))
134134
}
135+
136+
pub fn set_chain_config(
137+
deps: DepsMut,
138+
chain: ChainNameRaw,
139+
max_uint: nonempty::Uint256,
140+
max_target_decimals: u8,
141+
) -> Result<Response, Error> {
142+
match state::may_load_chain_config(deps.storage, &chain)
143+
.change_context_lazy(|| Error::LoadChainConfig(chain.clone()))?
144+
{
145+
Some(_) => bail!(Error::ChainConfigAlreadySet(chain)),
146+
None => state::save_chain_config(deps.storage, &chain, max_uint, max_target_decimals)
147+
.change_context_lazy(|| Error::SaveChainConfig(chain))?
148+
.then(|_| Ok(Response::new())),
149+
}
150+
}

contracts/interchain-token-service/src/msg.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::collections::HashMap;
22

3+
use axelar_wasm_std::nonempty;
34
use axelarnet_gateway::AxelarExecutableMsg;
45
use cosmwasm_schema::{cw_serde, QueryResponses};
56
use msgs_derive::EnsurePermissions;
@@ -33,6 +34,13 @@ pub enum ExecuteMsg {
3334
/// The admin is allowed to remove the ITS address of a chain for emergencies.
3435
#[permission(Elevated)]
3536
DeregisterItsContract { chain: ChainNameRaw },
37+
/// Set the chain configuration for a chain.
38+
#[permission(Governance)]
39+
SetChainConfig {
40+
chain: ChainNameRaw,
41+
max_uint: nonempty::Uint256, // The maximum uint value that is supported by the chain's token standard
42+
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
43+
},
3644
}
3745

3846
#[cw_serde]

contracts/interchain-token-service/src/state.rs

+30-4
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,26 @@ use router_api::{Address, ChainNameRaw};
1010
pub enum Error {
1111
#[error(transparent)]
1212
Std(#[from] StdError),
13-
#[error("ITS contract got into an invalid state, its config is missing")]
14-
MissingConfig,
1513
#[error("its address for chain {0} not found")]
1614
ItsContractNotFound(ChainNameRaw),
1715
#[error("its address for chain {0} already registered")]
1816
ItsContractAlreadyRegistered(ChainNameRaw),
19-
#[error("gateway token already registered {0}")]
20-
GatewayTokenAlreadyRegistered(nonempty::String),
2117
}
2218

2319
#[cw_serde]
2420
pub struct Config {
2521
pub axelarnet_gateway: Addr,
2622
}
2723

24+
#[cw_serde]
25+
pub struct ChainConfig {
26+
max_uint: nonempty::Uint256,
27+
max_target_decimals: u8,
28+
}
29+
2830
const CONFIG: Item<Config> = Item::new("config");
2931
const ITS_CONTRACTS: Map<&ChainNameRaw, Address> = Map::new("its_contracts");
32+
const CHAIN_CONFIGS: Map<&ChainNameRaw, ChainConfig> = Map::new("chain_configs");
3033

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

44+
pub fn may_load_chain_config(
45+
storage: &dyn Storage,
46+
chain: &ChainNameRaw,
47+
) -> Result<Option<ChainConfig>, Error> {
48+
Ok(CHAIN_CONFIGS.may_load(storage, chain)?)
49+
}
50+
51+
pub fn save_chain_config(
52+
storage: &mut dyn Storage,
53+
chain: &ChainNameRaw,
54+
max_uint: nonempty::Uint256,
55+
max_target_decimals: u8,
56+
) -> Result<(), Error> {
57+
Ok(CHAIN_CONFIGS.save(
58+
storage,
59+
chain,
60+
&ChainConfig {
61+
max_uint,
62+
max_target_decimals,
63+
},
64+
)?)
65+
}
66+
4167
pub fn may_load_its_contract(
4268
storage: &dyn Storage,
4369
chain: &ChainNameRaw,

contracts/interchain-token-service/tests/execute.rs

+48-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
use std::str::FromStr;
2+
13
use assert_ok::assert_ok;
24
use axelar_wasm_std::response::inspect_response_msg;
35
use axelar_wasm_std::{assert_err_contains, permission_control};
46
use axelarnet_gateway::msg::ExecuteMsg as AxelarnetGatewayExecuteMsg;
57
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
6-
use cosmwasm_std::HexBinary;
8+
use cosmwasm_std::{HexBinary, Uint256};
79
use interchain_token_service::contract::{self, ExecuteError};
810
use interchain_token_service::events::Event;
911
use interchain_token_service::msg::ExecuteMsg;
@@ -314,3 +316,48 @@ fn execute_message_when_invalid_message_type_fails() {
314316
);
315317
assert_err_contains!(result, ExecuteError, ExecuteError::InvalidMessageType);
316318
}
319+
320+
#[test]
321+
fn set_chain_config_should_succeed() {
322+
let chain = "ethereum".parse().unwrap();
323+
let max_uint = Uint256::from_str("120000000000000000000000000")
324+
.unwrap()
325+
.try_into()
326+
.unwrap();
327+
let decimals = 18;
328+
329+
let mut deps = mock_dependencies();
330+
utils::instantiate_contract(deps.as_mut()).unwrap();
331+
332+
assert_ok!(utils::set_chain_config(
333+
deps.as_mut(),
334+
chain,
335+
max_uint,
336+
decimals
337+
));
338+
}
339+
340+
#[test]
341+
fn set_chain_config_should_fail_if_chain_config_is_already_set() {
342+
let chain: ChainNameRaw = "ethereum".parse().unwrap();
343+
let max_uint = Uint256::from_str("120000000000000000000000000")
344+
.unwrap()
345+
.try_into()
346+
.unwrap();
347+
let decimals = 18;
348+
349+
let mut deps = mock_dependencies();
350+
utils::instantiate_contract(deps.as_mut()).unwrap();
351+
352+
assert_ok!(utils::set_chain_config(
353+
deps.as_mut(),
354+
chain.clone(),
355+
max_uint,
356+
decimals
357+
));
358+
assert_err_contains!(
359+
utils::set_chain_config(deps.as_mut(), chain, max_uint, decimals),
360+
ExecuteError,
361+
ExecuteError::ChainConfigAlreadySet(_)
362+
)
363+
}

contracts/interchain-token-service/tests/utils/execute.rs

+19
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use axelar_core_std::nexus;
44
use axelar_core_std::nexus::query::IsChainRegisteredResponse;
55
use axelar_core_std::query::AxelarQueryMsg;
66
use axelar_wasm_std::error::ContractError;
7+
use axelar_wasm_std::nonempty;
78
use cosmwasm_std::testing::{mock_env, mock_info, MockApi, MockQuerier, MockStorage};
89
use cosmwasm_std::{
910
from_json, to_json_binary, Addr, DepsMut, HexBinary, MemoryStorage, OwnedDeps, Response,
@@ -58,6 +59,24 @@ pub fn deregister_its_contract(
5859
)
5960
}
6061

62+
pub fn set_chain_config(
63+
deps: DepsMut,
64+
chain: ChainNameRaw,
65+
max_uint: nonempty::Uint256,
66+
max_target_decimals: u8,
67+
) -> Result<Response, ContractError> {
68+
contract::execute(
69+
deps,
70+
mock_env(),
71+
mock_info(params::GOVERNANCE, &[]),
72+
ExecuteMsg::SetChainConfig {
73+
chain,
74+
max_uint,
75+
max_target_decimals,
76+
},
77+
)
78+
}
79+
6180
pub fn make_deps() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<AxelarQueryMsg>> {
6281
let addr = Addr::unchecked(params::GATEWAY);
6382
let mut deps = OwnedDeps {

packages/axelar-wasm-std/src/nonempty/uint.rs

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ impl fmt::Display for Uint64 {
5353
// TODO: consider using macro for these types
5454
#[cw_serde]
5555
#[derive(Copy, PartialOrd, Eq, IntoInner)]
56+
#[serde(try_from = "cosmwasm_std::Uint256")]
5657
pub struct Uint256(cosmwasm_std::Uint256);
5758

5859
impl TryFrom<cosmwasm_std::Uint256> for Uint256 {

0 commit comments

Comments
 (0)