From e7946e5685a0657048366a62a275f378943ab4cf Mon Sep 17 00:00:00 2001 From: Peter Watts Date: Fri, 6 Feb 2026 15:26:52 +1100 Subject: [PATCH 1/5] Rewrite protocol docs: add Relay Chain, Allocator, Mermaid diagrams, and legal language fixes Complete rewrite of the Settlement Protocol documentation section: - Add Relay Chain documentation (Chain ID 537713, Sovereign Stack, Celestia DA) - Add Allocator component docs (MPC chain signatures, Payload Builders) - Restructure from flat component pages to narrative-driven architecture - Add comprehensive flow walkthroughs (Execution, Settlement, Withdrawal, Revert) - Add 8 inline Mermaid sequence diagrams with interactive zoom/pan - Add Design Principles and Security & Audits pages - Add solver and app integration guides - Consolidate contract references and addresses - Separate Vaults as distinct protocol section - Apply legal language fixes (crosschain, revert, destination, asset terminology) - Add 13 URL redirects for old pages - Delete 8 obsolete files Co-Authored-By: Claude Opus 4.6 --- docs.json | 135 ++- references/protocol/addresses.mdx | 85 ++ references/protocol/audits.mdx | 15 - references/protocol/components/allocator.mdx | 99 ++ references/protocol/components/depository.mdx | 75 ++ references/protocol/components/hub.mdx | 70 ++ references/protocol/components/oracle.mdx | 83 ++ .../protocol/contracts/evm-depository.mdx | 370 +++++++ .../protocol/contracts/solana-depository.mdx | 973 ++++++++++++++++++ .../protocol/depository/architecture.mdx | 34 - references/protocol/depository/overview.mdx | 10 - references/protocol/depository/security.mdx | 11 - references/protocol/design.mdx | 101 ++ references/protocol/guides/for-apps.mdx | 69 ++ references/protocol/guides/for-solvers.mdx | 80 ++ references/protocol/how-it-works.mdx | 182 ++++ references/protocol/hub/architecture.mdx | 14 - references/protocol/hub/overview.mdx | 12 - references/protocol/oracle/architecture.mdx | 35 - references/protocol/oracle/overview.mdx | 12 - references/protocol/overview.mdx | 113 +- references/protocol/relay-chain.mdx | 55 + references/protocol/security.mdx | 87 ++ references/protocol/vaults/overview.mdx | 2 + 24 files changed, 2501 insertions(+), 221 deletions(-) create mode 100644 references/protocol/addresses.mdx delete mode 100644 references/protocol/audits.mdx create mode 100644 references/protocol/components/allocator.mdx create mode 100644 references/protocol/components/depository.mdx create mode 100644 references/protocol/components/hub.mdx create mode 100644 references/protocol/components/oracle.mdx create mode 100644 references/protocol/contracts/evm-depository.mdx create mode 100644 references/protocol/contracts/solana-depository.mdx delete mode 100644 references/protocol/depository/architecture.mdx delete mode 100644 references/protocol/depository/overview.mdx delete mode 100644 references/protocol/depository/security.mdx create mode 100644 references/protocol/design.mdx create mode 100644 references/protocol/guides/for-apps.mdx create mode 100644 references/protocol/guides/for-solvers.mdx create mode 100644 references/protocol/how-it-works.mdx delete mode 100644 references/protocol/hub/architecture.mdx delete mode 100644 references/protocol/hub/overview.mdx delete mode 100644 references/protocol/oracle/architecture.mdx delete mode 100644 references/protocol/oracle/overview.mdx create mode 100644 references/protocol/relay-chain.mdx create mode 100644 references/protocol/security.mdx diff --git a/docs.json b/docs.json index e446357..bbd3f40 100644 --- a/docs.json +++ b/docs.json @@ -227,70 +227,61 @@ "tab": "Relay Protocol", "groups": [ { - "group": "Relay Protocol", + "group": "Settlement", "pages": [ "references/protocol/overview", - "references/protocol/audits", + "references/protocol/how-it-works", + "references/protocol/relay-chain", { - "group": "Depository", + "group": "Components", "pages": [ - "references/protocol/depository/overview", - "references/protocol/depository/architecture", - "references/protocol/depository/security", - { - "group": "Guides", - "pages": [ - "references/protocol/depository/guides/integrating" - ] - }, - { - "group": "Contracts Reference", - "pages": [ - "references/protocol/depository/contracts/Solana-Relay-Depository", - "references/protocol/depository/contracts/Evm-Relay-Depository" - ] - }, - "references/protocol/depository/addresses" + "references/protocol/components/depository", + "references/protocol/components/oracle", + "references/protocol/components/hub", + "references/protocol/components/allocator" + ] + }, + "references/protocol/design", + "references/protocol/security", + { + "group": "Guides", + "pages": [ + "references/protocol/guides/for-solvers", + "references/protocol/guides/for-apps" ] }, { - "group": "Oracle", + "group": "Contract Reference", "pages": [ - "references/protocol/oracle/overview", - "references/protocol/oracle/architecture" + "references/protocol/contracts/evm-depository", + "references/protocol/contracts/solana-depository" ] }, + "references/protocol/addresses" + ] + }, + { + "group": "Vaults", + "pages": [ + "references/protocol/vaults/overview", + "references/protocol/vaults/architecture", + "references/protocol/vaults/security", + "references/protocol/vaults/backend", { - "group": "Hub", + "group": "Guides", "pages": [ - "references/protocol/hub/overview", - "references/protocol/hub/architecture" + "references/protocol/vaults/guides/bridging", + "references/protocol/vaults/guides/interacting" ] }, { - "group": "Vaults", + "group": "Contracts Reference", "pages": [ - "references/protocol/vaults/overview", - "references/protocol/vaults/architecture", - "references/protocol/vaults/security", - "references/protocol/vaults/backend", - { - "group": "Guides", - "pages": [ - "references/protocol/vaults/guides/bridging", - "references/protocol/vaults/guides/interacting" - ] - }, - { - "group": "Contracts Reference", - "pages": [ - "references/protocol/vaults/contracts/RelayPoolFactory", - "references/protocol/vaults/contracts/RelayPool", - "references/protocol/vaults/contracts/RelayPoolNativeGateway", - "references/protocol/vaults/contracts/RelayBridgeFactory", - "references/protocol/vaults/contracts/RelayBridge" - ] - } + "references/protocol/vaults/contracts/RelayPoolFactory", + "references/protocol/vaults/contracts/RelayPool", + "references/protocol/vaults/contracts/RelayPoolNativeGateway", + "references/protocol/vaults/contracts/RelayBridgeFactory", + "references/protocol/vaults/contracts/RelayBridge" ] } ] @@ -457,6 +448,54 @@ { "source": "/references/api/api_core_concepts/quote-errors", "destination": "/references/api/api_core_concepts/handling-errors" + }, + { + "source": "/references/protocol/audits", + "destination": "/references/protocol/security" + }, + { + "source": "/references/protocol/depository/overview", + "destination": "/references/protocol/components/depository" + }, + { + "source": "/references/protocol/depository/architecture", + "destination": "/references/protocol/components/depository" + }, + { + "source": "/references/protocol/depository/security", + "destination": "/references/protocol/security" + }, + { + "source": "/references/protocol/depository/guides/integrating", + "destination": "/references/protocol/guides/for-apps" + }, + { + "source": "/references/protocol/depository/contracts/Evm-Relay-Depository", + "destination": "/references/protocol/contracts/evm-depository" + }, + { + "source": "/references/protocol/depository/contracts/Solana-Relay-Depository", + "destination": "/references/protocol/contracts/solana-depository" + }, + { + "source": "/references/protocol/depository/addresses", + "destination": "/references/protocol/addresses" + }, + { + "source": "/references/protocol/oracle/overview", + "destination": "/references/protocol/components/oracle" + }, + { + "source": "/references/protocol/oracle/architecture", + "destination": "/references/protocol/components/oracle" + }, + { + "source": "/references/protocol/hub/overview", + "destination": "/references/protocol/components/hub" + }, + { + "source": "/references/protocol/hub/architecture", + "destination": "/references/protocol/components/hub" } ] } diff --git a/references/protocol/addresses.mdx b/references/protocol/addresses.mdx new file mode 100644 index 0000000..221f978 --- /dev/null +++ b/references/protocol/addresses.mdx @@ -0,0 +1,85 @@ +--- +title: Contracts Addresses +description: Addresses of Relay Depository contracts +sidebarTitle: Contracts Addresses +--- + +{/* Generated using: "curl https://api.relay.link/chains | jq '.chains[] | "|\(.protocol.v2.chainId)|\(.protocol.v2.depository)|\(.vmType)|\(.id)"" */} + +| Chain | Address | VM Type | EVM Chain Id | +| -------------- | -------------------------------------------- | ----------- | ------------ | +| ethereum | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 1 | +| optimism | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 10 | +| cronos | 0x59916da825d2d2ec1bf878d71c88826f6633ecca | ethereum-vm | 25 | +| bnb | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 56 | +| gnosis | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 100 | +| unichain | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 130 | +| polygon | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 137 | +| sonic | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 146 | +| manta | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 169 | +| mint | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 185 | +| boba | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 288 | +| zksync | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 324 | +| shape | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 360 | +| appchain | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 466 | +| worldchain | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 480 | +| redstone | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 690 | +| flow | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 747 | +| hyperevm | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 999 | +| metis | 0x59916da825d2d2ec1bf878d71c88826f6633ecca | ethereum-vm | 1088 | +| polygon_zkevm | 0x59916da825d2d2ec1bf878d71c88826f6633ecca | ethereum-vm | 1101 | +| lisk | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 1135 | +| sei | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 1329 | +| perennial | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 1424 | +| story | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 1514 | +| gravity | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 1625 | +| soneium | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 1868 | +| swellchain | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 1923 | +| sanko | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 1996 | +| ronin | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 2020 | +| abstract | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 2741 | +| morph | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 2818 | +| hychain | 0x59916da825d2d2ec1bf878d71c88826f6633ecca | ethereum-vm | 2911 | +| mantle | 0x59916da825d2d2ec1bf878d71c88826f6633ecca | ethereum-vm | 5000 | +| somnia | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 5031 | +| superseed | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 5330 | +| cyber | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 7560 | +| powerloom | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 7869 | +| arena_z | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 7897 | +| b3 | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 8333 | +| base | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 8453 | +| onchain_points | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 17071 | +| apechain | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 33139 | +| funki | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 33979 | +| mode | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 34443 | +| arbitrum | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 42161 | +| arbitrum_nova | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 42170 | +| celo | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 42220 | +| hemi | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 43111 | +| avalanche | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 43114 | +| gunz | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 43419 | +| zircuit | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 48900 | +| superposition | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 55244 | +| ink | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 57073 | +| linea | 0x59916da825d2d2ec1bf878d71c88826f6633ecca | ethereum-vm | 59144 | +| bob | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 60808 | +| anime | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 69000 | +| berachain | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 80094 | +| blast | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 81457 | +| plume | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 98866 | +| taiko | 0x59916da825d2d2ec1bf878d71c88826f6633ecca | ethereum-vm | 167000 | +| syndicate | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 510003 | +| scroll | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 534352 | +| zero | 0xa88cf7864951147a08707ed732237eaa9b1c3b9b | ethereum-vm | 543210 | +| xai | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 660279 | +| katana | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 747474 | +| forma | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 984122 | +| zora | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 7777777 | +| bitcoin | bc1qdqqsq6y7csd0cr3ye45h9lv8ydh777j2wehgl6 | bitcoin-vm | | +| eclipse | 99vQwtBwYtrqqD9YSXbdum3KBdxPAVxYTaQ3cfnJSrN2 | solana-vm | | +| soon | 99vQwtBwYtrqqD9YSXbdum3KBdxPAVxYTaQ3cfnJSrN2 | solana-vm | | +| corn | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 21000000 | +| degen | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 666666666 | +| solana | 99vQwtBwYtrqqD9YSXbdum3KBdxPAVxYTaQ3cfnJSrN2 | solana-vm | | +| ancient8 | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 888888888 | +| rari | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 1380012617 | diff --git a/references/protocol/audits.mdx b/references/protocol/audits.mdx deleted file mode 100644 index d5adefc..0000000 --- a/references/protocol/audits.mdx +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: Audits -description: "Our code has been audited by leading security firms" -sidebarTitle: Audits ---- - -We implement and deploy the Relay Protocol incrementally, starting with a minimal set of features and building up to a fully trustless cross-chain payments system. Each component is audited by leading security firms to ensure the safety of user funds. - -Here is the list of audits we have completed so far: - -| Date | Scope | Auditor | Report | -| ------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| February 2025 | [Vaults](./vaults/), RelayPeriphery contracts (Router, CreditMaster renamed [Depository](./depository/) ) | [Spearbit/Cantina](https://cantina.xyz/solutions/spearbit). | [Report 1](https://github.com/relayprotocol/relay-vaults/blob/main/docs/report-cantinacode-relay-protocol-0203.pdf), [Report 2](https://github.com/relayprotocol/relay-vaults/blob/main/docs/report-cantinacode-relay-protocol-rereview-0310.pdf) | -| June 2025 | Solana Relay [Depository](./depository/) | [Certora](https://www.certora.com/) | [Report](https://github.com/relayprotocol/relay-depository/blob/main/audit-reports/Certora-Relay-Escrow-Report.pdf) | -| November 2025 | Relay [Depository](./depository/) (2nd audit for both EVM and SVM), Relay Allocator (and payload builders) | [Zellic](https://www.zellic.io/) | [Report](https://github.com/relayprotocol/settlement-protocol/blob/main/docs/audits/Relay-Settlement-Protocol-Zellic-Audit.pdf) | diff --git a/references/protocol/components/allocator.mdx b/references/protocol/components/allocator.mdx new file mode 100644 index 0000000..d662e19 --- /dev/null +++ b/references/protocol/components/allocator.mdx @@ -0,0 +1,99 @@ +--- +title: Allocator +description: "The MPC-based component that authorizes crosschain withdrawals" +sidebarTitle: Allocator +--- + +## Overview + +The Allocator is the component responsible for authorizing withdrawals from [Depository](/references/protocol/components/depository) contracts. When a solver wants to claim funds they've earned by filling orders, the Allocator verifies their [Hub](/references/protocol/components/hub) balance and generates a cryptographic proof that the Depository will accept. + +The Allocator uses **MPC (Multi-Party Computation) chain signatures** via the NEAR protocol, meaning no single entity holds the private keys needed to authorize withdrawals. + +## How It Works + +```mermaid +sequenceDiagram + participant Solver + participant Allocator + participant Hub + participant PB as Payload Builder + participant MPC as NEAR MPC + participant Depository + + Solver->>Allocator: 1. Request withdrawal (chain, asset, amount) + Allocator->>Hub: 2. Verify solver balance + Hub-->>Allocator: Balance confirmed + Allocator->>PB: 3. Construct chain-specific payload + PB-->>Allocator: Withdrawal request (e.g. CallRequest) + Allocator->>MPC: 4. Request MPC signature + MPC-->>Allocator: Signed proof + Allocator-->>Solver: 5. Return signed withdrawal proof + Solver->>Depository: 6. Submit proof to target chain + Depository-->>Solver: Funds released +``` + +The withdrawal authorization process: + +1. **Solver requests withdrawal** — The solver specifies the target chain, asset, and amount they want to withdraw + +2. **Balance check** — The Allocator reads the solver's balance on the Hub to confirm sufficient funds are available + +3. **Payload construction** — A chain-specific **Payload Builder** constructs the withdrawal request in the format the target chain's Depository expects + +4. **MPC signing** — The Allocator requests an MPC signature from the NEAR chain signatures network. This generates a valid signature without any single party having access to the full private key + +5. **Proof delivery** — The signed withdrawal proof is returned to the solver, who submits it to the Depository contract on the target chain + +## Payload Builders + +Different chains require different transaction formats. The Allocator uses specialized Payload Builders for each chain type: + +| Builder | Chains | Signature Format | +|---------|--------|------------------| +| **EVM Payload Builder** | Ethereum, Base, Arbitrum, Optimism, + all EVM chains | EIP-712 typed data | +| **Solana Payload Builder** | Solana, Eclipse, Soon | Ed25519 | +| **Bitcoin Payload Builder** | Bitcoin | ECDSA (secp256k1) | +| **Sui Payload Builder** | Sui | Ed25519 | + +Each Payload Builder constructs the chain-specific request structure (e.g., `CallRequest` for EVM, `TransferRequest` for Solana) with the appropriate encoding (ABI for EVM, Borsh for Solana). + +## Security Model + +The Allocator is a trust-critical component — it controls access to funds in the Depository. Several safeguards protect against misuse: + +### MPC Chain Signatures + +The Allocator's signing keys are managed through NEAR's MPC chain signatures. This means: + +- **No single key holder** — The private key is split across multiple MPC nodes, and a threshold must cooperate to produce a signature +- **Programmable authorization** — The Allocator is a smart contract that can enforce rules (balance checks, rate limits) before requesting a signature +- **Auditable** — All signing requests go through an onchain contract, creating a transparent record + +### Replay Protection + +Every withdrawal proof includes: + +- **Nonce** — A unique, incrementing value that prevents the same proof from being used twice +- **Expiration** — A timestamp after which the proof is no longer valid, preventing stale proofs from being used + +### Security Council + +The Allocator is governed by a **Security Council** — a multisig composed of multiple independent parties across different timezones. The Security Council can: + +- **Pause** the Allocator (1-of-N threshold) — Any member can immediately halt withdrawals in an emergency +- **Unpause** the Allocator (majority threshold) — Resume operations after a pause +- **Replace** the Allocator (supermajority threshold) — Set a new Allocator in case of a bug or required upgrade +- **Change membership** (supermajority threshold) — Add or remove Security Council members + +### Global Pause + +Because all withdrawals across all chains flow through a single Allocator, the entire protocol can be paused with a single transaction. This is a significant security advantage over protocols where escrow contracts on each chain must be paused individually. + + +The Allocator cannot mint new balances or alter the Hub ledger. It can only authorize withdrawals up to a solver's existing Hub balance. Even a compromised Allocator cannot create funds that don't exist. + + +## Source Code + +The Allocator contract (`RelayAllocator.sol`) is part of the [`settlement-protocol`](https://github.com/relayprotocol/settlement-protocol) repository. It is deployed on Aurora (NEAR ecosystem) for direct integration with NEAR MPC chain signatures. diff --git a/references/protocol/components/depository.mdx b/references/protocol/components/depository.mdx new file mode 100644 index 0000000..68eac3c --- /dev/null +++ b/references/protocol/components/depository.mdx @@ -0,0 +1,75 @@ +--- +title: Depository +description: "The escrow contract deployed on every supported chain" +sidebarTitle: Depository +--- + +## Overview + +The Depository is a smart contract deployed on every chain that Relay supports. It serves as the entry point for user funds — accepting deposits and releasing withdrawals when authorized by the [Allocator](/references/protocol/components/allocator). + +There are currently 80+ Depository contracts deployed, one per supported chain. Each contract is **non-upgradable**, meaning its logic cannot be changed after deployment. See [Contract Addresses](/references/protocol/addresses) for the full list. + +## How It Works + +The Depository has two core responsibilities: + +1. **Accept deposits** — Users send funds to the Depository, tagged with an **orderId** that ties the deposit to a specific solver commitment +2. **Execute withdrawals** — When a solver presents a valid proof from the Allocator, the Depository releases funds + +The Depository does not track orders, verify fills, or manage balances. It is intentionally minimal — a secure vault that holds funds and releases them only when presented with a valid cryptographic proof. + +## Deposits + +Deposits are designed to be as gas-efficient as possible. On EVM chains, a deposit costs approximately **21,000 gas** — close to a raw ETH transfer. + +### EVM Chains + +The EVM Depository supports two deposit methods: + +- **`depositNative(depositor, id)`** — Deposit native ETH (or the chain's native asset) +- **`depositErc20(depositor, token, amount, id)`** — Deposit any ERC20 token + +Both methods emit events that the [Oracle](/references/protocol/components/oracle) monitors to verify deposits. + +### Solana + +The Solana Depository is an Anchor program that supports: + +- **`deposit_native(amount, id)`** — Deposit native SOL +- **`deposit_token(amount, id)`** — Deposit SPL tokens (including Token-2022) + +Funds are held in a program-derived address (PDA) vault. + +## Withdrawals + +Withdrawals are triggered by solvers who have accumulated balances on the [Hub](/references/protocol/components/hub). The process: + +1. Solver requests a withdrawal from the [Allocator](/references/protocol/components/allocator) +2. Allocator generates a signed **CallRequest** (EVM) or **TransferRequest** (Solana) +3. Solver submits the signed request to the Depository's `execute` function +4. Depository verifies the signature against the registered allocator address +5. Funds are transferred to the solver + +Each withdrawal request includes a **nonce** and **expiration** to prevent replay attacks and stale proofs. + + +Only the registered Allocator can authorize withdrawals. The Allocator address is set at deployment and can only be changed by the contract owner (a security council multisig). + + +## Security + +The Depository is designed with minimal trust assumptions: + +- **Non-upgradable** — Contract logic cannot change after deployment +- **Single authority** — Only the registered Allocator can authorize fund movements +- **Short custody** — Funds are held briefly. Most orders fill within 2 seconds, and solvers withdraw funds every few minutes +- **Audited** — Reviewed by [Spearbit](/references/protocol/security) (February 2025) and [Certora](/references/protocol/security) (June 2025) + +## Contract References + +For full API documentation of the Depository contracts: + +- [EVM Depository Reference](/references/protocol/contracts/evm-depository) +- [Solana Depository Reference](/references/protocol/contracts/solana-depository) +- [Contract Addresses](/references/protocol/addresses) diff --git a/references/protocol/components/hub.mdx b/references/protocol/components/hub.mdx new file mode 100644 index 0000000..2763cd6 --- /dev/null +++ b/references/protocol/components/hub.mdx @@ -0,0 +1,70 @@ +--- +title: Hub +description: "The central ledger tracking all token ownership on the Relay Chain" +sidebarTitle: Hub +--- + +## Overview + +The Hub is a smart contract on the [Relay Chain](/references/protocol/relay-chain) that serves as the central ledger for the protocol. It tracks the ownership of all funds deposited into [Depository](/references/protocol/components/depository) contracts across every supported chain. + +Implemented as an [ERC6909](https://eips.ethereum.org/EIPS/eip-6909) multi-token contract, the Hub can represent any deposited asset (ETH, USDC, SOL, etc.) from any chain as a token balance. When the [Oracle](/references/protocol/components/oracle) attests that a deposit or fill occurred, the Hub updates balances accordingly. + +## How It Works + +The Hub maintains a global balance sheet: + +- When a user **deposits** into a Depository, the Oracle attests this, and the Hub **mints** a corresponding token balance for the user +- When a solver **fills** an order, the Oracle attests this, and the Hub **transfers** the balance from the user to the solver +- When a solver **withdraws** from a Depository, the Oracle attests this, and the Hub **burns** the corresponding balance + +This means the Hub always reflects the current state of all funds across all chains in real-time. + +## Actions + +The Hub processes three types of actions, all triggered by the [Oracle](/references/protocol/components/oracle): + +| Action | Trigger | Effect | +|--------|---------|--------| +| **MINT** | User deposit attested | Creates token balance for the depositor | +| **TRANSFER** | Solver fill attested | Moves balance from user to solver | +| **BURN** | Solver withdrawal attested | Removes balance (funds already claimed) | + +Each action is idempotent — the same attestation cannot be processed twice, preventing double-counting. + +## Real-Time Settlement + +A key advantage of the Hub model is **real-time, per-order settlement**. Every order settles individually as soon as the Oracle attests the fill. There is no batching window or settlement delay. + +This is possible because settlement happens on the Relay Chain, where gas is extremely cheap (~$0.005 per settlement). In contrast, protocols that settle on origin chains must batch orders to amortize the high gas costs, forcing solvers to wait hours before they can reclaim capital. + +**Benefit for solvers:** Immediate balance updates mean solvers can track their available capital in real-time and make withdrawal decisions based on current inventory needs, rather than waiting for batch cycles. + +## Token Representation + +The Hub uses the ERC6909 standard (multi-token) to represent deposited assets. Each unique combination of asset and origin chain gets a token ID on the Hub. + +For example: +- ETH deposited on Optimism → token ID `X` +- ETH deposited on Base → token ID `Y` +- USDC deposited on Arbitrum → token ID `Z` + +Token metadata (name, symbol, decimals, origin chain) is stored on-chain, and each Hub token can be viewed through an ERC20-compatible wrapper contract (`ERC20View`) for standard wallet compatibility. + +## ERC6909 Interface + +The Hub implements the full ERC6909 interface, including: + +- **`balanceOf(owner, tokenId)`** — Check a user's or solver's balance for a specific token +- **`transfer(receiver, tokenId, amount)`** — Transfer Hub tokens between addresses +- **`approve(spender, tokenId, amount)`** — Approve another address to spend tokens +- **Operator system** — Grant an address full control over all token balances +- **EIP-712 permits** — Gasless approval signatures + + +While the Hub supports direct transfers between addresses, in practice most balance changes come from Oracle attestations (MINT, TRANSFER, BURN) rather than user-initiated transfers. + + +## Source Code + +The Hub contract (`RelayHub.sol`) is part of the [`settlement-protocol`](https://github.com/relayprotocol/settlement-protocol) repository and deployed on the [Relay Chain](https://explorer.chain.relay.link). diff --git a/references/protocol/components/oracle.mdx b/references/protocol/components/oracle.mdx new file mode 100644 index 0000000..c9a0884 --- /dev/null +++ b/references/protocol/components/oracle.mdx @@ -0,0 +1,83 @@ +--- +title: Oracle +description: "The verification layer that attests deposits and fills to the Hub" +sidebarTitle: Oracle +--- + +## Overview + +The Oracle is the verification engine of the protocol. It reads events from origin and destination chains, verifies that deposits and fills occurred correctly, and submits signed attestations to the [Hub](/references/protocol/components/hub) on the [Relay Chain](/references/protocol/relay-chain). + +Unlike push-based oracles that continuously stream data crosschain, Relay uses a **pull-based** model. The Oracle reads historical chain data on-demand when attestation is needed. This keeps costs low and avoids the overhead of maintaining persistent message channels to every chain. + +## What the Oracle Attests + +The Oracle can attest to four types of events: + +### Deposits + +When a user deposits into the [Depository](/references/protocol/components/depository) on an origin chain, the Oracle verifies the deposit occurred and attests it to the Hub. This triggers a **MINT** action, creating a token balance on the Hub that represents the deposited funds. + +### Fills + +When a solver fills a user's order on a destination chain, the Oracle verifies the fill matched the user's intent (correct destination, amount, and action). It attests this to the Hub as a **TRANSFER** action, moving the balance from the user to the solver. + +### Reverts + +If a solver fails to fill within the expected timeframe, the Oracle can attest a revert. This restores the user's balance on the Hub, allowing them to withdraw their original deposit. + +### Withdrawals + +When a solver claims funds from a Depository, the Oracle verifies the withdrawal occurred and attests it to the Hub as a **BURN** action, reducing the solver's Hub balance to stay in sync. + +## Architecture + +```mermaid +sequenceDiagram + participant Chains as Supported Chains + participant Oracle + participant Hub as Hub (Relay Chain) + + loop For each event + Chains-->>Oracle: 1. Monitor deposits and fills + Oracle->>Chains: 2. Read and verify chain data + Note over Oracle: 3. Sign EIP-712 attestation + end + Oracle->>Hub: 4. Submit executeMultiple() + Hub->>Hub: Process actions (MINT / TRANSFER / BURN) +``` + +The Oracle operates as a multi-step pipeline: + +1. **Monitor** — Watch for relevant events on supported chains (deposits, fills) +2. **Verify** — Read chain data to confirm the event details match expected values +3. **Sign** — Create an EIP-712 typed data signature over the attestation +4. **Submit** — Call `executeMultiple()` on the Oracle contract on the Relay Chain + +The Oracle contract on the Relay Chain processes the signed attestation and calls the appropriate action on the Hub (MINT, TRANSFER, or BURN). + +### Pull-Based Verification + +A key cost optimization is that the Oracle does not require message-passing infrastructure (like LayerZero or Hyperlane) between chains. Instead: + +- Validators read **historical chain data** on-demand, rather than continuously watching message inboxes +- Attestation requests are initiated and delivered on the Hub, meaning **no gas is spent on origin or destination chains** for verification +- This dramatically reduces the per-order cost of verification compared to protocols that emit and relay crosschain messages + +### Batch Execution + +The Oracle supports batch attestation via `executeMultiple()`. Multiple attestations (across different orders and action types) can be submitted in a single transaction on the Relay Chain, further amortizing gas costs. + +If an individual attestation in a batch has already been processed, it is skipped rather than reverting the entire batch. + +## Decentralization + +The Oracle is designed to support a **consensus-based multi-operator model**, where multiple independent parties run oracle nodes and attestations require agreement from a threshold of operators. This eliminates any single point of trust in the verification process. + + +The Oracle is a trust-critical component — it determines which deposits and fills are considered valid. However, it cannot steal funds. Even a compromised Oracle can only incorrectly attribute balances on the Hub. User funds in the Depository remain protected by the Allocator's withdrawal authorization. + + +## Source Code + +The Oracle contract is part of the [`settlement-protocol`](https://github.com/relayprotocol/settlement-protocol) repository. The contract is deployed on the Relay Chain and can be viewed on the [Relay Chain Explorer](https://explorer.chain.relay.link). diff --git a/references/protocol/contracts/evm-depository.mdx b/references/protocol/contracts/evm-depository.mdx new file mode 100644 index 0000000..e584e24 --- /dev/null +++ b/references/protocol/contracts/evm-depository.mdx @@ -0,0 +1,370 @@ +--- +title: EVM Depository +description: "Solidity API reference for the EVM Relay Depository contract" +sidebarTitle: EVM Depository +--- + +# Solidity API + +## EVM Relay Depository + +Provides secure deposit functionality and execution of allocator-signed requests for EVM chains + +_EVM implementation using EIP-712 for structured data signing, supporting native ETH and ERC20 tokens_ + +### AddressCannotBeZero + +```solidity +error AddressCannotBeZero() +``` + +Revert if the address is zero + +### InvalidSignature + +```solidity +error InvalidSignature() +``` + +Revert if the signature is invalid + +### CallRequestExpired + +```solidity +error CallRequestExpired() +``` + +Revert if the call request is expired + +### CallRequestAlreadyUsed + +```solidity +error CallRequestAlreadyUsed() +``` + +Revert if the call request has already been used + +### CallFailed + +```solidity +error CallFailed(bytes returnData) +``` + +Revert if a call fails + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| returnData | bytes | The data returned from the failed call | + +### RelayNativeDeposit + +```solidity +event RelayNativeDeposit(address from, uint256 amount, bytes32 id) +``` + +Emit event when a native deposit is made + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| from | address | The address that made the deposit | +| amount | uint256 | The amount of native currency deposited | +| id | bytes32 | The unique identifier associated with the deposit | + +### RelayErc20Deposit + +```solidity +event RelayErc20Deposit(address from, address token, uint256 amount, bytes32 id) +``` + +Emit event when an erc20 deposit is made + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| from | address | The address that made the deposit | +| token | address | The address of the ERC20 token | +| amount | uint256 | The amount of tokens deposited | +| id | bytes32 | The unique identifier associated with the deposit | + +### RelayCallExecuted + +```solidity +event RelayCallExecuted(bytes32 id, struct Call call) +``` + +Emit event when a call is executed + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| id | bytes32 | The identifier of the call request | +| call | struct Call | The call details that were executed | + +### _CALL_TYPEHASH + +```solidity +bytes32 _CALL_TYPEHASH +``` + +The EIP-712 typehash for the Call struct + +_Used in structured data hashing for signature verification_ + +### _CALL_REQUEST_TYPEHASH + +```solidity +bytes32 _CALL_REQUEST_TYPEHASH +``` + +The EIP-712 typehash for the CallRequest struct + +_Used in structured data hashing for signature verification_ + +### callRequests + +```solidity +mapping(bytes32 => bool) callRequests +``` + +Set of executed call requests + +_Maps the hash of a call request to whether it has been executed_ + +### allocator + +```solidity +address allocator +``` + +The allocator address + +_Must be set to a secure and trusted entity_ + +### constructor + +```solidity +constructor(address _owner, address _allocator) public +``` + +Initializes the contract with an owner and allocator + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| _owner | address | The address that will own the contract | +| _allocator | address | The address authorized to sign withdrawal requests | + +### setAllocator + +```solidity +function setAllocator(address _allocator) external +``` + +Set the allocator address + +_Only callable by the contract owner_ + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| _allocator | address | The new allocator address | + +### depositNative + +```solidity +function depositNative(address depositor, bytes32 id) external payable +``` + +Deposit native tokens and emit a `RelayNativeDeposit` event + +_Emits a RelayNativeDeposit event with the deposit details_ + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| depositor | address | The address of the depositor - set to `address(0)` to credit `msg.sender` | +| id | bytes32 | The identifier associated with the deposit | + +### depositErc20 + +```solidity +function depositErc20(address depositor, address token, uint256 amount, bytes32 id) public +``` + +Deposit erc20 tokens and emit an `RelayErc20Deposit` event + +_Transfers tokens from msg.sender to this contract and emits a RelayErc20Deposit event_ + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| depositor | address | The address of the depositor - set to `address(0)` to credit `msg.sender` | +| token | address | The erc20 token to deposit | +| amount | uint256 | The amount to deposit | +| id | bytes32 | The identifier associated with the deposit | + +### depositErc20 + +```solidity +function depositErc20(address depositor, address token, bytes32 id) external +``` + +Deposit erc20 tokens and emit an `RelayErc20Deposit` event + +_Uses the full allowance granted to this contract and calls depositErc20_ + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| depositor | address | The address of the depositor - set to `address(0)` to credit `msg.sender` | +| token | address | The erc20 token to deposit | +| id | bytes32 | The identifier associated with the deposit | + +### execute + +```solidity +function execute(struct CallRequest request, bytes signature) external returns (struct CallResult[] results) +``` + +Execute a `CallRequest` signed by the allocator + +_Verifies the signature, expiration, and uniqueness before execution_ + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| request | struct CallRequest | The `CallRequest` to execute | +| signature | bytes | The signature from the allocator | + +#### Return Values + +| Name | Type | Description | +| ---- | ---- | ----------- | +| results | struct CallResult[] | The results of the calls | + +### _executeCalls + +```solidity +function _executeCalls(bytes32 id, struct Call[] calls) internal returns (struct CallResult[] returnData) +``` + +Internal function to execute a list of calls + +_Handles multiple calls and properly manages failures based on allowFailure flag_ + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| id | bytes32 | The identifier of the call request | +| calls | struct Call[] | The array of calls to execute | + +#### Return Values + +| Name | Type | Description | +| ---- | ---- | ----------- | +| returnData | struct CallResult[] | The results of each executed call | + +### _hashCallRequest + +```solidity +function _hashCallRequest(struct CallRequest request) internal view returns (bytes32 structHash, bytes32 eip712Hash) +``` + +Helper function to hash a `CallRequest` and return the EIP-712 digest + +_Implements EIP-712 structured data hashing for the complex CallRequest type_ + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| request | struct CallRequest | The `CallRequest` to hash | + +#### Return Values + +| Name | Type | Description | +| ---- | ---- | ----------- | +| structHash | bytes32 | The struct hash | +| eip712Hash | bytes32 | The EIP712 hash | + +### _domainNameAndVersion + +```solidity +function _domainNameAndVersion() internal pure returns (string name, string version) +``` + +Returns the domain name and version of the contract to be used in the domain separator + +_Implements required function from EIP712 base contract_ + +#### Return Values + +| Name | Type | Description | +| ---- | ---- | ----------- | +| name | string | The domain name | +| version | string | The version | + +## Call + +A structure representing a single call to be executed + +### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | + +```solidity +struct Call { + address to; + bytes data; + uint256 value; + bool allowFailure; +} +``` + +## CallRequest + +A request containing multiple calls to be executed after signature verification + +### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | + +```solidity +struct CallRequest { + struct Call[] calls; + uint256 nonce; + uint256 expiration; +} +``` + +## CallResult + +The result of an executed call + +### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | + +```solidity +struct CallResult { + bool success; + bytes returnData; +} +``` + diff --git a/references/protocol/contracts/solana-depository.mdx b/references/protocol/contracts/solana-depository.mdx new file mode 100644 index 0000000..e95cf46 --- /dev/null +++ b/references/protocol/contracts/solana-depository.mdx @@ -0,0 +1,973 @@ +--- +title: Solana Depository +description: "Anchor API reference for the Solana Relay Depository program" +sidebarTitle: Solana Depository +--- + +# API + +**Version:** 0.1.0 + +**Format Version:** 46 + +# Module `relay_depository` + +## Modules + +## Module `program` + +Module representing the program. + +```rust +pub mod program { /* ... */ } +``` + +### Types + +#### Struct `RelayDepository` + +Type representing the program. + +```rust +pub struct RelayDepository; +``` + +## Module `relay_depository` + +```rust +pub mod relay_depository { /* ... */ } +``` + +### Functions + +#### Function `initialize` + +Initialize the relay depository program with owner and allocator + +Creates and initializes the relay depository account with the specified +owner and allocator. + +###### Parameters +* `ctx` - The context containing the accounts + +###### Returns +* `Ok(())` on success + +```rust +pub fn initialize(ctx: Context<''_, ''_, ''_, ''_, Initialize<''_>>) -> Result<()> { /* ... */ } +``` + +#### Function `set_allocator` + +Update the allocator public key + +Allows the owner to change the authorized allocator that can sign transfer requests. + +###### Parameters +* `ctx` - The context containing the accounts +* `new_allocator` - The public key of the new allocator + +###### Returns +* `Ok(())` on success +* `Err(error)` if not authorized + +```rust +pub fn set_allocator(ctx: Context<''_, ''_, ''_, ''_, SetAllocator<''_>>, new_allocator: Pubkey) -> Result<()> { /* ... */ } +``` + +#### Function `set_owner` + +Update the owner public key + +Allows the current owner to transfer ownership to a new address. + +###### Parameters +* `ctx` - The context containing the accounts +* `new_owner` - The public key of the new owner + +###### Returns +* `Ok(())` on success +* `Err(error)` if not authorized + +```rust +pub fn set_owner(ctx: Context<''_, ''_, ''_, ''_, SetOwner<''_>>, new_owner: Pubkey) -> Result<()> { /* ... */ } +``` + +#### Function `deposit_native` + +Deposit native SOL tokens into the vault + +Transfers SOL from the sender to the vault and emits a deposit event. + +###### Parameters +* `ctx` - The context containing the accounts +* `amount` - The amount of SOL to deposit +* `id` - A unique identifier for the deposit + +###### Returns +* `Ok(())` on success + +```rust +pub fn deposit_native(ctx: Context<''_, ''_, ''_, ''_, DepositNative<''_>>, amount: u64, id: [u8; 32]) -> Result<()> { /* ... */ } +``` + +#### Function `deposit_token` + +Deposit SPL tokens into the vault + +Creates the vault's token account if needed, transfers tokens from the sender, +and emits a deposit event. + +###### Parameters +* `ctx` - The context containing the accounts +* `amount` - The amount of tokens to deposit +* `id` - A unique identifier for the deposit + +###### Returns +* `Ok(())` on success + +```rust +pub fn deposit_token(ctx: Context<''_, ''_, ''_, ''_, DepositToken<''_>>, amount: u64, id: [u8; 32]) -> Result<()> { /* ... */ } +``` + +#### Function `execute_transfer` + +Execute a transfer with allocator signature + +Verifies the allocator's signature, transfers tokens to the recipient, +and marks the request as used. + +###### Parameters +* `ctx` - The context containing the accounts +* `request` - The transfer request details and signature + +###### Returns +* `Ok(())` on success +* `Err(error)` if signature is invalid or request can't be processed + +```rust +pub fn execute_transfer(ctx: Context<''_, ''_, ''_, ''_, ExecuteTransfer<''_>>, request: TransferRequest) -> Result<()> { /* ... */ } +``` + +## Module `instruction` + +An Anchor generated module containing the program's set of +instructions, where each method handler in the `#[program]` mod is +associated with a struct defining the input arguments to the +method. These should be used directly, when one wants to serialize +Anchor instruction data, for example, when specifying +instructions on a client. + +```rust +pub mod instruction { /* ... */ } +``` + +### Types + +#### Struct `Initialize` + +Instruction. + +```rust +pub struct Initialize; +``` + +#### Struct `SetAllocator` + +Instruction. + +```rust +pub struct SetAllocator { + pub new_allocator: Pubkey, +} +``` + +##### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `new_allocator` | `Pubkey` | | + +#### Struct `SetOwner` + +Instruction. + +```rust +pub struct SetOwner { + pub new_owner: Pubkey, +} +``` + +##### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `new_owner` | `Pubkey` | | + + +#### Struct `DepositNative` + +Instruction. + +```rust +pub struct DepositNative { + pub amount: u64, + pub id: [u8; 32], +} +``` + +##### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `amount` | `u64` | | +| `id` | `[u8; 32]` | | + +#### Struct `DepositToken` + +Instruction. + +```rust +pub struct DepositToken { + pub amount: u64, + pub id: [u8; 32], +} +``` + +##### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `amount` | `u64` | | +| `id` | `[u8; 32]` | | + +#### Struct `ExecuteTransfer` + +Instruction. + +```rust +pub struct ExecuteTransfer { + pub request: TransferRequest, +} +``` + +##### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `request` | `TransferRequest` | | + +## Module `cpi` + +**Attributes:** + +- `#[(feature = "cpi")]` + +```rust +pub mod cpi { /* ... */ } +``` + +### Modules + +## Module `accounts` + +An Anchor generated module, providing a set of structs +mirroring the structs deriving `Accounts`, where each field is +an `AccountInfo`. This is useful for CPI. + +```rust +pub mod accounts { /* ... */ } +``` + +### Re-exports + +#### Re-export `crate::__cpi_client_accounts_set_allocator::*` + +```rust +pub use crate::__cpi_client_accounts_set_allocator::*; +``` + +#### Re-export `crate::__cpi_client_accounts_set_owner::*` + +```rust +pub use crate::__cpi_client_accounts_set_owner::*; +``` + +#### Re-export `crate::__cpi_client_accounts_initialize::*` + +```rust +pub use crate::__cpi_client_accounts_initialize::*; +``` + +#### Re-export `crate::__cpi_client_accounts_deposit_token::*` + +```rust +pub use crate::__cpi_client_accounts_deposit_token::*; +``` + +#### Re-export `crate::__cpi_client_accounts_execute_transfer::*` + +```rust +pub use crate::__cpi_client_accounts_execute_transfer::*; +``` + +#### Re-export `crate::__cpi_client_accounts_deposit_native::*` + +```rust +pub use crate::__cpi_client_accounts_deposit_native::*; +``` + +### Types + +#### Struct `Return` + +```rust +pub struct Return { + // Some fields omitted +} +``` + +##### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| *private fields* | ... | *Some fields have been omitted* | + +### Functions + +#### Function `initialize` + +```rust +pub fn initialize<''a, ''b, ''c, ''info>(ctx: anchor_lang::context::CpiContext<''a, ''b, ''c, ''info, crate::cpi::accounts::Initialize<''info>>) -> anchor_lang::Result<()> { /* ... */ } +``` + +#### Function `set_allocator` + +```rust +pub fn set_allocator<''a, ''b, ''c, ''info>(ctx: anchor_lang::context::CpiContext<''a, ''b, ''c, ''info, crate::cpi::accounts::SetAllocator<''info>>, new_allocator: Pubkey) -> anchor_lang::Result<()> { /* ... */ } +``` + +#### Function `set_owner` + +```rust +pub fn set_owner<''a, ''b, ''c, ''info>(ctx: anchor_lang::context::CpiContext<''a, ''b, ''c, ''info, crate::cpi::accounts::SetOwner<''info>>, new_owner: Pubkey) -> anchor_lang::Result<()> { /* ... */ } +``` + +#### Function `deposit_native` + +```rust +pub fn deposit_native<''a, ''b, ''c, ''info>(ctx: anchor_lang::context::CpiContext<''a, ''b, ''c, ''info, crate::cpi::accounts::DepositNative<''info>>, amount: u64, id: [u8; 32]) -> anchor_lang::Result<()> { /* ... */ } +``` + +#### Function `deposit_token` + +```rust +pub fn deposit_token<''a, ''b, ''c, ''info>(ctx: anchor_lang::context::CpiContext<''a, ''b, ''c, ''info, crate::cpi::accounts::DepositToken<''info>>, amount: u64, id: [u8; 32]) -> anchor_lang::Result<()> { /* ... */ } +``` + +#### Function `execute_transfer` + +```rust +pub fn execute_transfer<''a, ''b, ''c, ''info>(ctx: anchor_lang::context::CpiContext<''a, ''b, ''c, ''info, crate::cpi::accounts::ExecuteTransfer<''info>>, request: TransferRequest) -> anchor_lang::Result<()> { /* ... */ } +``` + +## Module `accounts` + +An Anchor generated module, providing a set of structs +mirroring the structs deriving `Accounts`, where each field is +a `Pubkey`. This is useful for specifying accounts for a client. + +```rust +pub mod accounts { /* ... */ } +``` + +### Re-exports + +#### Re-export `crate::__client_accounts_execute_transfer::*` + +```rust +pub use crate::__client_accounts_execute_transfer::*; +``` + +#### Re-export `crate::__client_accounts_deposit_token::*` + +```rust +pub use crate::__client_accounts_deposit_token::*; +``` + +#### Re-export `crate::__client_accounts_initialize::*` + +```rust +pub use crate::__client_accounts_initialize::*; +``` + +#### Re-export `crate::__client_accounts_set_owner::*` + +```rust +pub use crate::__client_accounts_set_owner::*; +``` + +#### Re-export `crate::__client_accounts_set_allocator::*` + +```rust +pub use crate::__client_accounts_set_allocator::*; +``` + +#### Re-export `crate::__client_accounts_deposit_native::*` + +```rust +pub use crate::__client_accounts_deposit_native::*; +``` + +## Types + +### Struct `RelayDepository` + +Relay depository account that stores configuration and state + +This account is a PDA derived from the `RELAY_DEPOSITORY_SEED` and +contains the ownership and allocation information. + +```rust +pub struct RelayDepository { + pub owner: Pubkey, + pub allocator: Pubkey, + pub vault_bump: u8, +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `owner` | `Pubkey` | The owner of the relay depository who can update settings | +| `allocator` | `Pubkey` | The authorized allocator that can sign transfer requests | +| `vault_bump` | `u8` | The bump seed for the vault PDA, used for deriving the vault address | + +### Struct `UsedRequest` + +Account that tracks whether a transfer request has been used + +This account is created for each transfer request to prevent replay attacks. + +```rust +pub struct UsedRequest { + pub is_used: bool, +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `is_used` | `bool` | Flag indicating whether the request has been processed | + +### Struct `Initialize` + +Accounts required for initializing the relay depository + +```rust +pub struct Initialize<''info> { + pub relay_depository: Account<''info, RelayDepository>, + pub vault: UncheckedAccount<''info>, + pub owner: Signer<''info>, + pub allocator: UncheckedAccount<''info>, + pub system_program: Program<''info, System>, +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `relay_depository` | `Account<''info, RelayDepository>` | The relay depository account to be initialized
This is a PDA derived from the RELAY_DEPOSITORY_SEED | +| `vault` | `UncheckedAccount<''info>` | PDA that will hold SOL deposits
CHECK: This is a PDA derived from the VAULT_SEED | +| `owner` | `Signer<''info>` | The owner account that pays for initialization
Must match the AUTHORIZED_PUBKEY | +| `allocator` | `UncheckedAccount<''info>` | The allocator account that will be authorized to sign transfer requests
CHECK: Used as public key only | +| `system_program` | `Program<''info, System>` | | + +### Struct `InitializeBumps` + +```rust +pub struct InitializeBumps { + pub relay_depository: u8, + pub vault: u8, +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `relay_depository` | `u8` | | +| `vault` | `u8` | | + + +### Struct `SetAllocator` + +Accounts required for updating the allocator + +```rust +pub struct SetAllocator<''info> { + pub relay_depository: Account<''info, RelayDepository>, + pub owner: Signer<''info>, +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `relay_depository` | `Account<''info, RelayDepository>` | The relay depository account to update | +| `owner` | `Signer<''info>` | The owner of the relay depository | + +### Struct `SetAllocatorBumps` + +```rust +pub struct SetAllocatorBumps { + pub relay_depository: u8, +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `relay_depository` | `u8` | | + +### Struct `SetOwner` + +Accounts required for updating the owner + +```rust +pub struct SetOwner<''info> { + pub relay_depository: Account<''info, RelayDepository>, + pub owner: Signer<''info>, +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `relay_depository` | `Account<''info, RelayDepository>` | The relay depository account to update | +| `owner` | `Signer<''info>` | The current owner of the relay depository | + +### Struct `SetOwnerBumps` + +```rust +pub struct SetOwnerBumps { + pub relay_depository: u8, +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `relay_depository` | `u8` | | + +### Struct `DepositNative` + +Accounts required for depositing native currency + +```rust +pub struct DepositNative<''info> { + pub relay_depository: Account<''info, RelayDepository>, + pub sender: Signer<''info>, + pub depositor: UncheckedAccount<''info>, + pub vault: UncheckedAccount<''info>, + pub system_program: Program<''info, System>, +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `relay_depository` | `Account<''info, RelayDepository>` | The relay depository account | +| `sender` | `Signer<''info>` | The sender of the deposit | +| `depositor` | `UncheckedAccount<''info>` | The account credited for the deposit
CHECK: The account credited for the deposit | +| `vault` | `UncheckedAccount<''info>` | The vault PDA that will receive the SOL
CHECK: The vault PDA that will receive the SOL | +| `system_program` | `Program<''info, System>` | The system program | + +### Struct `DepositNativeBumps` + +```rust +pub struct DepositNativeBumps { + pub relay_depository: u8, +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `relay_depository` | `u8` | | + + +### Struct `DepositToken` + +Accounts required for depositing tokens + +```rust +pub struct DepositToken<''info> { + pub relay_depository: Account<''info, RelayDepository>, + pub sender: Signer<''info>, + pub depositor: UncheckedAccount<''info>, + pub vault: UncheckedAccount<''info>, + pub mint: InterfaceAccount<''info, anchor_spl::token_interface::Mint>, + pub sender_token_account: InterfaceAccount<''info, anchor_spl::token_interface::TokenAccount>, + pub vault_token_account: UncheckedAccount<''info>, + pub token_program: Interface<''info, anchor_spl::token_interface::TokenInterface>, + pub associated_token_program: Program<''info, anchor_spl::associated_token::AssociatedToken>, + pub system_program: Program<''info, System>, +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `relay_depository` | `Account<''info, RelayDepository>` | The relay depository account | +| `sender` | `Signer<''info>` | The sender of the deposit | +| `depositor` | `UncheckedAccount<''info>` | The account credited for the deposit
CHECK: The account credited for the deposit | +| `vault` | `UncheckedAccount<''info>` | The vault PDA that will receive the tokens
CHECK: The vault PDA that will receive the tokens | +| `mint` | `InterfaceAccount<''info, anchor_spl::token_interface::Mint>` | The mint of the token being deposited | +| `sender_token_account` | `InterfaceAccount<''info, anchor_spl::token_interface::TokenAccount>` | The sender's token account | +| `vault_token_account` | `UncheckedAccount<''info>` | CHECK: The vault's token account | +| `token_program` | `Interface<''info, anchor_spl::token_interface::TokenInterface>` | The token program | +| `associated_token_program` | `Program<''info, anchor_spl::associated_token::AssociatedToken>` | The associated token program | +| `system_program` | `Program<''info, System>` | The system program | + +### Struct `DepositTokenBumps` + +```rust +pub struct DepositTokenBumps { + pub relay_depository: u8, +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `relay_depository` | `u8` | | + +### Struct `ExecuteTransfer` + +**Attributes:** + +- `#[instruction(request: TransferRequest)]` + +Accounts required for executing a transfer + +```rust +pub struct ExecuteTransfer<''info> { + pub relay_depository: Account<''info, RelayDepository>, + pub executor: Signer<''info>, + pub recipient: UncheckedAccount<''info>, + pub vault: UncheckedAccount<''info>, + pub mint: Option>, + pub recipient_token_account: Option>, + pub vault_token_account: Option>, + pub used_request: Account<''info, UsedRequest>, + pub ix_sysvar: AccountInfo<''info>, + pub token_program: Interface<''info, anchor_spl::token_interface::TokenInterface>, + pub associated_token_program: Program<''info, anchor_spl::associated_token::AssociatedToken>, + pub system_program: Program<''info, System>, +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `relay_depository` | `Account<''info, RelayDepository>` | The relay depository account
CHECK: The relay depository account | +| `executor` | `Signer<''info>` | The executor of the transfer
CHECK: The executor of the transfer | +| `recipient` | `UncheckedAccount<''info>` | The recipient of the transfer
CHECK: The recipient of the transfer | +| `vault` | `UncheckedAccount<''info>` | The vault PDA that will receive the tokens
CHECK: The vault PDA that will receive the tokens | +| `mint` | `Option>` | The mint of the token being transferred | +| `recipient_token_account` | `Option>` | The recipient's token account | +| `vault_token_account` | `Option>` | The vault's token account | +| `used_request` | `Account<''info, UsedRequest>` | The account that tracks whether a transfer request has been used

This account is created for each transfer request to prevent replay attacks. | +| `ix_sysvar` | `AccountInfo<''info>` | The instruction sysvar for ed25519 verification
CHECK: The instruction sysvar for ed25519 verification | +| `token_program` | `Interface<''info, anchor_spl::token_interface::TokenInterface>` | The token program | +| `associated_token_program` | `Program<''info, anchor_spl::associated_token::AssociatedToken>` | The associated token program | +| `system_program` | `Program<''info, System>` | The system program | + +### Struct `ExecuteTransferBumps` + +```rust +pub struct ExecuteTransferBumps { + pub relay_depository: u8, + pub used_request: u8, +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `relay_depository` | `u8` | | +| `used_request` | `u8` | | + +### Struct `TransferRequest` + +Structure representing a transfer request signed by the allocator + +```rust +pub struct TransferRequest { + pub recipient: Pubkey, + pub token: Option, + pub amount: u64, + pub nonce: u64, + pub expiration: i64, +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `recipient` | `Pubkey` | The recipient of the transfer | +| `token` | `Option` | The token mint (None for native SOL, Some(mint) for SPL tokens) | +| `amount` | `u64` | The amount to transfer | +| `nonce` | `u64` | A unique nonce | +| `expiration` | `i64` | The expiration timestamp for the request | + +#### Implementations + +##### Methods + +- ```rust + pub fn get_hash(self: &Self) -> Hash { /* ... */ } + ``` + Computes a hash of the serialized request for signature verification + +### Struct `TransferExecutedEvent` + +Event emitted when a transfer is executed + +```rust +pub struct TransferExecutedEvent { + pub request: TransferRequest, + pub executor: Pubkey, + pub id: Pubkey, +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `request` | `TransferRequest` | The transfer request that was executed | +| `executor` | `Pubkey` | The public key of the executor who processed the transfer | +| `id` | `Pubkey` | The unique identifier for the used request account | + +### Struct `DepositEvent` + +Event emitted when a deposit is made + +```rust +pub struct DepositEvent { + pub depositor: Pubkey, + pub token: Option, + pub amount: u64, + pub id: [u8; 32], +} +``` + +#### Fields + +| Name | Type | Documentation | +|------|------|---------------| +| `depositor` | `Pubkey` | The public key of the depositor | +| `token` | `Option` | The token mint (None for native SOL, Some(mint) for SPL tokens) | +| `amount` | `u64` | The amount deposited | +| `id` | `[u8; 32]` | A unique identifier for the deposit | + +### Enum `CustomError` + +**Attributes:** + +- `#[repr(u32)]` + +Custom error codes for the relay depository program + +```rust +pub enum CustomError { + TransferRequestAlreadyUsed, + InvalidMint, + Unauthorized, + AllocatorSignerMismatch, + MessageMismatch, + MalformedEd25519Data, + MissingSignature, + SignatureExpired, + InvalidRecipient, + InvalidVaultTokenAccount, + InsufficientVaultBalance, +} +``` + +#### Variants + +##### `TransferRequestAlreadyUsed` + +Thrown when trying to execute a transfer request that has already been used + +##### `InvalidMint` + +Thrown when the provided mint does not match the expected mint + +##### `Unauthorized` + +Thrown when an account attempts an operation it is not authorized for + +##### `AllocatorSignerMismatch` + +Thrown when the signature's signer doesn't match the expected allocator + +##### `MessageMismatch` + +Thrown when the signed message doesn't match the expected request + +##### `MalformedEd25519Data` + +Thrown when the Ed25519 signature data is malformed + +##### `MissingSignature` + +Thrown when a required signature is missing + +##### `SignatureExpired` + +Thrown when the signature has expired + +##### `InvalidRecipient` + +Thrown when the recipient doesn't match the expected recipient + +##### `InvalidVaultTokenAccount` + +Thrown when the vault token account doesn't match the expected address + +##### `InsufficientVaultBalance` + +Thrown when a transfer would leave the vault with insufficient balance for rent + +#### Implementations + +##### Methods + +- ```rust + pub fn name(self: &Self) -> String { /* ... */ } + ``` + Gets the name of this [#enum_name]. + +## Functions + +### Function `check_id` + +Confirms that a given pubkey is equivalent to the program ID + +```rust +pub fn check_id(id: &anchor_lang::solana_program::pubkey::Pubkey) -> bool { /* ... */ } +``` + +### Function `id` + +Returns the program ID + +```rust +pub fn id() -> anchor_lang::solana_program::pubkey::Pubkey { /* ... */ } +``` + +### Function `id_const` + +Const version of `ID` + +```rust +pub const fn id_const() -> anchor_lang::solana_program::pubkey::Pubkey { /* ... */ } +``` + +### Function `entry` + +The Anchor codegen exposes a programming model where a user defines +a set of methods inside of a `#[program]` module in a way similar +to writing RPC request handlers. The macro then generates a bunch of +code wrapping these user defined methods into something that can be +executed on Solana. + +These methods fall into one category for now. + +Global methods - regular methods inside of the `#[program]`. + +Care must be taken by the codegen to prevent collisions between +methods in these different namespaces. For this reason, Anchor uses +a variant of sighash to perform method dispatch, rather than +something like a simple enum variant discriminator. + +The execution flow of the generated code can be roughly outlined: + +* Start program via the entrypoint. +* Strip method identifier off the first 8 bytes of the instruction + data and invoke the identified method. The method identifier + is a variant of sighash. See docs.rs for `anchor_lang` for details. +* If the method identifier is an IDL identifier, execute the IDL + instructions, which are a special set of hardcoded instructions + baked into every Anchor program. Then exit. +* Otherwise, the method identifier is for a user defined + instruction, i.e., one of the methods in the user defined + `#[program]` module. Perform method dispatch, i.e., execute the + big match statement mapping method identifier to method handler + wrapper. +* Run the method handler wrapper. This wraps the code the user + actually wrote, deserializing the accounts, constructing the + context, invoking the user's code, and finally running the exit + routine, which typically persists account changes. + +The `entry` function here, defines the standard entry to a Solana +program, where execution begins. + +```rust +pub fn entry<''info>(program_id: &Pubkey, accounts: &''info [AccountInfo<''info>], data: &[u8]) -> anchor_lang::solana_program::entrypoint::ProgramResult { /* ... */ } +``` + +### Function `get_transfer_fee` + +Calculates the transfer fee for a token + +Determines the fee amount for the given mint and transfer amount, +taking into account the token extension for transfer fees if present. + +###### Parameters +* `mint_account` - The mint account of the token +* `pre_fee_amount` - The amount to transfer before fees + +###### Returns +* The calculated fee amount + +```rust +pub fn get_transfer_fee(mint_account: &InterfaceAccount<''_, anchor_spl::token_interface::Mint>, pre_fee_amount: u64) -> Result { /* ... */ } +``` + +## Constants and Statics + +### Static `ID` + +The static program ID + +```rust +pub static ID: anchor_lang::solana_program::pubkey::Pubkey = _; +``` + +### Constant `ID_CONST` + +Const version of `ID` + +```rust +pub const ID_CONST: anchor_lang::solana_program::pubkey::Pubkey = _; +``` + diff --git a/references/protocol/depository/architecture.mdx b/references/protocol/depository/architecture.mdx deleted file mode 100644 index 11bb9c3..0000000 --- a/references/protocol/depository/architecture.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Architecture -description: Architecture of Relay Depository -sidebarTitle: Architecture ---- - -The architecture of the Relay Depository contracts depends on the VM type of the chain (see VM-specific details below), but high-level, every depository has two core components: - -- deposit methods: these are contract methods that can be called by users - to deposit funds (optionally, an id can be attached to every individual - deposit) -- allocator: entity which can sign messages authorizing the withdrawal of - funds from the depository - -From the protocol perspective, deposits of funds to the depository represent deposits to the Relay Hub. -Once arrived on the Relay Hub, ownership of funds is being tracked there, according to the rules of the Hub. -The tracking depends on the implementation of the Hub, and can happen offchain (if the Hub is implemented in a centralized way), or onchain (if the Hub is implemented as a chain). -See [Relay Hub](../hub) section for more details on its inner workings. - -Below you can find an overview of the depository implementation details across all of the supported VM types. - -### Ethereum VM - -On Ethereum VM chains, there's two ways to deposit funds to the depository: - -- native tokens deposit: using the `depositNative(address depositor, bytes32 id)` method -- erc20 tokens deposit: using one of the `depositErc20(address depositor, address token, uint256 amount, bytes32 id)` or `depositErc20(address depositor, address token, bytes32 id)` methods (the second method is going to consume any allowance given by `msg.sender` to the depository contract) - -### Solana VM - -On Solana VM chains, there's two ways to deposit funds to the depository: - -- native tokens deposit: using the `deposit_native(amount: u64, id: [u8; 32])` method -- spl tokens deposit: using the `deposit_token(amount: u64, id: [u8; 32])` method diff --git a/references/protocol/depository/overview.mdx b/references/protocol/depository/overview.mdx deleted file mode 100644 index 2adadb7..0000000 --- a/references/protocol/depository/overview.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: Overview -description: Introducing Relay Depository -sidebarTitle: Overview ---- - -Relay Depository represents a set of smart contracts across different chains and VM types providing an entry point to the Relay Hub. -They allow users to deposit tokens into the Relay Protocol from any chain that is supported by the protocol, tokens which can then be used for executing various actions on the Relay Hub. - -Relay Depository contracts are open-sourced and available on [GitHub](https://github.com/relayprotocol/relay-protocol-contracts). Feel free to contribute to the project. diff --git a/references/protocol/depository/security.mdx b/references/protocol/depository/security.mdx deleted file mode 100644 index 2fe5159..0000000 --- a/references/protocol/depository/security.mdx +++ /dev/null @@ -1,11 +0,0 @@ -## Threat model - -The Depository contract is receiving from users and an `allocator` can sign messages authorizing the withdrawal of funds from the depository. As such the `allocator` itself has to be trusted to only sign messages if the user's intent was successfully filled. - -The contract is **not** upgradable, meaning that once deployed, the code cannot be changed. - -## Audits - -The Relay Depository contracts written in Solidity have been audited by [Spearbit](https://spearbit.com/). At the time the contract was called `CreditMaster` and you can download the [audit reports](https://github.com/relayprotocol/relay-vaults/blob/main/docs/report-cantinacode-relay-protocol-0203.pdf) (Note that the audit includes other contracts as well, such as the [vaults contracts](../vaults/)). - -The Relay Depository contracts written in Solana have been audited by [Certora](https://www.certora.com/) and you can find the [audit reports](https://github.com/relayprotocol/relay-depository/blob/main/audit-reports/Certora-Relay-Escrow-Report.pdf). diff --git a/references/protocol/design.mdx b/references/protocol/design.mdx new file mode 100644 index 0000000..30e2e05 --- /dev/null +++ b/references/protocol/design.mdx @@ -0,0 +1,101 @@ +--- +title: Design Principles +description: "The design philosophy behind Relay Settlement" +sidebarTitle: Design +--- + +## Overview + +Relay Settlement is designed from the ground up for high-throughput crosschain payments. Every design decision optimizes for the properties that matter most in production: cost, speed, capital efficiency, and ease of chain expansion. + +This page explains the key design principles and how they differ from other intent settlement approaches. + +## Cost Efficiency + +The primary cost optimization in Relay is **minimizing gas consumption on origin and destination chains**. These are the chains users and solvers interact with — often popular, congested networks with expensive gas. + +### Minimal Deposit Cost + +User deposits cost approximately **21,000 gas** — nearly the cost of a raw native transfer. This is achieved by: + +- **No event-based verification** — The Depository does not need to emit complex events for crosschain message passing. The Oracle reads chain state directly. +- **No approval flow** — For native deposits, users send funds directly to the Depository contract. No separate approve + deposit transaction. +- **Simple storage** — The Depository only stores minimal data per deposit (depositor address, amount, orderId). + +Compare this to typical escrow patterns that require **~77,000 gas** for deposits (storing order details, emitting structured events, managing state). + +### Zero-Overhead Fills + +Solvers can fill orders with a **simple transfer** to the user on the destination chain. There is no requirement to route through a smart contract, emit events, or call specific functions on the destination. + +The Oracle verifies fills by reading destination chain state directly, removing the need for any fill-specific infrastructure on each destination chain. + +### Hub Settlement + +All settlement activity happens on the [Relay Chain](/references/protocol/relay-chain), where gas costs approximately **$0.005 per operation**. This means the cost of verifying and settling orders is negligible, regardless of how expensive the origin and destination chains are. + +## Capital Efficiency + +Capital efficiency determines how quickly solvers can recycle their funds to fill new orders. Relay maximizes this in two ways: + +### Real-Time Settlement + +Every order settles **individually and immediately** on the Hub. As soon as the Oracle attests a fill, the solver's balance is updated. There is no batching window. + +Compare this to approaches like optimistic settlement, where solvers wait **1–4 hours** for a batch cycle before getting paid. During that waiting period, capital is locked and cannot be reused. + +### Flexible Withdrawal + +Solvers accrue balances on the Hub and can withdraw from **any** Depository on **any** chain at any time. This gives solvers full control over their withdrawal strategy: + +- **High-frequency withdrawals** — Withdraw every few minutes to maximize capital velocity +- **Batched withdrawals** — Withdraw less frequently to minimize transaction costs +- **Strategic rebalancing** — Withdraw on chains where capital is needed most + +The Hub balance accrues regardless of withdrawal frequency, so solvers can optimize based on their own inventory needs. + +## Speed + +Relay is designed for **sub-3-second fill times** in production. Several design choices enable this: + +- **RFQ-based quoting** — Solvers provide firm quotes before execution, eliminating price uncertainty +- **Optimistic filling** — Solvers can fill before origin chain finality, taking on minimal confirmation risk for recent blocks +- **Instant settlement** — Solvers know their balance is updated immediately after fill attestation, giving them confidence to fill aggressively + +## Chain Expansion + +Adding support for a new chain in Relay requires minimal effort compared to other protocols: + +| Step | What's Required | +|------|----------------| +| **Deploy Depository** | Deploy the Depository contract on the new chain | +| **Configure Oracle** | Add the chain ID, VM type, Depository address, and RPC URL to the Oracle configuration | +| **Configure Hub** | Register the chain ID and Depository address on the Hub | +| **Add Payload Builder** | If the chain uses a new VM type, add a Payload Builder for withdrawal proof generation | + +No bridge integration, message-passing infrastructure, or crosschain contract deployment is needed on the new chain. The Depository is the only contract that touches the origin chain. + + +This minimal footprint is why Relay supports 80+ chains. Each new chain only requires a single contract deployment and configuration update. + + +## Redundancy + +The protocol is designed with fallback paths for critical operations: + +- **Multiple solvers** — If one solver fails to fill, another can step in. The Depository doesn't lock deposits to a single solver. +- **Revert path** — If no solver fills within the commitment window, users can reclaim their deposit +- **Global pause** — The [Allocator](/references/protocol/components/allocator) can pause all withdrawals across all chains with a single transaction in case of emergency +- **Security Council** — A multisig of independent parties can pause, unpause, or replace the Allocator + +## Design Comparison + +| Property | **Point-to-Point** | **Batch Settlement** | **Relay Hub** | +|----------|-------------------|---------------------|---------------| +| **Deposit cost** | ~21,000 gas | ~77,000 gas | ~21,000 gas | +| **Fill cost** | None | ~120,000 gas | None | +| **Settlement cost** | N/A | Amortized per batch | ~$0.005 per order | +| **Time to payment** | Instant (no protection) | 1–4 hours | Seconds | +| **New chain cost** | Low | High (bridge infra) | Low (Depository only) | +| **Capital lock-up** | None | Hours | Minutes | +| **Security** | None (trusted) | Onchain escrow | Non-custodial escrow | diff --git a/references/protocol/guides/for-apps.mdx b/references/protocol/guides/for-apps.mdx new file mode 100644 index 0000000..29ff2ad --- /dev/null +++ b/references/protocol/guides/for-apps.mdx @@ -0,0 +1,69 @@ +--- +title: For Apps +description: "How application integrators interact with the settlement protocol" +sidebarTitle: For Apps +--- + +## Overview + +Application developers typically interact with Relay through the [Relay API](/api-reference), which abstracts the settlement protocol entirely. However, understanding the protocol flow can help when debugging transactions or building advanced integrations. + +This page explains how the protocol surfaces in API responses and how to trace an order through the settlement flow. + +## Protocol Deposits via the API + +When requesting a quote using the Relay API's `/quote/v2` endpoint, the response includes a **`protocol`** field when the order will be settled through the Relay Settlement protocol: + +```json +{ + "steps": [...], + "protocol": { + "orderId": "0x1234...abcd", + "depositChainId": 10, + "paymentDetails": { + "recipient": "0xDepositoryAddress...", + "amount": "1000000", + "token": "0xUSDCAddress..." + } + } +} +``` + +The key fields: + +- **`orderId`** — A unique identifier for this order, used to track it through the settlement flow +- **`depositChainId`** — The chain where the user's deposit will go +- **`paymentDetails`** — The Depository address, token, and amount for the deposit + + +The `protocol` field only appears when the order uses the settlement protocol. Some routes may use alternative execution paths. + + +## Tracing an Order + +Once an order is submitted, you can trace it through the settlement flow: + +### 1. Deposit Transaction + +The user's deposit transaction is a standard transfer to the [Depository](/references/protocol/components/depository) contract on the origin chain. You can find this on the origin chain's block explorer. + +### 2. Settlement on Relay Chain + +After the solver fills the order, the [Oracle](/references/protocol/components/oracle) attests the deposit and fill on the [Relay Chain](/references/protocol/relay-chain). These attestation transactions are visible on the [Relay Chain Explorer](https://explorer.chain.relay.link). + +### 3. Fill Transaction + +The solver's fill transaction is on the destination chain. This is the transaction that delivers the user's requested action (transfer, swap, etc.). + +## When to Use Direct Protocol Integration + +Most applications should use the Relay API. Consider direct protocol integration only if you need to: + +- Build custom deposit flows outside the standard API +- Monitor settlement status at the protocol level +- Implement custom order management + +For direct integration details, see the [Depository](/references/protocol/components/depository) component documentation and contract references: + +- [EVM Depository Reference](/references/protocol/contracts/evm-depository) +- [Solana Depository Reference](/references/protocol/contracts/solana-depository) diff --git a/references/protocol/guides/for-solvers.mdx b/references/protocol/guides/for-solvers.mdx new file mode 100644 index 0000000..9975fbc --- /dev/null +++ b/references/protocol/guides/for-solvers.mdx @@ -0,0 +1,80 @@ +--- +title: For Solvers +description: "How solvers interact with the settlement protocol" +sidebarTitle: For Solvers +--- + +## Overview + +Solvers (also called relayers) are the liquidity providers that fill crosschain orders in Relay. They front their own capital to execute user intents on destination chains, and earn payment by claiming the user's deposited funds from the [Depository](/references/protocol/components/depository). + +This page explains the solver lifecycle and how each protocol component fits into the solver's workflow. + + +Most solvers interact with Relay through the Relay API, which abstracts the protocol details. This page describes the underlying protocol flow for solvers who want to understand how settlement works, or who want to integrate directly. + + +## Solver Lifecycle + +### 1. Receive Orders + +Solvers receive order information through the Relay API. Each order specifies: + +- **Origin chain and asset** — What the user is depositing +- **Destination chain and action** — What the user wants executed +- **Amount and fees** — The deposit amount and solver compensation + +### 2. Fill Orders + +When a solver commits to an order, they execute the user's requested action on the destination chain. This can be a simple transfer, a swap, or any arbitrary onchain action. Fills can be as gas-efficient as a standard transfer — no routing through protocol contracts is required on the destination. + +### 3. Settlement + +After filling, the solver waits for the [Oracle](/references/protocol/components/oracle) to attest both the user's deposit and the solver's fill. The Oracle submits attestations to the [Hub](/references/protocol/components/hub) on the [Relay Chain](/references/protocol/relay-chain), which credits the solver's balance. + +Settlement happens in real-time — there is no batching window. The solver's Hub balance updates as soon as the Oracle processes the attestation. + +### 4. Withdrawal + +Solvers accrue balances on the Hub and can withdraw from any [Depository](/references/protocol/components/depository) on any chain at any time. The withdrawal process: + +1. Request a withdrawal proof from the [Allocator](/references/protocol/components/allocator) +2. Submit the signed proof to the Depository on the target chain +3. Receive funds from the Depository + +## Managing Balances + +### Hub Balance + +The solver's Hub balance represents the total amount they are owed across all chains. This balance increases with each settled fill and decreases with each withdrawal. + +Solvers can check their Hub balance by reading the [Hub](/references/protocol/components/hub) contract on the Relay Chain. Each asset from each origin chain has a unique token ID on the Hub. + +### Withdrawal Strategy + +Solvers choose their own withdrawal strategy based on capital needs: + +- **Frequent withdrawals** — Withdraw every few minutes for maximum capital velocity. Best for solvers with limited capital. +- **Batched withdrawals** — Withdraw less frequently to reduce transaction costs. Best for solvers with deep capital pools. +- **Strategic rebalancing** — Withdraw on chains where capital is running low, regardless of where the original deposits occurred. + + +The Hub balance accrues in real-time regardless of withdrawal frequency. Solvers can optimize their withdrawal timing independently of settlement. + + +## Fees and Payment + +Solver compensation is determined during the quoting phase, before the order is executed. The solver's fee is included in the deposit amount — the user deposits slightly more than the fill amount, and the difference is the solver's payment. + +Settlement fees on the Relay Chain are paid by the solver but are minimal (~$0.005 per order). + +## Direct Integration + +While the Relay API provides the simplest integration path, solvers can interact with the protocol directly: + +- **Monitor deposits** — Watch Depository contracts for new deposits +- **Submit fills** — Execute fills on destination chains +- **Request settlement** — Trigger Oracle attestation +- **Withdraw funds** — Request proofs from the Allocator and submit to Depositories + +For contract-level details, see the [EVM Depository Reference](/references/protocol/contracts/evm-depository) and [Solana Depository Reference](/references/protocol/contracts/solana-depository). diff --git a/references/protocol/how-it-works.mdx b/references/protocol/how-it-works.mdx new file mode 100644 index 0000000..4097816 --- /dev/null +++ b/references/protocol/how-it-works.mdx @@ -0,0 +1,182 @@ +--- +title: How It Works +description: "End-to-end walkthrough of the settlement protocol flows" +sidebarTitle: How It Works +--- + +Every crosschain order in Relay passes through three sequential flows: **Execution**, **Settlement**, and **Withdrawal**. This page walks through each in detail, followed by how reverts are handled when a solver fails to fill. + +## Execution Flow + +Execution is the user-facing part of the process. The user deposits funds on the origin chain, and a solver fills their order on the destination chain. + +```mermaid +sequenceDiagram + participant User + participant Depository as Depository (Origin) + participant Solver + participant Dest as Destination Chain + + User->>+Depository: 1. Deposit funds (orderId) + Note over Depository: ~21,000 gas + Depository-->>-User: Deposit confirmed + Solver->>Depository: 2. Detect deposit + Solver->>+Dest: 3. Fill order (own capital) + Dest-->>-User: Action executed +``` + +**Step by step:** + +1. **User requests a quote** — The user specifies what they want (e.g., bridge 1 ETH from Optimism to Base). The Relay API returns quotes from available solvers. + +2. **User deposits into Depository** — The user sends funds to the [Depository](/references/protocol/components/depository) contract on the origin chain. The deposit is tagged with an **orderId** that links it to the solver's commitment. This costs approximately 21,000 gas — close to a simple transfer. + +3. **Solver fills on destination** — The solver detects the deposit and executes the user's requested action on the destination chain using their own capital. The fill can be a simple transfer, a swap, or any arbitrary onchain action. + + +Because deposits go to the Depository (not to the solver directly), user funds are protected. If the solver fails to fill, the user can reclaim their deposit. + + +## Settlement Flow + +Settlement is the process of verifying that the solver correctly filled the order, and crediting them on the Hub. + +```mermaid +sequenceDiagram + participant Origin as Origin Chain + participant Oracle + participant Dest as Destination Chain + participant Hub as Hub (Relay Chain) + + Oracle->>Origin: 1. Read deposit data + Oracle->>Dest: 2. Read fill data + Note over Oracle: Verify deposit + fill match + Oracle->>Hub: 3. Submit EIP-712 attestation + Hub->>Hub: MINT (user balance) + Hub->>Hub: TRANSFER (user → solver) + Note over Hub: Solver balance updated +``` + +**Step by step:** + +1. **Oracle reads origin chain** — The [Oracle](/references/protocol/components/oracle) reads the origin chain to verify that the user's deposit occurred and matches the expected order. + +2. **Oracle reads destination chain** — The Oracle reads the destination chain to verify that the solver's fill matches the user's intent (correct destination, amount, and action). + +3. **Oracle attests to the Hub** — The Oracle signs an EIP-712 attestation and submits it to the [Hub](/references/protocol/components/hub) contract on the [Relay Chain](/references/protocol/relay-chain). This attestation triggers two actions: + - **MINT** — The user's deposit is represented as a token balance on the Hub + - **TRANSFER** — That balance is transferred from the user to the solver + +4. **Solver balance updated** — The solver's balance on the Hub increases, reflecting the payment they are owed for the fill. + + +Settlement happens in real-time, per-order. There is no batching window. As soon as the Oracle verifies a fill, the solver's balance is updated on the Hub. + + +## Withdrawal Flow + +Withdrawal is how solvers extract funds from the Depository to replenish their capital. Solvers accumulate balances on the Hub and can withdraw from any origin chain at any time. + +```mermaid +sequenceDiagram + participant Solver + participant Allocator + participant Hub as Hub (Relay Chain) + participant MPC as NEAR MPC + participant Depository as Depository (Origin) + + Solver->>Allocator: 1. Request withdrawal + Allocator->>Hub: 2. Check solver balance + Hub-->>Allocator: Balance confirmed + Allocator->>MPC: 3. Request MPC signature + MPC-->>Allocator: Signed proof + Allocator-->>Solver: 4. Return signed proof + Solver->>+Depository: 5. Submit proof + Depository->>Depository: Verify signature + nonce + Depository-->>-Solver: 6. Release funds +``` + +**Step by step:** + +1. **Solver requests withdrawal** — The solver specifies which chain and asset they want to withdraw from, and the amount. + +2. **Allocator verifies balance** — The [Allocator](/references/protocol/components/allocator) checks the solver's balance on the Hub to confirm they have sufficient funds. + +3. **Allocator generates proof** — The Allocator creates a chain-specific cryptographic proof (EIP-712 signature for EVM chains, Ed25519 for Solana) authorizing the withdrawal from the Depository on the target chain. + +4. **Solver submits proof** — The solver submits the signed proof to the [Depository](/references/protocol/components/depository) contract on the target chain. + +5. **Depository releases funds** — The Depository verifies the signature, confirms the nonce hasn't been used, and transfers the funds to the solver. + +6. **Hub balance reduced** — The Hub burns the corresponding token balance (via a BURN action from the Oracle), keeping the ledger in sync. + + +Solvers choose their own withdrawal strategy. They can withdraw frequently to maximize capital velocity, or batch withdrawals to minimize transaction costs. The Hub balance accrues in real-time regardless. + + +## Revert Flow + +If a solver fails to fill an order within the expected timeframe, the user's funds are protected and can be returned. + +```mermaid +sequenceDiagram + participant Solver + participant Oracle + participant Hub as Hub (Relay Chain) + participant Depository as Depository (Origin) + participant User + + Note over Solver: Fill window expires + Oracle->>Hub: 1. Attest revert + Hub->>Hub: 2. Restore user balance + User->>Depository: 3. Withdraw deposit + Depository-->>User: 4. Funds returned +``` + +**Step by step:** + +1. **Order expires** — If the solver doesn't fill within the commitment window, the order is marked as expired. + +2. **Oracle attests revert** — The Oracle verifies that the fill did not occur and attests a revert to the Hub. + +3. **User balance restored** — The Hub restores the user's token balance, effectively unlocking their deposit. + +4. **User withdraws** — The user (or the protocol on their behalf) can trigger a withdrawal from the Depository to reclaim their funds. + +## Comparison: Settlement Approaches + +```mermaid +graph TD + subgraph P2P["Point-to-Point"] + U1[User] -->|Direct transfer| S1[Solver] + style P2P fill:#f9f9f9 + end + + subgraph Batch["Batch Settlement"] + U2[User] -->|Deposit ~77k gas| E2[Escrow] + E2 -->|Batch every ~4hrs| S2[Solver] + style Batch fill:#f9f9f9 + end + + subgraph RelayHub["Relay Hub"] + U3[User] -->|Deposit ~21k gas| D3[Depository] + D3 -.->|Attest| H3[Hub] + H3 -->|Real-time credit| S3[Solver] + style RelayHub fill:#f0f0ff + end +``` + +Different intent protocols handle settlement in different ways. Here's how Relay compares: + +| | **Point-to-Point** | **Batch Settlement** | **Relay Hub** | +|---|---|---|---| +| **Example** | Direct transfer | Across | Relay | +| **Settlement location** | Origin chain | Origin chain (batched) | Relay Chain | +| **Deposit gas** | ~21,000 | ~77,000 | ~21,000 | +| **Fill overhead** | None | ~120,000 gas | None | +| **Settlement cost** | N/A | Amortized | ~$0.005 on Relay Chain | +| **Time to payment** | Instant | ~4 hours (batch window) | Seconds (real-time) | +| **Capital efficiency** | High (but no protection) | Low (long lock-up) | High (real-time + protected) | +| **Chain expansion** | Easy | Requires bridge integration | Deploy Depository only | + +The key innovation is that by moving settlement to a dedicated low-cost chain, Relay achieves the capital efficiency of direct transfers with the security guarantees of an escrow system. diff --git a/references/protocol/hub/architecture.mdx b/references/protocol/hub/architecture.mdx deleted file mode 100644 index 1749c83..0000000 --- a/references/protocol/hub/architecture.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: Architecture -description: Architecture of Relay Hub -sidebarTitle: Architecture ---- - -The Relay Hub tracks ownership of funds and can move funds between different accounts according to specific rules. -The current reference implementation of the Hub is centralized service, with short-term plans to move to a decentralized design (eg. Relay Hub as a chain). - -As described in the [Oracle](../oracle) section, the Hub takes oracle attestations of various external actions and changes its internal state according to specific rules. -On top of the responding to external triggers, the Hub also supports some internal actions, executed by users directly via the Hub interface: - -- withdrawals: users can request withdrawing any funds the have available on the Hub - this will result in a lock being held on the withdrawn funds until an oracle attestation of the withdrawal is sent back to the Hub -- unlocks: in rare cases, it can happen that order initiation deposits will not result in any action done by the solver (fill or refund) - when this happens, anyone is able to trigger an unlock of the corresponding Hub order-locked balance once the initial order expires diff --git a/references/protocol/hub/overview.mdx b/references/protocol/hub/overview.mdx deleted file mode 100644 index 7ba94fa..0000000 --- a/references/protocol/hub/overview.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Overview -description: Introducing Relay Hub -sidebarTitle: Overview ---- - -The Relay Hub is the core component of the Relay protocol. -It is responsible for tracking ownership of funds, doing so based on a combination of external actions attested by an oracle, and internal actions executed by users directly via the Hub interface. - -Today, the Relay core team is running an internal version of the Relay Hub. - -A reference implementation of the Relay Hub is available on [GitHub](https://github.com/relayprotocol/relay-protocol-hub). Feel free to contribute to the project. diff --git a/references/protocol/oracle/architecture.mdx b/references/protocol/oracle/architecture.mdx deleted file mode 100644 index 3edbe7b..0000000 --- a/references/protocol/oracle/architecture.mdx +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: Architecture -description: Architecture of Relay Oracle -sidebarTitle: Architecture ---- - -The Relay Oracle can attest a few types of messages (this list can be extended in the future as new functionality is added to the protocol): - -- depository deposits -- depository withdrawals -- solver fills -- solver refunds - -### Depository deposits - -As mentioned in the [Depository](../depository) section, users can deposit funds to the Relay Hub via the depository of any given chain. -These deposits are initiated on external chains, which requires an oracle attestation of the deposit details (eg. token, amount, depositor, optional id). - -### Depository withdrawals - -Whenever a user wants to withdraw funds available on the Relay Hub, they need to request a signature from the allocator, authorizing the withdrawal of those funds. -This signature can then be used in a transaction for triggering the withdrawal of funds. -However, it's never guaranteed that a signature the allocator released is going to be executed onchain. -The oracle is responsible for determining the status of an allocator signature: whether it was executed onchain, it's still pending / possible to be executed onchain, or it's expired and never possible to have it included onchain. - -### Solver fills - -User deposits can specify an optional id, which usually represents the id of an order to be executed on their behalf. -In this context, when the user deposit gets on the Hub, it's going to be locked and associated to the order id. -If the order-designated solver successfully fills the order, they can use an oracle attestation denoting the successful fill in order to get the order-locked funds. - -### Solver refund - -Similar to the solver fill logic, but relevant when the solver is unable to fill the order, and refunds it instead. -The oracle is going to attest a successful order refund. diff --git a/references/protocol/oracle/overview.mdx b/references/protocol/oracle/overview.mdx deleted file mode 100644 index 04b0060..0000000 --- a/references/protocol/oracle/overview.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Overview -description: Introducing Relay Oracle -sidebarTitle: Overview ---- - -Relay Oracle is the communication channel between external chains and the Relay Hub. -Given that the Relay Hub has no access to outside information, it relies on the Oracle for attesting specific pieces of information. - -Today, the Relay core team is running an internal version of the Relay Oracle. - -A reference implementation of a Relay Oracle is available on [GitHub](https://github.com/relayprotocol/relay-protocol-oracle). Feel free to contribute to the project. diff --git a/references/protocol/overview.mdx b/references/protocol/overview.mdx index ab33f7a..46d47e8 100644 --- a/references/protocol/overview.mdx +++ b/references/protocol/overview.mdx @@ -1,56 +1,109 @@ --- -title: The Relay Protocol -description: "Learn about the Relay Protocol" +title: Relay Settlement +description: "A crosschain settlement protocol optimized for speed, cost, and reliability" sidebarTitle: Overview --- ## Introduction -The Relay Protocol is a cross-chain payments system that connects users to relayers willing to perform onchain actions on their behalf. Relay's design minimizes gas costs, enables rapid chain expansion, and maximizes capital efficiency all while remaining open and trustless. Such a system is particularly well-suited for low-value, high frequency cross-chain actions such as those expected in a world of chain abstraction. +Relay Settlement is a crosschain intent settlement protocol designed for high-throughput payments across blockchains. It connects users to solvers who fill crosschain orders using their own capital, and then settles those orders on the [Relay Chain](/references/protocol/relay-chain) — a dedicated, low-cost settlement chain. -## Background: Cross-Chain Relaying +The protocol minimizes gas costs on origin and destination chains, enables rapid chain expansion, and maximizes capital efficiency for solvers — all while remaining non-custodial and verifiable. -In its current state, the Relay API is similar to other “intent-based” protocols like Across, where liquidity providers known as _Relayers_ use their own capital to “fast fill” user requests to move between chains, and then rebalance using slow, expensive bridges under the hood. + +Relay Settlement powers the [Relay](https://relay.link) app and API. Most integrators use the [Relay API](/api-reference) rather than interacting with the protocol directly, but the protocol is open and permissionless. + -This design has two key advantages over typical bridging: +## How Crosschain Relaying Works -- **Speed** - Because relayers are fronting their own capital, they can take on confirmation risk and fill optimistically, without waiting for global consensus -- **Cost** - Because rebalancing is done infrequently and in batches, the security cost of using bridges is amortized across many users +Crosschain intent protocols like Relay use liquidity providers known as _solvers_ to "fast fill" user requests. Rather than waiting for slow canonical bridges, solvers front their own capital on the destination chain, and then claim payment from the user's escrowed deposit on the origin chain. -The typical flow for intent-based protocols is as follows: +This design has two key advantages: -1. User _escrows_ funds on the origin chain -2. Relayer _fills_ the order on the destination chain -3. Relayer later _settles_ the order on the origin, to claim _payment_ +- **Speed** — Solvers can fill optimistically without waiting for chain finality, completing orders in seconds +- **Cost** — Expensive bridge operations are batched and amortized, keeping per-order costs low -![title](/images/flow1.png) +The typical flow for intent-based protocols is: -Each of these steps has a cost, which different protocols minimize in different ways. Across, which pioneered this design, reduces the cost of settlement with two optimizations: +1. User **deposits** funds into escrow on the origin chain +2. Solver **fills** the order on the destination chain +3. Solver **settles** the order to claim payment -- Instead of proving every fill with an oracle or cross-chain message, settlement is done optimistically, by assuming it is correct, and having a challenge period -- Settlement is done in large batches across the whole protocol, every 4 hours +Where protocols differ is in *how* they handle settlement — the process of verifying fills and releasing payment. This is where Relay introduces significant improvements. -As a result, the vast majority of the remaining cost comes from the _Deposit_ (~77,000 gas) and _Fill_ (~120,000 gas). This is where **Relay** is able to achieve significant improvements: +## What Makes Relay Different -## Overview of the Relay Protocol +Most intent protocols settle on the origin chain, which means gas costs grow with every order. Relay takes a fundamentally different approach: -The Relay protocol is designed to minimize the cost of deposits and fills by using a novel architecture that separates the concerns of deposit, market making, liquidity providing and intent filling on the destination chain. We design the protocol with the goal of suppressing any need for trusted intermediaries, including the need to prove and settle fills. +**Settlement happens on a dedicated hub chain, not on the origin.** -This overview was designed to give a high level summary, and did not go into technical detail or discuss how adversarial scenarios are handled. These will be addressed in a future whitepaper. +Instead of proving every fill back to the origin chain, Relay uses an [Oracle](/references/protocol/components/oracle) to verify fills off-chain and attest to a central [Hub](/references/protocol/components/hub) on the [Relay Chain](/references/protocol/relay-chain). Solvers accrue balances on the Hub and can withdraw from any origin chain at any time. -The protocol will eventually be made of various components which can work independently or together. As of today, some of these components are already implemented and in use, while others are still in development. The ones that are currently implemented include: +This architecture enables: -- [Relay Depository](./depository/) -- [Relay Oracle](./oracle/) -- [Relay Hub](./hub/) -- [Relay Vaults](./vaults/) +- **~21,000 gas deposits** — Users pay close to a raw transfer cost (vs ~77,000 for typical escrow) +- **Zero-overhead fills** — Solvers can fill with a simple transfer, no contract interaction needed on destination +- **Real-time settlement** — Every order settles individually and immediately (vs 4-hour batch windows) +- **Capital efficiency** — Solvers get paid in seconds, not hours +- **Instant chain expansion** — Adding a chain requires deploying a single Depository contract -These components will also evolve over time to support the overall protocol architecture and their current implementation may not reflect the final design, but rather provide a good intermediate state that enables us to evolve from a simple intent filling API to a fully trustless cross-chain payments system. +## Architecture Overview -You can interact with the Relay Protocol through our [Relay](https://relay.link) app, which provides a reference UI. You can also find the source code for all the components of the Relay Protocol on [GitHub](https://github.com/relayprotocol/). +```mermaid +graph LR + subgraph Origin["Origin Chain (80+)"] + D[Depository] + end -### Basic Flow of the current implementation + subgraph RC["Relay Chain"] + H[Hub] + A[Allocator] + end -Alice sends her funds to the Depository contract on the origin chain. The [Relayer](/how-it-works/the-relay-solver) fills Alice's request on the destination chain. The [Hub](./hub/) keeps track of that deposit. The Relayer can then trigger the [Oracle](./oracle/) to verify that the intent was correctly filled. If so, the Relayer can then settle the payment and access their user's deposited funds. + O[Oracle] -In order to fill on multiple chains, the Relayer needs to keep balances on every supported network. The [Vaults](./vaults/) are used to rebalance the Relayer's balances across chains. + User -->|1. Deposit| D + Solver -->|2. Fill| Dest[Destination Chain] + O -->|3. Verify deposit| D + O -->|3. Verify fill| Dest + O -->|4. Attest| H + H -->|5. Credit balance| Solver + Solver -->|6. Request withdrawal| A + A -->|7. Generate proof| Solver + Solver -->|8. Submit proof| D + D -->|9. Release funds| Solver +``` + +The protocol consists of four core components that work together: + +| Component | Role | Location | +|-----------|------|----------| +| [**Depository**](/references/protocol/components/depository) | Holds user deposits on each origin chain | Every supported chain (80+) | +| [**Oracle**](/references/protocol/components/oracle) | Verifies deposits and fills, attests to the Hub | Off-chain service | +| [**Hub**](/references/protocol/components/hub) | Tracks token ownership and solver balances | Relay Chain | +| [**Allocator**](/references/protocol/components/allocator) | Generates withdrawal proofs for solvers | Relay Chain / MPC | + +These components are supported by the [Relay Chain](/references/protocol/relay-chain), a purpose-built settlement chain where the Hub contract lives and all settlement operations are processed. + +## The Three Flows + +Every crosschain order passes through three flows. Read the full walkthrough in [How It Works](/references/protocol/how-it-works). + +### Execution + +The user deposits funds into the [Depository](/references/protocol/components/depository) on the origin chain. A solver detects the order and fills it on the destination chain using their own capital. + +### Settlement + +The [Oracle](/references/protocol/components/oracle) reads both chains to verify the deposit and fill occurred correctly. It attests this to the [Hub](/references/protocol/components/hub) on the Relay Chain, which credits the solver's balance. + +### Withdrawal + +The solver requests a withdrawal from the [Allocator](/references/protocol/components/allocator), which generates a cryptographic proof. The solver submits this proof to the [Depository](/references/protocol/components/depository) on the origin chain to claim the user's deposited funds. + +## Source Code + +All protocol components are open source on [GitHub](https://github.com/relayprotocol/): + +- [`settlement-protocol`](https://github.com/relayprotocol/settlement-protocol) — Hub, Oracle, and Allocator contracts +- [`relay-depository`](https://github.com/relayprotocol/relay-depository) — Depository contracts (EVM + Solana) diff --git a/references/protocol/relay-chain.mdx b/references/protocol/relay-chain.mdx new file mode 100644 index 0000000..08a72fd --- /dev/null +++ b/references/protocol/relay-chain.mdx @@ -0,0 +1,55 @@ +--- +title: The Relay Chain +description: "A purpose-built settlement chain for crosschain intent processing" +sidebarTitle: Relay Chain +--- + +## Overview + +The Relay Chain is a dedicated blockchain purpose-built for settlement of crosschain intents. It serves as the home of the [Hub](/references/protocol/components/hub) contract — the central ledger that tracks all token ownership and solver balances in the protocol. + +By running settlement on a dedicated chain, Relay avoids competing for blockspace on congested networks and keeps settlement costs minimal, regardless of how expensive the origin or destination chains are. + +## Why a Dedicated Chain + +Intent protocols typically settle on the origin chain, which means settlement competes with all other activity on that chain. This creates several problems: + +- **Cost** — Gas prices on popular chains (Ethereum, Base, Arbitrum) fluctuate with demand, making settlement costs unpredictable +- **Throughput** — High-volume order flow can be bottlenecked by chain congestion +- **Complexity** — Each origin chain requires its own settlement logic and proof verification + +The Relay Chain solves these by providing: + +- **Predictable, low-cost gas** — Settlement costs ~$0.005 per order, regardless of origin/destination chain gas prices +- **Dedicated throughput** — No competition with external traffic. The chain is optimized for settlement operations +- **Single settlement point** — All orders across all chains settle in one place, simplifying the protocol and reducing the number of moving parts + + +Users and app developers never interact with the Relay Chain directly. It operates entirely in the background — deposits and fills happen on the origin/destination chains, while settlement is handled automatically. + + +## Technical Details + +| Property | Value | +|----------|-------| +| **Chain ID** | `537713` | +| **RPC** | `https://rpc.chain.relay.link` | +| **Block Explorer** | [explorer.chain.relay.link](https://explorer.chain.relay.link) | +| **Native Asset** | ETH | +| **Stack** | Sovereign Stack (rollup) | +| **Data Availability** | Celestia | +| **VM** | EVM-compatible | + +## What Lives on the Relay Chain + +The Relay Chain hosts the core settlement contracts: + +- **[Hub](/references/protocol/components/hub)** — The ERC6909 multi-token ledger that tracks all balances +- **[Oracle](/references/protocol/components/oracle)** — The contract that processes oracle attestations (mint, burn, transfer) +- **[Allocator](/references/protocol/components/allocator)** — The contract that manages withdrawal proof generation + +All settlement activity — deposit attestations, fill verifications, balance transfers, and withdrawal authorizations — is processed as transactions on this chain. + +## Exploring Settlement Activity + +Every settlement transaction is visible on the [Relay Chain Block Explorer](https://explorer.chain.relay.link). You can trace the full lifecycle of any order by looking at the Oracle contract's attestation transactions, which record deposits, fills, and withdrawals. diff --git a/references/protocol/security.mdx b/references/protocol/security.mdx new file mode 100644 index 0000000..b3a9361 --- /dev/null +++ b/references/protocol/security.mdx @@ -0,0 +1,87 @@ +--- +title: Security & Audits +description: "Trust model, security architecture, and audit history" +sidebarTitle: Security & Audits +--- + +## Trust Model + +Relay Settlement is designed to be **non-custodial** — no single entity can unilaterally access user funds. Each component has different trust properties: + +### Depository (Minimal Trust) + +The Depository contracts are the most trust-minimized component: + +- **Non-upgradable** — Contract logic is immutable after deployment +- **Single authorization path** — Only the registered Allocator can authorize withdrawals +- **Short custody duration** — Funds are typically held for seconds to minutes, not hours or days +- **No admin withdrawal** — There is no backdoor function for the contract owner to withdraw user funds + +The Depository's security relies on the Allocator's signing authority. If the Allocator is compromised, it could authorize unauthorized withdrawals — but it cannot create balances that don't exist on the Hub. + +### Oracle (Trust-Critical) + +The Oracle determines which deposits and fills are considered valid: + +- **Can attribute balances** — The Oracle controls what gets minted, transferred, or burned on the Hub +- **Cannot steal Depository funds** — Even an incorrect attestation only affects Hub balances. The Allocator independently verifies Hub balances before authorizing any withdrawal. +- **Consensus-based** — Designed for multi-operator consensus, where multiple independent parties must agree on attestations + + +The Oracle is the main trust-bearing component. A compromised Oracle could incorrectly attribute balances, but the damage is bounded by the Allocator's balance checks and the Security Council's ability to pause the system. + + +### Hub (Smart Contract) + +The Hub is a deterministic smart contract on the [Relay Chain](/references/protocol/relay-chain): + +- **Rule-based** — Balance changes only occur through Oracle-attested actions (MINT, TRANSFER, BURN) +- **Idempotent** — The same attestation cannot be processed twice +- **Transparent** — All balance changes are visible on the Relay Chain block explorer + +### Allocator (Trust-Critical, MPC-Mitigated) + +The Allocator controls withdrawal authorization: + +- **MPC signatures** — No single entity holds the full signing key +- **Balance-bounded** — Can only authorize withdrawals up to a solver's Hub balance +- **Governed by Security Council** — A multisig can pause or replace the Allocator +- **Replay-protected** — Nonces and expirations prevent proof reuse + +## Security Council + +The Security Council is a multisig composed of multiple independent parties across different timezones. It governs the Allocator with tiered thresholds: + +| Action | Threshold | Purpose | +|--------|-----------|---------| +| **Pause** | 1-of-N | Any member can immediately halt all withdrawals | +| **Unpause** | Majority | Resume operations after investigation | +| **Replace Allocator** | Supermajority | Upgrade or fix the Allocator contract | +| **Change Membership** | Supermajority | Add or remove council members | + +The low pause threshold (1-of-N) ensures rapid response to emergencies, while the high thresholds for structural changes prevent unilateral action. + +## Global Pause Mechanism + +Because all withdrawals across all 80+ chains flow through a single Allocator, the entire protocol can be **paused with a single transaction**. This is a significant advantage over protocols where each chain's escrow contract must be individually paused — a process that takes time and could miss chains during an active exploit. + +## Audits + +The protocol has been audited by leading security firms: + +| Date | Scope | Auditor | Report | +|------|-------|---------|--------| +| February 2025 | Relay Depository (EVM) | [Spearbit](https://spearbit.com/) | [View Report](https://github.com/relayprotocol/relay-depository/blob/main/packages/ethereum-vm/audit/report.pdf) | +| June 2025 | Relay Depository (EVM) | [Certora](https://www.certora.com/) | [View Report](/references/protocol/depository/relay-escrow-report-certora.pdf) | +| November 2025 | Settlement Protocol | [Zellic](https://www.zellic.io/) | [View Report](https://github.com/relayprotocol/settlement-protocol/blob/main/smart-contracts/audit/report.pdf) | + +## Bug Bounty + +Details of the Relay bug bounty program can be found on the [Relay GitHub](https://github.com/relayprotocol/). + +## Source Code + +All protocol contracts are open source: + +- [`settlement-protocol`](https://github.com/relayprotocol/settlement-protocol) — Hub, Oracle, Allocator contracts +- [`relay-depository`](https://github.com/relayprotocol/relay-depository) — Depository contracts (EVM + Solana) diff --git a/references/protocol/vaults/overview.mdx b/references/protocol/vaults/overview.mdx index bc4979e..af7d536 100644 --- a/references/protocol/vaults/overview.mdx +++ b/references/protocol/vaults/overview.mdx @@ -4,6 +4,8 @@ description: Introducing Relay Vaults sidebarTitle: Overview --- +Relay Vaults is a separate protocol from [Relay Settlement](/references/protocol/overview), focused on liquidity provisioning rather than intent settlement. While Settlement handles the core flow of deposits, fills, and solver payments, Vaults provide the infrastructure solvers use to rebalance their capital across chains. + Relay's mission is to make transacting across chains as fast, cheap, and simple as online payments. **Relay Vaults** bring us closer to that vision. Vaults are permissionless, [ERC4626](https://ethereum.org/en/developers/docs/standards/tokens/erc-4626/) compliant, yield-bearing pools designed to provide liquidity that solvers use to facilitate faster, cheaper, and more efficient crosschain transactions. By leveraging idle capital from lending markets (e.g., Aave, Morpho), Relay Vaults enable solvers to instantly rebalance liquidity across chains. This improves crosschain withdrawals and rebalancing speed, reduces transaction fees, and ensures stable yield for depositors. From f1f7159887a02f56af27d29c3a6ec2686b51e91b Mon Sep 17 00:00:00 2001 From: Peter Watts Date: Fri, 6 Feb 2026 21:17:38 +1100 Subject: [PATCH 2/5] Add HTML/CSS chain-roles diagram to Relay Chain page Visual diagram showing the three chain roles in the protocol (Origin, Relay Chain, Destination) with components, actors, and cost annotations. Uses inline HTML/CSS for full visual control with brand purple styling, dark mode support, and responsive mobile layout. Co-Authored-By: Claude Opus 4.6 --- references/protocol/relay-chain.mdx | 64 ++++++++ style.css | 227 ++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+) diff --git a/references/protocol/relay-chain.mdx b/references/protocol/relay-chain.mdx index 08a72fd..53b963a 100644 --- a/references/protocol/relay-chain.mdx +++ b/references/protocol/relay-chain.mdx @@ -10,6 +10,70 @@ The Relay Chain is a dedicated blockchain purpose-built for settlement of crossc By running settlement on a dedicated chain, Relay avoids competing for blockspace on congested networks and keeps settlement costs minimal, regardless of how expensive the origin or destination chains are. +
+
+
+ Origin Chain + 80+ supported chains +
+
+
+ Depository + Holds user deposits +
+
+
User deposits funds
+
Solver withdraws funds
+
+
+ ~21,000 gas per deposit +
+
+ +
+
+ Relay Chain + Chain 537713 +
+
+
+ Hub + Tracks all balances +
+
+ Oracle + Verifies deposits & fills +
+
+ Allocator + Authorizes withdrawals +
+
+
+ ~$0.005 per settlement +
+
+ +
+
+ Destination Chain + Any supported chain +
+
+
+ Solver Fill + Executes user's order +
+
+
Solver fronts capital
+
User receives assets
+
+
+ Zero protocol overhead on fill +
+
+
+ ## Why a Dedicated Chain Intent protocols typically settle on the origin chain, which means settlement competes with all other activity on that chain. This creates several problems: diff --git a/style.css b/style.css index 5cfb810..ac8041e 100644 --- a/style.css +++ b/style.css @@ -357,6 +357,233 @@ } + /* Chain Roles Diagram */ + .chain-diagram { + display: flex; + gap: 0; + margin: 32px 0; + border-radius: 12px; + overflow: hidden; + border: 1px solid rgba(0, 0, 0, 0.1); + font-size: 14px; + line-height: 1.4; + } + + .dark .chain-diagram { + border-color: rgba(255, 255, 255, 0.1); + } + + .chain-lane { + flex: 1; + padding: 0; + display: flex; + flex-direction: column; + background-color: #fafafa; + position: relative; + } + + .dark .chain-lane { + background-color: #141414; + } + + .chain-lane-center { + background-color: #f0ecff; + } + + .dark .chain-lane-center { + background-color: #1a1040; + } + + .chain-lane + .chain-lane { + border-left: 1px solid rgba(0, 0, 0, 0.08); + } + + .dark .chain-lane + .chain-lane { + border-left-color: rgba(255, 255, 255, 0.08); + } + + .chain-lane-header { + padding: 12px 16px; + font-weight: 700; + font-size: 13px; + text-transform: uppercase; + letter-spacing: 0.5px; + text-align: center; + background-color: #eee; + color: #555; + border-bottom: 1px solid rgba(0, 0, 0, 0.08); + } + + .dark .chain-lane-header { + background-color: #1e1e1e; + color: #aaa; + border-bottom-color: rgba(255, 255, 255, 0.08); + } + + .chain-lane-header-center { + background-color: #4615C8; + color: #fff; + } + + .dark .chain-lane-header-center { + background-color: #4615C8; + color: #fff; + } + + .chain-lane-header span { + display: block; + font-weight: 400; + font-size: 11px; + letter-spacing: 0; + text-transform: none; + opacity: 0.7; + margin-top: 2px; + } + + .chain-lane-body { + flex: 1; + padding: 20px 16px; + display: flex; + flex-direction: column; + gap: 10px; + align-items: center; + justify-content: center; + } + + .chain-component { + width: 100%; + max-width: 160px; + padding: 10px 14px; + border-radius: 8px; + text-align: center; + font-weight: 600; + font-size: 13px; + background-color: #fff; + border: 1px solid rgba(0, 0, 0, 0.12); + color: #333; + } + + .dark .chain-component { + background-color: #1e1e1e; + border-color: rgba(255, 255, 255, 0.12); + color: #e0e0e0; + } + + .chain-component-purple { + background-color: #f3efff; + border-color: #c4b0f5; + color: #4615C8; + } + + .dark .chain-component-purple { + background-color: #221155; + border-color: #5b2af9; + color: #c4b0f5; + } + + .chain-component span { + display: block; + font-weight: 400; + font-size: 11px; + color: #888; + margin-top: 2px; + } + + .dark .chain-component span { + color: #777; + } + + .chain-component-purple span { + color: #7c5dba; + } + + .dark .chain-component-purple span { + color: #8b7aad; + } + + .chain-actor { + font-size: 12px; + color: #888; + text-align: center; + padding: 4px 0; + font-weight: 500; + } + + .dark .chain-actor { + color: #777; + } + + .chain-arrow { + display: flex; + align-items: center; + justify-content: center; + gap: 4px; + font-size: 11px; + color: #999; + padding: 4px 0; + } + + .dark .chain-arrow { + color: #666; + } + + .chain-arrow svg { + flex-shrink: 0; + } + + .chain-divider { + height: 1px; + background-color: rgba(0, 0, 0, 0.06); + margin: 4px 0; + width: 80%; + align-self: center; + } + + .dark .chain-divider { + background-color: rgba(255, 255, 255, 0.06); + } + + .chain-lane-footer { + padding: 10px 16px; + font-size: 11px; + color: #999; + text-align: center; + border-top: 1px solid rgba(0, 0, 0, 0.06); + line-height: 1.4; + } + + .dark .chain-lane-footer { + color: #666; + border-top-color: rgba(255, 255, 255, 0.06); + } + + @media (max-width: 640px) { + .chain-diagram { + flex-direction: column; + } + + .chain-lane + .chain-lane { + border-left: none; + border-top: 1px solid rgba(0, 0, 0, 0.08); + } + + .dark .chain-lane + .chain-lane { + border-left: none; + border-top-color: rgba(255, 255, 255, 0.08); + } + + .chain-lane-body { + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + padding: 16px; + } + + .chain-component { + max-width: 140px; + } + } + [data-page-title="Call Execution Integration Guide"] { h2#get-a-quote, h2#execute-the-call, From 0e4e028acd02ef58107b0f8f734f1ccd5fe2820f Mon Sep 17 00:00:00 2001 From: Peter Watts Date: Fri, 6 Feb 2026 23:00:54 +1100 Subject: [PATCH 3/5] Restructure protocol docs: components, flows, and content updates - Move Relay Chain into Components group, add Security Council and MPC Signing pages - Consolidate MPC content back into Allocator page - Create Contracts sidebar group (Addresses, EVM/SVM Depository Reference) - Rewrite Overview with 5 key dimensions (Speed, Cost, Capital Efficiency, Coverage, UX) - Restructure How It Works into Architecture + Flows sections with Mermaid diagrams - Add settlement flow image to Overview - Remove Design page, absorb relevant content into Overview - Update solver guide for direct protocol integration (not API) - Expand Addresses page with Relay Chain and Aurora contract placeholders - Update Security page to link to Security Council component - Clean up cross-references and internal links Co-Authored-By: Claude Opus 4.6 --- docs.json | 12 +- images/protocol/settlement-flow.png | Bin 0 -> 81021 bytes references/protocol/addresses.mdx | 28 ++- references/protocol/components/allocator.mdx | 44 ++-- references/protocol/components/hub.mdx | 2 +- references/protocol/components/oracle.mdx | 2 +- .../protocol/{ => components}/relay-chain.mdx | 64 ----- .../protocol/components/security-council.mdx | 37 +++ .../protocol/contracts/evm-depository.mdx | 4 +- .../protocol/contracts/solana-depository.mdx | 4 +- references/protocol/design.mdx | 101 -------- references/protocol/guides/for-apps.mdx | 2 +- references/protocol/guides/for-solvers.mdx | 28 +-- references/protocol/how-it-works.mdx | 90 +++---- references/protocol/overview.mdx | 104 ++++---- references/protocol/security.mdx | 25 +- style.css | 227 ------------------ 17 files changed, 203 insertions(+), 571 deletions(-) create mode 100644 images/protocol/settlement-flow.png rename references/protocol/{ => components}/relay-chain.mdx (61%) create mode 100644 references/protocol/components/security-council.mdx delete mode 100644 references/protocol/design.mdx diff --git a/docs.json b/docs.json index bbd3f40..a639e32 100644 --- a/docs.json +++ b/docs.json @@ -231,17 +231,17 @@ "pages": [ "references/protocol/overview", "references/protocol/how-it-works", - "references/protocol/relay-chain", { "group": "Components", "pages": [ + "references/protocol/components/relay-chain", "references/protocol/components/depository", "references/protocol/components/oracle", "references/protocol/components/hub", - "references/protocol/components/allocator" + "references/protocol/components/allocator", + "references/protocol/components/security-council" ] }, - "references/protocol/design", "references/protocol/security", { "group": "Guides", @@ -251,13 +251,13 @@ ] }, { - "group": "Contract Reference", + "group": "Contracts", "pages": [ + "references/protocol/addresses", "references/protocol/contracts/evm-depository", "references/protocol/contracts/solana-depository" ] - }, - "references/protocol/addresses" + } ] }, { diff --git a/images/protocol/settlement-flow.png b/images/protocol/settlement-flow.png new file mode 100644 index 0000000000000000000000000000000000000000..7e860ec2153f385587254696caf0bfe379a13165 GIT binary patch literal 81021 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%0ARTf*{yM-{x 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 Tqc2&T;NO`O(#I2zUB31|YQZ|u literal 0 HcmV?d00001 diff --git a/references/protocol/addresses.mdx b/references/protocol/addresses.mdx index 221f978..8c7c8f9 100644 --- a/references/protocol/addresses.mdx +++ b/references/protocol/addresses.mdx @@ -1,11 +1,33 @@ --- -title: Contracts Addresses -description: Addresses of Relay Depository contracts -sidebarTitle: Contracts Addresses +title: Addresses +description: Contract addresses for the Relay Settlement Protocol +sidebarTitle: Addresses --- +## Relay Chain + +Core settlement contracts deployed on the [Relay Chain](/references/protocol/components/relay-chain) (Chain ID `537713`). + +| Contract | Address | +|----------|---------| +| **Hub** | `TBD` | +| **Oracle** | `TBD` | + +## Aurora + +The [Allocator](/references/protocol/components/allocator) and [Security Council](/references/protocol/components/security-council) multisig are deployed on Aurora for integration with the NEAR MPC network. + +| Contract | Address | +|----------|---------| +| **Allocator** | `TBD` | +| **Security Council Multisig** | `TBD` | + +## Depository Contracts + {/* Generated using: "curl https://api.relay.link/chains | jq '.chains[] | "|\(.protocol.v2.chainId)|\(.protocol.v2.depository)|\(.vmType)|\(.id)"" */} +[Depository](/references/protocol/components/depository) contracts are deployed on every supported origin chain. + | Chain | Address | VM Type | EVM Chain Id | | -------------- | -------------------------------------------- | ----------- | ------------ | | ethereum | 0x4cd00e387622c35bddb9b4c962c136462338bc31 | ethereum-vm | 1 | diff --git a/references/protocol/components/allocator.mdx b/references/protocol/components/allocator.mdx index d662e19..6e270f6 100644 --- a/references/protocol/components/allocator.mdx +++ b/references/protocol/components/allocator.mdx @@ -1,6 +1,6 @@ --- title: Allocator -description: "The MPC-based component that authorizes crosschain withdrawals" +description: "The component that authorizes crosschain withdrawals" sidebarTitle: Allocator --- @@ -8,7 +8,7 @@ sidebarTitle: Allocator The Allocator is the component responsible for authorizing withdrawals from [Depository](/references/protocol/components/depository) contracts. When a solver wants to claim funds they've earned by filling orders, the Allocator verifies their [Hub](/references/protocol/components/hub) balance and generates a cryptographic proof that the Depository will accept. -The Allocator uses **MPC (Multi-Party Computation) chain signatures** via the NEAR protocol, meaning no single entity holds the private keys needed to authorize withdrawals. +The Allocator uses **MPC chain signatures** for signing, meaning no single entity holds the private keys needed to authorize withdrawals. It is governed by the [Security Council](/references/protocol/components/security-council), which can pause or replace it in an emergency. ## How It Works @@ -58,37 +58,31 @@ Different chains require different transaction formats. The Allocator uses speci Each Payload Builder constructs the chain-specific request structure (e.g., `CallRequest` for EVM, `TransferRequest` for Solana) with the appropriate encoding (ABI for EVM, Borsh for Solana). -## Security Model - -The Allocator is a trust-critical component — it controls access to funds in the Depository. Several safeguards protect against misuse: +## MPC Signing -### MPC Chain Signatures +The Allocator uses **Multi-Party Computation (MPC) chain signatures** for withdrawal authorization. Instead of a single private key controlling access to Depository funds, the signing key is split across multiple independent MPC nodes. A threshold of nodes must cooperate to produce a valid signature — no single entity ever holds the full key. -The Allocator's signing keys are managed through NEAR's MPC chain signatures. This means: +Key properties: - **No single key holder** — The private key is split across multiple MPC nodes, and a threshold must cooperate to produce a signature -- **Programmable authorization** — The Allocator is a smart contract that can enforce rules (balance checks, rate limits) before requesting a signature -- **Auditable** — All signing requests go through an onchain contract, creating a transparent record - -### Replay Protection +- **Programmable authorization** — The Allocator enforces rules (balance checks, rate limits) before requesting any signature +- **Chain-agnostic** — MPC signing can produce signatures for any curve or format (ECDSA, Ed25519, etc.), enabling support for EVM, Solana, Bitcoin, Sui, and other VMs +- **Auditable** — All signing requests flow through an onchain contract, creating a transparent record -Every withdrawal proof includes: +The protocol currently uses **NEAR MPC chain signatures** for its signing infrastructure. The Allocator contract is deployed on **Aurora** (NEAR ecosystem) for direct integration with the NEAR MPC network. -- **Nonce** — A unique, incrementing value that prevents the same proof from being used twice -- **Expiration** — A timestamp after which the proof is no longer valid, preventing stale proofs from being used + +The MPC signing layer is designed to be implementation-agnostic. While NEAR chain signatures are used today, the architecture allows for alternative MPC networks or signing backends without changes to the rest of the protocol. + -### Security Council - -The Allocator is governed by a **Security Council** — a multisig composed of multiple independent parties across different timezones. The Security Council can: - -- **Pause** the Allocator (1-of-N threshold) — Any member can immediately halt withdrawals in an emergency -- **Unpause** the Allocator (majority threshold) — Resume operations after a pause -- **Replace** the Allocator (supermajority threshold) — Set a new Allocator in case of a bug or required upgrade -- **Change membership** (supermajority threshold) — Add or remove Security Council members +## Security Model -### Global Pause +The Allocator is a trust-critical component — it controls access to funds in the Depository. Several safeguards protect against misuse: -Because all withdrawals across all chains flow through a single Allocator, the entire protocol can be paused with a single transaction. This is a significant security advantage over protocols where escrow contracts on each chain must be paused individually. +- **[MPC signing](#mpc-signing)** — No single entity holds the full signing key. A threshold of independent MPC nodes must cooperate to produce a valid signature. +- **Balance-bounded** — The Allocator can only authorize withdrawals up to a solver's existing [Hub](/references/protocol/components/hub) balance. It cannot mint new balances or alter the ledger. +- **[Security Council](/references/protocol/components/security-council)** — A multisig that can pause all withdrawals with a single transaction, or replace the Allocator entirely. +- **Replay protection** — Every withdrawal proof includes a unique nonce and expiration timestamp, preventing reuse of stale proofs. The Allocator cannot mint new balances or alter the Hub ledger. It can only authorize withdrawals up to a solver's existing Hub balance. Even a compromised Allocator cannot create funds that don't exist. @@ -96,4 +90,4 @@ The Allocator cannot mint new balances or alter the Hub ledger. It can only auth ## Source Code -The Allocator contract (`RelayAllocator.sol`) is part of the [`settlement-protocol`](https://github.com/relayprotocol/settlement-protocol) repository. It is deployed on Aurora (NEAR ecosystem) for direct integration with NEAR MPC chain signatures. +The Allocator contract (`RelayAllocator.sol`) is part of the [`settlement-protocol`](https://github.com/relayprotocol/settlement-protocol) repository. diff --git a/references/protocol/components/hub.mdx b/references/protocol/components/hub.mdx index 2763cd6..4136bb0 100644 --- a/references/protocol/components/hub.mdx +++ b/references/protocol/components/hub.mdx @@ -6,7 +6,7 @@ sidebarTitle: Hub ## Overview -The Hub is a smart contract on the [Relay Chain](/references/protocol/relay-chain) that serves as the central ledger for the protocol. It tracks the ownership of all funds deposited into [Depository](/references/protocol/components/depository) contracts across every supported chain. +The Hub is a smart contract on the [Relay Chain](/references/protocol/components/relay-chain) that serves as the central ledger for the protocol. It tracks the ownership of all funds deposited into [Depository](/references/protocol/components/depository) contracts across every supported chain. Implemented as an [ERC6909](https://eips.ethereum.org/EIPS/eip-6909) multi-token contract, the Hub can represent any deposited asset (ETH, USDC, SOL, etc.) from any chain as a token balance. When the [Oracle](/references/protocol/components/oracle) attests that a deposit or fill occurred, the Hub updates balances accordingly. diff --git a/references/protocol/components/oracle.mdx b/references/protocol/components/oracle.mdx index c9a0884..ed7455c 100644 --- a/references/protocol/components/oracle.mdx +++ b/references/protocol/components/oracle.mdx @@ -6,7 +6,7 @@ sidebarTitle: Oracle ## Overview -The Oracle is the verification engine of the protocol. It reads events from origin and destination chains, verifies that deposits and fills occurred correctly, and submits signed attestations to the [Hub](/references/protocol/components/hub) on the [Relay Chain](/references/protocol/relay-chain). +The Oracle is the verification engine of the protocol. It reads events from origin and destination chains, verifies that deposits and fills occurred correctly, and submits signed attestations to the [Hub](/references/protocol/components/hub) on the [Relay Chain](/references/protocol/components/relay-chain). Unlike push-based oracles that continuously stream data crosschain, Relay uses a **pull-based** model. The Oracle reads historical chain data on-demand when attestation is needed. This keeps costs low and avoids the overhead of maintaining persistent message channels to every chain. diff --git a/references/protocol/relay-chain.mdx b/references/protocol/components/relay-chain.mdx similarity index 61% rename from references/protocol/relay-chain.mdx rename to references/protocol/components/relay-chain.mdx index 53b963a..08a72fd 100644 --- a/references/protocol/relay-chain.mdx +++ b/references/protocol/components/relay-chain.mdx @@ -10,70 +10,6 @@ The Relay Chain is a dedicated blockchain purpose-built for settlement of crossc By running settlement on a dedicated chain, Relay avoids competing for blockspace on congested networks and keeps settlement costs minimal, regardless of how expensive the origin or destination chains are. -

-
-
- Origin Chain - 80+ supported chains -
-
-
- Depository - Holds user deposits -
-
-
User deposits funds
-
Solver withdraws funds
-
-
- ~21,000 gas per deposit -
-
- -
-
- Relay Chain - Chain 537713 -
-
-
- Hub - Tracks all balances -
-
- Oracle - Verifies deposits & fills -
-
- Allocator - Authorizes withdrawals -
-
-
- ~$0.005 per settlement -
-
- -
-
- Destination Chain - Any supported chain -
-
-
- Solver Fill - Executes user's order -
-
-
Solver fronts capital
-
User receives assets
-
-
- Zero protocol overhead on fill -
-
-
- ## Why a Dedicated Chain Intent protocols typically settle on the origin chain, which means settlement competes with all other activity on that chain. This creates several problems: diff --git a/references/protocol/components/security-council.mdx b/references/protocol/components/security-council.mdx new file mode 100644 index 0000000..4860ecf --- /dev/null +++ b/references/protocol/components/security-council.mdx @@ -0,0 +1,37 @@ +--- +title: Security Council +description: "The multisig governance body that protects the protocol" +sidebarTitle: Security Council +--- + +## Overview + +The Security Council is a multisig composed of multiple independent parties across different timezones. It serves as the protocol's emergency response and governance mechanism, with the authority to pause, unpause, and replace the [Allocator](/references/protocol/components/allocator). + +## Tiered Thresholds + +The Security Council uses tiered thresholds to balance rapid response with governance safety: + +| Action | Threshold | Purpose | +|--------|-----------|---------| +| **Pause** | 1-of-N | Any member can immediately halt all withdrawals | +| **Unpause** | Majority | Resume operations after investigation | +| **Replace Allocator** | Supermajority | Upgrade or fix the Allocator contract | +| **Change Membership** | Supermajority | Add or remove council members | + +The low pause threshold (1-of-N) ensures rapid response to emergencies, while the high thresholds for structural changes prevent unilateral action. + +## Global Pause + +Because all withdrawals across all 80+ chains flow through a single [Allocator](/references/protocol/components/allocator), the entire protocol can be **paused with a single transaction**. This is a significant advantage over protocols where each chain's escrow contract must be individually paused — a process that takes time and could miss chains during an active exploit. + +## Scope + +The Security Council governs the Allocator only. It cannot: + +- Modify the [Hub](/references/protocol/components/hub) ledger or create balances +- Withdraw user funds from the [Depository](/references/protocol/components/depository) +- Alter Oracle attestations +- Access funds held in any contract + +Its authority is limited to controlling the flow of withdrawals — pausing them in emergencies and replacing the Allocator if necessary. diff --git a/references/protocol/contracts/evm-depository.mdx b/references/protocol/contracts/evm-depository.mdx index e584e24..aa34d6f 100644 --- a/references/protocol/contracts/evm-depository.mdx +++ b/references/protocol/contracts/evm-depository.mdx @@ -1,7 +1,7 @@ --- -title: EVM Depository +title: EVM Depository Reference description: "Solidity API reference for the EVM Relay Depository contract" -sidebarTitle: EVM Depository +sidebarTitle: EVM Depository Reference --- # Solidity API diff --git a/references/protocol/contracts/solana-depository.mdx b/references/protocol/contracts/solana-depository.mdx index e95cf46..8d6236b 100644 --- a/references/protocol/contracts/solana-depository.mdx +++ b/references/protocol/contracts/solana-depository.mdx @@ -1,7 +1,7 @@ --- -title: Solana Depository +title: SVM Depository Reference description: "Anchor API reference for the Solana Relay Depository program" -sidebarTitle: Solana Depository +sidebarTitle: SVM Depository Reference --- # API diff --git a/references/protocol/design.mdx b/references/protocol/design.mdx deleted file mode 100644 index 30e2e05..0000000 --- a/references/protocol/design.mdx +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: Design Principles -description: "The design philosophy behind Relay Settlement" -sidebarTitle: Design ---- - -## Overview - -Relay Settlement is designed from the ground up for high-throughput crosschain payments. Every design decision optimizes for the properties that matter most in production: cost, speed, capital efficiency, and ease of chain expansion. - -This page explains the key design principles and how they differ from other intent settlement approaches. - -## Cost Efficiency - -The primary cost optimization in Relay is **minimizing gas consumption on origin and destination chains**. These are the chains users and solvers interact with — often popular, congested networks with expensive gas. - -### Minimal Deposit Cost - -User deposits cost approximately **21,000 gas** — nearly the cost of a raw native transfer. This is achieved by: - -- **No event-based verification** — The Depository does not need to emit complex events for crosschain message passing. The Oracle reads chain state directly. -- **No approval flow** — For native deposits, users send funds directly to the Depository contract. No separate approve + deposit transaction. -- **Simple storage** — The Depository only stores minimal data per deposit (depositor address, amount, orderId). - -Compare this to typical escrow patterns that require **~77,000 gas** for deposits (storing order details, emitting structured events, managing state). - -### Zero-Overhead Fills - -Solvers can fill orders with a **simple transfer** to the user on the destination chain. There is no requirement to route through a smart contract, emit events, or call specific functions on the destination. - -The Oracle verifies fills by reading destination chain state directly, removing the need for any fill-specific infrastructure on each destination chain. - -### Hub Settlement - -All settlement activity happens on the [Relay Chain](/references/protocol/relay-chain), where gas costs approximately **$0.005 per operation**. This means the cost of verifying and settling orders is negligible, regardless of how expensive the origin and destination chains are. - -## Capital Efficiency - -Capital efficiency determines how quickly solvers can recycle their funds to fill new orders. Relay maximizes this in two ways: - -### Real-Time Settlement - -Every order settles **individually and immediately** on the Hub. As soon as the Oracle attests a fill, the solver's balance is updated. There is no batching window. - -Compare this to approaches like optimistic settlement, where solvers wait **1–4 hours** for a batch cycle before getting paid. During that waiting period, capital is locked and cannot be reused. - -### Flexible Withdrawal - -Solvers accrue balances on the Hub and can withdraw from **any** Depository on **any** chain at any time. This gives solvers full control over their withdrawal strategy: - -- **High-frequency withdrawals** — Withdraw every few minutes to maximize capital velocity -- **Batched withdrawals** — Withdraw less frequently to minimize transaction costs -- **Strategic rebalancing** — Withdraw on chains where capital is needed most - -The Hub balance accrues regardless of withdrawal frequency, so solvers can optimize based on their own inventory needs. - -## Speed - -Relay is designed for **sub-3-second fill times** in production. Several design choices enable this: - -- **RFQ-based quoting** — Solvers provide firm quotes before execution, eliminating price uncertainty -- **Optimistic filling** — Solvers can fill before origin chain finality, taking on minimal confirmation risk for recent blocks -- **Instant settlement** — Solvers know their balance is updated immediately after fill attestation, giving them confidence to fill aggressively - -## Chain Expansion - -Adding support for a new chain in Relay requires minimal effort compared to other protocols: - -| Step | What's Required | -|------|----------------| -| **Deploy Depository** | Deploy the Depository contract on the new chain | -| **Configure Oracle** | Add the chain ID, VM type, Depository address, and RPC URL to the Oracle configuration | -| **Configure Hub** | Register the chain ID and Depository address on the Hub | -| **Add Payload Builder** | If the chain uses a new VM type, add a Payload Builder for withdrawal proof generation | - -No bridge integration, message-passing infrastructure, or crosschain contract deployment is needed on the new chain. The Depository is the only contract that touches the origin chain. - - -This minimal footprint is why Relay supports 80+ chains. Each new chain only requires a single contract deployment and configuration update. - - -## Redundancy - -The protocol is designed with fallback paths for critical operations: - -- **Multiple solvers** — If one solver fails to fill, another can step in. The Depository doesn't lock deposits to a single solver. -- **Revert path** — If no solver fills within the commitment window, users can reclaim their deposit -- **Global pause** — The [Allocator](/references/protocol/components/allocator) can pause all withdrawals across all chains with a single transaction in case of emergency -- **Security Council** — A multisig of independent parties can pause, unpause, or replace the Allocator - -## Design Comparison - -| Property | **Point-to-Point** | **Batch Settlement** | **Relay Hub** | -|----------|-------------------|---------------------|---------------| -| **Deposit cost** | ~21,000 gas | ~77,000 gas | ~21,000 gas | -| **Fill cost** | None | ~120,000 gas | None | -| **Settlement cost** | N/A | Amortized per batch | ~$0.005 per order | -| **Time to payment** | Instant (no protection) | 1–4 hours | Seconds | -| **New chain cost** | Low | High (bridge infra) | Low (Depository only) | -| **Capital lock-up** | None | Hours | Minutes | -| **Security** | None (trusted) | Onchain escrow | Non-custodial escrow | diff --git a/references/protocol/guides/for-apps.mdx b/references/protocol/guides/for-apps.mdx index 29ff2ad..52fe8d7 100644 --- a/references/protocol/guides/for-apps.mdx +++ b/references/protocol/guides/for-apps.mdx @@ -49,7 +49,7 @@ The user's deposit transaction is a standard transfer to the [Depository](/refer ### 2. Settlement on Relay Chain -After the solver fills the order, the [Oracle](/references/protocol/components/oracle) attests the deposit and fill on the [Relay Chain](/references/protocol/relay-chain). These attestation transactions are visible on the [Relay Chain Explorer](https://explorer.chain.relay.link). +After the solver fills the order, the [Oracle](/references/protocol/components/oracle) attests the deposit and fill on the [Relay Chain](/references/protocol/components/relay-chain). These attestation transactions are visible on the [Relay Chain Explorer](https://explorer.chain.relay.link). ### 3. Fill Transaction diff --git a/references/protocol/guides/for-solvers.mdx b/references/protocol/guides/for-solvers.mdx index 9975fbc..ced0682 100644 --- a/references/protocol/guides/for-solvers.mdx +++ b/references/protocol/guides/for-solvers.mdx @@ -10,27 +10,24 @@ Solvers (also called relayers) are the liquidity providers that fill crosschain This page explains the solver lifecycle and how each protocol component fits into the solver's workflow. - -Most solvers interact with Relay through the Relay API, which abstracts the protocol details. This page describes the underlying protocol flow for solvers who want to understand how settlement works, or who want to integrate directly. - - ## Solver Lifecycle -### 1. Receive Orders +### 1. Monitor Deposits -Solvers receive order information through the Relay API. Each order specifies: +Solvers watch [Depository](/references/protocol/components/depository) contracts across supported chains for new deposits. Each deposit includes: -- **Origin chain and asset** — What the user is depositing +- **Origin chain and asset** — What the user deposited - **Destination chain and action** — What the user wants executed - **Amount and fees** — The deposit amount and solver compensation +- **Order ID** — A unique identifier linking the deposit to the order ### 2. Fill Orders -When a solver commits to an order, they execute the user's requested action on the destination chain. This can be a simple transfer, a swap, or any arbitrary onchain action. Fills can be as gas-efficient as a standard transfer — no routing through protocol contracts is required on the destination. +When a solver decides to fill an order, they execute the user's requested action on the destination chain using their own capital. This can be a simple transfer, a swap, or any arbitrary onchain action. Fills can be as gas-efficient as a standard transfer — no routing through protocol contracts is required on the destination. ### 3. Settlement -After filling, the solver waits for the [Oracle](/references/protocol/components/oracle) to attest both the user's deposit and the solver's fill. The Oracle submits attestations to the [Hub](/references/protocol/components/hub) on the [Relay Chain](/references/protocol/relay-chain), which credits the solver's balance. +After filling, the solver waits for the [Oracle](/references/protocol/components/oracle) to attest both the user's deposit and the solver's fill. The Oracle submits attestations to the [Hub](/references/protocol/components/hub) on the [Relay Chain](/references/protocol/components/relay-chain), which credits the solver's balance. Settlement happens in real-time — there is no batching window. The solver's Hub balance updates as soon as the Oracle processes the attestation. @@ -68,13 +65,10 @@ Solver compensation is determined during the quoting phase, before the order is Settlement fees on the Relay Chain are paid by the solver but are minimal (~$0.005 per order). -## Direct Integration - -While the Relay API provides the simplest integration path, solvers can interact with the protocol directly: +## Contract References -- **Monitor deposits** — Watch Depository contracts for new deposits -- **Submit fills** — Execute fills on destination chains -- **Request settlement** — Trigger Oracle attestation -- **Withdraw funds** — Request proofs from the Allocator and submit to Depositories +For contract-level integration details, see: -For contract-level details, see the [EVM Depository Reference](/references/protocol/contracts/evm-depository) and [Solana Depository Reference](/references/protocol/contracts/solana-depository). +- [EVM Depository Reference](/references/protocol/contracts/evm-depository) +- [SVM Depository Reference](/references/protocol/contracts/solana-depository) +- [Addresses](/references/protocol/addresses) diff --git a/references/protocol/how-it-works.mdx b/references/protocol/how-it-works.mdx index 4097816..ddf8414 100644 --- a/references/protocol/how-it-works.mdx +++ b/references/protocol/how-it-works.mdx @@ -4,9 +4,49 @@ description: "End-to-end walkthrough of the settlement protocol flows" sidebarTitle: How It Works --- -Every crosschain order in Relay passes through three sequential flows: **Execution**, **Settlement**, and **Withdrawal**. This page walks through each in detail, followed by how reverts are handled when a solver fails to fill. +## Architecture -## Execution Flow +The protocol consists of four core components: + +| Component | Role | Location | +|-----------|------|----------| +| [**Depository**](/references/protocol/components/depository) | Holds user deposits on each origin chain | Every supported chain (80+) | +| [**Oracle**](/references/protocol/components/oracle) | Verifies deposits and fills, attests to the Hub | Off-chain service | +| [**Hub**](/references/protocol/components/hub) | Tracks token ownership and solver balances | Relay Chain | +| [**Allocator**](/references/protocol/components/allocator) | Generates withdrawal proofs for solvers | Relay Chain / MPC | + +These components are supported by the [Relay Chain](/references/protocol/components/relay-chain), a purpose-built settlement chain where the Hub contract lives and all settlement operations are processed. The [Security Council](/references/protocol/components/security-council) governs the Allocator with the ability to pause, unpause, or replace it in an emergency. + +## Flows + +Every crosschain order in Relay passes through three sequential flows: **Execution**, **Settlement**, and **Withdrawal**. + +```mermaid +sequenceDiagram + participant User + participant Solver + participant O as Origin Chain + participant D as Destination Chain + participant R as Relay Chain + + rect rgb(240, 240, 255) + Note over User,D: Execution + User->>O: 1. Deposit into Depository + Solver->>D: 2. Fill order + end + + rect rgb(240, 255, 240) + Note over Solver,R: Settlement + Solver->>R: 3. Oracle attests fill to Hub + end + + rect rgb(255, 240, 240) + Note over Solver,O: Withdrawal + Solver->>O: 4. Submit proof, claim funds + end +``` + +### Execution Flow Execution is the user-facing part of the process. The user deposits funds on the origin chain, and a solver fills their order on the destination chain. @@ -37,7 +77,7 @@ sequenceDiagram Because deposits go to the Depository (not to the solver directly), user funds are protected. If the solver fails to fill, the user can reclaim their deposit. -## Settlement Flow +### Settlement Flow Settlement is the process of verifying that the solver correctly filled the order, and crediting them on the Hub. @@ -63,7 +103,7 @@ sequenceDiagram 2. **Oracle reads destination chain** — The Oracle reads the destination chain to verify that the solver's fill matches the user's intent (correct destination, amount, and action). -3. **Oracle attests to the Hub** — The Oracle signs an EIP-712 attestation and submits it to the [Hub](/references/protocol/components/hub) contract on the [Relay Chain](/references/protocol/relay-chain). This attestation triggers two actions: +3. **Oracle attests to the Hub** — The Oracle signs an EIP-712 attestation and submits it to the [Hub](/references/protocol/components/hub) contract on the [Relay Chain](/references/protocol/components/relay-chain). This attestation triggers two actions: - **MINT** — The user's deposit is represented as a token balance on the Hub - **TRANSFER** — That balance is transferred from the user to the solver @@ -73,7 +113,7 @@ sequenceDiagram Settlement happens in real-time, per-order. There is no batching window. As soon as the Oracle verifies a fill, the solver's balance is updated on the Hub. -## Withdrawal Flow +### Withdrawal Flow Withdrawal is how solvers extract funds from the Depository to replenish their capital. Solvers accumulate balances on the Hub and can withdraw from any origin chain at any time. @@ -114,7 +154,7 @@ sequenceDiagram Solvers choose their own withdrawal strategy. They can withdraw frequently to maximize capital velocity, or batch withdrawals to minimize transaction costs. The Hub balance accrues in real-time regardless. -## Revert Flow +### Revert Flow If a solver fails to fill an order within the expected timeframe, the user's funds are protected and can be returned. @@ -142,41 +182,3 @@ sequenceDiagram 3. **User balance restored** — The Hub restores the user's token balance, effectively unlocking their deposit. 4. **User withdraws** — The user (or the protocol on their behalf) can trigger a withdrawal from the Depository to reclaim their funds. - -## Comparison: Settlement Approaches - -```mermaid -graph TD - subgraph P2P["Point-to-Point"] - U1[User] -->|Direct transfer| S1[Solver] - style P2P fill:#f9f9f9 - end - - subgraph Batch["Batch Settlement"] - U2[User] -->|Deposit ~77k gas| E2[Escrow] - E2 -->|Batch every ~4hrs| S2[Solver] - style Batch fill:#f9f9f9 - end - - subgraph RelayHub["Relay Hub"] - U3[User] -->|Deposit ~21k gas| D3[Depository] - D3 -.->|Attest| H3[Hub] - H3 -->|Real-time credit| S3[Solver] - style RelayHub fill:#f0f0ff - end -``` - -Different intent protocols handle settlement in different ways. Here's how Relay compares: - -| | **Point-to-Point** | **Batch Settlement** | **Relay Hub** | -|---|---|---|---| -| **Example** | Direct transfer | Across | Relay | -| **Settlement location** | Origin chain | Origin chain (batched) | Relay Chain | -| **Deposit gas** | ~21,000 | ~77,000 | ~21,000 | -| **Fill overhead** | None | ~120,000 gas | None | -| **Settlement cost** | N/A | Amortized | ~$0.005 on Relay Chain | -| **Time to payment** | Instant | ~4 hours (batch window) | Seconds (real-time) | -| **Capital efficiency** | High (but no protection) | Low (long lock-up) | High (real-time + protected) | -| **Chain expansion** | Easy | Requires bridge integration | Deploy Depository only | - -The key innovation is that by moving settlement to a dedicated low-cost chain, Relay achieves the capital efficiency of direct transfers with the security guarantees of an escrow system. diff --git a/references/protocol/overview.mdx b/references/protocol/overview.mdx index 46d47e8..59ef28d 100644 --- a/references/protocol/overview.mdx +++ b/references/protocol/overview.mdx @@ -4,102 +4,88 @@ description: "A crosschain settlement protocol optimized for speed, cost, and re sidebarTitle: Overview --- -## Introduction +Relay Settlement is a hyper-optimized intents protocol, designed for high-performance solvers that want to offer non-custodial crosschain relaying with the minimum possible overhead. It's optimized on 5 key dimensions: -Relay Settlement is a crosschain intent settlement protocol designed for high-throughput payments across blockchains. It connects users to solvers who fill crosschain orders using their own capital, and then settles those orders on the [Relay Chain](/references/protocol/relay-chain) — a dedicated, low-cost settlement chain. +- **Speed** — Sub-second fills with optimistic execution +- **Cost** — Minimal overhead on top of raw transfers +- **Capital Efficiency** — Flexible batching for maximum inventory re-use +- **Coverage** — Works on any chain, with minimal effort to deploy to new chains and VMs +- **UX** — Simple transfer flows + fast reverts + +Unlike other intent protocols, Relay Settlement does not attempt to be a solver marketplace, or offer any sort of orderflow auction. It's a pure utility, designed to be used by solvers that already have their own orderflow and just need efficient settlement. -The protocol minimizes gas costs on origin and destination chains, enables rapid chain expansion, and maximizes capital efficiency for solvers — all while remaining non-custodial and verifiable. Relay Settlement powers the [Relay](https://relay.link) app and API. Most integrators use the [Relay API](/api-reference) rather than interacting with the protocol directly, but the protocol is open and permissionless. -## How Crosschain Relaying Works +## How It Works + +Relay Settlement works similarly to other crosschain intent protocols: -Crosschain intent protocols like Relay use liquidity providers known as _solvers_ to "fast fill" user requests. Rather than waiting for slow canonical bridges, solvers front their own capital on the destination chain, and then claim payment from the user's escrowed deposit on the origin chain. +1. User locks funds on the origin chain +2. Solver fills the intent on the destination chain +3. Solver proves fulfillment, to unlock payment -This design has two key advantages: +The main difference is that instead of settlement happening point-to-point between the origin and destination, it uses a dedicated low-cost settlement hub. Solvers cheaply settle thousands of orders in real-time, accrue a balance on the [Hub](/references/protocol/components/hub), and then claim in a batch with one efficient proof when they need access to funds. -- **Speed** — Solvers can fill optimistically without waiting for chain finality, completing orders in seconds -- **Cost** — Expensive bridge operations are batched and amortized, keeping per-order costs low +![Settlement Flow](/images/protocol/settlement-flow.png) -The typical flow for intent-based protocols is: +Read the full walkthrough in [How It Works](/references/protocol/how-it-works). -1. User **deposits** funds into escrow on the origin chain -2. Solver **fills** the order on the destination chain -3. Solver **settles** the order to claim payment +## Speed -Where protocols differ is in *how* they handle settlement — the process of verifying fills and releasing payment. This is where Relay introduces significant improvements. +Relay is designed for **sub-second fill times** in production. Several design choices enable this: -## What Makes Relay Different +- **Optimistic execution** — Solvers fill orders before origin chain finality, taking on minimal confirmation risk for recent blocks +- **No auction delay** — There is no orderflow auction or solver competition window. Solvers receive orders and fill immediately. +- **Instant settlement** — The solver's Hub balance updates as soon as the Oracle attests the fill, giving solvers confidence to fill aggressively without waiting for batch cycles -Most intent protocols settle on the origin chain, which means gas costs grow with every order. Relay takes a fundamentally different approach: +The result is a user experience where crosschain transfers feel as fast as same-chain transfers. -**Settlement happens on a dedicated hub chain, not on the origin.** +## Cost -Instead of proving every fill back to the origin chain, Relay uses an [Oracle](/references/protocol/components/oracle) to verify fills off-chain and attest to a central [Hub](/references/protocol/components/hub) on the [Relay Chain](/references/protocol/relay-chain). Solvers accrue balances on the Hub and can withdraw from any origin chain at any time. +Crosschain intent systems can consume significant gas: passing order objects in calldata, verifying correct fulfillment, tracking intent status, emitting events. These might seem insignificant in an age of abundant blockspace, but add up in aggregate. Any gas paid is a wholesale cost that comes out before the solver's margin. -This architecture enables: +Relay keeps gas consumption to an absolute minimum, as close to raw transfers as possible: - **~21,000 gas deposits** — Users pay close to a raw transfer cost (vs ~77,000 for typical escrow) - **Zero-overhead fills** — Solvers can fill with a simple transfer, no contract interaction needed on destination -- **Real-time settlement** — Every order settles individually and immediately (vs 4-hour batch windows) -- **Capital efficiency** — Solvers get paid in seconds, not hours -- **Instant chain expansion** — Adding a chain requires deploying a single Depository contract +- **~$0.005 settlement** — Orders settle on a dedicated low-cost chain, not the origin -## Architecture Overview +This enables lower costs for users, and higher margins for solvers. -```mermaid -graph LR - subgraph Origin["Origin Chain (80+)"] - D[Depository] - end +## Capital Efficiency - subgraph RC["Relay Chain"] - H[Hub] - A[Allocator] - end +A core concern for solvers is getting paid as quickly as possible, in order to re-use capital for future orderflow. - O[Oracle] +Claiming payment for every intent on the origin can be expensive, so most high-volume solvers want some form of batching. But not all batching is the same — some protocols globally batch all intents on an hourly interval, resulting in significant capital lockup. - User -->|1. Deposit| D - Solver -->|2. Fill| Dest[Destination Chain] - O -->|3. Verify deposit| D - O -->|3. Verify fill| Dest - O -->|4. Attest| H - H -->|5. Credit balance| Solver - Solver -->|6. Request withdrawal| A - A -->|7. Generate proof| Solver - Solver -->|8. Submit proof| D - D -->|9. Release funds| Solver -``` +Relay Settlement allows solvers to withdraw whenever they want, choosing their optimal point on the trade-off spectrum between cost and capital efficiency. Settle thousands of orders in real-time on the Hub, and batch withdrawals on your own schedule. -The protocol consists of four core components that work together: +## Coverage -| Component | Role | Location | -|-----------|------|----------| -| [**Depository**](/references/protocol/components/depository) | Holds user deposits on each origin chain | Every supported chain (80+) | -| [**Oracle**](/references/protocol/components/oracle) | Verifies deposits and fills, attests to the Hub | Off-chain service | -| [**Hub**](/references/protocol/components/hub) | Tracks token ownership and solver balances | Relay Chain | -| [**Allocator**](/references/protocol/components/allocator) | Generates withdrawal proofs for solvers | Relay Chain / MPC | +Relay can work on any chain, and deployment to new chains and VMs is designed to be as simple as possible — so it can be ready day 1. -These components are supported by the [Relay Chain](/references/protocol/relay-chain), a purpose-built settlement chain where the Hub contract lives and all settlement operations are processed. +- Heavy logic is shifted to the Hub, so each new VM is a small module +- Pull-based verification allows oracles to watch more chains with fewer resources +- No dependencies on specific EIPs, precompiles, or advanced RPC methods -## The Three Flows +This has allowed Relay to expand to 80+ chains. More often than not, it's available weeks before a chain publicly launches. -Every crosschain order passes through three flows. Read the full walkthrough in [How It Works](/references/protocol/how-it-works). +There are also zero external dependencies in the critical path of accepting and filling orders. The only dependency is at the point of settlement, where an oracle needs to be operational to verify correct fulfillment and release funds. -### Execution +## UX -The user deposits funds into the [Depository](/references/protocol/components/depository) on the origin chain. A solver detects the order and fills it on the destination chain using their own capital. +Too many protocols prioritize protocol simplicity over end-user simplicity. Relay's philosophy is different — if there's any opportunity to reduce user friction, it takes it, even at the expense of protocol complexity. -### Settlement +**Deposit UX** -The [Oracle](/references/protocol/components/oracle) reads both chains to verify the deposit and fill occurred correctly. It attests this to the [Hub](/references/protocol/components/hub) on the Relay Chain, which credits the solver's balance. +Users don't want to make two transactions to approve and deposit. Gasless flows can be great, but shouldn't be forced on users with gas. Relay allows depositing via a simple transfer, for both native and ERC20 tokens — a UX that can compete with centralized exchanges. -### Withdrawal +**Failure UX** -The solver requests a withdrawal from the [Allocator](/references/protocol/components/allocator), which generates a cryptographic proof. The solver submits this proof to the [Depository](/references/protocol/components/depository) on the origin chain to claim the user's deposited funds. +For most intent protocols, failures are an afterthought. If something goes wrong, the user has a path to collect their funds out of escrow once the order has expired. In a world where prices move fast and items sell out, this is unacceptable. Relay allows solvers to instantly revert once they determine an order can't be filled. ## Source Code diff --git a/references/protocol/security.mdx b/references/protocol/security.mdx index b3a9361..853c483 100644 --- a/references/protocol/security.mdx +++ b/references/protocol/security.mdx @@ -28,12 +28,12 @@ The Oracle determines which deposits and fills are considered valid: - **Consensus-based** — Designed for multi-operator consensus, where multiple independent parties must agree on attestations -The Oracle is the main trust-bearing component. A compromised Oracle could incorrectly attribute balances, but the damage is bounded by the Allocator's balance checks and the Security Council's ability to pause the system. +The Oracle is the main trust-bearing component. A compromised Oracle could incorrectly attribute balances, but the damage is bounded by the Allocator's balance checks and the [Security Council's](/references/protocol/components/security-council) ability to pause the system. ### Hub (Smart Contract) -The Hub is a deterministic smart contract on the [Relay Chain](/references/protocol/relay-chain): +The Hub is a deterministic smart contract on the [Relay Chain](/references/protocol/components/relay-chain): - **Rule-based** — Balance changes only occur through Oracle-attested actions (MINT, TRANSFER, BURN) - **Idempotent** — The same attestation cannot be processed twice @@ -41,29 +41,18 @@ The Hub is a deterministic smart contract on the [Relay Chain](/references/proto ### Allocator (Trust-Critical, MPC-Mitigated) -The Allocator controls withdrawal authorization: +The [Allocator](/references/protocol/components/allocator) controls withdrawal authorization: -- **MPC signatures** — No single entity holds the full signing key +- **[MPC signatures](/references/protocol/components/allocator#mpc-signing)** — No single entity holds the full signing key - **Balance-bounded** — Can only authorize withdrawals up to a solver's Hub balance -- **Governed by Security Council** — A multisig can pause or replace the Allocator +- **Governed by [Security Council](/references/protocol/components/security-council)** — A multisig can pause or replace the Allocator - **Replay-protected** — Nonces and expirations prevent proof reuse ## Security Council -The Security Council is a multisig composed of multiple independent parties across different timezones. It governs the Allocator with tiered thresholds: +The [Security Council](/references/protocol/components/security-council) is a multisig that governs the Allocator. Any single member can immediately pause all withdrawals, while structural changes (replacing the Allocator, changing membership) require a supermajority. Because all withdrawals flow through a single Allocator, the entire protocol across 80+ chains can be paused with a single transaction. -| Action | Threshold | Purpose | -|--------|-----------|---------| -| **Pause** | 1-of-N | Any member can immediately halt all withdrawals | -| **Unpause** | Majority | Resume operations after investigation | -| **Replace Allocator** | Supermajority | Upgrade or fix the Allocator contract | -| **Change Membership** | Supermajority | Add or remove council members | - -The low pause threshold (1-of-N) ensures rapid response to emergencies, while the high thresholds for structural changes prevent unilateral action. - -## Global Pause Mechanism - -Because all withdrawals across all 80+ chains flow through a single Allocator, the entire protocol can be **paused with a single transaction**. This is a significant advantage over protocols where each chain's escrow contract must be individually paused — a process that takes time and could miss chains during an active exploit. +See the full [Security Council](/references/protocol/components/security-council) page for details on tiered thresholds and scope. ## Audits diff --git a/style.css b/style.css index ac8041e..5cfb810 100644 --- a/style.css +++ b/style.css @@ -357,233 +357,6 @@ } - /* Chain Roles Diagram */ - .chain-diagram { - display: flex; - gap: 0; - margin: 32px 0; - border-radius: 12px; - overflow: hidden; - border: 1px solid rgba(0, 0, 0, 0.1); - font-size: 14px; - line-height: 1.4; - } - - .dark .chain-diagram { - border-color: rgba(255, 255, 255, 0.1); - } - - .chain-lane { - flex: 1; - padding: 0; - display: flex; - flex-direction: column; - background-color: #fafafa; - position: relative; - } - - .dark .chain-lane { - background-color: #141414; - } - - .chain-lane-center { - background-color: #f0ecff; - } - - .dark .chain-lane-center { - background-color: #1a1040; - } - - .chain-lane + .chain-lane { - border-left: 1px solid rgba(0, 0, 0, 0.08); - } - - .dark .chain-lane + .chain-lane { - border-left-color: rgba(255, 255, 255, 0.08); - } - - .chain-lane-header { - padding: 12px 16px; - font-weight: 700; - font-size: 13px; - text-transform: uppercase; - letter-spacing: 0.5px; - text-align: center; - background-color: #eee; - color: #555; - border-bottom: 1px solid rgba(0, 0, 0, 0.08); - } - - .dark .chain-lane-header { - background-color: #1e1e1e; - color: #aaa; - border-bottom-color: rgba(255, 255, 255, 0.08); - } - - .chain-lane-header-center { - background-color: #4615C8; - color: #fff; - } - - .dark .chain-lane-header-center { - background-color: #4615C8; - color: #fff; - } - - .chain-lane-header span { - display: block; - font-weight: 400; - font-size: 11px; - letter-spacing: 0; - text-transform: none; - opacity: 0.7; - margin-top: 2px; - } - - .chain-lane-body { - flex: 1; - padding: 20px 16px; - display: flex; - flex-direction: column; - gap: 10px; - align-items: center; - justify-content: center; - } - - .chain-component { - width: 100%; - max-width: 160px; - padding: 10px 14px; - border-radius: 8px; - text-align: center; - font-weight: 600; - font-size: 13px; - background-color: #fff; - border: 1px solid rgba(0, 0, 0, 0.12); - color: #333; - } - - .dark .chain-component { - background-color: #1e1e1e; - border-color: rgba(255, 255, 255, 0.12); - color: #e0e0e0; - } - - .chain-component-purple { - background-color: #f3efff; - border-color: #c4b0f5; - color: #4615C8; - } - - .dark .chain-component-purple { - background-color: #221155; - border-color: #5b2af9; - color: #c4b0f5; - } - - .chain-component span { - display: block; - font-weight: 400; - font-size: 11px; - color: #888; - margin-top: 2px; - } - - .dark .chain-component span { - color: #777; - } - - .chain-component-purple span { - color: #7c5dba; - } - - .dark .chain-component-purple span { - color: #8b7aad; - } - - .chain-actor { - font-size: 12px; - color: #888; - text-align: center; - padding: 4px 0; - font-weight: 500; - } - - .dark .chain-actor { - color: #777; - } - - .chain-arrow { - display: flex; - align-items: center; - justify-content: center; - gap: 4px; - font-size: 11px; - color: #999; - padding: 4px 0; - } - - .dark .chain-arrow { - color: #666; - } - - .chain-arrow svg { - flex-shrink: 0; - } - - .chain-divider { - height: 1px; - background-color: rgba(0, 0, 0, 0.06); - margin: 4px 0; - width: 80%; - align-self: center; - } - - .dark .chain-divider { - background-color: rgba(255, 255, 255, 0.06); - } - - .chain-lane-footer { - padding: 10px 16px; - font-size: 11px; - color: #999; - text-align: center; - border-top: 1px solid rgba(0, 0, 0, 0.06); - line-height: 1.4; - } - - .dark .chain-lane-footer { - color: #666; - border-top-color: rgba(255, 255, 255, 0.06); - } - - @media (max-width: 640px) { - .chain-diagram { - flex-direction: column; - } - - .chain-lane + .chain-lane { - border-left: none; - border-top: 1px solid rgba(0, 0, 0, 0.08); - } - - .dark .chain-lane + .chain-lane { - border-left: none; - border-top-color: rgba(255, 255, 255, 0.08); - } - - .chain-lane-body { - flex-direction: row; - flex-wrap: wrap; - justify-content: center; - padding: 16px; - } - - .chain-component { - max-width: 140px; - } - } - [data-page-title="Call Execution Integration Guide"] { h2#get-a-quote, h2#execute-the-call, From b27ec4763e6a22f5c49da71b65dbcd125b144570 Mon Sep 17 00:00:00 2001 From: Peter Watts Date: Sun, 15 Feb 2026 16:30:01 +1100 Subject: [PATCH 4/5] Refine overview and how-it-works copy - Link to Relay Chain instead of "dedicated low-cost settlement hub" - Remove Info callout, Source Code section, and redundant links from overview - Tighten Failure UX and Coverage copy - Clarify Hub and Allocator both live on Relay Chain in how-it-works Co-Authored-By: Claude Opus 4.6 --- references/protocol/how-it-works.mdx | 2 +- references/protocol/overview.mdx | 22 +++------------------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/references/protocol/how-it-works.mdx b/references/protocol/how-it-works.mdx index ddf8414..b3fbd12 100644 --- a/references/protocol/how-it-works.mdx +++ b/references/protocol/how-it-works.mdx @@ -15,7 +15,7 @@ The protocol consists of four core components: | [**Hub**](/references/protocol/components/hub) | Tracks token ownership and solver balances | Relay Chain | | [**Allocator**](/references/protocol/components/allocator) | Generates withdrawal proofs for solvers | Relay Chain / MPC | -These components are supported by the [Relay Chain](/references/protocol/components/relay-chain), a purpose-built settlement chain where the Hub contract lives and all settlement operations are processed. The [Security Council](/references/protocol/components/security-council) governs the Allocator with the ability to pause, unpause, or replace it in an emergency. +These components are supported by the [Relay Chain](/references/protocol/components/relay-chain), a purpose-built settlement chain where the Hub and Allocator contracts live and all settlement operations are processed. The [Security Council](/references/protocol/components/security-council) governs the Allocator with the ability to pause, unpause, or replace it in an emergency. ## Flows diff --git a/references/protocol/overview.mdx b/references/protocol/overview.mdx index 59ef28d..bb35743 100644 --- a/references/protocol/overview.mdx +++ b/references/protocol/overview.mdx @@ -14,11 +14,6 @@ Relay Settlement is a hyper-optimized intents protocol, designed for high-perfor Unlike other intent protocols, Relay Settlement does not attempt to be a solver marketplace, or offer any sort of orderflow auction. It's a pure utility, designed to be used by solvers that already have their own orderflow and just need efficient settlement. - - -Relay Settlement powers the [Relay](https://relay.link) app and API. Most integrators use the [Relay API](/api-reference) rather than interacting with the protocol directly, but the protocol is open and permissionless. - - ## How It Works Relay Settlement works similarly to other crosschain intent protocols: @@ -27,12 +22,10 @@ Relay Settlement works similarly to other crosschain intent protocols: 2. Solver fills the intent on the destination chain 3. Solver proves fulfillment, to unlock payment -The main difference is that instead of settlement happening point-to-point between the origin and destination, it uses a dedicated low-cost settlement hub. Solvers cheaply settle thousands of orders in real-time, accrue a balance on the [Hub](/references/protocol/components/hub), and then claim in a batch with one efficient proof when they need access to funds. +The main difference is that instead of settlement happening point-to-point between the origin and destination, it all happens on the [Relay Chain](/references/protocol/components/relay-chain). Solvers cheaply settle thousands of orders in real-time, accrue a balance, and then claim in a batch with one efficient proof when they need access to funds. ![Settlement Flow](/images/protocol/settlement-flow.png) -Read the full walkthrough in [How It Works](/references/protocol/how-it-works). - ## Speed Relay is designed for **sub-second fill times** in production. Several design choices enable this: @@ -67,14 +60,12 @@ Relay Settlement allows solvers to withdraw whenever they want, choosing their o Relay can work on any chain, and deployment to new chains and VMs is designed to be as simple as possible — so it can be ready day 1. -- Heavy logic is shifted to the Hub, so each new VM is a small module +- Heavy logic is shifted to the Relay Chain, so each new VM is a small module - Pull-based verification allows oracles to watch more chains with fewer resources - No dependencies on specific EIPs, precompiles, or advanced RPC methods This has allowed Relay to expand to 80+ chains. More often than not, it's available weeks before a chain publicly launches. -There are also zero external dependencies in the critical path of accepting and filling orders. The only dependency is at the point of settlement, where an oracle needs to be operational to verify correct fulfillment and release funds. - ## UX Too many protocols prioritize protocol simplicity over end-user simplicity. Relay's philosophy is different — if there's any opportunity to reduce user friction, it takes it, even at the expense of protocol complexity. @@ -85,11 +76,4 @@ Users don't want to make two transactions to approve and deposit. Gasless flows **Failure UX** -For most intent protocols, failures are an afterthought. If something goes wrong, the user has a path to collect their funds out of escrow once the order has expired. In a world where prices move fast and items sell out, this is unacceptable. Relay allows solvers to instantly revert once they determine an order can't be filled. - -## Source Code - -All protocol components are open source on [GitHub](https://github.com/relayprotocol/): - -- [`settlement-protocol`](https://github.com/relayprotocol/settlement-protocol) — Hub, Oracle, and Allocator contracts -- [`relay-depository`](https://github.com/relayprotocol/relay-depository) — Depository contracts (EVM + Solana) +For most intent protocols, failures are an afterthought. If something goes wrong, the user is forced to manually collect their funds out of escrow, often after a delay. Relay allows solvers to instantly revert once they determine an order can't be filled, abstracting complexity and giving users a UX that feels similar to using a single chain. From 13c7719a87939ac7d317e1017083f3f348db21cb Mon Sep 17 00:00:00 2001 From: Peter Watts Date: Mon, 16 Feb 2026 12:30:14 +1100 Subject: [PATCH 5/5] Rewrite protocol flows: pull-based Oracle, refund/forced exit, withdrawal updates - Oracle reframed as pull-based: never initiates, only responds to requests - Settlement flow: solver requests attestation from Oracle, submits to Hub - Withdrawal flow: solver drives entire flow through Oracle - Add Refund flow: solver-initiated instant refund on origin, settled like a fill - Rename Revert flow to Forced Exit: user-initiated after fill window expires - Add solver abandon concept to shorten forced exit window - Update Oracle component page: request-response pipeline, five attestation types - Change all "revert" terminology to "refund" in user-facing docs - Simplify diagrams: merge Oracle validators/contract, remove NEAR MPC participant Co-Authored-By: Claude Opus 4.6 --- references/protocol/components/allocator.mdx | 25 ++-- references/protocol/components/hub.mdx | 17 ++- references/protocol/components/oracle.mdx | 78 ++++++----- references/protocol/how-it-works.mdx | 140 +++++++++++++------ references/protocol/overview.mdx | 4 +- references/protocol/security.mdx | 6 +- 6 files changed, 175 insertions(+), 95 deletions(-) diff --git a/references/protocol/components/allocator.mdx b/references/protocol/components/allocator.mdx index 6e270f6..4c86f96 100644 --- a/references/protocol/components/allocator.mdx +++ b/references/protocol/components/allocator.mdx @@ -15,29 +15,28 @@ The Allocator uses **MPC chain signatures** for signing, meaning no single entit ```mermaid sequenceDiagram participant Solver - participant Allocator - participant Hub + participant Hub as Hub (Relay Chain) + participant Oracle + participant Allocator as Allocator (Aurora) participant PB as Payload Builder - participant MPC as NEAR MPC participant Depository - Solver->>Allocator: 1. Request withdrawal (chain, asset, amount) - Allocator->>Hub: 2. Verify solver balance - Hub-->>Allocator: Balance confirmed - Allocator->>PB: 3. Construct chain-specific payload + Solver->>Hub: 1. Transfer to withdrawal address + Oracle->>Hub: 2. Detect withdrawal transfer + Oracle->>Allocator: 3. Submit withdrawal request + Allocator->>PB: 4. Construct chain-specific payload PB-->>Allocator: Withdrawal request (e.g. CallRequest) - Allocator->>MPC: 4. Request MPC signature - MPC-->>Allocator: Signed proof - Allocator-->>Solver: 5. Return signed withdrawal proof - Solver->>Depository: 6. Submit proof to target chain + Allocator->>Allocator: 5. Generate MPC signature + Allocator-->>Solver: 6. Return signed withdrawal proof + Solver->>Depository: 7. Submit proof to target chain Depository-->>Solver: Funds released ``` The withdrawal authorization process: -1. **Solver requests withdrawal** — The solver specifies the target chain, asset, and amount they want to withdraw +1. **Solver initiates withdrawal** — The solver transfers their [Hub](/references/protocol/components/hub) balance to a deterministic [withdrawal address](/references/protocol/components/hub#withdrawal-addresses) that encodes the withdrawal parameters (target chain, asset, amount, recipient) -2. **Balance check** — The Allocator reads the solver's balance on the Hub to confirm sufficient funds are available +2. **Oracle detects transfer** — The [Oracle](/references/protocol/components/oracle) monitors the Hub for transfers to withdrawal addresses, decodes the parameters, and forwards the request to the Allocator 3. **Payload construction** — A chain-specific **Payload Builder** constructs the withdrawal request in the format the target chain's Depository expects diff --git a/references/protocol/components/hub.mdx b/references/protocol/components/hub.mdx index 4136bb0..f6e8ace 100644 --- a/references/protocol/components/hub.mdx +++ b/references/protocol/components/hub.mdx @@ -16,7 +16,7 @@ The Hub maintains a global balance sheet: - When a user **deposits** into a Depository, the Oracle attests this, and the Hub **mints** a corresponding token balance for the user - When a solver **fills** an order, the Oracle attests this, and the Hub **transfers** the balance from the user to the solver -- When a solver **withdraws** from a Depository, the Oracle attests this, and the Hub **burns** the corresponding balance +- When a solver **withdraws**, they transfer their balance to a [withdrawal address](#withdrawal-addresses) on the Hub. After funds are claimed from the Depository, the Oracle attests the withdrawal and the Hub **burns** the corresponding balance This means the Hub always reflects the current state of all funds across all chains in real-time. @@ -61,9 +61,18 @@ The Hub implements the full ERC6909 interface, including: - **Operator system** — Grant an address full control over all token balances - **EIP-712 permits** — Gasless approval signatures - -While the Hub supports direct transfers between addresses, in practice most balance changes come from Oracle attestations (MINT, TRANSFER, BURN) rather than user-initiated transfers. - +## Withdrawal Addresses + +Solvers initiate withdrawals by transferring their Hub balance to a **withdrawal address** — a deterministic virtual address derived from the withdrawal parameters: + +- Target chain and depository +- Currency and amount +- Recipient address +- Withdrawal nonce + +The withdrawal address is computed by hashing these parameters together. It doesn't correspond to a real contract — it's a signal. The [Oracle](/references/protocol/components/oracle) monitors the Hub for transfers to these addresses, decodes the parameters, and forwards the request to the [Allocator](/references/protocol/components/allocator) for proof generation. + +This pattern keeps the Hub simple — it's pure ERC6909 with no custom withdrawal logic. The withdrawal semantics are layered on top by convention. ## Source Code diff --git a/references/protocol/components/oracle.mdx b/references/protocol/components/oracle.mdx index ed7455c..4f516ba 100644 --- a/references/protocol/components/oracle.mdx +++ b/references/protocol/components/oracle.mdx @@ -1,67 +1,75 @@ --- title: Oracle -description: "The verification layer that attests deposits and fills to the Hub" +description: "The decentralized validator set that verifies deposits and fills" sidebarTitle: Oracle --- ## Overview -The Oracle is the verification engine of the protocol. It reads events from origin and destination chains, verifies that deposits and fills occurred correctly, and submits signed attestations to the [Hub](/references/protocol/components/hub) on the [Relay Chain](/references/protocol/components/relay-chain). +The Oracle is a decentralized validator set that verifies crosschain activity and produces signed attestations for the [Hub](/references/protocol/components/hub) on the [Relay Chain](/references/protocol/components/relay-chain). Validators independently read origin and destination chains, verify that deposits and fills occurred correctly, and sign EIP-712 attestations. Attestations require signatures from a threshold of validators before the Hub will accept them. -Unlike push-based oracles that continuously stream data crosschain, Relay uses a **pull-based** model. The Oracle reads historical chain data on-demand when attestation is needed. This keeps costs low and avoids the overhead of maintaining persistent message channels to every chain. +The Oracle never initiates actions — it only responds to requests. Solvers and users call the Oracle when they need verification, and the Oracle returns signed attestations that the caller then submits to the Hub. This **pull-based** model keeps costs low and avoids the overhead of maintaining persistent message channels to every chain. ## What the Oracle Attests -The Oracle can attest to four types of events: +The Oracle can attest to five types of events: ### Deposits -When a user deposits into the [Depository](/references/protocol/components/depository) on an origin chain, the Oracle verifies the deposit occurred and attests it to the Hub. This triggers a **MINT** action, creating a token balance on the Hub that represents the deposited funds. +When requested, the Oracle verifies that a user's deposit into the [Depository](/references/protocol/components/depository) occurred on the origin chain. The resulting attestation triggers a **MINT** action on the Hub, creating a token balance that represents the deposited funds. ### Fills -When a solver fills a user's order on a destination chain, the Oracle verifies the fill matched the user's intent (correct destination, amount, and action). It attests this to the Hub as a **TRANSFER** action, moving the balance from the user to the solver. +When requested, the Oracle verifies that a solver's fill on the destination chain matched the user's intent (correct destination, amount, and action). The resulting attestation triggers a **TRANSFER** action on the Hub, moving the balance from the user to the solver. -### Reverts +### Refunds -If a solver fails to fill within the expected timeframe, the Oracle can attest a revert. This restores the user's balance on the Hub, allowing them to withdraw their original deposit. +If a solver can't fill an order, they can send funds directly to the user on the origin chain. The solver then requests an attestation from the Oracle, which verifies the refund occurred and returns a signed attestation. The solver submits this to the Hub to get reimbursed — the same settlement process as a fill. + +### Forced Exits + +If a solver fails to fill **and** doesn't refund, the user can request a forced exit attestation after the order's fill window expires. The Oracle verifies the fill did not occur and returns a signed attestation. The user submits this to the Hub to restore their balance and reclaim their deposit. ### Withdrawals -When a solver claims funds from a Depository, the Oracle verifies the withdrawal occurred and attests it to the Hub as a **BURN** action, reducing the solver's Hub balance to stay in sync. +The Oracle plays two roles in the withdrawal flow: + +1. **Proof generation** — When a solver requests a withdrawal, the Oracle verifies the solver's transfer to a [withdrawal address](/references/protocol/components/hub#withdrawal-addresses) on the Hub, decodes the withdrawal parameters, and forwards the request to the [Allocator](/references/protocol/components/allocator) on Aurora for proof generation. + +2. **Burn attestation** — After the solver claims funds from the Depository, the solver requests a burn attestation. The Oracle verifies the withdrawal occurred on the target chain and returns a signed attestation that the solver submits to the Hub as a **BURN** action. -## Architecture +## How It Works ```mermaid sequenceDiagram + participant Caller as Caller (Solver / User) + participant V as Oracle Validators participant Chains as Supported Chains - participant Oracle + participant OC as Oracle Contract (Relay Chain) participant Hub as Hub (Relay Chain) - loop For each event - Chains-->>Oracle: 1. Monitor deposits and fills - Oracle->>Chains: 2. Read and verify chain data - Note over Oracle: 3. Sign EIP-712 attestation - end - Oracle->>Hub: 4. Submit executeMultiple() - Hub->>Hub: Process actions (MINT / TRANSFER / BURN) + Caller->>V: 1. Request attestation + V->>Chains: 2. Read and verify chain data + Note over V: 3. Each validator signs EIP-712 attestation + V-->>Caller: 4. Return signed attestation + Caller->>OC: 5. Submit with threshold signatures + OC->>OC: 6. Verify signature threshold + OC->>Hub: 7. Execute actions (MINT / TRANSFER / BURN) ``` -The Oracle operates as a multi-step pipeline: +The Oracle operates as a request-response pipeline: -1. **Monitor** — Watch for relevant events on supported chains (deposits, fills) -2. **Verify** — Read chain data to confirm the event details match expected values -3. **Sign** — Create an EIP-712 typed data signature over the attestation -4. **Submit** — Call `executeMultiple()` on the Oracle contract on the Relay Chain - -The Oracle contract on the Relay Chain processes the signed attestation and calls the appropriate action on the Hub (MINT, TRANSFER, or BURN). +1. **Request** — A solver or user calls the Oracle to request verification of a crosschain event +2. **Verify** — Each validator independently reads chain data to confirm event details +3. **Sign** — Each validator creates an EIP-712 typed data signature over the attestation +4. **Return** — The signed attestation is returned to the caller, who submits it to the Oracle contract on the Relay Chain. The contract verifies the threshold is met and executes the actions on the Hub ### Pull-Based Verification A key cost optimization is that the Oracle does not require message-passing infrastructure (like LayerZero or Hyperlane) between chains. Instead: -- Validators read **historical chain data** on-demand, rather than continuously watching message inboxes -- Attestation requests are initiated and delivered on the Hub, meaning **no gas is spent on origin or destination chains** for verification +- Validators read **historical chain data** on-demand when a caller requests verification +- Attestations are submitted by the caller to the Relay Chain, meaning **no gas is spent on origin or destination chains** for verification - This dramatically reduces the per-order cost of verification compared to protocols that emit and relay crosschain messages ### Batch Execution @@ -70,13 +78,19 @@ The Oracle supports batch attestation via `executeMultiple()`. Multiple attestat If an individual attestation in a batch has already been processed, it is skipped rather than reverting the entire batch. -## Decentralization +## Oracle Contract + +The Oracle contract on the Relay Chain is a thin verification layer. It receives batches of signed attestations, verifies that each attestation has the required number of valid signatures from registered validators, and calls the corresponding action on the Hub. The contract itself does not perform any crosschain verification — it trusts the validator set's signatures and enforces the threshold requirement. + +The contract manages the validator set via role-based access control, and ensures idempotency so that the same attestation cannot be processed twice. + +## Security -The Oracle is designed to support a **consensus-based multi-operator model**, where multiple independent parties run oracle nodes and attestations require agreement from a threshold of operators. This eliminates any single point of trust in the verification process. +The Oracle is a trust-critical component — it determines which deposits and fills are considered valid. Several properties limit the impact of a compromise: - -The Oracle is a trust-critical component — it determines which deposits and fills are considered valid. However, it cannot steal funds. Even a compromised Oracle can only incorrectly attribute balances on the Hub. User funds in the Depository remain protected by the Allocator's withdrawal authorization. - +- **Consensus threshold** — Attestations require signatures from multiple independent validators, preventing any single validator from submitting false attestations +- **Cannot steal funds** — Even a compromised Oracle can only incorrectly attribute balances on the Hub. User funds in the Depository remain protected by the [Allocator's](/references/protocol/components/allocator) independent withdrawal authorization. +- **Bounded impact** — The [Security Council](/references/protocol/components/security-council) can pause the system if anomalous attestations are detected ## Source Code diff --git a/references/protocol/how-it-works.mdx b/references/protocol/how-it-works.mdx index b3fbd12..96a3a89 100644 --- a/references/protocol/how-it-works.mdx +++ b/references/protocol/how-it-works.mdx @@ -11,15 +11,15 @@ The protocol consists of four core components: | Component | Role | Location | |-----------|------|----------| | [**Depository**](/references/protocol/components/depository) | Holds user deposits on each origin chain | Every supported chain (80+) | -| [**Oracle**](/references/protocol/components/oracle) | Verifies deposits and fills, attests to the Hub | Off-chain service | +| [**Oracle**](/references/protocol/components/oracle) | Verifies deposits and fills, attests to the Hub | Validator set + Relay Chain contract | | [**Hub**](/references/protocol/components/hub) | Tracks token ownership and solver balances | Relay Chain | -| [**Allocator**](/references/protocol/components/allocator) | Generates withdrawal proofs for solvers | Relay Chain / MPC | +| [**Allocator**](/references/protocol/components/allocator) | Generates withdrawal proofs for solvers | Aurora / MPC | These components are supported by the [Relay Chain](/references/protocol/components/relay-chain), a purpose-built settlement chain where the Hub and Allocator contracts live and all settlement operations are processed. The [Security Council](/references/protocol/components/security-council) governs the Allocator with the ability to pause, unpause, or replace it in an emergency. ## Flows -Every crosschain order in Relay passes through three sequential flows: **Execution**, **Settlement**, and **Withdrawal**. +Every crosschain order in Relay passes through three sequential flows: **Execution**, **Settlement**, and **Withdrawal**. If the solver can't fill, a **Refund** or **Forced Exit** flow returns funds to the user. ```mermaid sequenceDiagram @@ -37,12 +37,13 @@ sequenceDiagram rect rgb(240, 255, 240) Note over Solver,R: Settlement - Solver->>R: 3. Oracle attests fill to Hub + Solver->>R: 3. Submit attestation to Hub end rect rgb(255, 240, 240) - Note over Solver,O: Withdrawal - Solver->>O: 4. Submit proof, claim funds + Note over Solver,R: Withdrawal + Solver->>R: 4. Transfer to withdrawal address + Solver->>O: 5. Submit proof, claim funds end ``` @@ -84,14 +85,17 @@ Settlement is the process of verifying that the solver correctly filled the orde ```mermaid sequenceDiagram participant Origin as Origin Chain + participant Solver participant Oracle participant Dest as Destination Chain participant Hub as Hub (Relay Chain) - Oracle->>Origin: 1. Read deposit data - Oracle->>Dest: 2. Read fill data + Solver->>Oracle: 1. Request attestation + Oracle->>Origin: 2. Read deposit data + Oracle->>Dest: 3. Read fill data Note over Oracle: Verify deposit + fill match - Oracle->>Hub: 3. Submit EIP-712 attestation + Oracle-->>Solver: 4. Return signed attestation + Solver->>Hub: 5. Submit attestation Hub->>Hub: MINT (user balance) Hub->>Hub: TRANSFER (user → solver) Note over Hub: Solver balance updated @@ -99,16 +103,18 @@ sequenceDiagram **Step by step:** -1. **Oracle reads origin chain** — The [Oracle](/references/protocol/components/oracle) reads the origin chain to verify that the user's deposit occurred and matches the expected order. +1. **Solver requests attestation** — After filling an order, the solver calls the [Oracle](/references/protocol/components/oracle) to request settlement. + +2. **Oracle reads origin chain** — The Oracle reads the origin chain to verify that the user's deposit occurred and matches the expected order. + +3. **Oracle reads destination chain** — The Oracle reads the destination chain to verify that the solver's fill matches the user's intent (correct destination, amount, and action). -2. **Oracle reads destination chain** — The Oracle reads the destination chain to verify that the solver's fill matches the user's intent (correct destination, amount, and action). +4. **Oracle returns attestation** — Oracle validators each verify the data and sign an EIP-712 attestation. Once a threshold of signatures is collected, the signed attestation is returned to the solver. -3. **Oracle attests to the Hub** — The Oracle signs an EIP-712 attestation and submits it to the [Hub](/references/protocol/components/hub) contract on the [Relay Chain](/references/protocol/components/relay-chain). This attestation triggers two actions: +5. **Solver submits to Hub** — The solver submits the signed attestations to the Oracle contract on the [Relay Chain](/references/protocol/components/relay-chain), which verifies the threshold and executes on the [Hub](/references/protocol/components/hub). This triggers two actions: - **MINT** — The user's deposit is represented as a token balance on the Hub - **TRANSFER** — That balance is transferred from the user to the solver -4. **Solver balance updated** — The solver's balance on the Hub increases, reflecting the payment they are owed for the fill. - Settlement happens in real-time, per-order. There is no batching window. As soon as the Oracle verifies a fill, the solver's balance is updated on the Hub. @@ -120,65 +126,117 @@ Withdrawal is how solvers extract funds from the Depository to replenish their c ```mermaid sequenceDiagram participant Solver - participant Allocator participant Hub as Hub (Relay Chain) - participant MPC as NEAR MPC + participant Oracle + participant Allocator as Allocator (Aurora) participant Depository as Depository (Origin) - Solver->>Allocator: 1. Request withdrawal - Allocator->>Hub: 2. Check solver balance - Hub-->>Allocator: Balance confirmed - Allocator->>MPC: 3. Request MPC signature - MPC-->>Allocator: Signed proof - Allocator-->>Solver: 4. Return signed proof - Solver->>+Depository: 5. Submit proof + Solver->>Hub: 1. Initiate withdrawal + Solver->>Oracle: 2. Request withdrawal proof + Oracle-->>Allocator: 3. Verify + forward + Allocator-->>Oracle: 4. Return MPC-signed proof + Oracle-->>Solver: 5. Return proof + Solver->>+Depository: 6. Submit proof Depository->>Depository: Verify signature + nonce - Depository-->>-Solver: 6. Release funds + Depository-->>-Solver: 7. Release funds + Solver->>Oracle: 8. Request burn attestation + Oracle-->>Solver: 9. Return signed attestation + Solver->>Hub: 10. Submit attestation (BURN) ``` **Step by step:** -1. **Solver requests withdrawal** — The solver specifies which chain and asset they want to withdraw from, and the amount. +1. **Solver initiates withdrawal** — The solver transfers their Hub balance to a deterministic **withdrawal address** — a virtual address derived from the withdrawal parameters (target chain, depository, currency, recipient, nonce). This transfer on the Hub signals the intent to withdraw. + +2. **Solver requests withdrawal proof** — The solver calls the [Oracle](/references/protocol/components/oracle) to request a withdrawal proof. The Oracle reads the Hub, verifies the transfer to the withdrawal address, and decodes the withdrawal parameters. + +3. **Oracle verifies and forwards** — The Oracle verifies the withdrawal request and forwards it to the [Allocator](/references/protocol/components/allocator) on Aurora, which constructs a chain-specific payload via the appropriate Payload Builder. + +4. **Allocator generates proof** — The [Allocator](/references/protocol/components/allocator) generates an MPC-signed cryptographic proof (EIP-712 for EVM, Ed25519 for Solana) that authorizes the withdrawal. + +5. **Proof returned to solver** — The signed proof is returned to the solver via the Oracle. -2. **Allocator verifies balance** — The [Allocator](/references/protocol/components/allocator) checks the solver's balance on the Hub to confirm they have sufficient funds. +6. **Solver submits proof** — The solver submits the signed proof to the [Depository](/references/protocol/components/depository) contract on the target chain. -3. **Allocator generates proof** — The Allocator creates a chain-specific cryptographic proof (EIP-712 signature for EVM chains, Ed25519 for Solana) authorizing the withdrawal from the Depository on the target chain. +7. **Depository releases funds** — The Depository verifies the signature, confirms the nonce hasn't been used, and transfers the funds to the solver. -4. **Solver submits proof** — The solver submits the signed proof to the [Depository](/references/protocol/components/depository) contract on the target chain. +8. **Solver requests burn attestation** — The solver calls the Oracle to attest the completed withdrawal. -5. **Depository releases funds** — The Depository verifies the signature, confirms the nonce hasn't been used, and transfers the funds to the solver. +8. **Oracle returns attestation** — The Oracle verifies the withdrawal occurred on the target chain and returns a signed attestation. -6. **Hub balance reduced** — The Hub burns the corresponding token balance (via a BURN action from the Oracle), keeping the ledger in sync. +9. **Hub balance burned** — The solver submits the attestation to the Hub as a **BURN** action, keeping the ledger in sync. Solvers choose their own withdrawal strategy. They can withdraw frequently to maximize capital velocity, or batch withdrawals to minimize transaction costs. The Hub balance accrues in real-time regardless. -### Revert Flow +### Refund Flow -If a solver fails to fill an order within the expected timeframe, the user's funds are protected and can be returned. +If a solver can't fill an order on the destination, they can instantly refund the user on the origin chain. This is a variation of the execution flow — instead of filling on the destination, the solver sends funds directly to the user on the origin. The refund is then settled like a fill, and the solver gets reimbursed on the Hub. ```mermaid sequenceDiagram + participant User participant Solver + participant Origin as Origin Chain participant Oracle participant Hub as Hub (Relay Chain) - participant Depository as Depository (Origin) + + Solver->>Origin: 1. Send funds to user on origin + Origin-->>User: 2. Funds returned + Solver->>Oracle: 3. Request refund attestation + Oracle->>Origin: 4. Verify refund + Oracle-->>Solver: 5. Return signed attestation + Solver->>Hub: 6. Submit attestation + Hub->>Hub: MINT + TRANSFER (solver reimbursed) +``` + +**Step by step:** + +1. **Solver refunds on origin** — The solver sends funds directly to the user on the origin chain, returning them immediately. + +2. **User receives funds** — The user gets their deposit back on the origin chain without waiting for any expiry window. + +3. **Solver requests attestation** — The solver calls the [Oracle](/references/protocol/components/oracle) to request a refund attestation. + +4. **Oracle verifies refund** — The Oracle reads the origin chain to verify the refund occurred and matched the order's requirements. + +5. **Oracle returns attestation** — Validators sign and return the attestation to the solver. + +6. **Solver submits to Hub** — The solver submits the attestation to the [Hub](/references/protocol/components/hub), which mints and transfers the balance to the solver — the same settlement process as a fill. + + +Refunds are the fastest way to return user funds. Because the solver acts immediately on the origin chain, the user doesn't need to wait for any expiry window. + + +### Forced Exit Flow + +If a solver fails to fill **and** doesn't refund, the user's funds are still protected. After the order's fill window expires, the user can trigger a forced exit to reclaim their deposit. Solvers can also **abandon** an order early, which shortens the expiry window and allows the forced exit to happen faster. + +```mermaid +sequenceDiagram + participant Solver participant User + participant Oracle + participant Hub as Hub (Relay Chain) + participant Depository as Depository (Origin) + Note over Solver: Abandon order (optional) Note over Solver: Fill window expires - Oracle->>Hub: 1. Attest revert - Hub->>Hub: 2. Restore user balance - User->>Depository: 3. Withdraw deposit - Depository-->>User: 4. Funds returned + User->>Oracle: 1. Request forced exit attestation + Oracle-->>User: 2. Return signed attestation + User->>Hub: 3. Submit attestation + Hub->>Hub: Restore user balance + User->>Depository: 4. Withdraw deposit + Depository-->>User: 5. Funds returned ``` **Step by step:** -1. **Order expires** — If the solver doesn't fill within the commitment window, the order is marked as expired. +1. **User requests forced exit** — After the fill window expires (or after the solver abandons), the user calls the [Oracle](/references/protocol/components/oracle) to request a forced exit attestation. If the solver knows they can't fill, they can **abandon** the order early — this signals intent to not fill and shortens the expiry window so the user gets their funds back faster. -2. **Oracle attests revert** — The Oracle verifies that the fill did not occur and attests a revert to the Hub. +2. **Oracle returns attestation** — The Oracle verifies that the fill did not occur and returns a signed attestation to the user. -3. **User balance restored** — The Hub restores the user's token balance, effectively unlocking their deposit. +3. **User submits to Hub** — The user submits the attestation to the [Hub](/references/protocol/components/hub), which restores their token balance. -4. **User withdraws** — The user (or the protocol on their behalf) can trigger a withdrawal from the Depository to reclaim their funds. +4. **User withdraws** — The user (or the protocol on their behalf) triggers a withdrawal from the [Depository](/references/protocol/components/depository) to reclaim their funds. diff --git a/references/protocol/overview.mdx b/references/protocol/overview.mdx index bb35743..e401158 100644 --- a/references/protocol/overview.mdx +++ b/references/protocol/overview.mdx @@ -10,7 +10,7 @@ Relay Settlement is a hyper-optimized intents protocol, designed for high-perfor - **Cost** — Minimal overhead on top of raw transfers - **Capital Efficiency** — Flexible batching for maximum inventory re-use - **Coverage** — Works on any chain, with minimal effort to deploy to new chains and VMs -- **UX** — Simple transfer flows + fast reverts +- **UX** — Simple transfer flows + fast refunds Unlike other intent protocols, Relay Settlement does not attempt to be a solver marketplace, or offer any sort of orderflow auction. It's a pure utility, designed to be used by solvers that already have their own orderflow and just need efficient settlement. @@ -76,4 +76,4 @@ Users don't want to make two transactions to approve and deposit. Gasless flows **Failure UX** -For most intent protocols, failures are an afterthought. If something goes wrong, the user is forced to manually collect their funds out of escrow, often after a delay. Relay allows solvers to instantly revert once they determine an order can't be filled, abstracting complexity and giving users a UX that feels similar to using a single chain. +For most intent protocols, failures are an afterthought. If something goes wrong, the user is forced to manually collect their funds out of escrow, often after a delay. Relay allows solvers to instantly refund once they determine an order can't be filled, abstracting complexity and giving users a UX that feels similar to using a single chain. diff --git a/references/protocol/security.mdx b/references/protocol/security.mdx index 853c483..0162ddf 100644 --- a/references/protocol/security.mdx +++ b/references/protocol/security.mdx @@ -21,14 +21,14 @@ The Depository's security relies on the Allocator's signing authority. If the Al ### Oracle (Trust-Critical) -The Oracle determines which deposits and fills are considered valid: +The [Oracle](/references/protocol/components/oracle) determines which deposits and fills are considered valid: - **Can attribute balances** — The Oracle controls what gets minted, transferred, or burned on the Hub - **Cannot steal Depository funds** — Even an incorrect attestation only affects Hub balances. The Allocator independently verifies Hub balances before authorizing any withdrawal. -- **Consensus-based** — Designed for multi-operator consensus, where multiple independent parties must agree on attestations +- **Consensus threshold** — Attestations require signatures from a threshold of independent validators. The onchain Oracle contract verifies the threshold is met before executing any action on the Hub. -The Oracle is the main trust-bearing component. A compromised Oracle could incorrectly attribute balances, but the damage is bounded by the Allocator's balance checks and the [Security Council's](/references/protocol/components/security-council) ability to pause the system. +The Oracle is the main trust-bearing component. A compromised minority of validators cannot submit false attestations. A compromised majority could incorrectly attribute balances, but the damage is bounded by the Allocator's balance checks and the [Security Council's](/references/protocol/components/security-council) ability to pause the system. ### Hub (Smart Contract)