From f967760dc007dbe6a7a075ddfebd966d09b23060 Mon Sep 17 00:00:00 2001 From: Yorke Rhodes Date: Thu, 6 Apr 2023 16:50:29 -0400 Subject: [PATCH] Docs improvements and prep for deduping with docs repo (#48) * Structure README for docs * README updates * Address pr comments * Document address recipient --- README.md | 235 +++++++++++++++++++++++++++------ contracts/HypNative.sol | 2 +- contracts/libs/TokenRouter.sol | 3 +- 3 files changed, 197 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index e50a9f1db..f885613f1 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,124 @@ -# Hyperlane Warp Route +# Hyperlane Warp Routes -This repo contains the base Hyperlane ERC20 and ERC721 tokens (HypERC20 and HypERC721). These tokens extend the base standards with an additional `transferRemote` function. Warp Routes make any token or native asset interchain without custom contracts. Read more about Warp Routes and how to deploy your own at [Warp API docs](https://docs.hyperlane.xyz/docs/developers/warp-api). +This repo contains contracts, deployment, and SDK tooling for Hyperlane Warp Routes. -**NOTE:** ERC721 collateral variants are assumed to [enumerable](https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#IERC721Enumerable) and [metadata](https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#IERC721Metadata) compliant. +## Warp Route Architecture -## Versions +A *warp route* is a collection of [`TokenRouter`](./contracts/libs/TokenRouter.sol) contracts deployed across a set of Hyperlane chains. These contracts leverage the `Router` pattern to implement access control and routing logic for remote token transfers. These contracts send and receive [`Message`](./contracts/libs/Message.sol)s which encode payloads containing a transfer `amount` and `recipient` address. -| Git Ref | Release Date | Notes | -| ------- | ------------ | ----- | -| [audit-v2-remediation]() | 2023-02-15 | Hyperlane V2 Audit remediation | -| [main]() | ~ | Bleeding edge | +```mermaid +%%{ init: { + "theme": "neutral", + "themeVariables": { + "mainBkg": "#025AA1", + "textColor": "white", + "clusterBkg": "white" + }, + "themeCSS": ".edgeLabel { color: black }" +}}%% + +graph LR + subgraph "Ethereum" + HYP_E[TokenRouter] + style HYP_E fill:orange + Mailbox_E[(Mailbox)] + end + + subgraph "Polygon" + HYP_P[TokenRouter] + style HYP_P fill:orange + Mailbox_P[(Mailbox)] + end + + + subgraph "Gnosis" + HYP_G[TokenRouter] + style HYP_G fill:orange + Mailbox_G[(Mailbox)] + end + + HYP_E -. "router" .- HYP_P -. "router" .- HYP_G + +``` + +The Token Router contract comes in several flavors and a warp route can be composed of a combination of these flavors. + +- [`Native`](./contracts/HypNative.sol) - for warping native assets (e.g. ETH) from the canonical chain +- [`Collateral`](./contracts/HypERC20Collateral.sol) - for warping tokens, ERC20 or ERC721, from the canonical chain +- [`Synthetic`](./contracts/HypERC20.sol) - for representing tokens, Native/ERC20 or ERC721, on a non-canonical chain + +## Interchain Security Models + +Warp routes are unique amongst token bridging solutions because they provide modular security. Because the `TokenRouter` implements the `IMessageRecipient` interface, it can be configured with a custom interchain security module. Please refer to the relevant guide to specifying interchain security modules on the [Messaging API receive docs](https://docs.hyperlane.xyz/docs/apis/messaging-api/receive#interchain-security-modules). + +## Remote Transfer Lifecycle Diagrams + +To initiate a remote transfer, users call the `TokenRouter.transferRemote` function with the `destination` chain ID, `recipient` address, and transfer `amount`. + +```solidity +interface TokenRouter { + function transferRemote( + uint32 destination, + bytes32 recipient, + uint256 amount + ) public returns (bytes32 messageId); +} +``` + +**NOTE:** The [Relayer](https://docs.hyperlane.xyz/docs/protocol/agents/relayer) shown below must be compensated. Please refer to the relevant guide on [paying for interchain gas](https://docs.hyperlane.xyz/docs/build-with-hyperlane/guides/paying-for-interchain-gas) on the `messageID` returned from the `transferRemote` call. + +Depending on the flavor of TokenRouter on the source and destination chain, this flow looks slightly different. The following diagrams illustrate these differences. + +### Transfer Alice's `amount` native ETH from Ethereum to Bob on Polygon + +```mermaid +%%{ init: { + "theme": "neutral", + "themeVariables": { + "mainBkg": "#025AA1", + "textColor": "white", + "clusterBkg": "white" + }, + "themeCSS": ".edgeLabel { color: black }" +}}%% + +graph TB + Bob((Bob)) + style Bob fill:black + Alice((Alice)) + style Alice fill:black -## Remote Transfer Lifecycle + Relayer([Relayer]) + + subgraph "Ethereum" + HYP_E[NativeTokenRouter] + style HYP_E fill:orange + Mailbox_E[(Mailbox)] + end + + Alice == "transferRemote(Polygon, Bob, amount)\n{value: amount}" ==> HYP_E + linkStyle 0 color:green; + HYP_E -- "dispatch(Polygon, (Bob, amount))" --> Mailbox_E + + subgraph "Polygon" + HYP_P[SyntheticTokenRouter] + style HYP_P fill:orange + Mailbox_P[(Mailbox)] + end + + Mailbox_E -. "indexing" .-> Relayer + + Relayer == "process(Ethereum, (Bob, amount))" ==> Mailbox_P + Mailbox_P -- "handle(Ethereum, (Bob, amount))" --> HYP_P + + HYP_E -. "router" .- HYP_P + + HYP_P -- "mint(Bob, amount)" --> Bob + linkStyle 6 color:green; +``` + + +### Transfer Alice's ERC20 `amount` from Ethereum to Bob on Polygon ```mermaid %%{ init: { @@ -19,7 +126,7 @@ This repo contains the base Hyperlane ERC20 and ERC721 tokens (HypERC20 and HypE "themeVariables": { "mainBkg": "#025AA1", "textColor": "white", - "clusterBkg": "beige" + "clusterBkg": "white" }, "themeCSS": ".edgeLabel { color: black }" }}%% @@ -27,54 +134,99 @@ This repo contains the base Hyperlane ERC20 and ERC721 tokens (HypERC20 and HypE graph TB Alice((Alice)) Bob((Bob)) - Validator((Validator)) - Relayer((Relayer)) - %% Watcher((Watcher)) + style Alice fill:black + style Bob fill:black + + Relayer([Relayer]) subgraph "Ethereum" Token_E[ERC20] - HYP_E[HYP-ERC20] - M_E[(Mailbox)] - %% POS_E[Proof of Stake] + style Token_E fill:green + HYP_E[CollateralTokenRouter] + style HYP_E fill:orange + Mailbox_E[(Mailbox)] end - Alice == "0. approve(HYP, infinity)" ==> Token_E - Alice == "1. transferRemote(Polygon, Bob, 5)" ==> HYP_E - Token_E -- "2. transferFrom(Alice, 5)" --> HYP_E - HYP_E -- "3. dispatch(Polygon, HYP, (Bob, 5))" --> M_E + Alice == "approve(CollateralTokenRouter, infinity)" ==> Token_E + Alice == "transferRemote(Polygon, Bob, amount)" ==> HYP_E + Token_E -- "transferFrom(Alice, amount)" --> HYP_E + linkStyle 2 color:green; + HYP_E -- "dispatch(Polygon, (Bob, amount))" --> Mailbox_E + + subgraph "Polygon" + HYP_P[SyntheticRouter] + style HYP_P fill:orange + Mailbox_P[(Mailbox)] + end - M_E-."indexing".->Relayer - %% M_E-."indexing".->Watcher - M_E -. "indexing" .-> Validator + Mailbox_E -. "indexing" .-> Relayer - %% Validator == "staking" ==> POS_E - %% Watcher == "slashing" ==> POS_E - Validator -. "signing" .-> ISM_STORE + Relayer == "process(Ethereum, (Bob, amount))" ==> Mailbox_P + Mailbox_P -- "handle(Ethereum, (Bob, amount))" --> HYP_P - subgraph "Cloud Storage" - ISM_STORE[(ISM\nMetadata)] - end + HYP_E -. "router" .- HYP_P + HYP_P -- "mint(Bob, amount)" --> Bob + linkStyle 8 color:green; +``` + +### Transfer Alice's `amount` synthetic MATIC from Ethereum back to Bob as native MATIC on Polygon + +```mermaid +%%{ init: { + "theme": "neutral", + "themeVariables": { + "mainBkg": "#025AA1", + "textColor": "white", + "clusterBkg": "white" + }, + "themeCSS": ".edgeLabel { color: black }" +}}%% - ISM_STORE -. "metadata" .-> Relayer - ISM_STORE -. "moduleType" .- ISM_P - %% Watcher -. "indexing" .- ISM_P +graph TB + Bob((Bob)) + style Bob fill:black + Alice((Alice)) + style Alice fill:black - Relayer == "4. process(metadata, Ethereum, HYP, (Bob, 5))"==> M_P + Relayer([Relayer]) - subgraph "Polygon" - ISM_P[ISM] - M_P[(Mailbox)] - HYP_P[HYP-ERC20] + subgraph "Ethereum" + HYP_E[SyntheticTokenRouter] + style HYP_E fill:orange + Mailbox_E[(Mailbox)] + end - M_P -- "6. handle(Ethereum, (Bob, 5))" --> HYP_P - M_P -- "5. verify(metadata, Ethereum, (Bob, 5))" --> ISM_P + Alice == "transferRemote(Polygon, Bob, amount)" ==> HYP_E + Alice -- "burn(Alice, amount)" --> HYP_E + linkStyle 1 color:green; + HYP_E -- "dispatch(Polygon, (Bob, amount))" --> Mailbox_E + + subgraph "Polygon" + HYP_P[NativeTokenRouter] + style HYP_P fill:orange + Mailbox_P[(Mailbox)] end - ISM_P -. "interchainSecurityModule" .- HYP_P - HYP_P -- "7. mint(Bob, 5)" --> Bob + Mailbox_E -. "indexing" .-> Relayer + Relayer == "process(Ethereum, (Bob, amount))" ==> Mailbox_P + Mailbox_P -- "handle(Ethereum, (Bob, amount))" --> HYP_P + + HYP_E -. "router" .- HYP_P + HYP_P -- "transfer(){value: amount}" --> Bob + linkStyle 7 color:green; ``` +**NOTE:** ERC721 collateral variants are assumed to [enumerable](https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#IERC721Enumerable) and [metadata](https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#IERC721Metadata) compliant. + +## Versions + +| Git Ref | Release Date | Notes | +| ------- | ------------ | ----- | +| [audit-v2-remediation]() | 2023-02-15 | Hyperlane V2 Audit remediation | +| [main]() | ~ | Bleeding edge | + + ## Setup for local development ```sh @@ -99,3 +251,4 @@ yarn lint ## Learn more For more information, see the [Hyperlane documentation](https://docs.hyperlane.xyz/docs/introduction/readme). + diff --git a/contracts/HypNative.sol b/contracts/HypNative.sol index 4accae486..e4291fef7 100644 --- a/contracts/HypNative.sol +++ b/contracts/HypNative.sol @@ -35,7 +35,7 @@ contract HypNative is TokenRouter { uint32 _destination, bytes32 _recipient, uint256 _amount - ) external payable override returns (bytes32 messageId) { + ) public payable override returns (bytes32 messageId) { require(msg.value >= _amount, "Native: amount exceeds msg.value"); uint256 gasPayment = msg.value - _amount; messageId = _dispatchWithGas( diff --git a/contracts/libs/TokenRouter.sol b/contracts/libs/TokenRouter.sol index 80f48f679..c072d88b7 100644 --- a/contracts/libs/TokenRouter.sol +++ b/contracts/libs/TokenRouter.sol @@ -11,6 +11,7 @@ import {Message} from "./Message.sol"; */ abstract contract TokenRouter is GasRouter { using TypeCasts for bytes32; + using TypeCasts for address; using Message for bytes; /** @@ -50,7 +51,7 @@ abstract contract TokenRouter is GasRouter { uint32 _destination, bytes32 _recipient, uint256 _amountOrId - ) external payable virtual returns (bytes32 messageId) { + ) public payable virtual returns (bytes32 messageId) { bytes memory metadata = _transferFromSender(_amountOrId); messageId = _dispatchWithGas( _destination,