BOBA ERC1155 bridges consists of two bridge contracts. The L1ERC1155Bridge contract is deployed on L1 and the L2ERC1155Bridge contract is deployed on L2. It supports native L1 ERC1155 tokens and native L2 ERC1155 tokens to be moved back and forth.
Native L1 ERC1155 token: the original token contract was deployed on L1
Native L2 ERC1155 token: the original token contract was deployed on L2
Bridging a token to Boba takes several minutes, and bridging a token from Boba to Ethereum takes 7 days. Not all tokens are bridgeable - developers must use specialized token contracts (e.g. L2StandardERC1155.sol) to enable this functionality.
When deploying your L2StandardERC1155, please take caution if you extend the contract with more features, as an incorrect implementation may result in loss of tokens. For instance, do not add a method that would allow updating the corresponding 'l1Contract' address for an L2StandardERC1155. An update in between operation would deem the previous tokens to be locked on the bridge. Furthermore, The ERC1155Bridge contracts use the information at the time of registration to obtain the l1Token information and send messages between the bridges.
Assuming you have already deployed an ERC1155 token contract on L1, and you wish to transfer those tokens to L2, please make sure that your L1 ERC1155 token contract is ERC1155 compatible. Your contract must implement ERC165 and ERC721 interfaces. We will check the interface before registering your token contracts to our bridges.
bytes4 erc1155 =0xd9b67a26;require(ERC165Checker.supportsInterface(_l1Contract, erc1155),"L1 token is not ERC1155 compatible");
After verifying the interface, please deploy L2StandardERC1155 on Boba. The L1_ERC1155_TOKEN_CONTRACT_ADDRESS is the address of your token on Ethereum.
constFactory__L2StandardERC1155=newethers.ContractFactory(L2StandardERC1155.abi,L2StandardERC1155.bytecode, L2Wallet)constL2StandardERC1155=awaitFactory__L2StandardERC1155.deploy(L2_ERC1155_BRIDGE_ADDRESS,// L2 ERC1155 Bridge AddressL1_ERC1155_TOKEN_CONTRACT_ADDRESS,// Your L1 Token AddressURI)awaitL2StandardERC1155.deployTransaction.wait()
If you want to deploy your own L2 ERC1155 token contract, please follow requirements:
Your L2 token contract must be ERC1155 compatible and implemented ERC165 and ERC1155 interfaces.
The following functions in your L2 ERC1155 contract should be overriden by
In your L2 ERC1155 token contract, you must add the following code to bypass our interface check in our bridge
pragmasolidity >0.7.5;import"@openzeppelin/contracts/token/ERC1155/ERC1155.sol";import"./IL2StandardERC1155.sol";contractL2StandardERC1155isIL2StandardERC1155, ERC1155 {// [This is mandatory]// This is your L1 token contract address. Only one pair of your token contracts can be registered in the bridges.addresspublicoverride l1Contract;// [This is not mandatory] You can use other names or other ways to only allow l2 bridge to mint and burn tokens// This is L2 brigde contract addressaddresspublic l2Bridge;// [This is not mandatory]// Only l2Brigde (L2 bridge) can mint or burn tokensmodifieronlyL2Bridge {require(msg.sender == l2Bridge,"Only L2 Bridge can mint and burn"); _; }// [This is mandatory]// You must export this interfacefunctionsupportsInterface(bytes4_interfaceId) publicviewoverride(IERC165,ERC1155) returns (bool) {bytes4 bridgingSupportedInterface = IL2StandardERC1155.l1Contract.selector^ IL2StandardERC1155.mint.selector^ IL2StandardERC1155.burn.selector^ IL2StandardERC1155.mintBatch.selector^ IL2StandardERC1155.burnBatch.selector;return _interfaceId == bridgingSupportedInterface || super.supportsInterface(_interfaceId); }// [The input is mandatory] The input must be `address _to, uint256 _tokenId, uint256 _amount, bytes memory _data`// [SECURITY] Make sure that only L2 ERC1155 bridge can mint tokens function mint(address _to, uint256 _tokenId, uint256 _amount, bytes memory _data) public virtual override onlyL2Bridge {
_mint(_to, _tokenId, _amount, _data);emitMint(_to, _tokenId, _amount); } // [The input is mandatory] The input must be `address _to, uint256[] memory _tokenIds, uint256[] memory _amounts, bytes memory _data`
// [SECURITY] Make sure that only L2 ERC1155 bridge can mint tokens function mintBatch(address _to, uint256[] memory _tokenIds, uint256[] memory _amounts, bytes memory _data) public virtual override onlyL2Bridge {
_mintBatch(_to, _tokenIds, _amounts, _data);emitMintBatch(_to, _tokenIds, _amounts); }// [The input is mandatory] The input must be `address _from, uint256 _tokenId, uint256 _amount`// [SECURITY] Make sure that only L2 ERC1155 bridge can burn tokensfunctionburn(address_from,uint256_tokenId,uint256_amount) publicvirtualoverrideonlyL2Bridge {_burn(_from, _tokenId, _amount);emitBurn(_from, _tokenId, _amount); }// [The input is mandatory] The input must be `address _from, uint256[] memory _tokenIds, uint256[] memory _amounts`// [SECURITY] Make sure that only L2 ERC1155 bridge can burn tokens function burnBatch(address _from, uint256[] memory _tokenIds, uint256[] memory _amounts) public virtual override onlyL2Bridge {
_burnBatch(_from, _tokenIds, _amounts);emitBurnBatch(_from, _tokenIds, _amounts); }}
NOTE: Once you have your L2 ERC1155 token contract address, please contact us so we can register that address in the L1 and L2 bridges.
Deploy your ERC115 token on Boba and then deploy L1StandardERC1155 on Ethereum. The L1_ERC1155_TOKEN_CONTRACT_ADDRESS is the address of your token on Boba.
constFactory__L1StandardERC1155=newethers.ContractFactory(L1StandardERC1155.abi,L1StandardERC1155.bytecode, L1Wallet)constL1StandardERC1155=awaitFactory__L1StandardERC1155.deploy(L1_ERC1155_BRIDGE_ADDRESS,// L1 ERC1155 Bridge AddressL2_ERC1155_TOKEN_CONTRACT_ADDRESS,// Your L2 Token AddressURI)awaitL1StandardERC1155.deployTransaction.wait()
If you want to deploy your own L1 ERC1155 token contract, please follow requirements:
Your L1 token contract must be ERC1155 compatible and implemented ERC165 and ERC1155 interfaces.
The following functions in your L1 ERC1155 contract should be overriden by
In your L1 ERC1155 token contract, you must add the following code to bypass our interface check in our bridge
pragmasolidity >0.7.5;import"@openzeppelin/contracts/token/ERC1155/ERC1155.sol";import"./IL1StandardERC1155.sol";contractL1StandardERC1155isIL1StandardERC1155, ERC1155 {// [This is mandatory]// This is your L2 token contract address. Only one pair of your token contracts can be registered in the bridges.addresspublicoverride l2Contract;// [This is not mandatory] You can use other names or other ways to only allow l2 bridge to mint and burn tokens// This is L1 brigde contract addressaddresspublic l1Bridge;// [This is not mandatory]// Only l1Brigde (L1 bridge) can mint or burn tokensmodifieronlyL1Bridge {require(msg.sender == l1Bridge,"Only L1 Bridge can mint and burn"); _; }// [This is mandatory]// You must export this interfacefunctionsupportsInterface(bytes4_interfaceId) publicviewoverride(IERC165,ERC1155) returns (bool) {bytes4 bridgingSupportedInterface = IL1StandardERC1155.l1Contract.selector^ IL1StandardERC1155.mint.selector^ IL1StandardERC1155.burn.selector^ IL1StandardERC1155.mintBatch.selector^ IL1StandardERC1155.burnBatch.selector;return _interfaceId == bridgingSupportedInterface || super.supportsInterface(_interfaceId); }// [The input is mandatory] The input must be `address _to, uint256 _tokenId, uint256 _amount, bytes memory _data`// [SECURITY] Make sure that only L1 ERC1155 bridge can mint tokens function mint(address _to, uint256 _tokenId, uint256 _amount, bytes memory _data) public virtual override onlyL1Bridge {
_mint(_to, _tokenId, _amount, _data);emitMint(_to, _tokenId, _amount); } // [The input is mandatory] The input must be `address _to, uint256[] memory _tokenIds, uint256[] memory _amounts, bytes memory _data`
// [SECURITY] Make sure that only L1 ERC1155 bridge can mint tokens function mintBatch(address _to, uint256[] memory _tokenIds, uint256[] memory _amounts, bytes memory _data) public virtual override onlyL1Bridge {
_mintBatch(_to, _tokenIds, _amounts, _data);emitMintBatch(_to, _tokenIds, _amounts); }// [The input is mandatory] The input must be `address _from, uint256 _tokenId, uint256 _amount`// [SECURITY] Make sure that only L1 ERC1155 bridge can burn tokensfunctionburn(address_from,uint256_tokenId,uint256_amount) publicvirtualoverrideonlyL1Bridge {_burn(_from, _tokenId, _amount);emitBurn(_from, _tokenId, _amount); }// [The input is mandatory] The input must be `address _from, uint256[] memory _tokenIds, uint256[] memory _amounts`// [SECURITY] Make sure that only L1 ERC1155 bridge can burn tokens function burnBatch(address _from, uint256[] memory _tokenIds, uint256[] memory _amounts) public virtual override onlyL1Bridge {
_burnBatch(_from, _tokenIds, _amounts);emitBurnBatch(_from, _tokenIds, _amounts); }}
NOTE: Once you have your L1 token contract address, please contact us so we can register that address in the L1 and L2 bridges.
CASE 1 - Native L1 token - Bridge tokens from Ethereum to Boba
First, users transfer their token to the L1 Bridge, starting with an approval.
Users then call the deposit or depositTo function to deposit token to L2. The token arrives on L2 after L1 conf blocks.
consttx=awaitL1ERC1155Brige.deposit(L1_ERC1155_TOKEN_CONTRACT_ADDRESS,TOKEN_ID,TOKEN_AMOUNT,DATA,// event data - you can pass `0x` if you don't want to emit any data in the events9999999// L2 gas)awaittx.wait()
CASE 2 - Native L1 token - Bridge tokens from Boba to Ethereum
Prior to the exit, the L2 Bridge burns the L2 tokens, so the first step is for the user to approve the transaction.
Users have to approve the Boba for the exit fee next. They then call the withdraw or withdrawTo function to exit the tokens from Boba to Ethereum. The token will arrive on L1 after the seven days.
constexitFee=awaitBOBABillingContract.exitFee()constapproveBOBATx=awaitL2BOBAToken.approve(L2ERC1155Brige.address, exitFee)awaitapproveBOBATx.wait()consttx=awaitL1ERC1155Brige.withdraw(L2_ERC1155_TOKEN_CONTRACT_ADDRESS,TOKEN_ID,TOKEN_AMOUNT,DATA,// event data - you can pass `0x` if you don't want to emit any data in the events9999999// L2 gas)awaittx.wait()
CASE 3 - Native L2 token - Bridge tokens from Boba to Ethereum
Users have to transfer their tokens to the L2 Bridge, so they start by approving the transaction.
Users have to approve the Boba for the exit fee next. They then call the withdraw or withdrawTo function to exit tokens from L2. The token will arrive on L1 after the seven days.
constexitFee=awaitBOBABillingContract.exitFee()constapproveBOBATx=awaitL2BOBAToken.approve(L2ERC1155Brige.address, exitFee)awaitapproveBOBATx.wait()consttx=awaitL2ERC1155Brige.withdraw(L2_ERC1155_TOKEN_CONTRACT_ADDRESS,TOKEN_ID,TOKEN_AMOUNT,DATA,// event data - you can pass `0x` if you don't want to emit any data in the events9999999// L2 gas)awaittx.wait()
CASE 4 - Native L2 token - Bridge tokens from Ethereum to Boba
The L1 Bridge has to burn the L1 tokens, so the user needs to approve the transaction first.
Users then call the deposit or depositTo function to deposit tokens to L2. The token arrives on L2 after L1 conf blocks.
consttx=awaitL1ERC1155Brige.deposit(L1_ERC1155_TOKEN_CONTRACT_ADDRESS,TOKEN_ID,TOKEN_AMOUNT,DATA,// event data - you can pass `0x` if you don't want to emit any data in the events9999999// L2 gas)awaittx.wait()
NOTE
To bridge tokens from Alt L2s to Alt L1, you need to add the BOBA as the value to cover the exit fee.
constexitFee=awaitBOBABillingContract.exitFee()consttx=awaitL2ERC1155Brige.withdraw(L2_ERC1155_TOKEN_CONTRACT_ADDRESS,TOKEN_ID,TOKEN_AMOUNT,DATA,// event data - you can pass `0x` if you don't want to emit any data in the events9999999,// L2 gas {value: exitFee} // exit fee)awaittx.wait()