# ISO-20022 Intake / Gateway Contract on Different Blockchain Networks **Version:** 1.0 **Last Updated:** 2026-02-23 **Status:** Active **Companion to:** [SMART_CONTRACTS_ISO20022_FIN_METHODOLOGY.md](SMART_CONTRACTS_ISO20022_FIN_METHODOLOGY.md) --- ## 1. Purpose This document describes **how the intake or gateway contract** that receives ISO-20022 (and Fin) messages **works across different blockchain networks**: same logical contract, same address where possible, two delivery paths (relayer-submitted vs cross-chain), and per-chain configuration without breaking deterministic deployment. --- ## 2. Role of the Intake / Gateway Contract The **ISO intake contract** is the **single on-chain entry point** that: 1. **Accepts** a **canonical ISO message** (see [SMART_CONTRACTS_ISO20022_FIN_METHODOLOGY.md](SMART_CONTRACTS_ISO20022_FIN_METHODOLOGY.md)) from either: - an **off-chain relayer** (gateway that parsed MX/MT and submits the canonical payload), or - a **cross-chain message** (e.g. CCIP) that carries the canonical payload from another chain. 2. **Enforces** idempotency (by `instructionId` / `msgId`), **authorisation** (relayer role or CCIP router), and optional **policy** (ComplianceGuard, allowlists). 3. **Executes** the intended action: mint, transfer, or unlock for bridge, and **emits events** with canonical metadata for audit and ISO-20022 reporting. The contract does **not** parse raw MX/MT; it only ever sees the **canonical struct**. Parsing and mapping happen off-chain or on the source chain before submission. --- ## 3. Same Address on Every Network To keep integration simple and avoid per-chain address maps, the intake contract is deployed at the **same address on every supported chain**, following the same pattern as [UniversalCCIPBridge](../runbooks/MULTI_CHAIN_EXECUTION_CROSS_CHAIN_MESSAGE_HANDLING.md) and [MULTI_CHAIN_EXECUTION_DETERMINISTIC_DEPLOYMENT](../runbooks/MULTI_CHAIN_EXECUTION_DETERMINISTIC_DEPLOYMENT.md). ### 3.1 Deterministic Deployment (CREATE2) - **Formula:** `address = keccak256(0xff ++ deployer ++ salt ++ keccak256(bytecode))[12:]`. - **Identical bytecode** on every chain (same compiler, no chain-specific branches in bytecode). - **Identical constructor / initializer args** for the core contract; any **chain-specific** config (e.g. CCIP router, relayer list) is set **after** deployment via `initialize()` or setters. ### 3.2 Suggested Salt and Initialization | Item | Value | |------|--------| | **Contract name** | ISO20022IntakeGateway (or equivalent) | | **Salt** | `keccak256("ISO20022IntakeGateway")` (fixed, documented) | | **Constructor** | Minimal (e.g. none) or same admin everywhere | | **initialize(args)** | `admin`, optional `ccipRouter`, optional `relayer`; same `admin` on all chains; `ccipRouter` can be set to 0 and configured per chain later | This yields **one canonical intake contract address** across all networks (e.g. 138, 1, 56, 10, 137, 42161, 8453, 43114, and 651940 if supported). Integrators and off-chain gateways can use that single address regardless of chain. --- ## 4. Two Ways Messages Reach the Intake Contract Messages reach the intake contract in two ways: **direct submission by a relayer** (same chain) or **delivery via a cross-chain protocol** (e.g. CCIP) from another chain. ### 4.1 Path A: Relayer-Submitted (Same Chain) **Flow:** 1. Off-chain **gateway** receives ISO-20022 MX or SWIFT Fin MT. 2. Gateway **parses, validates, and maps** to the **canonical struct** (see methodology doc). 3. Gateway (as a **relayer**) calls the intake contract on the **target chain**: - `submitInbound(CanonicalMessage calldata m)` for credits (mint / release), or - `submitOutbound(CanonicalMessage calldata m)` for debits (burn / lock), with the relayer’s EOA or contract holding **RELAYER_ROLE** (or **INTAKE_RELAYER_ROLE**). **On-chain:** - `msg.sender` must have the relayer role. - Contract checks `processedInstructions[m.instructionId]` (or `processedMessages[m.msgId]`); reverts if already processed. - Contract optionally checks ComplianceGuard / PolicyManager using `m.debtorId`, `m.creditorId`, `m.purpose`. - Contract performs the action (mint, transfer, bridge unlock) and sets `processedInstructions[m.instructionId] = true`. - Contract emits an event with canonical fields for audit and pacs.002/camt.054 mapping. **Per-chain:** Only the **relayer address(es)** need to be configured per chain (e.g. different gateway EOA or multisig per network). The intake contract bytecode and address stay the same. ### 4.2 Path B: Cross-Chain Delivery (e.g. CCIP) **Flow:** 1. On the **source chain**, an authorised sender (e.g. the **same intake contract** at the same address, or a dedicated “sender” contract) encodes the **canonical struct** into `bytes data` and sends a **CCIP** (or other cross-chain) message to the **destination chain**, with **receiver** = intake contract address (same canonical address). 2. On the **destination chain**, the **CCIP router** calls the intake contract’s **receive** entry point (e.g. `ccipReceive(Any2EVMMessage calldata message)`). 3. The intake contract: - Verifies the call is from the **CCIP router** (or a designated receiver adapter) via `msg.sender == ccipRouter` or a **ROUTER_ROLE** check. - Decodes `message.data` to obtain the **CanonicalMessage**. - Applies **replay protection** using `message.messageId` and/or the decoded `instructionId` (must not already be in `processedMessages` / `processedInstructions`). - Optionally validates **source chain** and **sender** from `message.sourceChainSelector` and `message.sender` (allowlist or “same intake contract on source chain”). - Executes the same logic as Path A (mint / transfer / unlock) and emits the same canonical events. **Per-chain:** The **CCIP router address** is chain-specific. It is set in `initialize()` or via `setCCIPRouter(address)` after deployment so that the same bytecode is used everywhere. On chains without CCIP (e.g. 651940), the router can be set to `address(0)` and Path B disabled; only Path A (relayer) is used. --- ## 5. Contract Interface (Summary) The intake contract exposes at least: | Entry point | Caller | Purpose | |-------------|--------|---------| | **submitInbound(CanonicalMessage)** | Relayer (Path A) | Process an inbound credit (mint / release from bridge). | | **submitOutbound(CanonicalMessage)** | Relayer (Path A) | Process an outbound debit (burn / lock for bridge). | | **ccipReceive(Any2EVMMessage)** | CCIP router only (Path B) | Decode payload to CanonicalMessage and process as inbound (or outbound if encoded so). | Optional: - **setCCIPRouter(address)** – Admin; for deterministic deploy, init with router=0 then set per chain. - **addRelayer(address)** / **removeRelayer(address)** – Admin; manage who can call submitInbound/submitOutbound. Idempotency key: **instructionId** (and optionally msgId). Storage: `mapping(bytes32 => bool) public processedInstructions;` and, for CCIP, `mapping(bytes32 => bool) public processedMessages;` keyed by CCIP `messageId` to avoid replay from the transport layer. --- ## 6. How It Works on Different Networks (By Chain Type) ### 6.1 Chains With CCIP (e.g. 138, 1, 56, 10, 137, 42161, 8453, 43114) - **Deploy** the intake contract via CREATE2 with the same salt and init args (e.g. admin; router=0). - **Post-deploy:** Call `setCCIPRouter(ccipRouterAddress)` with that chain’s CCIP router. - **Relayer:** Grant RELAYER_ROLE to the gateway(s) that will submit canonical messages on this chain. - **Behaviour:** Both Path A (relayer) and Path B (CCIP) are active. Messages can arrive from off-chain (Path A) or from another chain (Path B) with the same canonical format. ### 6.2 Chains Without CCIP (e.g. ALL Mainnet 651940) - **Deploy** the same contract at the same address via CREATE2 (same salt, same init; no CCIP router). - Leave **CCIP router** as `address(0)` (or never set it). **Path B is unused.** - **Relayer:** Only Path A is used; the off-chain gateway submits canonical messages via `submitInbound` / `submitOutbound` from an address with RELAYER_ROLE. - Optionally, a **custom cross-chain transport** (e.g. AlltraCustomBridge-style) could later call a dedicated function that accepts the same canonical payload, with access control analogous to the CCIP router check. ### 6.3 Same Address, Different Config - **Address:** Identical across all networks (CREATE2 + same bytecode + same constructor/init args). - **Config that can differ per chain:** - CCIP router address (or 0), - Relayer list (RELAYER_ROLE), - Optional: ComplianceGuard / PolicyManager / vault addresses if set via setters after deploy. No per-chain address map is needed in application logic; only the single intake contract address is used, and chain-specific behaviour is controlled by which roles and router are set on that chain. --- ## 7. Security and Replay - **Path A:** Idempotency by `instructionId` (and optionally `msgId`). Only RELAYER_ROLE can submit; relayer identity is per chain. - **Path B:** Replay protection by CCIP `messageId` and by decoded `instructionId`; only the CCIP router (or ROUTER_ROLE) can call `ccipReceive`. Validate source chain and sender if required (e.g. only accept from the same intake contract on allowed source chains). - **Payload integrity:** Optional check of `payloadHash` in the canonical struct against an off-chain attested hash; contract can store or emit it for audit. --- ## 8. Downstream Actions The intake contract does not hold balances long-term; it **forwards** the intent to: - **Mint:** Call token factory or mint controller (with reserve/attestation checks as in [MULTI_CHAIN_EXECUTION_ISO20022_EMONEY](../runbooks/MULTI_CHAIN_EXECUTION_ISO20022_EMONEY.md)). - **Transfer:** Call token `transfer` or a vault that holds tokens. - **Bridge unlock:** Call the bridge/vault contract’s release or unlock function with the same canonical metadata so that bridge and e-money runbooks stay aligned. All such downstream calls should carry or emit the same canonical identifiers (instructionId, msgId, debtorId, creditorId, payloadHash) for audit and ISO-20022 reporting. --- ## 9. Related Documents | Document | Description | |----------|-------------| | [SMART_CONTRACTS_ISO20022_FIN_METHODOLOGY.md](SMART_CONTRACTS_ISO20022_FIN_METHODOLOGY.md) | Canonical format, mapping, validation, and contract interface for ISO/Fin. | | [MULTI_CHAIN_EXECUTION_ISO20022_EMONEY.md](../runbooks/MULTI_CHAIN_EXECUTION_ISO20022_EMONEY.md) | E-Money and ISO-20022 canonical message semantics. | | [MULTI_CHAIN_EXECUTION_CROSS_CHAIN_MESSAGE_HANDLING.md](../runbooks/MULTI_CHAIN_EXECUTION_CROSS_CHAIN_MESSAGE_HANDLING.md) | Cross-chain message handling, same address, replay, sender verification. | | [MULTI_CHAIN_EXECUTION_DETERMINISTIC_DEPLOYMENT.md](../runbooks/MULTI_CHAIN_EXECUTION_DETERMINISTIC_DEPLOYMENT.md) | CREATE2, salts, and deployment order. | --- **Document Control** - **Owner:** Configuration / Integration - **Review:** When intake contract interface or supported chains change