A cross-chain token bridge
Clone the examples repo and look at the app before starting!
This dapp implements a cross-chain token bridge. It consists of
two contracts, the TokenSender
contract and the ERC20Bridged
contract, which extends OpenZepplin's standard ERC20
contract
with bridging functionality. In the default configuration, TokenSender
is deployed on Ethereum and ERC20Bridged
is deployed on Polygon,
but this can be changed by
updating the Cubist config file.
TokenSender
is the "native-token" side of the cross-chain
bridge. It receives payment in the native token of the chain on which it is
deployed; in response, it issues ERC20 tokens from the
ERC20Bridged
contract. ERC20Bridged
is the "wrapped-token"
side of the bridge. It defines an ERC20 token---in the example deployment
script, 'FooBarBaz' (ticker 'FBB')---that is minted in response to commands
from TokenSender
. In addition to all standard ERC20 functionality, users
can burn these wrapped tokens in order to release native tokens from
TokenSender
.
TokenSender
in slightly more detail
The TokenSender
contract has just three methods:
- The constructor saves the address of the partner
ERC20Bridged
contract. bridgeSend
is the method to which users send payments; its argument is the address on the wrapped-token chain that will receive the proceeds. This method first takes a 0.1% fee, then instructsERC20Bridged
to mint wrapped tokens corresponding to the balance and send them to the recipient.bridgeReceive
is the method that is invoked when a user burns wrapped tokens (by interacting withERC20Bridged
on the wrapped-token chain) in order to release native tokens on the native-token chain. Because of theonlyOwner
modifier, this method can only be invoked by the bridge owner.
Because TokenSender
is intended to be a very simple example, it lacks features that a deployment-ready token bridge should have. Most notably, it does not implement fee accounting or a mechanism for sweeping fees. It may be a fun exercise to implement these yourself! (Hint: you'll probably need to add a state variable for tracking fees, and a method that (1) checks that the caller is authorized, (2) releases the accrued fees, and (3) updates the fee-tracking variable.)
ERC20Bridged
in slightly more detail
The ERC20Bridged
method is more complex, but most of that complexity is just the functionality inherited from the base contract (as a reminder, the base contract is just OpenZeppelin's
standard ERC20
implementation).
The three methods that ERC20Bridged
implements correspond to the functionality on the TokenSender
side:
- The constructor sets up the underlying
ERC20
contract (setting the name and symbol) and saves the address of the partnerTokenSender
contract. bridgeMint
is the method that is called by the bridge provider in response toTokenSender
'sbridgeSend
method: when it receives a cross-chain call indicating the recipient and the amount, it invokes the OpenZeppelinERC20
contract's_mint
method, which mints new tokens and issues them to the recipient. Because of theonlyOwner
modifier, this method can only be invoked by the bridge owner.bridgeSend
is the method that a user invokes in order to burn wrapped tokens and send native tokens viaTokenSender
. The arguments are the address on the native-token chain that will receive the proceeds, and the amount to send. This method first burns the tokens using the OpenZepplinERC20
contract's_burn
method (which will fail if the user does not own enough tokens), then makes a cross-chain call to theTokenSender
contract'sbridgeReceive
method instructing it to send native tokens to the intended recipient.
The next Cubist release will support bridge providers beyond the Cubist Trusted Relayer---e.g., Axelar or Layer Zero. You can swap bridge providers just as you swap chains, with a single line in the configuration file. Contract us at [email protected] or on discord if you're interested in bridge provider support!