Bridging Custom ERC20 Tokens Using Standard Bridge
Mantle v2 Tectonic has been released, please move to the new documentation!
For an L1/L2 token pair to work on the Standard Bridge, there has to be a layer of original mint (where the minting and burning of tokens is controlled by the business logic), and a bridged layer where the Standard Bridge controls minting and burning. The most common configuration is to have L1 as the layer of original mint, and L2 as the bridged layer, this allows for ERC-20 contracts that were written with no knowledge of Mantle Mainnet to be bridged. The contract on the bridged layer has to implement the legacy IL2StandardERC20
interface (only if the bridged layer is L2).
For this to be done securely, the only entity that is allowed to mint and burn tokens on the bridged layer has to be the Standard Bridge, to ensure that the tokens on the bridged layer are backed up by real tokens on the layer of original mint. It is also necessary that the ERC-20 token contract on the layer of original mint not implement either of the interfaces, to make sure the bridge contracts don't get confused and think it is the bridged layer.
Warning: The standard bridge does not support certain ERC-20 configurations:
For the purpose we import the L2StandardERC20
from the @mantleio/contracts
package. This standard token implementation is based on the OpenZeppelin ERC20 contract and implements the required IL2StandardERC20
interface.
You can import @mantleio/contracts
to use the Mantle contracts within your own codebase. Install via npm
or yarn
:
Within your contracts:
Deploying the custom token
Download the necessary packages.
Copy
.env.example
to.env
.
Edit
.env
to set the deployment parameters:
PRIVATE_KEY
, the hex private key for an account that has enough ETH for the deployment.L1_RPC
, Ethereum endpoint RPC URL.L2_RPC
, Mantle endpoint RPC URL.L1_BRIDGE
, L1 standard bridge contract address.L2_BRIDGE
, L2 standard bridge contract address.L1_TOKEN_ADDRESS
, the address of the L1 ERC20 which you want to bridge. The default value,0xeE7Bf96bFd25931976F45a16C4483d336169Bc0F
is a test ERC-20 contract on Goerli that lets you callfaucet
to give yourself test tokens.
Open the hardhat console.
Deploy the contract.
Transferring tokens
Get the token addresses.
Get setup for L1 (provider, wallet, tokens, etc)
Get the L1 wallet.
Get the L1 contract.
Get tokens on L1 (and verify the balance)
Transfer tokens
Create and use CrossDomainMessenger
(the Mantle SDK object used to bridge assets).
Import the Mantle SDK.
Create the cross domain messenger.
Deposit (from Goerli to Mantle Testnet, or Ethereum to Mantle Mainnet)
Give the L2 bridge an allowance to use the user's token. The L2 address is necessary to know which bridge is responsible and needs the allowance.
Check your balances on L1 and L2.
Do the actual deposit
Wait for the deposit to be relayed.
Check your balances on L1 and L2.
Withdrawal (from Mantle Mainnet to Ethereum, or Mantle Testnet to Goerli)
Initiate the withdrawal on L2
Wait until the root state is published on L1, and then prove the withdrawal. This is likely to take within 30 minutes.
Wait the fraud challenge period (a short period on Goerli, currently 7 days(but may be adjusted in the future which can be checked here) on the production network) and then finish the withdrawal.
Check your balances on L1 and L2. The balance on L2 should be back to zero.
Use Factory Contract to Deploy L2StandardERC20
The token contracts deployed using Factory contract are standard IL2StandardERC20
contracts and can only take the name "L2StandardERC20". If you want to use a different name and methods, you can inherit the L2StandardERC20 contract and deploy is as a custom contract.
Call the
createStandardL2Token
method of theL2StandardTokenFactory
contract by passing thel1Token
,name
,symbol
, anddecimal
parameters. The mainnet factory contract is deployed at:0x4200000000000000000000000000000000000012
![](https://docs.mantle.xyz/~gitbook/image?url=https%3A%2F%2F3701411312-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FNHXgeqhgrznnRobzznxl%252Fuploads%252Foj6eBjNhvHqSWSBBCver%252Fimage.png%3Falt%3Dmedia%26token%3De22273ec-d35e-4a78-89a0-10199756da71&width=768&dpr=4&quality=100&sign=ae3908d7&sv=1)
The corresponding contract address will be generated upon a successful call. The creation transaction can be found under the internal transaction tab on the Explorer. Examples:
Newly created contract: https://explorer.mantle.xyz/address/0x7c6b91D9Be155A6Db01f749217d76fF02A7227F2
![](https://docs.mantle.xyz/~gitbook/image?url=https%3A%2F%2F3701411312-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FNHXgeqhgrznnRobzznxl%252Fuploads%252FvxIVUYKWm4SlH3Seb6y2%252Fimage.png%3Falt%3Dmedia%26token%3D72e9a3ec-c304-47d5-bbdf-4ab450b100da&width=768&dpr=4&quality=100&sign=13e47d94&sv=1)
Internal transactions on mainnet can take upto ~12 minutes before they show up on the Explorer.
The contract verification status, contract code, and the relevant parameters can also be found on the Explorer. Example: https://explorer.mantle.xyz/address/0x7c6b91D9Be155A6Db01f749217d76fF02A7227F2/contracts#address-tabs
![](https://docs.mantle.xyz/~gitbook/image?url=https%3A%2F%2F3701411312-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FNHXgeqhgrznnRobzznxl%252Fuploads%252F8wTCuBRYPcR2EwxDVMsP%252Fimage.png%3Falt%3Dmedia%26token%3D1358cd1f-5b95-44ff-8d8c-7d276c284ce6&width=768&dpr=4&quality=100&sign=331126a7&sv=1)
Last updated