Skip to content

Commit db66643

Browse files
authored
add auto approve to ulxly bridge asset when bridging erc20 (#621)
* add auto approve to ulxly bridge asset when bridging erc20 * split err check * add missing slashes to example command
1 parent d86ee94 commit db66643

File tree

2 files changed

+205
-2
lines changed

2 files changed

+205
-2
lines changed

cmd/ulxly/ulxly.go

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
ethclient "github.com/ethereum/go-ethereum/ethclient"
3131
ethrpc "github.com/ethereum/go-ethereum/rpc"
3232

33+
"github.com/0xPolygon/polygon-cli/bindings/tokens"
3334
"github.com/0xPolygon/polygon-cli/bindings/ulxly"
3435
"github.com/0xPolygon/polygon-cli/bindings/ulxly/polygonrollupmanager"
3536
"github.com/0xPolygon/polygon-cli/cmd/flag_loader"
@@ -619,7 +620,7 @@ func logAndReturnJsonError(cmd *cobra.Command, client *ethclient.Client, tx *typ
619620
}
620621

621622
func bridgeAsset(cmd *cobra.Command) error {
622-
bridgeAddress := *inputUlxlyArgs.bridgeAddress
623+
bridgeAddr := *inputUlxlyArgs.bridgeAddress
623624
privateKey := *inputUlxlyArgs.privateKey
624625
gasLimit := *inputUlxlyArgs.gasLimit
625626
destinationAddress := *inputUlxlyArgs.destAddress
@@ -641,18 +642,52 @@ func bridgeAsset(cmd *cobra.Command) error {
641642
defer client.Close()
642643

643644
// Initialize and assign variables required to send transaction payload
644-
bridgeV2, toAddress, auth, err := generateTransactionPayload(cmd.Context(), client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID)
645+
bridgeV2, toAddress, auth, err := generateTransactionPayload(cmd.Context(), client, bridgeAddr, privateKey, gasLimit, destinationAddress, chainID)
645646
if err != nil {
646647
log.Error().Err(err).Msg("error generating transaction payload")
647648
return err
648649
}
649650

651+
bridgeAddress := common.HexToAddress(bridgeAddr)
650652
value, _ := big.NewInt(0).SetString(amount, 0)
651653
tokenAddress := common.HexToAddress(tokenAddr)
652654
callData := common.Hex2Bytes(strings.TrimPrefix(callDataString, "0x"))
653655

654656
if tokenAddress == common.HexToAddress("0x0000000000000000000000000000000000000000") {
655657
auth.Value = value
658+
} else {
659+
// in case it's a token transfer, we need to ensure that the bridge contract
660+
// has enough allowance to transfer the tokens on behalf of the user
661+
tokenContract, iErr := tokens.NewERC20(tokenAddress, client)
662+
if iErr != nil {
663+
log.Error().Err(iErr).Msg("error getting token contract")
664+
return iErr
665+
}
666+
667+
allowance, iErr := tokenContract.Allowance(&bind.CallOpts{Pending: false}, auth.From, bridgeAddress)
668+
if iErr != nil {
669+
log.Error().Err(iErr).Msg("error getting token allowance")
670+
return iErr
671+
}
672+
673+
if allowance.Cmp(value) < 0 {
674+
log.Info().
675+
Str("amount", value.String()).
676+
Str("tokenAddress", tokenAddress.String()).
677+
Str("bridgeAddress", bridgeAddress.String()).
678+
Str("userAddress", auth.From.String()).
679+
Msg("approving bridge contract to spend tokens on behalf of user")
680+
681+
// Approve the bridge contract to spend the tokens on behalf of the user
682+
approveTxn, iErr := tokenContract.Approve(auth, bridgeAddress, value)
683+
if iErr = logAndReturnJsonError(cmd, client, approveTxn, auth, iErr); iErr != nil {
684+
return iErr
685+
}
686+
log.Info().Msg("approveTxn: " + approveTxn.Hash().String())
687+
if iErr = WaitMineTransaction(cmd.Context(), client, approveTxn, timeoutTxnReceipt); iErr != nil {
688+
return iErr
689+
}
690+
}
656691
}
657692

658693
bridgeTxn, err := bridgeV2.BridgeAsset(auth, destinationNetwork, toAddress, value, tokenAddress, isForced, callData)

doc/bridge-asset-guide.md

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# Bridging assets using polycli
2+
3+
## Requirements
4+
5+
The instructions in this doc assume you have `Foundry` installed.
6+
It's also required to have a funded account in the network that originally has the funds.
7+
8+
## General setup
9+
10+
All the steps below will require you to interact with the RPC and also to provide an account.
11+
12+
Lets prepare some environment variables to use along the steps.
13+
14+
```bash
15+
export rpc_url_l1 = <RPC_URL>
16+
export acc_private_key_l1 = <0xPRIVATE_KEY>
17+
export acc_addr_l1 = $(cast wallet address --private-key $acc_private_key_l1)
18+
```
19+
> replace the placeholders by the correct value
20+
21+
---
22+
23+
## ERC20
24+
25+
To bridge ERC20 tokens we first need to have a ERC20 token deployed and funds to the account that
26+
will bridge them to the other network.
27+
28+
In case you don't have the token contract deployed, the following steps will help you deploying one.
29+
30+
Firstly, lets use forge to create all the resources needed for the token contract deploy.
31+
32+
```bash
33+
forge init erc20-example
34+
cd erc20-example
35+
```
36+
37+
Inside of the newly created directory `erc20-example` let's clean-up some unnecessary files and
38+
folders
39+
40+
```bash
41+
rm -Rf ./script
42+
rm -Rf ./test
43+
rm ./src/Counter.sol
44+
```
45+
46+
Lets create our token contract file
47+
48+
```bash
49+
touch ./src/MyToken.sol
50+
```
51+
52+
Then add this code to `./src/MyToken.sol`
53+
54+
```solidity
55+
// SPDX-License-Identifier: MIT
56+
pragma solidity ^0.8.20;
57+
58+
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
59+
import "@openzeppelin/contracts/access/Ownable.sol";
60+
61+
contract MyToken is ERC20, Ownable {
62+
constructor(
63+
string memory name,
64+
string memory symbol,
65+
address initialOwner
66+
) ERC20(name, symbol) Ownable(initialOwner) {}
67+
68+
function mint(address to, uint256 amount) public onlyOwner {
69+
_mint(to, amount);
70+
}
71+
}
72+
```
73+
74+
Next step is to use forge to install our contract dependencies
75+
76+
```bash
77+
forge install OpenZeppelin/openzeppelin-contracts
78+
```
79+
80+
Then build the contract
81+
82+
```bash
83+
forge build
84+
```
85+
86+
We are done for now, we have an ERC20 contract ready to be deployed to a network in the next steps.
87+
88+
### L1 to L2
89+
90+
Well, to bridge an ERC20 asset from L1 to L2, we first need to deploy ou ERC20 contract to the
91+
network.
92+
93+
```bash
94+
forge create --broadcast \
95+
--rpc-url $rpc_url_l1 \
96+
--private-key $acc_private_key_l1 \
97+
src/MyToken.sol:MyToken \
98+
--constructor-args "MyToken" "MTK" $acc_addr_l1
99+
```
100+
101+
The response to the command above should be something like this:
102+
103+
```log
104+
Deployer: 0x...
105+
Deployed to: 0x...
106+
Transaction hash: 0x...
107+
```
108+
109+
Store the value from `Deployed to:` to be used further, this is the address of our token contract
110+
deployed in the network. Lets add it to a env var, so we can use it later easily.
111+
112+
```bash
113+
export token_address = <DEPLOYED_TO>
114+
```
115+
116+
The first thing we need to do after the contract deployment, it to mint some funds to the account
117+
that will be used to bridge the funds to the other network. For convenience and to make it simples,
118+
we will use the same account that we used to deploy the SC.
119+
120+
```bash
121+
cast send $token_address \
122+
"mint(address,uint256)" $acc_addr_l1 100000000000000000000 \
123+
--rpc-url $rpc_url_l1 \
124+
--private-key $acc_private_key_l1
125+
```
126+
127+
We can check if the mint worked as expected, by checking the balance for the account we minted the
128+
funds
129+
130+
```bash
131+
cast call $token_address \
132+
"balanceOf(address)(uint256)" $acc_addr_l1 \
133+
--rpc-url $rpc_url_l1
134+
```
135+
136+
Now we are ready to bridge the funds from L1 to L2.
137+
138+
Next step is to use polycli to bridge the asset from L1 to L2. Some information from the network is
139+
required to perform this action, we need:
140+
141+
- `bridge-address`: this is the address of the bridge smart contract that is deployed to the network
142+
that has the funds, in this case is the address of the bridge contract in the L1 network. if you are
143+
running a local kurtosis-cdk environment, you can find this address in the JSON logged when the env
144+
starts in the field `"polygonZkEVML2BridgeAddress"`.
145+
- `destination-network`: this is the ID of the network that will receive the asset, in this case the ID
146+
must correspond to the L2
147+
- `value`: the amount of tokens we want to move from L1 to L2
148+
- `token-address`: the address of the token contract, in this case the one we just created
149+
- `destination-address`: the address of the account that will receive the tokens in the L2 network.
150+
151+
more details can be found running the following help command
152+
153+
```bash
154+
polycli ulxly bridge asset --help
155+
```
156+
157+
and here is the command that will bridge the assets from l1 to l2
158+
159+
```bash
160+
polycli ulxly bridge asset \
161+
--bridge-address <BRIDGE_ADDRESS> \
162+
--rpc-url $rpc_url \
163+
--private-key $private_key \
164+
--destination-network 1 \
165+
--value 10000000000000000 \
166+
--token-address $token_address \
167+
--destination-address
168+
```

0 commit comments

Comments
 (0)