Skip to content

Commit 4e09945

Browse files
authored
Merge pull request 0xPolygon#341 from 0xPolygon/empieichO-docs-review
Dev tools: Matic.js message passing between zkEVM and Ethereum
2 parents 264ade4 + fe5ccdc commit 4e09945

File tree

2 files changed

+262
-0
lines changed

2 files changed

+262
-0
lines changed
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
This document demonstrates inter-layer message passing using the messaging layer of the Polygon bridge. As an example, we go over how to customize wrapped tokens using adapter contracts, and how to use Matic.js to bridge assets from Ethereum to Polygon zkEVM and vice versa.
2+
3+
!!! info "Terminology"
4+
5+
Within the scope of this doc, we refer to Ethereum as the _root_ chain and zkEVM as the _child_ chain.
6+
7+
The existing zkEVM bridge uses the ERC20 standard contract for creating wrapped tokens depending on the token's native network.
8+
9+
Often, organizations want to customize their wrapped tokens by extending some functionalities.
10+
11+
These functionalities could include: blacklisting, putting a cap on minting, or any sound auxiliary functionality.
12+
13+
This can be done by deploying **adapter contracts** that use the messaging layer of the bridge.
14+
15+
## Adapter contracts
16+
17+
An adapter is a wrapper contract that implements the [Polygon bridge library](https://github.com/0xPolygonHermez/code-examples/blob/main/customERC20-bridge-example/contracts/base/PolygonERC20BridgeBase.sol). An example implementation for ERC20 can be found [here](https://github.com/0xPolygonHermez/code-examples/blob/main/customERC20-bridge-example/contracts/lib/PolygonERC20BridgeLib.sol).
18+
19+
Ideally, the following adapter contracts are expected,
20+
21+
1. `OriginChainBridgeAdapter`
22+
2. `WrapperChainBridgeAdapter`
23+
24+
Irrespective of whether an ERC20 token is Ethereum native (root chain) or zkEVM native (child chain), an adapter contract should have the following functions: (these are already part of the [library](https://github.com/0xPolygonHermez/code-examples/blob/main/customERC20-bridge-example/contracts/base/PolygonERC20BridgeBase.sol)).
25+
26+
```solidity
27+
function bridgeToken(
28+
address destinationAddress,
29+
uint256 amount,
30+
bool forceUpdateGlobalExitRoot
31+
) external {}
32+
33+
function onMessageReceived(
34+
address originAddress,
35+
uint32 originNetwork,
36+
bytes memory data
37+
) external payable {}
38+
```
39+
40+
For the sake of maintaining consistency among wrapped tokens in terms of the bridging mechanism, there are certain standard functions and variables that need to be included in the adapter contracts.
41+
42+
## Standardizations
43+
44+
1. Adapter contracts need to implement the [Polygon bridge library](https://github.com/0xPolygonHermez/code-examples/blob/main/customERC20-bridge-example/contracts/base/PolygonERC20BridgeBase.sol) and expose `bridgeToken()` and `onMessageReceived()` functions.
45+
2. There should be two separate adapter contracts; `OriginChainBridgeAdapter` and `WrapperChainBridgeAdapter`.
46+
3. `bridgeToken` function should match the exact function signature and be similar to [this](https://github.com/maticnetwork/static/blob/master/network/mainnet/cherry/artifacts/zkevm/ZkEVMBridgeAdapter.json) ABI.
47+
48+
### Nice to have
49+
50+
Expose the following variables,
51+
52+
1. `originTokenAddress`: Address of the native token.
53+
2. `originTokenNetwork`: `networkId` of the chain to which the token is native.
54+
3. `wrappedTokenAddress`: Address of the wrapped token.
55+
56+
## Bridging mechanism
57+
58+
`PolygonZkEVMBridge` is the main bridge contract. It exposes a `bridgeMessage()` function, which users can call in order to bridge messages from L1 to L2, or vice versa. The `claimMessage()` function can be called on the receiving chain to claim the sent message.
59+
60+
For example, a user who wants to bridge a message from Ethereum to zkEVM, can call `bridgeMessage()` on Ethereum and then call `claimMessage()` on zkEVM. Once the `claimMessage()` function is called, the bridge calls `onMessageReceived` for the specified destination address.
61+
62+
Adapter contracts are basically abstractions that use `bridgeMessage()` to bridge and `onMessageReceived()` to process a `claimMessage()` on respective chains.
63+
64+
![Figure: Adapter contract](../../../img/learn/maticjs-adapter-contract-01.png)
65+
66+
## ERC20 transfer contract interaction
67+
68+
The transfer of ERC20 tokens using each of the adapter contracts and the actions performed in the process are described below.
69+
70+
### OriginChainBridgeAdapter
71+
72+
When depositing an ERC20 token from Ethereum to zkEVM, the adapter contract calls the `bridgeToken()` function.
73+
74+
During withdrawal from zkEVM, the [PolygonZkEvmBridge.sol](https://github.com/0xPolygonHermez/zkevm-contracts/blob/main/contracts/PolygonZkEVMBridge.sol) contract calls the `onMessageReceived()` function when `claimMessage()` is invoked.
75+
76+
### WrapperChainBridgeAdapter
77+
78+
When withdrawing an ERC20 token from zkEVM to Ethereum, the adapter contract calls the `bridgeToken()` function.
79+
80+
During a deposit to zkEVM, the [PolygonZkEvmBridge.sol](https://github.com/0xPolygonHermez/zkevm-contracts/blob/main/contracts/PolygonZkEVMBridge.sol) contract calls the `onMessageReceived()` function when `claimMessage()` is invoked.
81+
82+
### From Ethereum → zkEVM
83+
84+
!!! warning
85+
86+
It is assumed that the token being bridged is native to the root chain.
87+
88+
1. Deploy your adapter contracts on both the root chain and the child chain. (Note the address, you’ll need it later!)
89+
2. Approve the tokens to be transferred by calling the `approve()` function (on the root token) with the address of the `originChainBridgeAdapter` and the token amount, as arguments.
90+
3. Proceed to call `bridgeToken()` while using as arguments: the recipient, amount, and setting `forceUpdateGlobalExitRoot` to `true` on the `originChainBridgeAdapter` in the root chain (i.e., Ethereum).
91+
4. Get the Merkle proof for this bridge transaction using the [proof API](https://proof-generator.polygon.technology/api/zkevm/testnet/merkle-proof?net_id=0&deposit_cnt=).
92+
5. Proceed to call `claimMessage()` with the respective arguments on the `PolygonZkEVMBridge.sol` contract in the child chain (i.e., zkEVM).
93+
94+
The bridge will call the `onMessageReceived` function in the `WrapperChainBridgeAdapter` contract. Which should ideally have the logic to mint wrapped tokens to the recipient.
95+
96+
### From zkEVM → Ethereum
97+
98+
!!! warning
99+
100+
It is assumed that the token being bridged is native to the root chain.
101+
102+
1. Deploy your adapter contracts on both the root chain and the child chain. (Note the address, you’ll need it later!)
103+
2. Approve the tokens to be transferred by calling the `approve()` function (on the wrapped token) with the address of the `wrapperChainBridgeAdapter` and the token amount as arguments.
104+
3. Proceed to call `bridgeToken()`, using as arguments: the recipient, amount, and setting `forceUpdateGlobalExitRoot` to `true` on the `WrapperChainBridgeAdapter` in the child chain (i.e., zkEVM). Ideally, this function should have the logic to burn the wrapped tokens.
105+
4. Get the Merkle proof for this bridge transaction using the [proof API](https://proof-generator.polygon.technology/api/zkevm/testnet/merkle-proof?net_id=0&deposit_cnt=).
106+
5. Proceed to call `claimMessage()` with the respective arguments on the `PolygonZkEVMBridge.sol` contract in the root chain (i.e., Ethereum).
107+
108+
The bridge will call the `onMessageReceived` function in the `OriginChainBridgeAdapter` contract. Which should Ideally have the logic to mint unwrapped tokens to the recipient.
109+
110+
## Listing tokens in Bridge UI
111+
112+
!!! tip
113+
114+
Note that it is important to follow [standardizations](#standardizations) for easy listing.
115+
116+
1. Add your token to this [token list on GitHub](https://github.com/maticnetwork/polygon-token-list/blob/dev/src/tokens/zkevmPopularTokens.json).
117+
118+
Example:
119+
120+
```solidity
121+
{
122+
"chainId": 1101,
123+
"name": "Token Name",
124+
"symbol": "Token Symbol",
125+
"decimals": 6, // token decimal
126+
"address": "ZkEVM Address of the token",
127+
"logoURI": "Token logo url",
128+
"tags": ["zkevm", "stablecoin", "erc20, custom-zkevm-bridge"],
129+
"originTokenNetwork": 0, // 0 here is networkId of ethereum,
130+
"wrappedTokenNetwork": 1, // 1 here is networkId of zkEvm,
131+
"extensions": {
132+
"rootAddress": "Ethereum Address of the Token",
133+
"wrapperChainBridgeAdapter": "",
134+
"originChainBridgeAdapter": "",
135+
}
136+
},
137+
```
138+
139+
!!! note "Setting the correct network ID"
140+
141+
If the token is Ethereum native, then `originTokenNetwork` should be `0`. If the token is zkEVM native, then `originTokenNetwork` should be `1`. The same rule applies for the `wrappedTokenNetwork` field.
142+
143+
2. Raise a PR 🚀.
144+
145+
## Using Matic.js to bridge using adapter contracts
146+
147+
Deploy your `OriginChainBridgeAdapt` and `WrapperChainBridgeAdapter`.
148+
149+
Make sure you are using `matic.js version > 3.6.4`.
150+
151+
- Create an instance of the zkEVM client, passing the necessary parameters. Refer [here](./initialize.md) for more info.
152+
153+
```jsx
154+
const client = new ZkEvmClient();
155+
*await* client.init({})
156+
```
157+
158+
- Create an ERC20 token instance which you would like to bridge,
159+
160+
```jsx
161+
*const* erc20Token = client.erc20("<tokenAddress>", "<isRootChain>", "<bridgeAdapterAddress>");
162+
```
163+
164+
### Bridge from Ethereum → zkEVM
165+
166+
1. **Deposit**
167+
168+
```jsx
169+
const depositTx = await erc20Token.depositCustomERC20("1000000000000000000", "recipent address",true);
170+
const txHash = await depositTx.getTransactionHash();
171+
console.log("Transaction Hash", txHash);
172+
```
173+
174+
2. **Claim deposit**
175+
176+
```jsx
177+
const claimTx = await erc20.customERC20DepositClaim("<deposit tx hash>");
178+
const txHash = await claimTx.getTransactionHash();
179+
console.log("claimed txHash", ctxHash);
180+
```
181+
182+
### Bridge from zkEVM → Ethereum
183+
184+
1. **Withdraw**
185+
186+
```jsx
187+
const depositTx = await erc20Token.withdrawCustomERC20("1000000000000000000", "recipent address",true);
188+
const txHash = await depositTx.getTransactionHash();
189+
console.log("Transaction Hash", txHash);
190+
```
191+
192+
2. **Claim withdrawal**
193+
194+
```jsx
195+
const claimTx = await erc20.customERC20WithdrawExit("<withdraw tx hash>");
196+
const txHash = await claimTx.getTransactionHash();
197+
console.log("claimed txHash", ctxHash);
198+
```
199+
200+
201+
## Basic functions for error passing
202+
203+
Below we provide the two basic functions used for _error passing_ in each of the two directions: L1 --> L2 and L2 --> L1.
204+
205+
### Root to child (L1 → L2)
206+
207+
```jsx
208+
const bridgeTx = zkEvmClient.rootChainBridge.bridgeMessage(
209+
destinationNetwork: number,
210+
destinationAddress: string,
211+
forceUpdateGlobalExitRoot: boolean,
212+
permitData = '0x',
213+
option?: ITransactionOption
214+
);
215+
216+
const claimTx = zkEvmClient.childChainBridge.claimMessage(
217+
smtProof: string[],
218+
smtProofRollup: string[],
219+
globalIndex: string,
220+
mainnetExitRoot: string,
221+
rollupExitRoot: string,
222+
originNetwork: number,
223+
originTokenAddress: string,
224+
destinationNetwork: number,
225+
destinationAddress: string,
226+
amount: TYPE_AMOUNT,
227+
metadata: string,
228+
option: ITransactionOption
229+
);
230+
231+
// proof can be fetched from the proof gen API
232+
```
233+
234+
### Child to root (L2 → L1)
235+
236+
```jsx
237+
const bridgeTx = zkEvmClient.childChainBridge.bridgeMessage(
238+
destinationNetwork: number,
239+
destinationAddress: string,
240+
forceUpdateGlobalExitRoot: boolean,
241+
permitData = '0x',
242+
option?: ITransactionOption
243+
);
244+
245+
const claimTx = zkEvmClient.rootChainBridge.claimMessage(
246+
smtProof: string[],
247+
smtProofRollup: string[],
248+
globalIndex: string,
249+
mainnetExitRoot: string,
250+
rollupExitRoot: string,
251+
originNetwork: number,
252+
originTokenAddress: string,
253+
destinationNetwork: number,
254+
destinationAddress: string,
255+
amount: TYPE_AMOUNT,
256+
metadata: string,
257+
option: ITransactionOption
258+
);
259+
260+
// proof can be fetched from the proof gen API
261+
```

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,7 @@ nav:
462462
- zkEVM:
463463
- zkEVM client: tools/matic-js/zkevm/initialize.md
464464
- ERC20: tools/matic-js/zkevm/erc20.md
465+
- Message passing: tools/matic-js/zkevm/message-passing.md
465466
- Common methods: tools/matic-js/zkevm/common-methods.md
466467
- Storage:
467468
- IPFS: tools/storage/ipfs.md

0 commit comments

Comments
 (0)