Cross-Domain & Cross-Chain Communication
Canton Multi-Domain vs CosmWasm IBC
Canton's multi-domain architecture enables atomic transactions across different ledgers while maintaining sub-transaction privacy.
CANTON PROTOCOL
┌─────────────────────────────────────────────────────┐
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Domain A │ │ Domain B │ │
│ │ (Securities)│ │ (Payments) │ │
│ │ │ │ │ │
│ │ ┌──────────┐ │ │ ┌──────────┐ │ │
│ │ │Participant│◄─────────►│Participant│ │ │
│ │ │ 1 │ │ P1 │ │ 1 │ │ │
│ │ └──────────┘ │ Connected│ └──────────┘ │ │
│ │ │ Both │ │ │
│ │ ┌──────────┐ │ Domains │ ┌──────────┐ │ │
│ │ │Participant│ │ │ │Participant│ │ │
│ │ │ 2 │ │ │ │ 3 │ │ │
│ │ └──────────┘ │ │ └──────────┘ │ │
│ │ │ │ │ │
│ │ Sequencer │ │ Sequencer │ │
│ │ Mediator │ │ Mediator │ │
│ └──────────────┘ └──────────────┘ │
│ │
│ ═══════════════════════════════════════ │
│ ATOMIC CROSS-DOMAIN TRANSACTION │
│ (Participant 1 coordinates both sides) │
└─────────────────────────────────────────────────────┘
Each domain operates independently with its own consensus mechanism, governance, and operational policies.
Transactions spanning multiple domains are atomic - either all parts succeed or none do.
Each domain only sees the parts of the transaction relevant to its participants.
No single domain needs to know the complete global state - privacy is maintained throughout.
Inter-Blockchain Communication (IBC) enables standardized cross-chain communication in the Cosmos ecosystem through light client verification.
IBC PROTOCOL
┌─────────────────────────────────────────────────────┐
│ │
│ Chain A (Osmosis) Chain B (Juno) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ │ │ │ │
│ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │
│ │ │ Contract A │ │ │ │ Contract B │ │ │
│ │ │ (DEX Pool) │ │ │ │ (NFT Mkt) │ │ │
│ │ └──────┬──────┘ │ │ └──────▲──────┘ │ │
│ │ │ │ │ │ │ │
│ │ ▼ │ │ │ │ │
│ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │
│ │ │ IBC Module │ │ │ │ IBC Module │ │ │
│ │ │ Channel A │◄──────────►│ Channel B │ │ │
│ │ └─────────────┘ │ Packet │ └─────────────┘ │ │
│ │ │ Relay │ │ │
│ │ Light Client B │ │ Light Client A │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────────┐ │
│ │ RELAYER │ │
│ │ (Off-chain node) │ │
│ │ Submits proofs │ │
│ └─────────────────────┘ │
└─────────────────────────────────────────────────────┘
Each chain maintains a light client of connected chains for trustless verification.
Connections and channels provide structured communication paths between chains.
Reliable message delivery with timeout and acknowledgment mechanisms.
Any party can run a relayer to facilitate cross-chain communication.
| Aspect | Canton Multi-Domain | CosmWasm IBC |
|---|---|---|
| Atomicity | Native - all or nothing across domains | Eventual - timeout/ack based recovery |
| Latency | Single transaction finality | Multiple block confirmations required |
| Failure Handling | Automatic rollback | Manual timeout/refund logic |
| Privacy | Sub-transaction privacy maintained | All packet data visible on both chains |
| Trust Model | Domain operators | Light client consensus |
| Composability | Same Daml contracts work across domains | Custom IBC handlers per contract |
-- Delivery vs Payment across two domains
-- Securities on Domain A, Cash on Domain B
template DVPSettlement
with
buyer : Party
seller : Party
securityId : Text
quantity : Int
price : Decimal
where
signatory buyer, seller
-- Single atomic choice settles across both domains
choice Settle : ()
controller buyer, seller
do
-- Domain A: Transfer securities
exerciseByKey @Security
(seller, securityId)
TransferSecurity with
newOwner = buyer
units = quantity
-- Domain B: Transfer cash
exerciseByKey @CashAccount
(buyer, "USD")
Transfer with
recipient = seller
amount = price * intToDecimal quantity
-- Both succeed or both fail - atomic!
return ()
-- The Canton protocol ensures:
-- 1. Both domains validate their portion
-- 2. If either fails, both roll back
-- 3. Each party only sees relevant data
// IBC packet handling for cross-chain transfers
#[entry_point]
pub fn ibc_packet_receive(
deps: DepsMut,
env: Env,
msg: IbcPacketReceiveMsg,
) -> Result<IbcReceiveResponse, ContractError> {
let packet: TransferPacket = from_json(&msg.packet.data)?;
// Verify source channel
let config = CONFIG.load(deps.storage)?;
if msg.packet.src.channel_id != config.allowed_channel {
return Err(ContractError::UnauthorizedChannel {});
}
// Mint wrapped tokens on this chain
let recipient = deps.api.addr_validate(&packet.receiver)?;
// Create wrapped token denomination
let denom = format!(
"ibc/{}/{}",
msg.packet.src.channel_id,
packet.denom
);
BALANCES.update(
deps.storage,
(&recipient, &denom),
|balance| -> StdResult<_> {
Ok(balance.unwrap_or_default() + packet.amount)
}
)?;
// Acknowledge receipt
let ack = TransferAck::Success {};
Ok(IbcReceiveResponse::new()
.set_ack(to_json_binary(&ack)?)
.add_attribute("action", "ibc_receive")
.add_attribute("receiver", packet.receiver)
.add_attribute("amount", packet.amount.to_string()))
}
// Handle timeout - refund sender
#[entry_point]
pub fn ibc_packet_timeout(
deps: DepsMut,
_env: Env,
msg: IbcPacketTimeoutMsg,
) -> Result<IbcBasicResponse, ContractError> {
let packet: TransferPacket = from_json(&msg.packet.data)?;
// Refund the original sender
let sender = deps.api.addr_validate(&packet.sender)?;
BALANCES.update(
deps.storage,
(&sender, &packet.denom),
|balance| -> StdResult<_> {
Ok(balance.unwrap_or_default() + packet.amount)
}
)?;
Ok(IbcBasicResponse::new()
.add_attribute("action", "timeout_refund"))
}