Updated on March 29, 2022
Integrating with the Palm network bridge
The Palm Network enables NFT trading in a fast, cost-efficient, and eco-friendly manner. However, some users might decide to move their assets from the Palm Network to Ethereum in order to reach specific marketplaces. They can transfer their token(s) using the Palm network bridge.
As a developer you can support those users by ensuring your smart contracts are compatible with the Palm network bridge.
This article explains:
- What is the Palm network Bridge.
- How to integrate with the bridge.
What is the Palm network Bridge?
The Palm network bridge connects the Palm network with Ethereum. It allows transferring assets such as ERC-20 and ERC-721 tokens back and forth between the Palm Network and Ethereum.
The bridge works by locking tokens that have already been minted on one side of the bridge and then minting an equivalent token on the other side, using what we call a “synthetic” version of the ERC-721 contract:
Users can send their tokens back to the original side: the bridge will burn the synthetic token and release the original token that will be transferred to the destination wallet address:
A fee is required for transferring assets from Palm to Ethereum (to cover gas costs of minting and a carbon offset). Moving assets back to Palm will top up the depositor’s account with a small amount of PALM tokens.
What does an end-user see when she uses the bridge?
The Palm network provides a user-friendly Dapp where users can initiate the transfer and give their approval to pay transfer fees (in DAI).
Now, let’s dive a bit deeper into how the bridge operates:
The Palm bridge runs on ChainBridge, a communication protocol where events on the source chain are used to send messages routed to the destination chain, where they will be submitted as transactions.
A few concepts specific to ChainBridge:
- Relayers — Off-chain servers that listen for particular events on the source chain and submit signed proposals to the destination chain.
- Bridge contracts — Delegate calls to the handler contracts for deposits, start a transaction on the source chain, and execute the proposals on the target chain.
- Handler contracts — Palm’s handler contracts send mint/burn transactions depending on the user input.
- Target contracts — On Palm, target contracts are ERC-20, ERC-721 and ERC-1155 on each side of the bridge.
- Deposit() function — Here, a deposit is simply the initiation of a transfer of a piece of data, often representing instructions to lock a token in the bridge. In the reverse direction, the deposit is an instruction to burn a token.
- Resource ID — Identifier for the transferring token’s smart contract. Resource ID is used to link the equivalent contracts on both sides of the bridge.
- Chain ID — Identifier of the chain, for example, Palm Network or Ethereum
- Calldata — Payload contained by an event/proposal. The calldata represents a function to be executed on the targeted chain. On Palm, calldata represent the mint() functions.
Transfer flow
What actually happens when an end-user uses the bridge?
Here’s the workflow occurring when a user transfers an ERC-721 token from the Palm Network to Ethereum:
- The user calls the
deposit()
function on Palm Network’s bridge contract. The user must provide thetarget chain
, theresource ID
, and thecalldata
, which represent a token transfer to be executed on Ethereum. - The ERC-721 handler’s
deposit()
function is called, which verifies the data provided by the user. The bridge then locks the token on the ERC-721 contract. - Proposal - Palm’s bridge contract then emits a
Deposit
event containing the data that will be executed on Ethereum. On ChainBridge, this type of event is called aproposal
. - Once the bridge’s first relayer detects the event on Ethereum, it executes the proposal on Ethereum via the bridge. Effectively, the proposal delegates an
executeDeposit
call to the ERC-721 handler contract. - The ERC-721 handler’s
executeDeposit
function validates the parameters provided by the user and makes a call to the target ERC-721 contract to mint the token with the original ID (a custommint
function on the target contract is passed the token ID as part of the calldata to ensure this). The token is transferred to the recipient’s account on Ethereum.
How to ensure your token contract works with the bridge?
All you need to do to integrate with the bridge is to prepare your token contracts so that the Bridge supports them.
Making your token contracts bridge-compatible
Original contracts vs Synthetic contracts
In the context of the Palm network’s bridge, an original contract sits where tokens are primarily minted. A synthetic contract is deployed on the chain where tokens will be transferred via the bridge.
Deploying both original and synthetic contracts ensures that tokens can be transferred back and forth between the original and destination chains.
Here are the changes you will need to make to your contracts for them to be bridge-compatible: The below specifications apply to ERC-721 contracts. Further specifications will be provided for ERC-1155 or ERC-20 contracts in future.
Original contract
- Needs to include the Enumerable extension from Open Zeppelin libraries. This is to support enumerability of all the token ids in the contract as well as all token ids owned by each account, so that the bridge UI can help the user to select the correct token from their account.
Aside from Enumerable
, any custom implementation of ERC-721 is allowed: bulk minting, token ID auto-increment, etc…
Synthetic contract
-
Also needs to add the Enumerable extension from Open Zeppelin libraries.
-
Needs to have a custom
mint()
function.In order to mint a replica of the original token on the targeted chain, the synthetic contract must be able to mint tokens that have the same
IDs
andURIs
as the original.Custom
mint()
function example:1 2 3 4 5 6 7 8 9 10 11
/** * @dev Mints the specified token id to the recipient addresses * @dev The unused string parameter exists to support the API used by ChainBridge. * @dev Mint interface: function mint(address to, uint256 tokenId, string calldata _data) public where _data is the tokenUri * @param tokenId tokenId to be minted * @param recipient Address that will receive the tokens */ function mint(address recipient, uint256 tokenId, string calldata tokenUri) external onlyMinters { _mint(recipient, tokenId); _setTokenURI(tokenId, tokenUri); }
-
Needs to grant the bridge minting permission.
The bridge’s handler will need access to the synthetic contract’s
mint()
function.We recommend using role-based access controls to do this, and it also helps to avoid granting full admin functions to the bridge address.
You can set granular rights that only set controls on the
mint()
function.Granular
mint()
rights example:1 2 3 4 5 6 7
/** * @dev Throws if called by any account other than minters. Implemented using the underlying AccessControl methods. */ modifier onlyMinters() { require(hasRole(MINTER_ROLE, _msgSender()), "Caller does not have the MINTER_ROLE"); _; }
-
Needs to have a
burn()
function.Same as for the
mint()
function described above, the bridge will need to be granted permission to theburn()
function in order to burn synthetic tokens when transferring them back to the original chain. -
Needs to give the bridge burning permission.
Same as point 5. but for the
burn()
function.
If you would like to put all those bits into context, here’s a contract example that applies for both original and synthetic contracts:
ERC-721 contract example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
|
Testing your contracts
You can use the Palm Testnet bridge to live-test your contracts’ integration with the bridge.
All you need is to deploy them to Palm testnet and Rinkeby (Rinkeby is one of Ethereum’s testnets). For ERC-721 contracts, you will need to initialise it using the constructor, and grant the bridge’s ERC-721 handler contract MINTER_ROLE
(see table below for relevant contract addresses).
Your original and synthetic can be exactly the same as in production.
Read more about how to use the Palm Testnet bridge here.
Once you have prepared your contracts for the bridge, feel free to contact us on discord to validate your contracts compatibility, they will be tested by our team on the testnet and then set for production.
Helpful Resources - Address of Bridge Components
The following table contains the contracts and addresses of the main bridge components.
Chain | Address | Description |
Palm Mainnet | 0xB3C62Aed3be8e0577D4724C40a01379dbf895C01
|
Bridge Contract - main bridge smart contract |
Palm Mainnet | 0x97FAcbF880e47c27cafbA6bE3d677A50d536813e
|
ERC-20 Handler - account given permissions to mint/burn ERC-20 tokens on Palm Network |
Palm Mainnet | 0x317bc33A442AA0f6C8235cb2487f0Bb338eD27E4
|
ERC-721 Handler - account given permissions to mint/burn ERC-721 tokens on Palm Network |
Palm Mainnet | 0x22887Af68E57A76692f2686020FF563aC873eA24
|
Primary relayer - transactions for assets minted/burned on Palm Network can be found here |
Palm Mainnet | 0x2A84F0c208872184c9dfcd57B6cd7bF63BcF829E
|
Secondary relayer - this account periodically sweeps for transactions that did not successfully complete by the primary relayer |
Palm Mainnet | 0x8993D834b036913E25f35ed1Cbb288F26779e16a
|
Tollbooth account that collects fees in DAI from bridge users. Dai fees are regularly transferred to the Ethereum network using the bridge, swapped for Ether, and added to the Ethereum Relayer accounts. This way, it can pay for gas fees incurred by the bridge when it mints a token on behalf of the user. Carbon offset fees are also collected in this account and withdrawn for direct payment to carbon offset projects. |
Ethereum Mainnet | 0x7D0e63736aEb136aCd44C70D6e1A0f27fb897679
|
Bridge Contract - main bridge smart contract. |
Ethereum Mainnet | 0xf4684EB75659Bec9C3c3b19f075a6fd5ABa34b87
|
ERC-20 Handler - account given permissions to mint/burn ERC-20 tokens on Ethereum. |
Ethereum Mainnet | 0x4B4473093d98F0daA39e60406333B019c3A29D36
|
ERC-721 Handler - account given permissions to mint/burn ERC-721 tokens on Ethereum. |
Ethereum Mainnet | 0x22887Af68E57A76692f2686020FF563aC873eA24
|
Primary relayer - transactions for assets minted/burned on Ethereum can be found here. |
Ethereum Mainnet | 0x2A84F0c208872184c9dfcd57B6cd7bF63BcF829E
|
Secondary relayer - this account periodically sweeps for transactions that did not successfully complete on the primary relayer. |
Palm testnet | 0xdeD098F762456D4BEA387AcadcB1eAeA63E8e954 | Bridge Address |
Palm testnet | 0x0113A208409505470d3F9b4a7c43488e57564bD7 | ERC-20 Handler Address |
Palm testnet | 0x0114a4A5604f88076D6CDD5607115CE42812e404 | ERC-721 Handler Address |
Palm testnet | 0x5105F2e61A5139589c02e557bd4A61A5a22B2676 | ERC-1155 Handler Address |
Rinkeby | 0x21bE213d63e9F5CE1F93D2758F132817A41874e1 | Bridge Address |
Rinkeby | 0x515C78a5737093Dd8a2638800b93445C77e5bE1D | ERC-20 Handler Address |
Rinkeby | 0x3bD23d02a76804E839c4B73E978Ce05a406e964b | ERC-721 Handler Address |
Rinkeby | 0x91429E6686348645cc9B17C382F991d900966D7D | ERC-1155 Handler Address |
Question
Any question? Drop them on our Discord