https://github.com/gnosischain/tokenbridge-contracts/blob/xdaibridge/USDSMigration.md # USDS migration > > After the migration, xDAI Foreign Bridge take USDS as collateral instead of DAI. > > Background: https://forum.gnosis.io/t/gip-118-should-sdai-be-replaced-by-susds-in-the-bridge/9354 > To address a potential front-running issue\* from the previous design, a new implementation has been introduced. The key changes includes: 1. New USDS deposit contract on Gnosis Chain 2. `token` parameter is introduced in the `UserRequestForSignature` event. 3. The `executeSignaturesUSDS` function is removed from the BridgeRouter and XdaiForeignBridge contract. Target audiences: 1. 3rd party applications: please refer to [Call To Action](#call-to-action-update-your-code). 2. User: No action required. 3. Bridge validator: update the validator image to [v3.10.0](https://hub.docker.com/layers/gnosischain/tokenbridge-oracle/v3.10.0/images/sha256-f135fbec56c3755d40ebf68257bfab4258c80242566ed157a49e1e5cfc692c91). \*refer to Omega audit XDFB1. ## Table of Contents - [General Overview](#general-overview) - [Dev](#dev) - [Contracts overview](#contracts) - [Interact with contracts](#interact-with-the-contracts) - [Call to Action: Update your code](#call-to-action-update-your-code--indexer) - [Test with post migration environment](#how-to-test-with-post-migration-environment) - [Note for Bridge governors](#note-for-bridge-governors) ## General overview 1. The **xDAI bridge contract** is undergoing a critical upgrade: **DAI will be replaced with USDS as the default accepted token on Ethereum, while xDAI will continue to be minted on Gnosis Chain**. ### Key Changes for Third-Party Applications: Etherum -> Gnosis Chain - Third-party applications **must integrate** with the new **Bridge Router contract on Ethereum**. - The **Bridge Router** serves as the entry point for token relay transactions, routing them to the appropriate bridge contract on Gnosis Chain (**xDAI Bridge** or **Omnibridge**). - **DAI & USDS transactions must go through the Bridge Router.** Transactions sent directly to the xDAI Bridge on Ethereum will **fail** if they attempt to relay DAI after the upgrade, but they will succeed in relaying USDS provided the sender has approved USDS for the bridge (\*Check [Edge Case](#edge-case)) - **For tokens other than DAI & USDS**, using the Bridge Router is **optional**—third-party applications can continue interacting with Omnibridge directly. Gnosis Chain -> Ethereum - To get USDS on Ethereum, one **MUST** switch to USDSDepositContract on Gnosis Chain when initiating the transaction. - To get DAI on Ethereum, one **MUST** remain calling the Home xDAI Bridge as it is. - Third-party applications **MUST** update their indexer to index the new event format emitted from Gnosis Chain. - Claiming token on Ethereum remains the same function `executeSignatures(bytes memory message, bytes memory signatures)`. One can call it either on BridgeRouter or the XDaiForeignBridge contract on Ethereum. ### Transition Period: - Before the USDS upgrade on the xDAI Bridge, third-party applications have time to **adapt to the Bridge Router interface, new USDS Deposit contract** and **update their indexer for new event signature on Gnosis Chain**. ## Dev ### Prerequisite - [Node v10.18](https://nodejs.org/en/blog/release/v10.18.0) (as specified in .[nvm](https://github.com/nvm-sh/nvm)rc) - [Foundry v0.2.0 or later](https://book.getfoundry.sh/getting-started/installation) - Ethereum, Gnosis Chain RPC endpoint (for testing and deployment) ### Framework [Foundry](https://book.getfoundry.sh/) > > This repository was initially developed using [Truffle](https://archive.trufflesuite.com/docs/truffle/). However, due to the [sunsetting of Truffle](https://consensys.io/blog/consensys-announces-the-sunset-of-truffle-and-ganache-and-new-hardhat?utm_source=chatgpt.com), we transition our development workflow to Foundry. ### Setup ```sh nvm use npm i forge install forge build ``` ### Test ```sh source .env chmod +x ./test/foundry/fork-test.sh && ./test/foundry/fork-test.sh chmod +x ./test/foundry/e2e/e2e-test.sh && ./test/foundry/e2e/e2e-test.sh ``` For more details about testing, please check [this repository](https://github.com/gnosischain/xdaiBridge-usds-migration-test) ### Deploy ```sh forge script script/Deploy.s.sol:Deploy --rpc-url $RPC_MAINNET --private-key $PRIVATE_KEY --verify --etherscan-api-key --broadcast ``` ### Get deployed bytecode of the contract ```sh npm run get-deployed-bytecode ``` The result will be written into `scripts/usds_migration/deployBytecode_usds_migration.json`. ### Get keccak256 hash of the contract ```sh npm run get-contract-keccak256 ``` The result will be written into `scripts/usds_migration/keccak256hash.json`. | Contract | Contract Hash (keccak256) | | ----------------------------------------- | -------------------------------------------------------------------- | | xDaiForeignBridge | `0xbadd059fea2d7ab61bcd36db3418c6c73dca7a4fc02917ce5db2f78d962821f8` | | BridgeRouter | `0xab729fba35c9d369445f85c3556db5644eb7356228ed3fda441bff4e7e1eacb8` | | XDaiBridgePeripheral | `0x3d5e7ff9da196691d2dc57c93036c7414b68d63921dce530c5747017ea39afb5` | | XDaiBridgePeripheralForDaiPreUsdsUpgrade | `0x721cfb942f77f4b04de65a4dbb7b1686e2c363689c83de3bee58da3ab9ae0bb3` | | XDaiBridgePeripheralForUsdsPreUsdsUpgrade | `0x74284da7c9b250a0348d431981364bf4e82987ca93524f2c392ea2455fd1e8a9` | | HomeBridgeErcToNative | `0xb5a198416e6936a3443e718e53c675bf07621769bebb57b8c61389e740678f30` | | USDSDepositContract | `0x347eaf2c91db18b5e5096a009b4a398daae84149a87bdf0cefa4545ea39b0f8c` | For details about the keccak256 of the contract, please check [Sourcify's doc](https://docs.sourcify.dev/docs/full-vs-partial-match/#full-perfect-matches) and [Solidity's doc](https://docs.soliditylang.org/en/latest/metadata.html) ### Contract versions | Contract | Version | Optimizer Runs | | --------------------------------------------- | ----------------------- | -------------- | | BridgeRouter.sol | v0.8.25+commit.b61c2a91 | 10 | | XDaiBridgePeripheral.sol | v0.8.25+commit.b61c2a91 | 10 | | XDaiBridgePeripheralForDaiPreUsdsUpgrade.sol | v0.8.25+commit.b61c2a91 | 10 | | XDaiBridgePeripheralForUsdsPreUsdsUpgrade.sol | v0.8.25+commit.b61c2a91 | 10 | | XDaiForeignBridge.sol | v0.4.24+commit.e67f0147 | 10 | | HomeBridgeErcToNative.sol | v0.4.24+commit.e67f0147 | 10 | | USDSDepositContract.sol | v0.8.25+commit.b61c2a91 | 10 | # Contracts Overview ## New contracts 1. `BridgeRouter.sol`: An entry point for token transferring, abstracting relayTokens() for Omnibridge and xDAI bridge. Upgradeable with `TransparentUpgradeableProxy`. 2. `XDaiBridgePeripheral.sol`: Peripheral contract to convert between DAI and USDS after bridge migration. 3. `USDSDepositContract.sol`: Deposit contract to notify the bridge where the intended recieved token is USDS on Ethereum. Transitional contracts during migration 1. `XDaiBridgePeripheralForDaiPreUsdsUpgrade.sol`: Allow `relayTokens` with DAI. 2. `XDaiBridgePeripheralForUsdsPreUsdsUpgrade.sol`: Allow `relayTokens` with USDS. ## Modified contracts 1. `SavingsDaiConnector.sol`: `daiToken()` address is changed to USDS address, `sDaiToken()` address is changed to sUSDS address. 2. `XDaiForeignBridge.sol`: new function introduced 1. `swapSDAIToUSDS`: one time function for bridge migration 2. Add token parameter in message parsing. 3. `HomeBridgeErcToNative.sol`: token parameter is included in event `UserRequestForSignature`, in `Message` library for parsing and encoding. 4. `HomeOverdrawManagement.sol`: add token parameter in `fixAssetsAboveLimits` function. 5. `ErcToNativeBridgeHelper.sol`: add token parameter to `getMessageHash` function. ## Audits > > Original audit reports from the previous design. 1. [Omega](https://github.com/OmegaAudits/audits/blob/main/202510-Gnosis-Bridge-USDS-Upgrade.pdf) 2. Gnosis Ltd [1](./docs/audits/xdai-bridge-usds-upgrade-gnosis.pdf),[2](https://github.com/cducrest/audit-reports/blob/main/bridge-USDS-upgrade3.pdf) ### Contract addresses | Contract | Chain | Address | | ------------------------------------------ | ------------ | -------------------------------------------- | | BridgeRouter Proxy | Ethereum | `0x9a873656c19Efecbfb4f9FAb5B7acdeAb466a0B0` | | BridgeRouter Implementation | Ethereum | `0x74899961224538E423eFfD1A0Ff3346adf3F4C56` | | XDaiBridgePeripheral | Ethereum | `0x3b6669727927b934753B018EB421a84Ed4eb0a43` | | XDaiBridgePeripheralForDaiPreUsdsUpgrade | Ethereum | `0xF676cc15Eb6d15b794aeC65bC20052aFB53D9052` | | XDaiBridgePeripheralForUsdsPreUsdsUpgrade | Ethereum | `0x7df0e6a8BA609A6cC3Ab2fA33D953a3B5584f10C` | | XDaiForeignBridge(Implementation Contract) | Ethereum | `0x257bDD093Cab1Bd39eBF837dCB60f33d031d7d49` | | HomeBridgeErcToNative Implementation | Gnosis Chain | `0xe6998b0C03D3cb9ee8C04f266e573c7Fa8782846` | | USDSDepositContract.sol | Gnosis Chain | `0x5C183C8A49aBA6e31049997a56D75600E27FF8c9` | | Erc20ToNativeBridgeHelper.sol | Gnosis Chain | `0xe30269bc61E677cD60aD163a221e464B7022fbf5` | # Interacting with the contracts ## Before the xDAI Bridge migration **Bridge Router setup** Caller: BridgeRouterOwner `0x42F38ec5A75acCEc50054671233dfAC9C0E7A3F6` (same as the bridge owner) ``` router.setRoute(DAI,address(XDaiBridgePeripheralForDaiPreUsdsUpgrade)); router.setRoute(USDS,address(XDaiBridgePeripheralForUsdsPreUsdsUpgrade)); ``` ### Relay tokens from Ethereum ```mermaid graph TD User([User]) -->|"relayTokens: DAI"| BridgeRouter User -->|"relayTokens: USDS"| BridgeRouter User -->|"relayTokens: Other ERC20"| BridgeRouter User -->|"relayTokens: ETH"| BridgeRouter subgraph "BridgeRouter Contract" BridgeRouter[BridgeRouter] end subgraph "Pre-Upgrade Peripherals" DaiPeripheral[XDaiBridgePeripheralForDaiPreUsdsUpgrade] UsdsPeripheral[XDaiBridgePeripheralForUsdsPreUsdsUpgrade] end BridgeRouter -->|DAI| DaiPeripheral BridgeRouter -->|USDS| UsdsPeripheral BridgeRouter -->|Other ERC20| OmniBridge[Foreign OmniBridge] BridgeRouter -->|ETH| WETHRouter[WETH OmniBridge Router] DaiPeripheral --> XDaiForeignBridge UsdsPeripheral -->|"Convert USDS to DAI"| XDaiForeignBridge subgraph "Omnibridge contract" OmniBridge --> OmniBridgeHome[Mint bridged token on Gnosis Chain] WETHRouter --> OmniBridgeHome end subgraph "XDaiForeignBridge Contract (Pre-Upgrade)" XDaiForeignBridge[XDaiForeignBridge] -->|"DAI as collateral"| GnosisChain[Mint xDAI on Gnosis Chain] end ``` ### To claim token on Ethereum ```mermaid graph TD subgraph "BridgeRouter Contract" BridgeRouter[BridgeRouter] end subgraph "XDaiForeignBridge Contact" XDaiForeignBridge[XDaiForeignBridge] -->|DAI as collateral| GnosisChain[unlock DAI to recipient] end subgraph "ForeignAMB Contract" ForeignAMB[ForeignAMB] --> Action[Unlock token to recipient] end %% For claim operations (pre-upgrade) User -->|executeSignatures| BridgeRouter User -.->|revert executeSignaturesUSDS| BridgeRouter User -->|safeExecuteSignaturesWithAutoGasLimit| BridgeRouter BridgeRouter -->|executeSignatures| XDaiForeignBridge BridgeRouter -->|safeExecuteSignaturesWithAutoGasLimit| ForeignAMB[Foreign AMB] ``` 1. `BridgeRouter.relayTokens(address token, address recipient, uint256 amount)` -> When token is DAI / USDS from Ethereum, receive xDAI on GC. -> When token is other tokens from Ethereum, receive the bridged version token on GC. 2. `BridgeRouter.executeSignatures(bytes memory message, bytes memory signatures)` -> claim DAI on Ethereum 3. `BridgeRouter.executeSignaturesUSDS(bytes memory message, bytes memory signatures)` -> revert `ClaimUsdsNotSupported()` 4. `BridgeRouter.safeExecuteSignaturesWithAutoGasLimit(bytes memory message, bytes memory signatures)` -> claim token from Omnibridge 5. `xDAIForeignBridge.relayTokens(address recipient, uint256 amount)` -> relay DAI from Ethereum, receive xDAI on GC 6. `xDAIForeignBridge.executeSignatures(bytes memory message, bytes memory signatures)` -> claim DAI on Ethereum ### Function Callflow ```mermaid sequenceDiagram participant User participant BridgeRouter participant DaiPeripheral as XDaiBridgePeripheralForDaiPreUsdsUpgrade participant UsdsPeripheral as XDaiBridgePeripheralForUsdsPreUsdsUpgrade participant DaiUsds as DaiUsds participant XDaiForeignBridge as XDaiForeignBridge on Ethereum participant GnosisChain as XDaiHomeBridge on Gnosis Chain participant Receiver box Ethereum participant User participant BridgeRouter participant DaiPeripheral participant UsdsPeripheral participant DaiUsds participant XDaiForeignBridge end box Gnosis Chain participant GnosisChain participant Receiver end %% Relay DAI Flow par Note over User,Receiver: Relay DAI User->>BridgeRouter: DAI.approve(BridgeRouter, amount) User->>BridgeRouter: relayTokens(DAI, receiver, amount) BridgeRouter->>DaiPeripheral: Transfer DAI DaiPeripheral->>XDaiForeignBridge: relayTokens(receiver, amount) XDaiForeignBridge->>XDaiForeignBridge: Lock DAI as collateral XDaiForeignBridge-->>GnosisChain: Bridging process GnosisChain->>Receiver: Receive xDAI end %% Claim DAI Flow par Note over User,Receiver: Claim DAI User->>BridgeRouter: executeSignatures(message, signatures) BridgeRouter->>XDaiForeignBridge: executeSignatures(message, signatures) XDaiForeignBridge->>User: Transfer DAI to Receiver end %% Relay USDS Flow par Note over User,Receiver: Relay USDS User->>BridgeRouter: USDS.approve(BridgeRouter, amount) User->>BridgeRouter: relayTokens(USDS, receiver, amount) BridgeRouter->>UsdsPeripheral: Transfer USDS UsdsPeripheral->>DaiUsds: swapUsdsToDai() DaiUsds->>XDaiForeignBridge: Approve DAI XDaiForeignBridge->>XDaiForeignBridge: Lock DAI as collateral XDaiForeignBridge-->>GnosisChain: Bridging process GnosisChain->>Receiver: Receive xDAI end %% Claim USDS Flow (Fails) par Note over User,Receiver: Claim USDS User->>BridgeRouter: executeSignaturesUSDS(message, signatures) BridgeRouter->>BridgeRouter: Revert ClaimUsdsNotSupported() end ``` **Relay DAI** 1. DAI.approve(BridgeRouter, amount) -> [BridgeRouter.relayTokens(DAI, receiver, amount)](./contracts/upgradeable_contracts/erc20_to_native/BridgeRouter.sol#L37) -> [XDaiBridgePeripheralForDaiPreUsdsUpgrade.relayTokens(receiver, amount)](./contracts/upgradeable_contracts/erc20_to_native/XDaiBridgePeripheralForDaiPreUsdsUpgrade.sol#L34) -> [xDAIForeignBridge.relayTokens(receiver, amount)](./contracts/upgradeable_contracts/erc20_to_native/ForeignBridgeErcToNative.sol#L66) **Claim DAI** 1. [BridgeRouter.executeSignatures(message, signatures)](./contracts/upgradeable_contracts/erc20_to_native/BridgeRouter.sol#L77) -> [xDAIForeignBridge.executeSignatures(message, signatures)](./contracts/upgradeable_contracts/BasicForeignBridge.sol#L22) -> (internal) [onExecuteMessage](./contracts/upgradeable_contracts/erc20_to_native/XDaiForeignBridge.sol#L123) |Here is where the DAI is transferred **Relay USDS** 1. USDS.approve(BridgeRouter, amount) -> [BridgeRouter.relayTokens(USDS, receiver, amount)](./contracts/upgradeable_contracts/erc20_to_native/BridgeRouter.sol#L37) -> [XDaiBridgePeripheralForUsdsPreUsdsUpgrade.relayTokens(receiver, amount)](./contracts/upgradeable_contracts/erc20_to_native/XDaiBridgePeripheralForUsdsPreUsdsUpgrade.sol#L36) |Here is where USDS is swap to DAI -> [xDAIForeignBridge.relayTokens(receiver, amount)](./contracts/upgradeable_contracts/erc20_to_native/ForeignBridgeErcToNative.sol#L66) **Claim USDS** 1. [BridgeRouter.executeSignaturesUSDS(message,signatures)](./contracts/upgradeable_contracts/erc20_to_native/BridgeRouter.sol#L102) -> revert `ClaimUsdsNotSupported()` `0x662554fc43b5d87090a5f9e8b365ca35213d23ae7082671886b760dc510ee8da` XDaiForeignBridge's current implementation: https://etherscan.io/address/0x166124b75c798cedf1b43655e9b5284ebd5203db Code: https://github.com/gnosischain/tokenbridge-contracts/tree/xdaibridge/contracts/upgradeable_contracts/erc20_to_native ## Upgrade procedure ```mermaid graph TD subgraph "Upgrade txs" UpgradeBundledTx[Execute BundledSafeTx by bridge governors] UpgradeBundledTx -->|Disable interest for DAI, Enable interest for USDS| XDaiForeignBridge UpgradeBundledTx -->|Set USDS as collateral token| XDaiForeignBridge UpgradeBundledTx -->|Set DAI route| BridgeRouter --> |DAI| XDaiBridgePeripheral UpgradeBundledTx -->|Set USDS route| BridgeRouter --> |USDS| XDaiForeignBridge end subgraph "Pre upgrade configuration" PreConfig[Pre-Upgrade Configuration] PreConfig -->|Set DAI route to| BridgeRouterPreUpgrade[BridgeRouter] -->|DAI| DaiPeripheralPre[XDaiBridgePeripheralForDaiPreUsdsUpgrade] PreConfig -->|Set USDS route to| BridgeRouterPreUpgrade[BridgeRouter] -->|USDS |UsdsPeripheralPre[XDaiBridgePeripheralForUsdsPreUsdsUpgrade] PreConfig -->|DAI as collateral| XDaiForeignBridgeDaiCollateral[XDaiForeignBridge] end PreConfig -->|Upgrade| UpgradeBundledTx ``` Test for the upgrade procedures is written in [BridgeRouter.t.sol#upgradeBridgeAndSetupRoute](./test/foundry/BridgeRouter.t.sol#L533) ### Function calls during the proxy upgrade > > The function calls during the upgrade on BridgeProxy and Router is a bundled Safe transaction, that will be signed and executed by [bridge governors](https://docs.gnosischain.com/bridges/management/#bridge-governance). **Call on bridge contracts** ethereumXdaiBridgeProxy=`0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016` gnosisChainXdaiBridgeProxy = `0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6` ethereumBridgeOwner= `0x42F38ec5A75acCEc50054671233dfAC9C0E7A3F6` gnosisChainBridgeOwner= `0x7a48Dac683DA91e4faa5aB13D91AB5fd170875bd` - Ethereum ```solidity uint256 initialVersion = 9 address newImpl = 0x257bDD093Cab1Bd39eBF837dCB60f33d031d7d49 ethereumXdaiBridgeProxy.upgradeTo(initialVersion + 1, address(newImpl)); // disable interested for DAI and swap sDAI -> sUSDS ethereumXdaiBridgeProxy.swapSDAIToUSDS(); ethereumXdaiBridgeProxy.initializeInterest( address(USDS): 0xdC035D45d973E3EC169d2276DDab16f1e407384F, minCashThreshold: 1000000000000000000000000, minInterestPaid: 1000000000000000000000, gnosisInterestReceiver: 0x670daeaF0F1a5e336090504C68179670B5059088 ); ethereumXdaiBridgeProxy.invest(address(USDS)); ``` - Gnosis Chain ```solidity uint256 initialVersion = 6 address newImpl = 0xe6998b0C03D3cb9ee8C04f266e573c7Fa8782846 address usdsDepositContract = 0x5C183C8A49aBA6e31049997a56D75600E27FF8c9 gnosisChainXdaiBridgeProxy.upgradeTo(initialVersion + 1, address(newImpl)) gnosisChainXdaiBridgeProxy.setUSDSDepositContract(usdsDepositContract); ``` **Upgrade BrigeRouter implementation and Update routes** Caller: BridgeRouterOwner `0x42F38ec5A75acCEc50054671233dfAC9C0E7A3F6` (same as the bridge owner) ROUTER_PROXY_ADDRESS=`0x9a873656c19Efecbfb4f9FAb5B7acdeAb466a0B0` ProxyAdminContract=`0xD7e65A32bEd4ce8cc57Ec188F2bBb8016dc4b1cd` XDAI_BRIDGE_PERIPHERAL=`0x3b6669727927b934753B018EB421a84Ed4eb0a43` XDAI_FOREIGNBRIDGE_PROXY=`0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016` ```solidity proxyAdminContract.upgradeAndCall(ROUTER_PROXY_ADDRESS,newImplementation, ""); router.setRoute(address(DAI), address(xDAIBridgeperipheral)); router.setRoute(address(USDS), xDAIForeignBridgeProxy); ``` ## After the upgrade ### Relay token from Ethereum ```mermaid graph TD User([User]) -->|"relayTokens: DAI"| BridgeRouter User -->|"relayTokens: USDS"| BridgeRouter User -->|"relayTokens: Other ERC20"| BridgeRouter User -->|"relayTokens: ETH"| BridgeRouter subgraph "BridgeRouter Contract" BridgeRouter[BridgeRouter] end subgraph "Post-Upgrade Peripheral" UsdsPeripheral[XDaiBridgePeripheral] end BridgeRouter -->|DAI| UsdsPeripheral BridgeRouter -->|USDS| XDaiForeignBridge BridgeRouter -->|Other ERC20| OmniBridge[Foreign OmniBridge] BridgeRouter -->|ETH| WETHRouter[WETH OmniBridge Router] UsdsPeripheral -->|"Convert DAI to USDS"| XDaiForeignBridge subgraph "Omnibridge contract" OmniBridge --> OmniBridgeHome[Mint bridged token on Gnosis Chain] WETHRouter --> OmniBridgeHome end subgraph "XDaiForeignBridge Contract (Post-Upgrade)" XDaiForeignBridge[XDaiForeignBridge] -->|"USDS as collateral"| GnosisChain[Mint xDAI on Gnosis Chain] end ``` ### Relay xDAI from Gnosis Chain ```mermaid graph TD User([User]) --> |send xDAI and want DAI| xDAIHomeBridge User([User]) --> |send xDAI and want USDS| USDSDepositContract USDSDepositContract --> xDAIHomeBridge xDAIHomeBridge --> |emit new UserRequestForSignature|Event[recipient, value, nonce, token] ``` ### To claim token on Ethereum ```mermaid graph TD subgraph "BridgeRouter Contract" BridgeRouter[BridgeRouter] end subgraph "XDaiForeignBridge Contact" XDaiForeignBridge[XDaiForeignBridge] -->|exeucteSignatures| DAI_USDS[unlock USDS / swap USDS to DAI to recipient] end subgraph "ForeignAMB Contract" ForeignAMB[ForeignAMB] --> Action[Unlock token to recipient] end %% For claim operations User -->|executeSignatures| BridgeRouter User -->|safeExecuteSignaturesWithAutoGasLimit| BridgeRouter BridgeRouter -->|executeSignatures| XDaiForeignBridge BridgeRouter -->|safeExecuteSignaturesWithAutoGasLimit| ForeignAMB[Foreign AMB] ``` 1. `BridgeRouter.relayTokens(address token, address recipient, uint256 amount)` -> When token is DAI / USDS from Ethereum, receive xDAI on GC. -> When token is other tokens from Ethereum, receive the bridged version token on GC through Omnibridge. 2. `BridgeRouter.executeSignatures(bytes memory message, bytes memory signatures)` -> Call xDAIForeignBridge.executeSignatures 3. `BridgeRouter.safeExecuteSignaturesWithAutoGasLimit(bytes memory message, bytes memory signatures)` -> claim token from Omnibridge 4. `xDAIForeignBridge.relayTokens(address recipient, uint256 amount)` -> relay USDS from Ethereum, receive xDAI on GC 5. `xDAIForeignBridge.executeSignatures(bytes memory message, bytes memory signatures)` -> claim USDS / swap USDS->DAI on Ethereum ### Function Callflow ```mermaid sequenceDiagram box Ethereum participant User participant BridgeRouter participant XDaiBridgePeripheral participant DaiUsds as DaiUsds participant XDaiForeignBridge end box Gnosis Chain participant GnosisChain as xDaiHomeBridge on Gnosis Chain participant USDSDepositContract participant Receiver end %% Relay DAI Flow par Note over User,Receiver: Relay DAI User->>BridgeRouter: DAI.approve(BridgeRouter, amount) User->>BridgeRouter: relayTokens(DAI, receiver, amount) BridgeRouter->>XDaiBridgePeripheral: Transfer DAI XDaiBridgePeripheral->>DaiUsds: swapDaiToUsds() DaiUsds->>XDaiForeignBridge: Approve USDS XDaiForeignBridge->>XDaiForeignBridge: Lock USDS as collateral XDaiForeignBridge-->>GnosisChain: Bridging process GnosisChain->>Receiver: receive xDAI end %% Claim DAI Flow par Note over User,Receiver: Claim DAI User->>BridgeRouter: executeSignatures(message, signatures) BridgeRouter->>XDaiForeignBridge: executeSignatures(message, signatures) XDaiForeignBridge->>DaiUsds: swapUsdsToDai() XDaiForeignBridge->>User: Transfer DAI to receiver end %% Relay USDS Flow par Note over User,Receiver: Relay USDS User->>BridgeRouter: USDS.approve(BridgeRouter, amount) User->>BridgeRouter: relayTokens(USDS, receiver, amount) BridgeRouter->>XDaiForeignBridge: Direct transfer of USDS XDaiForeignBridge->>XDaiForeignBridge: Lock USDS as collateral XDaiForeignBridge-->>GnosisChain: Bridging process GnosisChain->>Receiver: receive xDAI end %% Claim USDS Flow par Note over User,Receiver: Claim USDS User->>BridgeRouter: executeSignatures(message, signatures) BridgeRouter->>XDaiForeignBridge: executeSignatures(message, signatures) XDaiForeignBridge->>User: Transfer USDS to receiver end %% Relay xDAI Flow par Note over User,Receiver: Relay xDAI and get DAI User->>GnosisChain: xDaiHomeBridge.transfer{value:msg.value} or xDaiHomeBridge.relayTokens{value:msg.value}(recipient) User ->> BridgeRouter: executeSignatures(message,recipient) BridgeRouter ->> XDaiForeignBridge: executeSignatures(message,recipient) XDaiForeignBridge ->> DaiUsds: swapUsdsToDai() XDaiForeignBridge ->> Receiver: transfer DAI end %% Relay xDAI Flow par Note over User,Receiver: Relay xDAI and get USDS User->>USDSDepositContract: USDSDepositContract.transfer{value:msg.value} or USDSDepositContract.relayTokens{value:msg.value}(recipient) User ->> BridgeRouter: executeSignatures(message,recipient) BridgeRouter ->> XDaiForeignBridge: executeSignatures(message,recipient) XDaiForeignBridge ->> Receiver: unlock USDS end ``` **Relay DAI** 1. DAI.approve(BridgeRouter, amount) -> [BridgeRouter.relayTokens(DAI, receiver, amount)](./contracts/upgradeable_contracts/erc20_to_native/BridgeRouter.sol#L37) -> [XDaiBridgePeripheral.relayTokens(receiver, amount)](./contracts/upgradeable_contracts/erc20_to_native/XDaiBridgePeripheral.sol#L35) -> [xDAIForeignBridge.relayTokens(receiver, amount)](./contracts/upgradeable_contracts/erc20_to_native/ForeignBridgeErcToNative.sol#L66) **Claim DAI** 1. [BridgeRouter.executeSignatures(message, signatures)](./contracts/upgradeable_contracts/erc20_to_native/BridgeRouter.sol#L77) -> [xDAIForeignBridge.executeSignatures(message, signatures)](./contracts/upgradeable_contracts/BasicForeignBridge.sol#L22) -> (internal)[onExecuteMessage](./contracts/upgradeable_contracts/erc20_to_native/XDaiForeignBridge.sol#L123) |Here is where USDS is swap to DAI **Relay USDS** 1. USDS.approve(bridgeRouter, amount) -> [BridgeRouter.relayTokens(USDS, receiver, amount)](./contracts/upgradeable_contracts/erc20_to_native/BridgeRouter.sol#L37) -> [xDAIForeignBridge.relayTokens(receiver, amount)](./contracts/upgradeable_contracts/erc20_to_native/ForeignBridgeErcToNative.sol#L66) **Claim USDS** 1. [BridgeRouter.executeSignatures(message, signatures)](./contracts/upgradeable_contracts/erc20_to_native/BridgeRouter.sol#L77) -> [xDAIForeignBridge.executeSignatures(message,signatures)](./contracts/upgradeable_contracts/BasicForeignBridge.sol#L22) -> (internal)[onExecuteMessage](./contracts/upgradeable_contracts/erc20_to_native/XDaiForeignBridge.sol#L123) |Here is where USDS is swap to DAI **Relay xDAI and get DAI** 1. [xDAIHomeBridge.transfer{value: msg.value}("")](./contracts/upgradeable_contracts/erc20_to_native/HomeBridgeErcToNative.sol#L28) or [xDAIHomeBridge.relayTokens{value: msg.value}(address recipient)](./contracts/upgradeable_contracts/erc20_to_native/HomeBridgeErcToNative.sol#L67) **Relay xDAI and get USDS** 1. [USDSDepositContract.transfer{value: msg.value}("")](./contracts/USDSDepositContract.sol#L13) or [USDSDepositContract.relayTokens{value: msg.value}(address recipient)](./contracts/USDSDepositContract.sol#L17) ### Edge case 1. After the upgrade, the xDAI Foreign Bridge assumes the sender wants to relay USDS. - If a user intends to relay DAI, but still has an existing USDS allowance for the bridge, the bridge will use the USDS allowance and relay USDS instead of DAI. - To avoid unintended relays: - Always interact with the Bridge Router when relaying DAI. - Approve only the exact amount of USDS you intend to relay, rather than leaving a large allowance. ### Note on BridgeRouter.sol BridgeRouter.sol uses TransparentUpgradeableProxy from Openzeppelin as proxy. 1. TransparentUpgradeableProxy.sol is deployed from [Openzeppelin/openzeppelin-contract@acd4ff7](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/acd4ff74de833399287ed6b31b4debf6b2b35527) v5.2.0. Audited in [2024-10-v5.1.pdf](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/audits/2024-10-v5.1.pdf) 2. ProxyAdmibn.sol is deployed from [Openzeppelin/openzeppelin-contract@acd4ff7](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/acd4ff74de833399287ed6b31b4debf6b2b35527) v5.2.0. Audited in [2023-10-v5.0.pdf](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/audits/2023-10-v5.0.pdf) To verify deployed bytecode, checkout [this repository](https://github.com/zengzengzenghuy/USDS-migration-xDAIBridge-contract-verification) # Call to Action: Update your code & indexer To modify your existing smart contract code to work with the xDAI bridge after USDS migration, complete the following changes: ## Contract Interface Ethereum -> Gnosis Chain ### Relay tokens ```solidity >>> Previous xDAIForeignBridge.relayTokens(address recipient, uint256 value) <<< Latest BridgeRouter.relayTokens(address token, address recipient, uint256 value) ``` ### Claim tokens ```solidity >>> Previous xDAIForeignBridge.executeSignatures(bytes message, bytes signatures) <<< Latest BridgeRouter.executeSignatures(bytes message, bytes signatures) ``` Gnosis Chain -> Ethereum ### Relay tokens ```solidity >>> Previous HomeBridgeErcToNative.relayTokens{value: msg.value}(address recipient) HomeBridgeErcToNative.call{value: msg.value}("") <<< Latest // To get DAI on Ethereum HomeBridgeErcToNative.relayTokens{value: msg.value}(address recipient) // To get DAI on Ethereum HomeBridgeErcToNative.call{value: msg.value}("") // To get USDS on Ethereum USDSDepositContract.relayTokens{value: msg.value}(address recipient) // To get USDS on Ethereum USDSDepositContract.call{value: msg.value}("") ``` ## Event signature ```solidity >>> Previous // 0xbcb4ebd89690a7455d6ec096a6bfc4a8a891ac741ffe4e678ea2614853248658 event UserRequestForSignature(address recipient, uint256 value, bytes32 nonce); <<< Latest // 0xe1e0bc4a1db39a361e3589cae613d7b4862e1f9114dd3ff12ff45be395046968 event UserRequestForSignature(address recipient, uint256 value, bytes32 nonce, address token); ``` # Glossary 1. **BridgeRouter**: Entry point contract after the migration on Ethereum, facilitating routing and token swapping. 2. **xDAIForeignBridge**: xDAI bridge on Ethereum. 3. **HomeBridgeErcToNative** / **xDAI Home Bridge**: xDAI bridge on Gnosis Chain. 4. **USDSDepositContract**: Deposit contract on Gnosis Chain that acts as an entry point contract if user wants to receive USDS on Ethereum. 5. **Foreign Chain**: Ethereum 6. **Home Chain**: Gnosis Chain # How to test with post migration environment To simulate the actual mainnet environment, we use [Tenderly Virtual TestNets](https://tenderly.co/virtual-testnets) for both Ethereum and Gnosis Chain. Third-party applications are encouraged to use the following RPC endpoints to simulate the post-migration environment. **Switch your RPC**: 1. Ethereum: https://virtual.mainnet.eu.rpc.tenderly.co/f3e5e498-bd28-4b49-a8f6-93f033e6fa6e - Explorer: https://dashboard.tenderly.co/explorer/vnet/f3e5e498-bd28-4b49-a8f6-93f033e6fa6e 2. Gnosis Chain: https://virtual.gnosis.eu.rpc.tenderly.co/4c9e4122-6c01-46bd-a44a-e08133d2d2cc - Explorer: https://dashboard.tenderly.co/explorer/vnet/4c9e4122-6c01-46bd-a44a-e08133d2d2cc # Note for bridge governors For call traces, state changes, emitted event, touched contracts, please check: 1. Tenderly Simulation on Ethereum: https://dashboard.tenderly.co/public/safe/safe-apps/simulator/7a369788-8c38-4ace-b7ed-1f25981e8bee 2. Tenderly Simulation on Gnosis Chain: https://dashboard.tenderly.co/public/safe/safe-apps/simulator/3940609f-bcf2-4428-9432-11a4c98ccd73 **Core contract in the upgrade** 1. XDaiForeignBridge.sol: `0x257bDD093Cab1Bd39eBF837dCB60f33d031d7d49` 2. HomeBridgeErcToNative.sol: `0xe6998b0C03D3cb9ee8C04f266e573c7Fa8782846` 3. USDSDepositContract.sol: `0x5C183C8A49aBA6e31049997a56D75600E27FF8c9` **Peripheral contract in the upgrade** 1. BridgeRouter.sol: `0x74899961224538E423eFfD1A0Ff3346adf3F4C56` 2. TransparentUpgradeableProxy.sol: `0x9a873656c19Efecbfb4f9FAb5B7acdeAb466a0B0` 3. ProxyAdmin.sol: `0xD7e65A32bEd4ce8cc57Ec188F2bBb8016dc4b1cd` 4. XDaiBridgePeripheral.sol: `0x3b6669727927b934753B018EB421a84Ed4eb0a43` **Contract & parameters mentioned in the upgrade transaction** 1. `0x257bDD093Cab1Bd39eBF837dCB60f33d031d7d49`(Ethereum): XDaiForeignBridge new implementation 2. `0xe6998b0C03D3cb9ee8C04f266e573c7Fa8782846`(Ethereum): HomeBridgeErcToNative new implementation 3. `0x5C183C8A49aBA6e31049997a56D75600E27FF8c9`(Gnosis Chain): USDSDepositContract 4. `0x74899961224538E423eFfD1A0Ff3346adf3F4C56`(Ethereum): BridgeRouter new implementaiton 5. `0x9a873656c19Efecbfb4f9FAb5B7acdeAb466a0B0`(Ethereum): TransparentUpgradeableProxy for BridgeRouter 6. `0xD7e65A32bEd4ce8cc57Ec188F2bBb8016dc4b1cd`(Ethereum): ProxyAdmin.sol for BridgeRouter, controls upgrade for the implementation contract w.r.t Proxy address 7. `0x3b6669727927b934753B018EB421a84Ed4eb0a43`(Ethereum): XDaiBridgePeripheral, swap DAI->USDS for user before bridging 8. `0x4aa42145aa6ebf72e164c9bbc74fbd3788045016`(Ethereum): XDaiForeignBridgeProxy 9. `0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6`(GnosisChain): HomeBridgErcToNativeProxy 10. `0x670daeaF0F1a5e336090504C68179670B5059088`(GnosisChain): InterestReceiver contract when `payInterest` is called. Related to sDAI yield when sDAI adapter `claim` function is [called](https://gnosisscan.io/address/0x670daeaf0f1a5e336090504c68179670b5059088#tokentxns) 11. `0x6B175474E89094C44Da98b954EedeAC495271d0F`(Ethereum): DAI token address 12. `0xdC035D45d973E3EC169d2276DDab16f1e407384F`(Ethereum): USDS token address 13. `minCashThreshold`: is the minimum USDS that the bridge need to hold for user to claim USDS back. It acts as a buffer and the minCashThreshold amount is not used for investing. Value: `1000000000000000000000000`. (Same as [`minCashThreshold(DAI)`](:https://etherscan.io/address/0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016#readProxyContract#F14)) 14. `minInterestPaid`: The minimum amount of interest from sUSDS that can be withdrawn during a payInterest call. The payInterest call will be invalid if the available interest < minInterestedPaid. Value: `1000000000000000000000`. (Same as [`minInterestedPaid(DAI)`](https://etherscan.io/address/0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016#readProxyContract#F11)) **Technical Analysis** We have requested Omega team to audit and verify the Safe transaction payload of the upgrade txs on both Ethereum and Gnosis Chain. The upgrade txs are consistent with the description on both this technical documentation and the governance forum. Upgrade tx on Ethereum: [url](https://app.safe.global/transactions/tx?safe=eth:0x42F38ec5A75acCEc50054671233dfAC9C0E7A3F6&id=multisig_0x42F38ec5A75acCEc50054671233dfAC9C0E7A3F6_0x7309151c47d50ea0bac9eac0b28a13b8c2904857d7ddd6c65653336d9b53acc0) Upgrade tx on Gnosis Chain: [url](https://app.safe.global/transactions/tx?safe=gno:0x7a48Dac683DA91e4faa5aB13D91AB5fd170875bd&id=multisig_0x7a48Dac683DA91e4faa5aB13D91AB5fd170875bd_0xb6d709f3f6fe73958bf4de18a2d8ba81b8981a18e0c17c9f608e61c03ec0e166) Omega validations - [x] The deployed bytecode of the deployed contracts match the deployed bytecode of the contracts audited by Omega. - [x] The parameters and state changes of the transactions match the intented upgrade behaviors - [x] The signatures in Bridge governors Safe correspond with the SafeTxHash and signatures of the Safe. Both transactions are executed on Nov 7 2025.