Skip to content

Commit e6526b6

Browse files
uLxLy Bridge Commands (#448)
* feat: copying for alonso * refactor: switching to cobra.. needs testing * fix: minor issues related to missing field and 0x prefix * fix: deposit_cnt type, missing function call, call data in wrong level * feat: adding ability to override global index while the bug is fixed * fix: dropping string tag * feat: adding dry-run and gas-price * feat: better logging of revert data * fix: issue with bridge address * fix: token too long for large bridge messages * fix: handling 0x prefix encoding issue * feat: intial implementation for claim-everything * feat: adding more logging * feat: additional logging * feat: surpressing error logs * fix: null tx * feat: better defaults for dry running * fix: dropping flag that should not be needed * fix: some lint issues * fix: generating docs * docs: updating some of the examples and docs * docs: updating * docs: removing old file * docs: minor review changes
1 parent 0622262 commit e6526b6

27 files changed

+2296
-650
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ Note: Do not modify this section! It is auto-generated by `cobra` using `make ge
7575

7676
- [polycli signer](doc/polycli_signer.md) - Utilities for security signing transactions
7777

78-
- [polycli ulxly](doc/polycli_ulxly.md) - Utilities for interacting with the lxly bridge
78+
- [polycli ulxly](doc/polycli_ulxly.md) - Utilities for interacting with the uLxLy bridge
7979

8080
- [polycli version](doc/polycli_version.md) - Get the current version of this application
8181

cmd/ulxly/BridgeAssetUsage.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
This command will directly attempt to make a deposit on the uLxLy bridge. This call responds to the method defined below:
2+
3+
```solidity
4+
/**
5+
* @notice Deposit add a new leaf to the merkle tree
6+
* note If this function is called with a reentrant token, it would be possible to `claimTokens` in the same call
7+
* Reducing the supply of tokens on this contract, and actually locking tokens in the contract.
8+
* Therefore we recommend to third parties bridges that if they do implement reentrant call of `beforeTransfer` of some reentrant tokens
9+
* do not call any external address in that case
10+
* note User/UI must be aware of the existing/available networks when choosing the destination network
11+
* @param destinationNetwork Network destination
12+
* @param destinationAddress Address destination
13+
* @param amount Amount of tokens
14+
* @param token Token address, 0 address is reserved for ether
15+
* @param forceUpdateGlobalExitRoot Indicates if the new global exit root is updated or not
16+
* @param permitData Raw data of the call `permit` of the token
17+
*/
18+
function bridgeAsset(
19+
uint32 destinationNetwork,
20+
address destinationAddress,
21+
uint256 amount,
22+
address token,
23+
bool forceUpdateGlobalExitRoot,
24+
bytes calldata permitData
25+
) public payable virtual ifNotEmergencyState nonReentrant {
26+
```
27+
28+
The source of this method is [here](https://github.com/0xPolygonHermez/zkevm-contracts/blob/c8659e6282340de7bdb8fdbf7924a9bd2996bc98/contracts/v2/PolygonZkEVMBridgeV2.sol#L198-L219).
29+
Below is an example of how we would make simple bridge of native ETH from Sepolia (L1) into Cardona (L2).
30+
31+
```bash
32+
polycli ulxly bridge asset \
33+
--bridge-address 0x528e26b25a34a4A5d0dbDa1d57D318153d2ED582 \
34+
--private-key 0x32430699cd4f46ab2422f1df4ad6546811be20c9725544e99253a887e971f92b \
35+
--destination-network 1 \
36+
--value 10000000000000000 \
37+
--rpc-url https://sepolia.drpc.org
38+
```
39+
40+
[This](https://sepolia.etherscan.io/tx/0xf57b8171b2f62dce3eedbe3e50d5ee8413d61438af64286b5017ed9d5d154816) is the transaction that was created and mined from running this command.
41+
42+
Here is another example that will bridge a [test ERC20 token](https://sepolia.etherscan.io/address/0xC92AeF5873d058a76685140F3328B0DED79733Af) from Sepolia (L1) into Cardona (L2). In order for this to work, the token would need to have an [approval](https://sepolia.etherscan.io/tx/0x028513b13a2a7899de4db56e60d1dad66c7b7e29f91c54f385fdfdfc8f14b8b4#eventlog) for the bridge to spend tokens for that particular user.
43+
44+
```bash
45+
polycli ulxly bridge asset \
46+
--bridge-address 0x528e26b25a34a4A5d0dbDa1d57D318153d2ED582 \
47+
--private-key 0x32430699cd4f46ab2422f1df4ad6546811be20c9725544e99253a887e971f92b \
48+
--destination-network 1 \
49+
--value 10000000000000000 \
50+
--token-address 0xC92AeF5873d058a76685140F3328B0DED79733Af \
51+
--destination-address 0x3878Cff9d621064d393EEF92bF1e12A944c5ba84 \
52+
--rpc-url https://sepolia.drpc.org
53+
```
54+
55+
[This](https://sepolia.etherscan.io/tx/0x8ed1c2c0f2e994c86867f401c86fea3c709a28a18629d473cf683049f176fa93) is the transaction that was created and mined from running this command.
56+
57+
Assuming you have funds on L2, a bridge from L2 to L1 looks pretty much the same.
58+
The command below will bridge `123456` of the native ETH on Cardona (L2) back to network 0 which corresponds to Sepolia (L1).
59+
60+
```bash
61+
polycli ulxly bridge asset \
62+
--bridge-address 0x528e26b25a34a4A5d0dbDa1d57D318153d2ED582 \
63+
--private-key 0x32430699cd4f46ab2422f1df4ad6546811be20c9725544e99253a887e971f92b \
64+
--destination-network 0 \
65+
--value 123456 \
66+
--destination-address 0x3878Cff9d621064d393EEF92bF1e12A944c5ba84 \
67+
--rpc-url https://rpc.cardona.zkevm-rpc.com
68+
```
69+
70+
[This](https://cardona-zkevm.polygonscan.com/tx/0x0294dae3cfb26881e5dde9f182531aa5be0818956d029d50e9872543f020df2e) is the transaction that was created and mined from running this command.

cmd/ulxly/BridgeMessageUsage.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
This command is very similar to `polycli ulxly bridge asset`, but instead this is a more generic interface that can be used to transfer ETH and make a contract call. This is the underlying solidity interface that we're referencing.
2+
3+
```solidity
4+
/**
5+
* @notice Bridge message and send ETH value
6+
* note User/UI must be aware of the existing/available networks when choosing the destination network
7+
* @param destinationNetwork Network destination
8+
* @param destinationAddress Address destination
9+
* @param forceUpdateGlobalExitRoot Indicates if the new global exit root is updated or not
10+
* @param metadata Message metadata
11+
*/
12+
function bridgeMessage(
13+
uint32 destinationNetwork,
14+
address destinationAddress,
15+
bool forceUpdateGlobalExitRoot,
16+
bytes calldata metadata
17+
) external payable ifNotEmergencyState {
18+
```
19+
20+
The source code for this particular method is [here](https://github.com/0xPolygonHermez/zkevm-contracts/blob/c8659e6282340de7bdb8fdbf7924a9bd2996bc98/contracts/v2/PolygonZkEVMBridgeV2.sol#L324-L337).
21+
22+
Below is a simple example of using this command to bridge a small amount of ETH from Sepolia (L1) to Cardona (L2). In this case, we're not including any call data, so it's essentially equivalent to a `bridge asset` call, but the deposit will not be automatically claimed on L2.
23+
24+
```bash
25+
polycli ulxly bridge message \
26+
--bridge-address 0x528e26b25a34a4A5d0dbDa1d57D318153d2ED582 \
27+
--private-key 0x32430699cd4f46ab2422f1df4ad6546811be20c9725544e99253a887e971f92b \
28+
--destination-network 1 \
29+
--value 10000000000000000 \
30+
--rpc-url https://sepolia.drpc.org
31+
```
32+
33+
[This](https://sepolia.etherscan.io/tx/0x1a6e2be69fa65e866889d95403b2fe820f08b6a07b96c6afbde646b8092addb2) is the transaction that was generated and mined from this command.
34+
35+
In most cases, you'll want to specify some `call-data` and a `destination-address` in order for a contract to be called on the destination chain. For example:
36+
```bash
37+
polycli ulxly bridge message \
38+
--bridge-address 0x528e26b25a34a4A5d0dbDa1d57D318153d2ED582 \
39+
--private-key 0x32430699cd4f46ab2422f1df4ad6546811be20c9725544e99253a887e971f92b \
40+
--destination-network 1 \
41+
--destination-address 0xC92AeF5873d058a76685140F3328B0DED79733Af \
42+
--call-data 0x40c10f190000000000000000000000003878cff9d621064d393eef92bf1e12a944c5ba84000000000000000000000000000000000000000000000000002386f26fc10000 \
43+
--value 0 \
44+
--rpc-url https://sepolia.drpc.org
45+
```
46+
[This](https://sepolia.etherscan.io/tx/0x517b9d827a3a81770d608a6b997e230d992e1e0cabc0fd2797285693b1cc6a9f) is the transaction that was created and mined from running the above command.
47+
48+
In this case, I've configured the destination address to be a test contract I've deployed on L2.
49+
```soldity
50+
// SPDX-License-Identifier: AGPL-3.0
51+
pragma solidity 0.8.20;
52+
53+
contract MessageEmitter {
54+
event MessageReceived (address originAddress, uint32 originNetwork, bytes data);
55+
56+
function onMessageReceived(address originAddress, uint32 originNetwork, bytes memory data) external payable {
57+
emit MessageReceived(originAddress, originNetwork, data);
58+
}
59+
}
60+
```
61+
62+
The idea is to have minimal contract that will meet the expected interface of the bridge contract: https://github.com/0xPolygonHermez/zkevm-contracts/blob/v9.0.0-rc.3-pp/contracts/interfaces/IBridgeMessageReceiver.sol
63+
64+
In this case, I didn't bother implementing the proxy to an ERC20 or extending some ERC20 contract. I'm just emitting an event to know that the transaction actually fired as expected.
65+
The calldata comes from running this command `cast calldata 'mint(address account, uint256 amount)' 0x3878Cff9d621064d393EEF92bF1e12A944c5ba84 10000000000000000`. Again, in this case no ERC20 will be minted because I didn't set it up.
66+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
This command is not used very often but can be used on L2 networks that have a gas token.
2+
3+
```solidity
4+
/**
5+
* @notice Bridge message and send ETH value
6+
* note User/UI must be aware of the existing/available networks when choosing the destination network
7+
* @param destinationNetwork Network destination
8+
* @param destinationAddress Address destination
9+
* @param amountWETH Amount of WETH tokens
10+
* @param forceUpdateGlobalExitRoot Indicates if the new global exit root is updated or not
11+
* @param metadata Message metadata
12+
*/
13+
function bridgeMessageWETH(
14+
uint32 destinationNetwork,
15+
address destinationAddress,
16+
uint256 amountWETH,
17+
bool forceUpdateGlobalExitRoot,
18+
bytes calldata metadata
19+
) external ifNotEmergencyState {
20+
```
21+
[Here](https://github.com/0xPolygonHermez/zkevm-contracts/blob/c8659e6282340de7bdb8fdbf7924a9bd2996bc98/contracts/v2/PolygonZkEVMBridgeV2.sol#L352-L367) is the source code that corresponds to this interface.
22+
23+
Assuming the network is configured with a gas token, you could call this method like this:
24+
25+
```bash
26+
polycli ulxly bridge weth \
27+
--bridge-address 0x528e26b25a34a4A5d0dbDa1d57D318153d2ED582 \
28+
--destination-address 0x3878Cff9d621064d393EEF92bF1e12A944c5ba84 \
29+
--private-key 0x32430699cd4f46ab2422f1df4ad6546811be20c9725544e99253a887e971f92b \
30+
--value 123456 \
31+
--destination-network 1 \
32+
--rpc-url http://l2-rpc-url.invalid \
33+
--token-address $WETH_ADDRESS
34+
```
35+

cmd/ulxly/ClaimAssetUsage.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
This command will connect to the bridge service, generate a proof, and then attempt to claim the deposit on which never network is referred to in the `--rpc-url` argument.
2+
This is the corresponding interface in the bridge contract:
3+
4+
```solidity
5+
/**
6+
* @notice Verify merkle proof and withdraw tokens/ether
7+
* @param smtProofLocalExitRoot Smt proof to proof the leaf against the network exit root
8+
* @param smtProofRollupExitRoot Smt proof to proof the rollupLocalExitRoot against the rollups exit root
9+
* @param globalIndex Global index is defined as:
10+
* | 191 bits | 1 bit | 32 bits | 32 bits |
11+
* | 0 | mainnetFlag | rollupIndex | localRootIndex |
12+
* note that only the rollup index will be used only in case the mainnet flag is 0
13+
* note that global index do not assert the unused bits to 0.
14+
* This means that when synching the events, the globalIndex must be decoded the same way that in the Smart contract
15+
* to avoid possible synch attacks
16+
* @param mainnetExitRoot Mainnet exit root
17+
* @param rollupExitRoot Rollup exit root
18+
* @param originNetwork Origin network
19+
* @param originTokenAddress Origin token address, 0 address is reserved for gas token address. If WETH address is zero, means this gas token is ether, else means is a custom erc20 gas token
20+
* @param destinationNetwork Network destination
21+
* @param destinationAddress Address destination
22+
* @param amount Amount of tokens
23+
* @param metadata Abi encoded metadata if any, empty otherwise
24+
*/
25+
function claimAsset(
26+
bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] calldata smtProofLocalExitRoot,
27+
bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] calldata smtProofRollupExitRoot,
28+
uint256 globalIndex,
29+
bytes32 mainnetExitRoot,
30+
bytes32 rollupExitRoot,
31+
uint32 originNetwork,
32+
address originTokenAddress,
33+
uint32 destinationNetwork,
34+
address destinationAddress,
35+
uint256 amount,
36+
bytes calldata metadata
37+
) external ifNotEmergencyState {
38+
```
39+
40+
[Here](https://github.com/0xPolygonHermez/zkevm-contracts/blob/c8659e6282340de7bdb8fdbf7924a9bd2996bc98/contracts/v2/PolygonZkEVMBridgeV2.sol#L433-L465) is a direct link to the source code as well.
41+
42+
In order to claim an asset or a message, you need to know deposit count. Usually this is in the event data of the transaction. Alternatively, you can usually directly attempt to see the pending deposits by querying the bridge API directly. In the case of Cardona, the bridge service is running here: https://bridge-api.cardona.zkevm-rpc.com
43+
44+
```bash
45+
curl -s https://bridge-api.cardona.zkevm-rpc.com/bridges/0x3878Cff9d621064d393EEF92bF1e12A944c5ba84 | jq '.'
46+
```
47+
48+
In the output of the above command, I can see a deposit that looks like this:
49+
```json
50+
{
51+
"leaf_type": 0,
52+
"orig_net": 0,
53+
"orig_addr": "0x0000000000000000000000000000000000000000",
54+
"amount": "123456",
55+
"dest_net": 0,
56+
"dest_addr": "0x3878Cff9d621064d393EEF92bF1e12A944c5ba84",
57+
"block_num": "9695587",
58+
"deposit_cnt": 9075,
59+
"network_id": 1,
60+
"tx_hash": "0x0294dae3cfb26881e5dde9f182531aa5be0818956d029d50e9872543f020df2e",
61+
"claim_tx_hash": "",
62+
"metadata": "0x",
63+
"ready_for_claim": true,
64+
"global_index": "9075"
65+
}
66+
```
67+
68+
If we want to claim this deposit, we can use a command like this:
69+
70+
```bash
71+
polycli ulxly claim asset \
72+
--bridge-address 0x528e26b25a34a4A5d0dbDa1d57D318153d2ED582 \
73+
--bridge-service-url https://bridge-api.cardona.zkevm-rpc.com \
74+
--private-key 0x32430699cd4f46ab2422f1df4ad6546811be20c9725544e99253a887e971f92b \
75+
--deposit-network 1 \
76+
--deposit-count 9075 \
77+
--rpc-url https://sepolia.drpc.org
78+
```
79+
80+
[Here](https://sepolia.etherscan.io/tx/0x21fee6e47a3b6733034fb963b20fe7accb0fb168257450f8f0053d6af8e4bc76) is the transaction that was created and mined based on this command.

cmd/ulxly/ClaimMessageUsage.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
This command is used to claim a message type deposit. Here is the interface of the method that's being used:
2+
3+
```solidity
4+
/**
5+
* @notice Verify merkle proof and execute message
6+
* If the receiving address is an EOA, the call will result as a success
7+
* Which means that the amount of ether will be transferred correctly, but the message
8+
* will not trigger any execution
9+
* @param smtProofLocalExitRoot Smt proof to proof the leaf against the exit root
10+
* @param smtProofRollupExitRoot Smt proof to proof the rollupLocalExitRoot against the rollups exit root
11+
* @param globalIndex Global index is defined as:
12+
* | 191 bits | 1 bit | 32 bits | 32 bits |
13+
* | 0 | mainnetFlag | rollupIndex | localRootIndex |
14+
* note that only the rollup index will be used only in case the mainnet flag is 0
15+
* note that global index do not assert the unused bits to 0.
16+
* This means that when synching the events, the globalIndex must be decoded the same way that in the Smart contract
17+
* to avoid possible synch attacks
18+
* @param mainnetExitRoot Mainnet exit root
19+
* @param rollupExitRoot Rollup exit root
20+
* @param originNetwork Origin network
21+
* @param originAddress Origin address
22+
* @param destinationNetwork Network destination
23+
* @param destinationAddress Address destination
24+
* @param amount message value
25+
* @param metadata Abi encoded metadata if any, empty otherwise
26+
*/
27+
function claimMessage(
28+
bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] calldata smtProofLocalExitRoot,
29+
bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] calldata smtProofRollupExitRoot,
30+
uint256 globalIndex,
31+
bytes32 mainnetExitRoot,
32+
bytes32 rollupExitRoot,
33+
uint32 originNetwork,
34+
address originAddress,
35+
uint32 destinationNetwork,
36+
address destinationAddress,
37+
uint256 amount,
38+
bytes calldata metadata
39+
) external ifNotEmergencyState {
40+
```
41+
42+
[Here](https://github.com/0xPolygonHermez/zkevm-contracts/blob/c8659e6282340de7bdb8fdbf7924a9bd2996bc98/contracts/v2/PolygonZkEVMBridgeV2.sol#L588-L623) is a link to the source code.
43+
44+
This command is essentially identical to `claim asset`, but it's specific to deposits that are of the message leaf type rather than assets. In order to use this command, I'm going to try to claim one of the messages that I sent while testing `polycli ulxly bridge message`.
45+
46+
```bash
47+
curl -s https://bridge-api.cardona.zkevm-rpc.com/bridges/0xC92AeF5873d058a76685140F3328B0DED79733Af | jq '.'
48+
```
49+
50+
This will show me the deposits that are destined for the test contract that I deployed on L2. At the moment here is the deposit I'm interested in:
51+
52+
```json
53+
{
54+
"leaf_type": 1,
55+
"orig_net": 0,
56+
"orig_addr": "0x3878Cff9d621064d393EEF92bF1e12A944c5ba84",
57+
"amount": "0",
58+
"dest_net": 1,
59+
"dest_addr": "0xC92AeF5873d058a76685140F3328B0DED79733Af",
60+
"block_num": "7435415",
61+
"deposit_cnt": 67305,
62+
"network_id": 0,
63+
"tx_hash": "0x517b9d827a3a81770d608a6b997e230d992e1e0cabc0fd2797285693b1cc6a9f",
64+
"claim_tx_hash": "",
65+
"metadata": "0x40c10f190000000000000000000000003878cff9d621064d393eef92bf1e12a944c5ba84000000000000000000000000000000000000000000000000002386f26fc10000",
66+
"ready_for_claim": true,
67+
"global_index": "18446744073709618921"
68+
}
69+
```
70+
71+
I'm going to use this command to try to claim this message on L2.
72+
73+
```bash
74+
polycli ulxly claim message \
75+
--bridge-address 0x528e26b25a34a4A5d0dbDa1d57D318153d2ED582 \
76+
--bridge-service-url https://bridge-api.cardona.zkevm-rpc.com \
77+
--private-key 0x32430699cd4f46ab2422f1df4ad6546811be20c9725544e99253a887e971f92b \
78+
--destination-address 0xC92AeF5873d058a76685140F3328B0DED79733Af \
79+
--deposit-network 0 \
80+
--deposit-count 67305 \
81+
--rpc-url https://rpc.cardona.zkevm-rpc.com
82+
```
83+
84+
[Here](https://cardona-zkevm.polygonscan.com/tx/0x6df4c4e43776d703bf1996334a4e1975bb3c124192563c93e3d199d9240dd56f#eventlog) is the transaction that was generated by this command. Everything looks to have worked properly. The `MessageReceived(address,uint32,bytes)` event with signature `0xe97c9b3f13b44bc13bde4743ae654dff72f8dc2ff9ff6070efc5999f77a37716` showed up in the explorer so our contract fired properly when the claim was made.

0 commit comments

Comments
 (0)