Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
fish-sammy committed Nov 3, 2024
2 parents 641cce2 + 08bb9c3 commit afde668
Show file tree
Hide file tree
Showing 21 changed files with 1,766 additions and 443 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/interchain-token-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ cw-storage-plus = { workspace = true }
cw2 = { workspace = true }
error-stack = { workspace = true }
hex = { workspace = true }
itertools = "0.11.0"
msgs-derive = { workspace = true }
report = { workspace = true }
router-api = { workspace = true }
Expand Down
26 changes: 21 additions & 5 deletions contracts/interchain-token-service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,30 @@

## Overview

The Interchain Token Service (ITS) Hub contract is a crucial component of a cross-chain ITS protocol. It facilitates the transfer of tokens between different blockchains, manages token deployments, and maintains balance integrity across chains. It connects to ITS edge contracts on different chains (e.g. EVM ITS [contract](https://github.com/axelarnetwork/interchain-token-service)).
The Interchain Token Service (ITS) Hub contract is a crucial component of a cross-chain ITS protocol. It facilitates the
transfer of tokens between different blockchains, manages token deployments, and tracks the token supply across chains.
It connects to ITS edge contracts on different chains (e.g. EVM
ITS [contract](https://github.com/axelarnetwork/interchain-token-service)).

## Key Components

1. **ITS Message Processing**: Processes incoming ITS messages from trusted sources.
2. **Balance Tracking**: Ensures accurate token balances are maintained during cross-chain operations.
3. **ITS Address Registry**: Tracks the trusted ITS address for each chain for routing.
2. **Token Supply Tracking**: Ensures accurate token supply is maintained during cross-chain operations.
3. **ITS Address Registry**: Tracks the trusted ITS address for each chain for routing. This avoids each chain's ITS
contract from having to know about all other ITS contract addresses.

### Cross-chain messaging
## Cross-chain messaging

The ITS Hub makes use of the Axelarnet gateway [contract](../contracts/axelarnet-gateway/) to facilitate sending or receiving cross-chain messages. Messages are sent via `CallContract`, and received when the Axelarnet gateway is executed (by a relayer / user) through `Execute`, which in turn executes ITS Hub's `Execute` method.
The ITS Hub makes use of the Axelarnet gateway [contract](../contracts/axelarnet-gateway/) to facilitate sending or
receiving cross-chain messages. Messages are sent via `CallContract`, and received when the Axelarnet gateway is
executed (by a relayer / user) through `Execute`, which in turn executes ITS Hub's `Execute` method.

## Token Supply Invariant

ITS Hub maintains the token supply for native interchain tokens for every chain they're deployed to. This helps isolate
the security risk between chains. A compromised chain can only affect the total token supply that was sent to that chain
in the worst case. For example, if USDC was deployed from Ethereum to Solana via the ITS Hub, and 10M USDC was moved to
Solana (in total). If there's a compromise on Solana (potentially the chain itself, or the Solana ITS contract, or
another related contract), an attacker can only withdraw at most 10M USDC back to Ethereum or another chain (and not all
the bridged USDC locked on the Ethereum ITS contract). ITS Hub will prevent all USDC transfers from Solana once 10M USDC
has been moved back out from it.
105 changes: 62 additions & 43 deletions contracts/interchain-token-service/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use error_stack::{bail, ensure, report, Report, ResultExt};
use router_api::ChainNameRaw;

use crate::primitives::{HubMessage, Message};
use crate::{TokenId, TokenManagerType};
use crate::{primitives, TokenId, TokenManagerType};

// ITS Message payload types
// Reference: https://github.com/axelarnetwork/interchain-token-service/blob/v1.2.4/DESIGN.md#interchain-communication-spec
Expand Down Expand Up @@ -80,13 +80,13 @@ pub enum Error {
impl Message {
pub fn abi_encode(self) -> HexBinary {
match self {
Message::InterchainTransfer {
Message::InterchainTransfer(primitives::InterchainTransfer {
token_id,
source_address,
destination_address,
amount,
data,
} => InterchainTransfer {
}) => InterchainTransfer {
messageType: MessageType::InterchainTransfer.into(),
tokenId: FixedBytes::<32>::new(token_id.into()),
sourceAddress: Vec::<u8>::from(source_address).into(),
Expand All @@ -95,13 +95,13 @@ impl Message {
data: into_vec(data).into(),
}
.abi_encode_params(),
Message::DeployInterchainToken {
Message::DeployInterchainToken(primitives::DeployInterchainToken {
token_id,
name,
symbol,
decimals,
minter,
} => DeployInterchainToken {
}) => DeployInterchainToken {
messageType: MessageType::DeployInterchainToken.into(),
tokenId: FixedBytes::<32>::new(token_id.into()),
name: name.into(),
Expand All @@ -110,11 +110,11 @@ impl Message {
minter: into_vec(minter).into(),
}
.abi_encode_params(),
Message::DeployTokenManager {
Message::DeployTokenManager(primitives::DeployTokenManager {
token_id,
token_manager_type,
params,
} => DeployTokenManager {
}) => DeployTokenManager {
messageType: MessageType::DeployTokenManager.into(),
tokenId: FixedBytes::<32>::new(token_id.into()),
tokenManagerType: token_manager_type.into(),
Expand All @@ -136,7 +136,7 @@ impl Message {
let decoded = InterchainTransfer::abi_decode_params(payload, true)
.map_err(Error::AbiDecodeFailed)?;

Message::InterchainTransfer {
primitives::InterchainTransfer {
token_id: TokenId::new(decoded.tokenId.into()),
source_address: Vec::<u8>::from(decoded.sourceAddress)
.try_into()
Expand All @@ -149,18 +149,20 @@ impl Message {
.map_err(Error::NonEmpty)?,
data: from_vec(decoded.data.into())?,
}
.into()
}
MessageType::DeployInterchainToken => {
let decoded = DeployInterchainToken::abi_decode_params(payload, true)
.map_err(Error::AbiDecodeFailed)?;

Message::DeployInterchainToken {
primitives::DeployInterchainToken {
token_id: TokenId::new(decoded.tokenId.into()),
name: decoded.name.try_into().map_err(Error::NonEmpty)?,
symbol: decoded.symbol.try_into().map_err(Error::NonEmpty)?,
decimals: decoded.decimals,
minter: from_vec(decoded.minter.into())?,
}
.into()
}
MessageType::DeployTokenManager => {
let decoded = DeployTokenManager::abi_decode_params(payload, true)
Expand All @@ -171,13 +173,14 @@ impl Message {
.then(TokenManagerType::from_repr)
.ok_or_else(|| Error::InvalidTokenManagerType)?;

Message::DeployTokenManager {
primitives::DeployTokenManager {
token_id: TokenId::new(decoded.tokenId.into()),
token_manager_type,
params: Vec::<u8>::from(decoded.params)
.try_into()
.map_err(Error::NonEmpty)?,
}
.into()
}
_ => bail!(Error::InvalidMessageType),
};
Expand Down Expand Up @@ -284,7 +287,7 @@ mod tests {

use super::{DeployInterchainToken, InterchainTransfer};
use crate::abi::{DeployTokenManager, Error, MessageType, SendToHub};
use crate::{HubMessage, Message, TokenManagerType};
use crate::{primitives, HubMessage, TokenManagerType};

fn from_hex(hex: &str) -> nonempty::HexBinary {
HexBinary::from_hex(hex).unwrap().try_into().unwrap()
Expand All @@ -297,43 +300,47 @@ mod tests {
let cases = vec![
HubMessage::SendToHub {
destination_chain: remote_chain.clone(),
message: Message::InterchainTransfer {
message: primitives::InterchainTransfer {
token_id: [0u8; 32].into(),
source_address: from_hex("00"),
destination_address: from_hex("00"),
amount: 1u64.try_into().unwrap(),
data: None,
},
}
.into(),
},
HubMessage::SendToHub {
destination_chain: remote_chain.clone(),
message: Message::InterchainTransfer {
message: primitives::InterchainTransfer {
token_id: [255u8; 32].into(),
source_address: from_hex("4F4495243837681061C4743b74B3eEdf548D56A5"),
destination_address: from_hex("4F4495243837681061C4743b74B3eEdf548D56A5"),
amount: Uint256::MAX.try_into().unwrap(),
data: Some(from_hex("abcd")),
},
}
.into(),
},
HubMessage::ReceiveFromHub {
source_chain: remote_chain.clone(),
message: Message::InterchainTransfer {
message: primitives::InterchainTransfer {
token_id: [0u8; 32].into(),
source_address: from_hex("00"),
destination_address: from_hex("00"),
amount: 1u64.try_into().unwrap(),
data: None,
},
}
.into(),
},
HubMessage::ReceiveFromHub {
source_chain: remote_chain.clone(),
message: Message::InterchainTransfer {
message: primitives::InterchainTransfer {
token_id: [255u8; 32].into(),
source_address: from_hex("4F4495243837681061C4743b74B3eEdf548D56A5"),
destination_address: from_hex("4F4495243837681061C4743b74B3eEdf548D56A5"),
amount: Uint256::MAX.try_into().unwrap(),
data: Some(from_hex("abcd")),
},
}
.into(),
},
];

Expand Down Expand Up @@ -428,63 +435,69 @@ mod tests {
let cases = vec![
HubMessage::SendToHub {
destination_chain: remote_chain.clone(),
message: Message::DeployInterchainToken {
message: primitives::DeployInterchainToken {
token_id: [0u8; 32].into(),
name: "t".try_into().unwrap(),
symbol: "T".try_into().unwrap(),
decimals: 0,
minter: None,
},
}
.into(),
},
HubMessage::SendToHub {
destination_chain: remote_chain.clone(),
message: Message::DeployInterchainToken {
message: primitives::DeployInterchainToken {
token_id: [1u8; 32].into(),
name: "Test Token".try_into().unwrap(),
symbol: "TST".try_into().unwrap(),
decimals: 18,
minter: Some(from_hex("1234")),
},
}
.into(),
},
HubMessage::SendToHub {
destination_chain: remote_chain.clone(),
message: Message::DeployInterchainToken {
message: primitives::DeployInterchainToken {
token_id: [0u8; 32].into(),
name: "Unicode Token 🪙".try_into().unwrap(),
symbol: "UNI🔣".try_into().unwrap(),
decimals: 255,
minter: Some(from_hex("abcd")),
},
}
.into(),
},
HubMessage::ReceiveFromHub {
source_chain: remote_chain.clone(),
message: Message::DeployInterchainToken {
message: primitives::DeployInterchainToken {
token_id: [0u8; 32].into(),
name: "t".try_into().unwrap(),
symbol: "T".try_into().unwrap(),
decimals: 0,
minter: None,
},
}
.into(),
},
HubMessage::ReceiveFromHub {
source_chain: remote_chain.clone(),
message: Message::DeployInterchainToken {
message: primitives::DeployInterchainToken {
token_id: [1u8; 32].into(),
name: "Test Token".try_into().unwrap(),
symbol: "TST".try_into().unwrap(),
decimals: 18,
minter: Some(from_hex("1234")),
},
}
.into(),
},
HubMessage::ReceiveFromHub {
source_chain: remote_chain.clone(),
message: Message::DeployInterchainToken {
message: primitives::DeployInterchainToken {
token_id: [0u8; 32].into(),
name: "Unicode Token 🪙".try_into().unwrap(),
symbol: "UNI🔣".try_into().unwrap(),
decimals: 255,
minter: Some(from_hex("abcd")),
},
}
.into(),
},
];

Expand All @@ -509,35 +522,39 @@ mod tests {
let cases = vec![
HubMessage::SendToHub {
destination_chain: remote_chain.clone(),
message: Message::DeployTokenManager {
message: primitives::DeployTokenManager {
token_id: [0u8; 32].into(),
token_manager_type: TokenManagerType::NativeInterchainToken,
params: from_hex("00"),
},
}
.into(),
},
HubMessage::SendToHub {
destination_chain: remote_chain.clone(),
message: Message::DeployTokenManager {
message: primitives::DeployTokenManager {
token_id: [1u8; 32].into(),
token_manager_type: TokenManagerType::Gateway,
params: from_hex("1234"),
},
}
.into(),
},
HubMessage::ReceiveFromHub {
source_chain: remote_chain.clone(),
message: Message::DeployTokenManager {
message: primitives::DeployTokenManager {
token_id: [0u8; 32].into(),
token_manager_type: TokenManagerType::NativeInterchainToken,
params: from_hex("00"),
},
}
.into(),
},
HubMessage::ReceiveFromHub {
source_chain: remote_chain.clone(),
message: Message::DeployTokenManager {
message: primitives::DeployTokenManager {
token_id: [1u8; 32].into(),
token_manager_type: TokenManagerType::Gateway,
params: from_hex("1234"),
},
}
.into(),
},
];

Expand Down Expand Up @@ -646,13 +663,14 @@ mod tests {
let large_data = vec![0u8; 1024 * 1024]; // 1MB of data
let original = HubMessage::SendToHub {
destination_chain: ChainNameRaw::from_str("large-data-chain").unwrap(),
message: Message::InterchainTransfer {
message: primitives::InterchainTransfer {
token_id: [0u8; 32].into(),
source_address: from_hex("1234"),
destination_address: from_hex("5678"),
amount: Uint256::from(1u128).try_into().unwrap(),
data: Some(large_data.try_into().unwrap()),
},
}
.into(),
};

let encoded = original.clone().abi_encode();
Expand All @@ -664,13 +682,14 @@ mod tests {
fn encode_decode_unicode_strings() {
let original = HubMessage::SendToHub {
destination_chain: ChainNameRaw::from_str("chain").unwrap(),
message: Message::DeployInterchainToken {
message: primitives::DeployInterchainToken {
token_id: [0u8; 32].into(),
name: "Unicode Token 🪙".try_into().unwrap(),
symbol: "UNI🔣".try_into().unwrap(),
decimals: 18,
minter: Some(from_hex("abcd")),
},
}
.into(),
};

let encoded = original.clone().abi_encode();
Expand Down
Loading

0 comments on commit afde668

Please sign in to comment.