Across is now live on Lens Chain!
Bridge Now
Across Documentation
V2 Developer Docs
V2 Developer Docs
  • How Across Works
    • Overview
      • Roles within Across
      • Fee Model
      • Validating Root Bundles
      • Disputing Root Bundles
  • Developers
    • Across API
    • Across SDK
    • Contract Addresses
      • Mainnet (Chain ID: 1)
      • Arbitrum (Chain ID: 42161)
      • Optimism (Chain ID: 10)
      • Base (Chain ID: 8453)
      • zkSync (Chain ID: 324)
      • Polygon (Chain ID: 137)
    • Selected Contract Functions
    • Running a Relayer
    • Integrating Across into your application
    • Composable Bridging
    • Developer notes
    • Migration from V2 to V3
  • ADDITIONAL INFO
    • Support Links
    • Bug Bounty
    • Audits
Powered by GitBook
LogoLogo
On this page
  • What is Composable Bridging:?
  • How does it work?
  • Requirements
  • Detailed instructions
  • Example: Implementing a Bridge and Unwrap
  1. Developers

Composable Bridging

Use Across to bridge + execute a transaction

PreviousIntegrating Across into your applicationNextDeveloper notes

Last updated 1 year ago

What is Composable Bridging:?

You can instruct Across to execute a transaction upon filling your deposit on the destination chain. This transaction would be executed atomically with the fill transaction.

NOTE: The transaction that gets executed on the destination chain must be non-reverting otherwise user deposits may risk getting locked.

How does it work?

When a relayer fills your deposit by calling fillRelay() on the destination SpokePool, if the deposit has a message attached, then the on your recipient address and pass in the following params:

Requirements

  • The deposit message is not empty

  • The recipient address is a contract on the destinationChainId that implements a public handleAcrossMessage(address,uint256,bool,address,bytes) function, and this function must be non-reverting

  • The additional gas cost to execute the above function is compensated for in the deposit's

Detailed instructions

  • Construct your message

  • Use the Across API to get an estimate of the relayerFeePct you should set for your message and recipient combination

  • Call deposit() passing in your message

  • Once the relayer calls fillRelay() on the destination, your recipient's handleAcrossMessage will be executed

Example: Implementing a Bridge and Unwrap

  • Imagine that I want to bridge ETH from Ethereum to Optimism and receive ETH, not wrapped WETH on Optimism

  • I will deploy the following contract on Optimism which unwraps received WETH into ETH and sends to a designated owner EOA

contract MyUnwrapper {
    WETHInterface weth;
    addess payable owner;
    constructor() public {
        owner = msg.sender;
    }
    function handleAcrossMessage(
        address tokenSent, 
        uint256 amount, 
        bool, // fillCompleted unused
        address, // relayer is unused 
        bytes memory // message is unused
    ) external { 
        require(tokenSent == address(weth), "received token not WETH");
        weth.withdraw(amount);
        (bool sent, bytes memory data) = owner.call{value: amount}("");
        require(sent, "Failed to send Ether");
    } 
}
  • Here we set message to something useless but not-zero so that the destination SpokePool ultimately calls handleAcrossMessage on MyUnwrapper

Call Across-API’s endpoint with params ?token=0xWETH-on-ethereum-address&destinationChainId=10&amount= x&originChainId=1&recipient=MyUnwrapper.address&message=0x1234

SpokePool will attempt to call handleAcrossMessage()
handleAcrossMessage(address tokenSent, uint256 amount, bool fillCompleted, address relayer, bytes memory message)
/suggested-fees
relayerFeePct