diff --git a/crates/relayer/src/chain/axon.rs b/crates/relayer/src/chain/axon.rs index 57aeb4a2b..0a804a8f7 100644 --- a/crates/relayer/src/chain/axon.rs +++ b/crates/relayer/src/chain/axon.rs @@ -78,6 +78,7 @@ use self::{contract::OwnableIBCHandler, monitor::AxonEventMonitor}; type ContractProvider = SignerMiddleware, Wallet>; type IBCContract = OwnableIBCHandler; type ERC20Contract = ERC20; +type ICS20TransferERC20Contract = ICS20TransferERC20; use super::{ client::ClientSettings, @@ -135,6 +136,14 @@ abigen!( ]" ); +// mapping(bytes32 => string) public denomTraces; +abigen!( + ICS20TransferERC20, + r"[ + function denomTraces(bytes32 hash) external view returns (string) + ]" +); + pub struct AxonChain { rt: Arc, config: AxonChainConfig, @@ -165,6 +174,13 @@ impl AxonChain { )) } + fn transfer_contract(&self) -> Result { + Ok(ICS20TransferERC20::new( + self.config.transfer_contract_address, + self.contract_provider()?, + )) + } + fn erc20_contract(&self, address: H160) -> Result { Ok(ERC20::new(address, self.contract_provider()?)) } @@ -354,14 +370,18 @@ impl ChainEndpoint for AxonChain { Ok(vec![]) } - // FIXME implement this after use a real ics token contract - fn query_denom_trace(&self, _hash: String) -> Result { - // TODO: implement the real `query_denom_trace` function later - warn!("axon query_denom_trace() cannot implement"); - Ok(DenomTrace { - path: "".to_owned(), - base_denom: "".to_owned(), - }) + fn query_denom_trace(&self, hash: String) -> Result { + let hash_bytes = H256::from_str(hash.trim_start_matches("ibc/")).map_err(Error::other)?; + let contract = self.transfer_contract().map_err(Error::other)?; + let full_path: String = self + .rt + .block_on(contract.denom_traces(hash_bytes.into()).call()) + .map_err(|err| Error::query(format!("{err:?}")))?; + if full_path.is_empty() { + return Err(Error::empty_denom_trace(hash)); + } + let dt: DenomTrace = parse_denom_trace(full_path)?; + Ok(dt) } fn query_commitment_prefix(&self) -> Result { @@ -1100,6 +1120,48 @@ impl ChainEndpoint for AxonChain { } } +/// Modified from ibc-go https://github.com/cosmos/ibc-go/blob/main/modules/apps/transfer/types/trace.go#L31 +fn parse_denom_trace(raw_denom: String) -> Result { + let parts: Vec<_> = raw_denom.split('/').collect(); + if parts[0] == raw_denom { + return Ok(DenomTrace { + path: Default::default(), + base_denom: raw_denom, + }); + } + let (path, base_denom) = extract_path_and_base_from_full_denom(parts); + Ok(DenomTrace { path, base_denom }) +} + +fn extract_path_and_base_from_full_denom(parts: Vec<&str>) -> (String, String) { + fn is_valid_channel_id(c: &str) -> bool { + const PREFIX: &str = "channel-"; + if !c.starts_with(PREFIX) { + return false; + } + let r = c[PREFIX.len()..].parse::(); + r.is_ok() + } + + let mut path = Vec::new(); + let mut base = Vec::new(); + let len = parts.len(); + + for i in (0..len).step_by(2) { + if i < len - 1 && len > 2 && is_valid_channel_id(parts[i + 1]) { + path.push(parts[i]); + path.push(parts[i + 1]); + } else { + base.extend_from_slice(&parts[i..]); + break; + } + } + let path = path.join("/"); + let base = base.join("/"); + + (path, base) +} + impl AxonChain { fn init_event_monitor(&mut self) -> Result { crate::time!("axon_init_event_monitor"); diff --git a/crates/relayer/src/config/axon.rs b/crates/relayer/src/config/axon.rs index 1844606ea..0bf61d09d 100644 --- a/crates/relayer/src/config/axon.rs +++ b/crates/relayer/src/config/axon.rs @@ -12,6 +12,7 @@ pub struct AxonChainConfig { pub websocket_addr: WebSocketClientUrl, pub rpc_addr: Url, pub contract_address: ethers::types::Address, + pub transfer_contract_address: ethers::types::Address, pub restore_block_count: u64, pub key_name: String, pub store_prefix: String, diff --git a/crates/relayer/tests/config/fixtures/relayer_conf_example.toml b/crates/relayer/tests/config/fixtures/relayer_conf_example.toml index 7eee3920b..b8f0d658b 100644 --- a/crates/relayer/tests/config/fixtures/relayer_conf_example.toml +++ b/crates/relayer/tests/config/fixtures/relayer_conf_example.toml @@ -90,6 +90,7 @@ id = 'axon-0' websocket_addr = "ws://127.0.0.1:8010" rpc_addr = "http://127.0.0.1:8000/" contract_address = "0x15Ff10fCc8A1a50bFbE07847A22664801eA79E0f" +transfer_contract_address = "0x0000000000000000000000000000000000000000" restore_block_count = 10000 key_name = "relayer_axon_wallet" store_prefix = "forcerelay" diff --git a/tools/test-framework/src/types/single/node.rs b/tools/test-framework/src/types/single/node.rs index 09ae71aa8..fc11292ea 100644 --- a/tools/test-framework/src/types/single/node.rs +++ b/tools/test-framework/src/types/single/node.rs @@ -219,6 +219,7 @@ impl FullNode { let DeployedContracts { contract_address, + transfer_contract_address, image_cell_contract_address, ckb_light_client_contract_address, .. @@ -232,6 +233,7 @@ impl FullNode { websocket_addr, rpc_addr, contract_address, + transfer_contract_address, restore_block_count, ckb_light_client_contract_address, image_cell_contract_address,