From adb8e80b27851c085a9503849267b597feb8c33e Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Wed, 4 Dec 2024 13:51:27 -0500 Subject: [PATCH 01/17] feat: copying for alonso --- cmd/ulxly/BridgeAssetUsage.md | 61 ++ cmd/ulxly/BridgeMessageUsage.md | 53 ++ cmd/ulxly/BridgeWETHMessageUsage.md | 55 ++ cmd/ulxly/ClaimAssetUsage.md | 76 ++ cmd/ulxly/ClaimMessageUsage.md | 57 ++ cmd/ulxly/delete.md | 70 ++ cmd/ulxly/ulxly.go | 1141 +++++++++++++++++---------- go.mod | 4 + go.sum | 6 + 9 files changed, 1085 insertions(+), 438 deletions(-) create mode 100644 cmd/ulxly/BridgeAssetUsage.md create mode 100644 cmd/ulxly/BridgeMessageUsage.md create mode 100644 cmd/ulxly/BridgeWETHMessageUsage.md create mode 100644 cmd/ulxly/ClaimAssetUsage.md create mode 100644 cmd/ulxly/ClaimMessageUsage.md create mode 100644 cmd/ulxly/delete.md diff --git a/cmd/ulxly/BridgeAssetUsage.md b/cmd/ulxly/BridgeAssetUsage.md new file mode 100644 index 00000000..eac962d7 --- /dev/null +++ b/cmd/ulxly/BridgeAssetUsage.md @@ -0,0 +1,61 @@ +This command will attempt to send a deposit transaction to the bridge contract. + +```solidity + /** + * @notice Deposit add a new leaf to the merkle tree + * note If this function is called with a reentrant token, it would be possible to `claimTokens` in the same call + * Reducing the supply of tokens on this contract, and actually locking tokens in the contract. + * Therefore we recommend to third parties bridges that if they do implement reentrant call of `beforeTransfer` of some reentrant tokens + * do not call any external address in that case + * note User/UI must be aware of the existing/available networks when choosing the destination network + * @param destinationNetwork Network destination + * @param destinationAddress Address destination + * @param amount Amount of tokens + * @param token Token address, 0 address is reserved for ether + * @param forceUpdateGlobalExitRoot Indicates if the new global exit root is updated or not + * @param permitData Raw data of the call `permit` of the token + */ + function bridgeAsset( + uint32 destinationNetwork, + address destinationAddress, + uint256 amount, + address token, + bool forceUpdateGlobalExitRoot, + bytes calldata permitData + ); + +``` + +Each transaction will require manual input of parameters. Example usage: + +```bash +polycli ulxly bridge-asset \ + --private-key 12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625 \ + --gas-limit 300000 \ + --amount 1000000000000000000 \ + --rpc-url http://127.0.0.1:8545 \ + --bridge-address 0xD71f8F956AD979Cc2988381B8A743a2fE280537D \ + --destination-network 1 \ + --destination-address 0xE34aaF64b29273B7D567FCFc40544c014EEe9970 +``` + +This command would use the supplied private key and attempt to send a deposit transaction to the bridge contract address with the input flags. +Successful deposit transaction will output logs like below: + +```bash +Deposit Transaction Successful: 0x8c9b82e8abdfb4aad5fccd91879397acfa73e4261282c8dc634734d05ad889d3 +``` + +Upon successful deposit, the transaction can be queried using `polycli ulxly deposit-get` command + + +Failed deposit transactions will output logs like below: + +```bash +Deposit Transaction Failed: 0x60385209b0e9db359c24c88c2fb8a5c9e4628fffe8d5fb2b5e64dfac3a2b7639 +Try increasing the gas limit: +Current gas limit: 100000 +Cumulative gas used for transaction: 98641 +``` + +The reason for failing may likely be due to the `out of gas` error. Increasing the `--gas-limit` flag value will likely resolve this. diff --git a/cmd/ulxly/BridgeMessageUsage.md b/cmd/ulxly/BridgeMessageUsage.md new file mode 100644 index 00000000..77aa8dce --- /dev/null +++ b/cmd/ulxly/BridgeMessageUsage.md @@ -0,0 +1,53 @@ +This command will attempt to send a deposit transaction to the bridge contract. + +```solidity + /** + * @notice Bridge message and send ETH value + * note User/UI must be aware of the existing/available networks when choosing the destination network + * @param destinationNetwork Network destination + * @param destinationAddress Address destination + * @param forceUpdateGlobalExitRoot Indicates if the new global exit root is updated or not + * @param metadata Message metadata + */ + function bridgeMessage( + uint32 destinationNetwork, + address destinationAddress, + bool forceUpdateGlobalExitRoot, + bytes calldata metadata + ); +``` + +Each transaction will require manual input of parameters. Example usage: + +```bash +polycli ulxly bridge-message \ + --private-key 12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625 \ + --gas-limit 300000 \ + --amount 1000000000000000000 \ + --rpc-url http://127.0.0.1:8545 \ + --bridge-address 0xD71f8F956AD979Cc2988381B8A743a2fE280537D \ + --destination-network 1 \ + --destination-address 0xE34aaF64b29273B7D567FCFc40544c014EEe9970 + --call-data 0x001010109200090028979743971976836486868648629808961824738090896826764980866fac97863898ca08928fc7279643 +``` + +This command would use the supplied private key and attempt to send a deposit transaction to the bridge contract address with the input flags. +Successful deposit transaction will output logs like below: + +```bash +Deposit Transaction Successful: 0x8c9b82e8abdfb4aad5fccd91879397acfa73e4261282c8dc634734d05ad889d3 +``` + +Upon successful deposit, the transaction can be queried using `polycli ulxly deposit-get` command + + +Failed deposit transactions will output logs like below: + +```bash +Deposit Transaction Failed: 0x60385209b0e9db359c24c88c2fb8a5c9e4628fffe8d5fb2b5e64dfac3a2b7639 +Try increasing the gas limit: +Current gas limit: 100000 +Cumulative gas used for transaction: 98641 +``` + +The reason for failing may likely be due to the `out of gas` error. Increasing the `--gas-limit` flag value will likely resolve this. diff --git a/cmd/ulxly/BridgeWETHMessageUsage.md b/cmd/ulxly/BridgeWETHMessageUsage.md new file mode 100644 index 00000000..5bc7d809 --- /dev/null +++ b/cmd/ulxly/BridgeWETHMessageUsage.md @@ -0,0 +1,55 @@ +This command will attempt to send a deposit transaction to the bridge contract. + +```solidity + /** + * @notice Bridge message and send ETH value + * note User/UI must be aware of the existing/available networks when choosing the destination network + * @param destinationNetwork Network destination + * @param destinationAddress Address destination + * @param amountWETH Amount of WETH tokens + * @param forceUpdateGlobalExitRoot Indicates if the new global exit root is updated or not + * @param metadata Message metadata + */ + function bridgeMessageWETH( + uint32 destinationNetwork, + address destinationAddress, + uint256 amountWETH, + bool forceUpdateGlobalExitRoot, + bytes calldata metadata + ); +``` + +Each transaction will require manual input of parameters. Example usage: + +```bash +polycli ulxly bridge-message-weth \ + --private-key 12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625 \ + --gas-limit 300000 \ + --amount 1000000000000000000 \ + --rpc-url http://127.0.0.1:8545 \ + --bridge-address 0xD71f8F956AD979Cc2988381B8A743a2fE280537D \ + --destination-network 1 \ + --destination-address 0xE34aaF64b29273B7D567FCFc40544c014EEe9970 + --call-data 0x001010109200090028979743971976836486868648629808961824738090896826764980866fac97863898ca08928fc7279643 +``` + +This command would use the supplied private key and attempt to send a deposit transaction to the bridge contract address with the input flags. +Successful deposit transaction will output logs like below: + +```bash +Deposit Transaction Successful: 0x8c9b82e8abdfb4aad5fccd91879397acfa73e4261282c8dc634734d05ad889d3 +``` + +Upon successful deposit, the transaction can be queried using `polycli ulxly deposit-get` command + + +Failed deposit transactions will output logs like below: + +```bash +Deposit Transaction Failed: 0x60385209b0e9db359c24c88c2fb8a5c9e4628fffe8d5fb2b5e64dfac3a2b7639 +Try increasing the gas limit: +Current gas limit: 100000 +Cumulative gas used for transaction: 98641 +``` + +The reason for failing may likely be due to the `out of gas` error. Increasing the `--gas-limit` flag value will likely resolve this. diff --git a/cmd/ulxly/ClaimAssetUsage.md b/cmd/ulxly/ClaimAssetUsage.md new file mode 100644 index 00000000..9c53af43 --- /dev/null +++ b/cmd/ulxly/ClaimAssetUsage.md @@ -0,0 +1,76 @@ +This command will attempt to send a claim transaction to the bridge contract. + +```solidity + /** + * @notice Verify merkle proof and withdraw tokens/ether + * @param smtProofLocalExitRoot Smt proof to proof the leaf against the network exit root + * @param smtProofRollupExitRoot Smt proof to proof the rollupLocalExitRoot against the rollups exit root + * @param globalIndex Global index is defined as: + * | 191 bits | 1 bit | 32 bits | 32 bits | + * | 0 | mainnetFlag | rollupIndex | localRootIndex | + * note that only the rollup index will be used only in case the mainnet flag is 0 + * note that global index do not assert the unused bits to 0. + * This means that when synching the events, the globalIndex must be decoded the same way that in the Smart contract + * to avoid possible synch attacks + * @param mainnetExitRoot Mainnet exit root + * @param rollupExitRoot Rollup exit root + * @param originNetwork Origin network + * @param originTokenAddress Origin token address, 0 address is reserved for ether + * @param destinationNetwork Network destination + * @param destinationAddress Address destination + * @param amount Amount of tokens + * @param metadata Abi encoded metadata if any, empty otherwise + */ + function claimAsset( + bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] calldata smtProofLocalExitRoot, + bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] calldata smtProofRollupExitRoot, + uint256 globalIndex, + bytes32 mainnetExitRoot, + bytes32 rollupExitRoot, + uint32 originNetwork, + address originTokenAddress, + uint32 destinationNetwork, + address destinationAddress, + uint256 amount, + bytes calldata metadata + ); + +``` + +Each transaction will require manual input of parameters. Example usage: + +```bash +polycli ulxly deposit-claim \ + --bridge-address 0xD71f8F956AD979Cc2988381B8A743a2fE280537D \ + --private-key 12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625 \ + --claim-index 0 \ + --claim-address 0xE34aaF64b29273B7D567FCFc40544c014EEe9970 \ + --claim-network 0 \ + --rpc-url http://127.0.0.1:32790 \ + --bridge-service-url http://127.0.0.1:32804 +``` + +This command would use the supplied private key and attempt to send a claim transaction to the bridge contract address with the input flags. +Successful deposit transaction will output logs like below: + +```bash +Claim Transaction Successful: 0x7180201b19e1aa596503d8541137d6f341e682835bf7a54aab6422c89158866b +``` + +Upon successful claim, the transferred funds can be queried in the destination network using tools like `cast balance --rpc-url ` + + +Failed deposit transactions will output logs like below: + +```bash +Claim Transaction Failed: 0x32ac34797159c79e57ae801c350bccfe5f8105d4dd3b717e31d811397e98036a +``` + +The reason for failing may be very difficult to debug. I have personally spun up a bridge-ui and compared the byte data of a successful transaction to the byte data of a failing claim transaction queried using: + +```! +curl http://127.0.0.1:32790 \ +-X POST \ +-H "Content-Type: application/json" \ +--data '{"method":"debug_traceTransaction","params":["0x32ac34797159c79e57ae801c350bccfe5f8105d4dd3b717e31d811397e98036a", {"tracer": "callTracer"}], "id":1,"jsonrpc":"2.0"}' | jq '.' +``` diff --git a/cmd/ulxly/ClaimMessageUsage.md b/cmd/ulxly/ClaimMessageUsage.md new file mode 100644 index 00000000..a2982319 --- /dev/null +++ b/cmd/ulxly/ClaimMessageUsage.md @@ -0,0 +1,57 @@ +This command will attempt to send a claim transaction to the bridge contract. + +```solidity + /** + * @notice Bridge message and send ETH value + * note User/UI must be aware of the existing/available networks when choosing the destination network + * @param destinationNetwork Network destination + * @param destinationAddress Address destination + * @param forceUpdateGlobalExitRoot Indicates if the new global exit root is updated or not + * @param metadata Message metadata + */ + function bridgeMessage( + uint32 destinationNetwork, + address destinationAddress, + bool forceUpdateGlobalExitRoot, + bytes calldata metadata + ); + +``` + +Each transaction will require manual input of parameters. Example usage: + +```bash +polycli ulxly deposit-claim \ + --bridge-address 0xD71f8F956AD979Cc2988381B8A743a2fE280537D \ + --private-key 12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625 \ + --claim-index 0 \ + --claim-address 0xE34aaF64b29273B7D567FCFc40544c014EEe9970 \ + --claim-network 0 \ + --rpc-url http://127.0.0.1:32790 \ + --bridge-service-url http://127.0.0.1:32804 +``` + +This command would use the supplied private key and attempt to send a claim transaction to the bridge contract address with the input flags. +Successful deposit transaction will output logs like below: + +```bash +Claim Transaction Successful: 0x7180201b19e1aa596503d8541137d6f341e682835bf7a54aab6422c89158866b +``` + +Upon successful claim, the transferred funds can be queried in the destination network using tools like `cast balance --rpc-url ` + + +Failed deposit transactions will output logs like below: + +```bash +Claim Transaction Failed: 0x32ac34797159c79e57ae801c350bccfe5f8105d4dd3b717e31d811397e98036a +``` + +The reason for failing may be very difficult to debug. I have personally spun up a bridge-ui and compared the byte data of a successful transaction to the byte data of a failing claim transaction queried using: + +```! +curl http://127.0.0.1:32790 \ +-X POST \ +-H "Content-Type: application/json" \ +--data '{"method":"debug_traceTransaction","params":["0x32ac34797159c79e57ae801c350bccfe5f8105d4dd3b717e31d811397e98036a", {"tracer": "callTracer"}], "id":1,"jsonrpc":"2.0"}' | jq '.' +``` diff --git a/cmd/ulxly/delete.md b/cmd/ulxly/delete.md new file mode 100644 index 00000000..7c9c96e9 --- /dev/null +++ b/cmd/ulxly/delete.md @@ -0,0 +1,70 @@ +This command will attempt to send a deposit transaction to the bridge contract. + +```solidity + /** + * @notice Deposit add a new leaf to the merkle tree + * note If this function is called with a reentrant token, it would be possible to `claimTokens` in the same call + * Reducing the supply of tokens on this contract, and actually locking tokens in the contract. + * Therefore we recommend to third parties bridges that if they do implement reentrant call of `beforeTransfer` of some reentrant tokens + * do not call any external address in that case + * note User/UI must be aware of the existing/available networks when choosing the destination network + * @param destinationNetwork Network destination + * @param destinationAddress Address destination + * @param amount Amount of tokens + * @param token Token address, 0 address is reserved for ether + * @param forceUpdateGlobalExitRoot Indicates if the new global exit root is updated or not + * @param permitData Raw data of the call `permit` of the token + */ + function bridgeAsset( + uint32 destinationNetwork, + address destinationAddress, + uint256 amount, + address token, + bool forceUpdateGlobalExitRoot, + bytes calldata permitData + ); + +``` + +Each transaction will require manual input of parameters. Example usage: + +```bash +polycli ulxly deposit-new \ + --private-key 12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625 \ + --gas-limit 300000 \ + --amount 1000000000000000000 \ + --rpc-url http://127.0.0.1:8545 \ + --bridge-address 0xD71f8F956AD979Cc2988381B8A743a2fE280537D \ + --destination-network 1 \ + --destination-address 0xE34aaF64b29273B7D567FCFc40544c014EEe9970 +``` +go run ulxly.go ulxly bridge-asset --private-key 846f556e5509c36b5756fa3741009310908d606db18d5916d43e6c7d22e0f5a7 --gas-limit 300000 --rpc-url https://sepolia.infura.io/v3/7c490aa772fa466293ee1ac62761330a --bridge-address 0x1348947e282138d8f377b467f7d9c2eb0f335d1f --destination-network 1 --amount 19 + +go run main.go ulxly bridge-message --private-key 846f556e5509c36b5756fa3741009310908d606db18d5916d43e6c7d22e0f5a7 --gas-limit 300000 --rpc-url https://sepolia.infura.io/v3/7c490aa772fa466293ee1ac62761330a --bridge-address 0x1348947e282138d8f377b467f7d9c2eb0f335d1f --destination-network 1 --amount 19 --call-data 0x18976546789087654356789654345678976543567897654356789765435468797865435468797654354678907654356879765436798765435467986543 + +go run main.go ulxly claim-asset --private-key 846f556e5509c36b5756fa3741009310908d606db18d5916d43e6c7d22e0f5a7 --gas-limit 30000000 --rpc-url https://sepolia.infura.io/v3/7c490aa772fa466293ee1ac62761330a --bridge-address 0x1348947e282138d8f377b467f7d9c2eb0f335d1f --claim-address 0x2536C2745Ac4A584656A830f7bdCd329c94e8F30 --claim-index 7780 --bridge-service-url https://bridge-api.internal.zkevm-rpc.com --deposit-network 1 + +go run main.go ulxly claim-message --private-key 846f556e5509c36b5756fa3741009310908d606db18d5916d43e6c7d +22e0f5a7 --gas-limit 300000 --rpc-url https://sepolia.infura.io/v3/7c490aa772fa466293ee1ac62761330a --bridge-address 0x1348947e282138d8f377b467f7d9c2eb0f335d1f --claim-address 0x2536C2745Ac4A584656A830f7bdCd329c94e8F30 --claim-index 7780 --call-data 0x18976546789087654356789654345678976543567897654356789765435468797865435468797654354678907654356879765436798765435467986543 --bridge-service-url https://bridge-api.internal.zkevm-rpc.com + + +This command would use the supplied private key and attempt to send a deposit transaction to the bridge contract address with the input flags. +Successful deposit transaction will output logs like below: + +```bash +Deposit Transaction Successful: 0x8c9b82e8abdfb4aad5fccd91879397acfa73e4261282c8dc634734d05ad889d3 +``` + +Upon successful deposit, the transaction can be queried using `polycli ulxly deposit-get` command + + +Failed deposit transactions will output logs like below: + +```bash +Deposit Transaction Failed: 0x60385209b0e9db359c24c88c2fb8a5c9e4628fffe8d5fb2b5e64dfac3a2b7639 +Try increasing the gas limit: +Current gas limit: 100000 +Cumulative gas used for transaction: 98641 +``` + +The reason for failing may likely be due to the `out of gas` error. Increasing the `--gas-limit` flag value will likely resolve this. diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index 4a008d4d..7ea04e68 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "context" - "crypto/ecdsa" _ "embed" "encoding/binary" "encoding/json" @@ -14,7 +13,6 @@ import ( "math/big" "net/http" "os" - "strconv" "strings" "time" @@ -29,6 +27,7 @@ import ( "github.com/0xPolygon/polygon-cli/bindings/ulxly" "github.com/rs/zerolog/log" "github.com/spf13/cobra" + "github.com/urfave/cli/v2" ) const ( @@ -53,6 +52,7 @@ type uLxLyArgs struct { BridgeServiceRPCURL *string ClaimRPCURL *string ClaimPrivateKey *string + NetworkID *uint32 ClaimBridgeAddress *string ClaimGasLimit *uint64 ClaimChainID *string @@ -102,371 +102,409 @@ type BridgeProof struct { } `json:"proof"` } -type BridgeDeposits struct { - Deposit []struct { - LeafType int `json:"leaf_type"` - OrigNet int `json:"orig_net"` +type BridgeDeposit struct { + Deposit struct { + LeafType uint8 `json:"leaf_type"` + OrigNet uint32 `json:"orig_net"` OrigAddr string `json:"orig_addr"` Amount string `json:"amount"` - DestNet int `json:"dest_net"` + DestNet uint32 `json:"dest_net"` DestAddr string `json:"dest_addr"` BlockNum string `json:"block_num"` - DepositCnt string `json:"deposit_cnt"` - NetworkID int `json:"network_id"` + DepositCnt uint32 `json:"deposit_cnt"` + NetworkID uint32 `json:"network_id"` TxHash string `json:"tx_hash"` ClaimTxHash string `json:"claim_tx_hash"` Metadata string `json:"metadata"` ReadyForClaim bool `json:"ready_for_claim"` GlobalIndex string `json:"global_index"` - } `json:"deposits"` - TotalCnt string `json:"total_cnt"` + } `json:"deposit"` + Code *int `json:"code"` + Message *string `json:"message"` } -var ulxlyInputArgs uLxLyArgs - var ULxLyCmd = &cobra.Command{ - Use: "ulxly", - Short: "Utilities for interacting with the lxly bridge", - Long: "These are low level tools for directly scanning bridge events and constructing proofs.", - Args: cobra.NoArgs, + Use: "ulxly", + Short: "Utilities for interacting with the lxly bridge", + Long: "These are low level tools for directly scanning bridge events and constructing proofs.", + DisableFlagParsing: true, + Run: initCli, } -//go:embed depositGetUsage.md -var depositGetUsage string -var depositGetCmd = &cobra.Command{ - Use: "deposit-get", - Short: "Get a range of deposits", - Long: depositGetUsage, - Args: cobra.NoArgs, - PreRunE: checkGetDepositArgs, - RunE: func(cmd *cobra.Command, args []string) error { - ctx := cmd.Context() - // Dial the Ethereum RPC server. - rpc, err := ethrpc.DialContext(ctx, *ulxlyInputArgs.RPCURL) - if err != nil { - log.Error().Err(err).Msg("Unable to Dial RPC") - return err +func readDeposit(ctx *cli.Context) error { + bridgeAddress := ctx.String(bridgeAddressFlag.Name) + rpcUrl := ctx.String(rpcURLFlag.Name) + toBlock := ctx.Uint64(toBlockFlag.Name) + fromBlock := ctx.Uint64(fromBlockFlag.Name) + filter := ctx.Uint64(filterFlag.Name) + // Dial the Ethereum RPC server. + rpc, err := ethrpc.DialContext(ctx.Context, rpcUrl) + if err != nil { + log.Error().Err(err).Msg("Unable to Dial RPC") + return err + } + defer rpc.Close() + ec := ethclient.NewClient(rpc) + + bridgeV2, err := ulxly.NewUlxly(common.HexToAddress(bridgeAddress), ec) + if err != nil { + return err + } + currentBlock := fromBlock + for currentBlock < toBlock { + endBlock := currentBlock + filter + if endBlock > toBlock { + endBlock = toBlock } - defer rpc.Close() - ec := ethclient.NewClient(rpc) - bridgeV2, err := ulxly.NewUlxly(common.HexToAddress(*ulxlyInputArgs.BridgeAddress), ec) + opts := bind.FilterOpts{ + Start: currentBlock, + End: &endBlock, + Context: ctx.Context, + } + evtV2Iterator, err := bridgeV2.FilterBridgeEvent(&opts) if err != nil { return err } - fromBlock := *ulxlyInputArgs.FromBlock - toBlock := *ulxlyInputArgs.ToBlock - currentBlock := fromBlock - for currentBlock < toBlock { - endBlock := currentBlock + *ulxlyInputArgs.FilterSize - if endBlock > toBlock { - endBlock = toBlock - } - opts := bind.FilterOpts{ - Start: currentBlock, - End: &endBlock, - Context: ctx, - } - evtV2Iterator, err := bridgeV2.FilterBridgeEvent(&opts) + for evtV2Iterator.Next() { + evt := evtV2Iterator.Event + log.Info().Uint32("deposit", evt.DepositCount).Uint64("block-number", evt.Raw.BlockNumber).Msg("Found ulxly Deposit") + var jBytes []byte + jBytes, err = json.Marshal(evt) if err != nil { return err } - - for evtV2Iterator.Next() { - evt := evtV2Iterator.Event - log.Info().Uint32("deposit", evt.DepositCount).Uint64("block-number", evt.Raw.BlockNumber).Msg("Found ulxly Deposit") - var jBytes []byte - jBytes, err = json.Marshal(evt) - if err != nil { - return err - } - fmt.Println(string(jBytes)) - } - err = evtV2Iterator.Close() - if err != nil { - log.Error().Err(err).Msg("error closing event iterator") - } - currentBlock = endBlock + fmt.Println(string(jBytes)) } - - return nil - }, -} - -//go:embed depositNewUsage.md -var depositNewUsage string -var depositNewCmd = &cobra.Command{ - Use: "deposit-new", - Short: "Make a uLxLy deposit transaction", - Long: depositNewUsage, - Args: cobra.NoArgs, - PreRunE: checkDepositArgs, - RunE: func(cmd *cobra.Command, args []string) error { - ctx := cmd.Context() - // Dial the Ethereum RPC server. - client, err := ethclient.DialContext(ctx, *ulxlyInputArgs.DepositRPCURL) + err = evtV2Iterator.Close() if err != nil { - log.Error().Err(err).Msg("Unable to Dial RPC") - return err + log.Error().Err(err).Msg("error closing event iterator") } - defer client.Close() - // Initialize and assign variables required to send transaction payload - bridgeV2, privateKey, fromAddress, gasLimit, gasPrice, toAddress, signer := generateTransactionPayload(ctx, client, *ulxlyInputArgs.DepositBridgeAddress, *ulxlyInputArgs.DepositPrivateKey, *ulxlyInputArgs.DepositGasLimit, *ulxlyInputArgs.DestinationAddress, *ulxlyInputArgs.DepositChainID) + currentBlock = endBlock + } - value := big.NewInt(*ulxlyInputArgs.Amount) - tokenAddress := common.HexToAddress(*ulxlyInputArgs.TokenAddress) - callData := common.Hex2Bytes(*ulxlyInputArgs.CallData) + return nil +} - tops := &bind.TransactOpts{ - Signer: func(address common.Address, transaction *types.Transaction) (*types.Transaction, error) { - return types.SignTx(transaction, signer, privateKey) - }, - From: fromAddress, - Context: ctx, - GasLimit: gasLimit, - GasPrice: gasPrice, - GasFeeCap: nil, - GasTipCap: nil, - } - if tokenAddress == common.HexToAddress("0x0000000000000000000000000000000000000000") { - tops = &bind.TransactOpts{ - Signer: func(address common.Address, transaction *types.Transaction) (*types.Transaction, error) { - return types.SignTx(transaction, signer, privateKey) - }, - Value: value, - From: fromAddress, - Context: ctx, - GasLimit: gasLimit, - GasPrice: gasPrice, - GasFeeCap: nil, - GasTipCap: nil, - } - } +func proof(ctx *cli.Context) error { + depositNumber := ctx.Uint64(depositNumberFlag.Name) + rawDepositData, err := getInputData(ctx) + if err != nil { + return err + } + return readDeposits(rawDepositData, uint32(depositNumber)) +} - var bridgeTxn *types.Transaction - switch { - case *ulxlyInputArgs.DepositMessage: - bridgeTxn, err = bridgeV2.BridgeMessage(tops, *ulxlyInputArgs.DestinationNetwork, toAddress, *ulxlyInputArgs.IsForced, callData) - if err != nil { - log.Error().Err(err).Msg("Unable to interact with bridge contract") - return err - } - case *ulxlyInputArgs.DepositWETH: - bridgeTxn, err = bridgeV2.BridgeMessageWETH(tops, *ulxlyInputArgs.DestinationNetwork, toAddress, value, *ulxlyInputArgs.IsForced, callData) - if err != nil { - log.Error().Err(err).Msg("Unable to interact with bridge contract") - return err - } - default: - bridgeTxn, err = bridgeV2.BridgeAsset(tops, *ulxlyInputArgs.DestinationNetwork, toAddress, value, tokenAddress, *ulxlyInputArgs.IsForced, callData) - if err != nil { - log.Error().Err(err).Msg("Unable to interact with bridge contract") - return err - } - } +func emptyProof(ctx *cli.Context) error { + p := new(Proof) - // Wait for the transaction to be mined - // TODO: Consider creating a function for this section - txnMinedTimer := time.NewTimer(time.Duration(*ulxlyInputArgs.DepositTimeoutTxnReceipt) * time.Second) - defer txnMinedTimer.Stop() - for { - select { - case <-txnMinedTimer.C: - log.Info().Msg("Wait timer for transaction receipt exceeded!") - return nil - default: - r, err := client.TransactionReceipt(ctx, bridgeTxn.Hash()) - if err != nil { - if err.Error() != "not found" { - log.Error().Err(err) - return err - } - time.Sleep(1 * time.Second) - continue - } - if r.Status != 0 { - log.Info().Interface("txHash", r.TxHash).Msg("Deposit transaction successful") - return nil - } else if r.Status == 0 { - log.Error().Interface("txHash", r.TxHash).Msg("Deposit transaction failed") - log.Info().Uint64("currentGasLimit", gasLimit).Uint64("cumulativeGasUsedForTx", r.CumulativeGasUsed).Msg("Perhaps try increasing the gas limit") - return nil - } - time.Sleep(1 * time.Second) - } - } - }, -} - -//go:embed depositClaimUsage.md -var depositClaimUsage string -var depositClaimCmd = &cobra.Command{ - Use: "deposit-claim", - Short: "Make a uLxLy claim transaction", - Long: depositClaimUsage, - Args: cobra.NoArgs, - PreRunE: checkClaimArgs, - RunE: func(cmd *cobra.Command, args []string) error { - ctx := cmd.Context() - // Dial Ethereum client - client, err := ethclient.DialContext(ctx, *ulxlyInputArgs.ClaimRPCURL) - if err != nil { - log.Error().Err(err).Msg("Unable to Dial RPC") - return err - } - defer client.Close() - // Initialize and assign variables required to send transaction payload - bridgeV2, privateKey, fromAddress, gasLimit, gasPrice, toAddress, signer := generateTransactionPayload(ctx, client, *ulxlyInputArgs.ClaimBridgeAddress, *ulxlyInputArgs.ClaimPrivateKey, *ulxlyInputArgs.ClaimGasLimit, *ulxlyInputArgs.ClaimAddress, *ulxlyInputArgs.ClaimChainID) + e := generateEmptyHashes(TreeDepth) + copy(p.Siblings[:], e) + fmt.Println(p.String()) + return nil +} - // Call the bridge service RPC URL to get the merkle proofs and exit roots and parses them to the correct formats. - bridgeServiceProofEndpoint := fmt.Sprintf("%s/merkle-proof?deposit_cnt=%s&net_id=%s", *ulxlyInputArgs.BridgeServiceRPCURL, *ulxlyInputArgs.ClaimIndex, *ulxlyInputArgs.ClaimOriginNetwork) - merkleProofArray, rollupMerkleProofArray, mainExitRoot, rollupExitRoot := getMerkleProofsExitRoots(bridgeServiceProofEndpoint) +func zeroProof(ctx *cli.Context) error { + p := new(Proof) - tops := &bind.TransactOpts{ - Signer: func(address common.Address, transaction *types.Transaction) (*types.Transaction, error) { - return types.SignTx(transaction, signer, privateKey) - }, - // Value: value, - From: fromAddress, - Context: ctx, - GasLimit: gasLimit, - GasPrice: gasPrice, - GasFeeCap: nil, - GasTipCap: nil, - } + e := generateZeroHashes(TreeDepth) + copy(p.Siblings[:], e) + fmt.Println(p.String()) + return nil +} - // Call the bridge service RPC URL to get the deposits data and parses them to the correct formats. - bridgeServiceDepositsEndpoint := fmt.Sprintf("%s/bridges/%s", *ulxlyInputArgs.BridgeServiceRPCURL, *ulxlyInputArgs.ClaimAddress) - globalIndex, originAddress, amount, metadata, err := getDeposits(bridgeServiceDepositsEndpoint) - if err != nil { - log.Error().Err(err) - return err - } +func bridgeAsset(ctx *cli.Context) error { + bridgeAddress := ctx.String(bridgeAddressFlag.Name) + privateKey := ctx.String(privKeyFlag.Name) + gasLimit := ctx.Uint64(gasLimitFlag.Name) + destinationAddress := ctx.String(destAddressFlag.Name) + chainID := ctx.String(chainIDFlag.Name) + amount := ctx.String(AmountFlag.Name) + tokenAddr := ctx.String(tokenAddressFlag.Name) + callDataString := ctx.String(callDataFlag.Name) + destinationNetwork := uint32(ctx.Uint(destNetworkFlag.Name)) + isForced := ctx.Bool(forceFlag.Name) + timeoutTxnReceipt := ctx.Uint64(timeoutFlag.Name) + RPCURL := ctx.String(rpcURLFlag.Name) + + // Dial the Ethereum RPC server. + client, err := ethclient.DialContext(ctx.Context, RPCURL) + if err != nil { + log.Error().Err(err).Msg("Unable to Dial RPC") + return err + } + defer client.Close() + // Initialize and assign variables required to send transaction payload + bridgeV2, toAddress, auth, err := generateTransactionPayload(ctx.Context, client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID) + if err != nil { + log.Error().Err(err).Msg("error generating transaction payload") + return err + } - claimOriginNetwork, _ := strconv.Atoi(*ulxlyInputArgs.ClaimOriginNetwork) // Convert ClaimOriginNetwork to int - claimDestinationNetwork, _ := strconv.Atoi(*ulxlyInputArgs.ClaimDestinationNetwork) // Convert ClaimDestinationNetwork to int - var claimTxn *types.Transaction - switch { - case *ulxlyInputArgs.ClaimMessage: - claimTxn, err = bridgeV2.ClaimMessage(tops, merkleProofArray, rollupMerkleProofArray, globalIndex, [32]byte(mainExitRoot), [32]byte(rollupExitRoot), uint32(claimOriginNetwork), originAddress, uint32(claimDestinationNetwork), toAddress, amount, metadata) - if err != nil { - log.Error().Err(err).Msg("Unable to interact with bridge contract") - return err - } + value, _ := big.NewInt(0).SetString(amount, 0) + tokenAddress := common.HexToAddress(tokenAddr) + callData := common.Hex2Bytes(callDataString) + + if tokenAddress == common.HexToAddress("0x0000000000000000000000000000000000000000") { + auth.Value = value + } + + bridgeTxn, err := bridgeV2.BridgeAsset(auth, destinationNetwork, toAddress, value, tokenAddress, isForced, callData) + if err != nil { + log.Error().Err(err).Msg("Unable to interact with bridge contract") + return err + } + log.Info().Msg("bridgeTxn: " + bridgeTxn.Hash().String()) + return WaitMineTransaction(ctx.Context, client, bridgeTxn, timeoutTxnReceipt) +} + +func bridgeMessage(ctx *cli.Context) error { + bridgeAddress := ctx.String(bridgeAddressFlag.Name) + privateKey := ctx.String(privKeyFlag.Name) + gasLimit := ctx.Uint64(gasLimitFlag.Name) + destinationAddress := ctx.String(destAddressFlag.Name) + chainID := ctx.String(chainIDFlag.Name) + amount := ctx.String(AmountFlag.Name) + tokenAddr := ctx.String(tokenAddressFlag.Name) + callDataString := ctx.String(callDataFlag.Name) + destinationNetwork := uint32(ctx.Uint(destNetworkFlag.Name)) + isForced := ctx.Bool(forceFlag.Name) + timeoutTxnReceipt := ctx.Uint64(timeoutFlag.Name) + RPCURL := ctx.String(rpcURLFlag.Name) + + // Dial the Ethereum RPC server. + client, err := ethclient.DialContext(ctx.Context, RPCURL) + if err != nil { + log.Error().Err(err).Msg("Unable to Dial RPC") + return err + } + defer client.Close() + // Initialize and assign variables required to send transaction payload + bridgeV2, toAddress, auth, err := generateTransactionPayload(ctx.Context, client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID) + if err != nil { + log.Error().Err(err).Msg("error generating transaction payload") + return err + } + + value, _ := big.NewInt(0).SetString(amount, 0) + tokenAddress := common.HexToAddress(tokenAddr) + callData := common.Hex2Bytes(callDataString) + + if tokenAddress == common.HexToAddress("0x0000000000000000000000000000000000000000") { + auth.Value = value + } + + bridgeTxn, err := bridgeV2.BridgeMessage(auth, destinationNetwork, toAddress, isForced, callData) + if err != nil { + log.Error().Err(err).Msg("Unable to interact with bridge contract") + return err + } + log.Info().Msg("bridgeTxn: " + bridgeTxn.Hash().String()) + return WaitMineTransaction(ctx.Context, client, bridgeTxn, timeoutTxnReceipt) +} + +func bridgeWETHMessage(ctx *cli.Context) error { + bridgeAddress := ctx.String(bridgeAddressFlag.Name) + privateKey := ctx.String(privKeyFlag.Name) + gasLimit := ctx.Uint64(gasLimitFlag.Name) + destinationAddress := ctx.String(destAddressFlag.Name) + chainID := ctx.String(chainIDFlag.Name) + amount := ctx.String(AmountFlag.Name) + callDataString := ctx.String(callDataFlag.Name) + destinationNetwork := uint32(ctx.Uint(destNetworkFlag.Name)) + isForced := ctx.Bool(forceFlag.Name) + timeoutTxnReceipt := ctx.Uint64(timeoutFlag.Name) + RPCURL := ctx.String(rpcURLFlag.Name) + // Dial the Ethereum RPC server. + client, err := ethclient.DialContext(ctx.Context, RPCURL) + if err != nil { + log.Error().Err(err).Msg("Unable to Dial RPC") + return err + } + defer client.Close() + // Initialize and assign variables required to send transaction payload + bridgeV2, toAddress, auth, err := generateTransactionPayload(ctx.Context, client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID) + if err != nil { + log.Error().Err(err).Msg("error generating transaction payload") + return err + } + // Check if WETH is allowed + wethAddress, err := bridgeV2.WETHToken(&bind.CallOpts{Pending: false}) + if err != nil { + log.Error().Err(err).Msg("error getting WETH address from the bridge smc") + return err + } + if wethAddress == (common.Address{}) { + return fmt.Errorf("bridge WETH not allowed. Native ETH token configured in this network. This tx will fail") + } + + value, _ := big.NewInt(0).SetString(amount, 0) + callData := common.Hex2Bytes(callDataString) + + bridgeTxn, err := bridgeV2.BridgeMessageWETH(auth, destinationNetwork, toAddress, value, isForced, callData) + if err != nil { + log.Error().Err(err).Msg("Unable to interact with bridge contract") + return err + } + log.Info().Msg("bridgeTxn: " + bridgeTxn.Hash().String()) + return WaitMineTransaction(ctx.Context, client, bridgeTxn, timeoutTxnReceipt) +} + +func claimAsset(ctx *cli.Context) error { + bridgeAddress := ctx.String(bridgeAddressFlag.Name) + privateKey := ctx.String(privKeyFlag.Name) + gasLimit := ctx.Uint64(gasLimitFlag.Name) + destinationAddress := ctx.String(destAddressFlag.Name) + chainID := ctx.String(chainIDFlag.Name) + timeoutTxnReceipt := ctx.Uint64(timeoutFlag.Name) + RPCURL := ctx.String(rpcURLFlag.Name) + depositCount := ctx.Uint64(depositCountFlag.Name) + depositNetwork := ctx.Uint64(depositNetworkFlag.Name) + bridgeServiceUrl := ctx.String(bridgeServiceUrlFlag.Name) + + // Dial Ethereum client + client, err := ethclient.DialContext(ctx.Context, RPCURL) + if err != nil { + log.Error().Err(err).Msg("Unable to Dial RPC") + return err + } + defer client.Close() + // Initialize and assign variables required to send transaction payload + bridgeV2, toAddress, auth, err := generateTransactionPayload(ctx.Context, client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID) + if err != nil { + log.Error().Err(err).Msg("error generating transaction payload") + return err + } + + ///////////////////////////////////////// TODO BORRAR + // gasPrice, err := client.SuggestGasPrice(ctx) // This call is done automatically if it is not set + // if err != nil { + // log.Error().Err(err).Msg("Cannot get suggested gas price") + // } + // auth.GasPrice = big.NewInt(0).Mul(gasPrice,big.NewInt(10)) + ///////////////////////////////////////////////////////// + + // Call the bridge service RPC URL to get the merkle proofs and exit roots and parses them to the correct formats. + bridgeServiceProofEndpoint := fmt.Sprintf("%s/merkle-proof?deposit_cnt=%d&net_id=%d", bridgeServiceUrl, depositCount, depositNetwork) + merkleProofArray, rollupMerkleProofArray, mainExitRoot, rollupExitRoot := getMerkleProofsExitRoots(bridgeServiceProofEndpoint) + + // Call the bridge service RPC URL to get the deposits data and parses them to the correct formats. + bridgeServiceDepositsEndpoint := fmt.Sprintf("%s/bridge?net_id=%d&deposit_cnt=%d", bridgeServiceUrl, depositNetwork, depositCount) + globalIndex, originAddress, amount, metadata, leafType, claimDestNetwork, claimOriginalNetwork, err := getDeposit(bridgeServiceDepositsEndpoint) + if err != nil { + log.Error().Err(err) + return err + } + if leafType != 0 { + log.Warn().Msg("Deposit leafType is not asset") + } + + claimTxn, err := bridgeV2.ClaimAsset(auth, merkleProofArray, rollupMerkleProofArray, globalIndex, [32]byte(mainExitRoot), [32]byte(rollupExitRoot), claimOriginalNetwork, originAddress, claimDestNetwork, toAddress, amount, metadata) + if err != nil { + log.Error().Err(err).Msg("Unable to interact with bridge contract") + return err + } + log.Info().Msg("claimTxn: " + claimTxn.Hash().String()) + return WaitMineTransaction(ctx.Context, client, claimTxn, timeoutTxnReceipt) +} + +func claimMessage(ctx *cli.Context) error { + bridgeAddress := ctx.String(bridgeAddressFlag.Name) + privateKey := ctx.String(privKeyFlag.Name) + gasLimit := ctx.Uint64(gasLimitFlag.Name) + destinationAddress := ctx.String(destAddressFlag.Name) + chainID := ctx.String(chainIDFlag.Name) + timeoutTxnReceipt := ctx.Uint64(timeoutFlag.Name) + RPCURL := ctx.String(rpcURLFlag.Name) + depositCount := ctx.Uint64(depositCountFlag.Name) + depositNetwork := ctx.Uint64(depositNetworkFlag.Name) + bridgeServiceUrl := ctx.String(bridgeServiceUrlFlag.Name) + + // Dial Ethereum client + client, err := ethclient.DialContext(ctx.Context, RPCURL) + if err != nil { + log.Error().Err(err).Msg("Unable to Dial RPC") + return err + } + defer client.Close() + // Initialize and assign variables required to send transaction payload + bridgeV2, toAddress, auth, err := generateTransactionPayload(ctx.Context, client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID) + if err != nil { + log.Error().Err(err).Msg("error generating transaction payload") + return err + } + + // Call the bridge service RPC URL to get the merkle proofs and exit roots and parses them to the correct formats. + bridgeServiceProofEndpoint := fmt.Sprintf("%s/merkle-proof?deposit_cnt=%d&net_id=%d", bridgeServiceUrl, depositCount, depositNetwork) + merkleProofArray, rollupMerkleProofArray, mainExitRoot, rollupExitRoot := getMerkleProofsExitRoots(bridgeServiceProofEndpoint) + + // Call the bridge service RPC URL to get the deposits data and parses them to the correct formats. + bridgeServiceDepositsEndpoint := fmt.Sprintf("%s/bridge?net_id=%d&deposit_cnt=%d", bridgeServiceUrl, depositNetwork, depositCount) + globalIndex, originAddress, amount, metadata, leafType, claimDestNetwork, claimOriginalNetwork, err := getDeposit(bridgeServiceDepositsEndpoint) + if err != nil { + log.Error().Err(err) + return err + } + if leafType != 1 { + log.Warn().Msg("Deposit leafType is not message") + } + + claimTxn, err := bridgeV2.ClaimMessage(auth, merkleProofArray, rollupMerkleProofArray, globalIndex, [32]byte(mainExitRoot), [32]byte(rollupExitRoot), claimOriginalNetwork, originAddress, claimDestNetwork, toAddress, amount, metadata) + if err != nil { + log.Error().Err(err).Msg("Unable to interact with bridge contract") + return err + } + log.Info().Msg("claimTxn: " + claimTxn.Hash().String()) + return WaitMineTransaction(ctx.Context, client, claimTxn, timeoutTxnReceipt) +} + +// Wait for the transaction to be mined +func WaitMineTransaction(ctx context.Context, client *ethclient.Client, tx *types.Transaction, txTimeout uint64) error { + txnMinedTimer := time.NewTimer(time.Duration(txTimeout) * time.Second) + defer txnMinedTimer.Stop() + for { + select { + case <-txnMinedTimer.C: + log.Info().Msg("Wait timer for transaction receipt exceeded!") + return nil default: - claimTxn, err = bridgeV2.ClaimAsset(tops, merkleProofArray, rollupMerkleProofArray, globalIndex, [32]byte(mainExitRoot), [32]byte(rollupExitRoot), uint32(claimOriginNetwork), originAddress, uint32(claimDestinationNetwork), toAddress, amount, metadata) + r, err := client.TransactionReceipt(ctx, tx.Hash()) if err != nil { - log.Error().Err(err).Msg("Unable to interact with bridge contract") - return err - } - } - - // Wait for the transaction to be mined - // TODO: Consider creating a function for this section - txnMinedTimer := time.NewTimer(time.Duration(*ulxlyInputArgs.ClaimTimeoutTxnReceipt) * time.Second) - defer txnMinedTimer.Stop() - for { - select { - case <-txnMinedTimer.C: - log.Info().Msg("Wait timer for transaction receipt exceeded!") - return nil - default: - r, err := client.TransactionReceipt(ctx, claimTxn.Hash()) - if err != nil { - if err.Error() != "not found" { - log.Error().Err(err) - return err - } - time.Sleep(1 * time.Second) - continue - } - if r.Status != 0 { - log.Info().Interface("txHash", r.TxHash).Msg("Claim transaction successful") - return nil - } else if r.Status == 0 { - log.Info().Interface("txHash", r.TxHash).Msg("Claim transaction failed") - return nil + if err.Error() != "not found" { + log.Error().Err(err) + return err } time.Sleep(1 * time.Second) + continue } + if r.Status != 0 { + log.Info().Interface("txHash", r.TxHash).Msg("Deposit transaction successful") + return nil + } else if r.Status == 0 { + log.Error().Interface("txHash", r.TxHash).Msg("Deposit transaction failed") + log.Info().Uint64("GasUsed", tx.Gas()).Uint64("cumulativeGasUsedForTx", r.CumulativeGasUsed).Msg("Perhaps try increasing the gas limit") + return nil + } + time.Sleep(1 * time.Second) } - }, + } } -//go:embed proofUsage.md -var proofUsage string -var ProofCmd = &cobra.Command{ - Use: "proof", - Short: "generate a merkle proof", - Long: proofUsage, - Args: cobra.NoArgs, - PreRunE: checkProofArgs, - RunE: func(cmd *cobra.Command, args []string) error { - rawDepositData, err := getInputData(cmd, args) - if err != nil { - return err - } - return readDeposits(rawDepositData) - }, -} - -var EmptyProofCmd = &cobra.Command{ - Use: "empty-proof", - Short: "print an empty proof structure", - Long: `Use this command to print an empty proof response that's filled with -zero-valued siblings like -0x0000000000000000000000000000000000000000000000000000000000000000. This -can be useful when you need to submit a dummy proof.`, - Args: cobra.NoArgs, - PreRunE: checkProofArgs, - RunE: func(cmd *cobra.Command, args []string) error { - p := new(Proof) - - e := generateEmptyHashes(TreeDepth) - copy(p.Siblings[:], e) - fmt.Println(p.String()) - return nil - }, -} - -var ZeroProofCmd = &cobra.Command{ - Use: "zero-proof", - Short: "print a proof structure with the zero hashes", - Long: `Use this command to print a proof response that's filled with the zero -hashes. This values are very helpful for debugging because it would -tell you how populated the tree is and roughly which leaves and -siblings are empty. It's also helpful for sanity checking a proof -response to understand if the hashed value is part of the zero hashes -or if it's actually an intermediate hash.`, - Args: cobra.NoArgs, - PreRunE: checkProofArgs, - RunE: func(cmd *cobra.Command, args []string) error { - p := new(Proof) - - e := generateZeroHashes(TreeDepth) - copy(p.Siblings[:], e) - fmt.Println(p.String()) - return nil - }, -} - -func checkProofArgs(cmd *cobra.Command, args []string) error { - return nil -} -func getInputData(_ *cobra.Command, args []string) ([]byte, error) { - if ulxlyInputArgs.InputFileName != nil && *ulxlyInputArgs.InputFileName != "" { - return os.ReadFile(*ulxlyInputArgs.InputFileName) +func getInputData(ctx *cli.Context) ([]byte, error) { + fileName := ctx.String(inputFileNameFlag.Name) + if fileName != "" { + return os.ReadFile(fileName) } - if len(args) > 1 { - concat := strings.Join(args[1:], " ") + if ctx.Args().Len() > 1 { + concat := strings.Join(ctx.Args().Slice()[1:], " ") return []byte(concat), nil } return io.ReadAll(os.Stdin) } -func readDeposits(rawDeposits []byte) error { +func readDeposits(rawDeposits []byte, depositNumber uint32) error { buf := bytes.NewBuffer(rawDeposits) scanner := bufio.NewScanner(buf) imt := new(IMT) @@ -497,12 +535,12 @@ func readDeposits(rawDeposits []byte) error { Str("root", common.Hash(imt.Roots[len(imt.Roots)-1]).String()). Msg("adding event to tree") // There's no point adding more leaves if we can prove the deposit already? - if evt.DepositCount >= *ulxlyInputArgs.DepositNum { + if evt.DepositCount >= depositNumber { break } } - p := imt.GetProof(*ulxlyInputArgs.DepositNum) + p := imt.GetProof(depositNumber) fmt.Println(p.String()) return nil } @@ -703,54 +741,52 @@ func generateEmptyHashes(height uint8) []common.Hash { return zeroHashes } -func generateTransactionPayload(ctx context.Context, client *ethclient.Client, ulxlyInputArgBridge string, ulxlyInputArgPvtKey string, ulxlyInputArgGasLimit uint64, ulxlyInputArgDestAddr string, ulxlyInputArgChainID string) (bridgeV2 *ulxly.Ulxly, privateKey *ecdsa.PrivateKey, fromAddress common.Address, gasLimit uint64, gasPrice *big.Int, toAddress common.Address, signer types.Signer) { - var err error +func generateTransactionPayload(ctx context.Context, client *ethclient.Client, ulxlyInputArgBridge string, ulxlyInputArgPvtKey string, ulxlyInputArgGasLimit uint64, ulxlyInputArgDestAddr string, ulxlyInputArgChainID string) (bridgeV2 *ulxly.Ulxly, toAddress common.Address, opts *bind.TransactOpts, err error) { bridgeV2, err = ulxly.NewUlxly(common.HexToAddress(ulxlyInputArgBridge), client) if err != nil { return } - privateKey, err = crypto.HexToECDSA(ulxlyInputArgPvtKey) + privateKey, err := crypto.HexToECDSA(ulxlyInputArgPvtKey) if err != nil { log.Error().Err(err).Msg("Unable to retrieve private key") } - publicKey := privateKey.Public() - publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) - if !ok { - log.Error().Msg("Error casting public key to ECDSA") - } - - fromAddress = crypto.PubkeyToAddress(*publicKeyECDSA) - // value := big.NewInt(*ulxlyInputArgs.Amount) - gasLimit = ulxlyInputArgGasLimit - gasPrice, err = client.SuggestGasPrice(ctx) - if err != nil { - log.Error().Err(err).Msg("Cannot get suggested gas price") - } + gasLimit := ulxlyInputArgGasLimit + // gasPrice, err := client.SuggestGasPrice(ctx) // This call is done automatically if it is not set + // if err != nil { + // log.Error().Err(err).Msg("Cannot get suggested gas price") + // } // gasTipCap, err := client.SuggestGasTipCap(ctx) // if err != nil { // log.Error().Err(err).Msg("Cannot get suggested gas tip cap") // } - toAddress = common.HexToAddress(ulxlyInputArgDestAddr) - chainID := new(big.Int) // For manual input of chainID, use the user's input if ulxlyInputArgChainID != "" { chainID.SetString(ulxlyInputArgChainID, 10) } else { // If there is no user input for chainID, infer it from context - chainID, err = client.NetworkID(ctx) + chainID, err = client.ChainID(ctx) if err != nil { log.Error().Err(err).Msg("Cannot get chain ID") return } } - signer = types.LatestSignerForChainID(chainID) - - return bridgeV2, privateKey, fromAddress, gasLimit, gasPrice, toAddress, signer + opts, err = bind.NewKeyedTransactorWithChainID(privateKey, chainID) + if err != nil { + log.Error().Err(err).Msg("Cannot generate transactionOpts") + return + } + opts.Context = ctx + opts.GasLimit = gasLimit + toAddress = common.HexToAddress(ulxlyInputArgDestAddr) + if toAddress == (common.Address{}) { + toAddress = opts.From + } + return bridgeV2, toAddress, opts, err } func getMerkleProofsExitRoots(bridgeServiceProofEndpoint string) (merkleProofArray [32][32]byte, rollupMerkleProofArray [32][32]byte, mainExitRoot []byte, rollupExitRoot []byte) { @@ -801,18 +837,18 @@ func getMerkleProofsExitRoots(bridgeServiceProofEndpoint string) (merkleProofArr return merkleProofArray, rollupMerkleProofArray, mainExitRoot, rollupExitRoot } -func getDeposits(bridgeServiceDepositsEndpoint string) (globalIndex *big.Int, originAddress common.Address, amount *big.Int, metadata []byte, err error) { - reqBridgeDeposits, err := http.Get(bridgeServiceDepositsEndpoint) +func getDeposit(bridgeServiceDepositsEndpoint string) (globalIndex *big.Int, originAddress common.Address, amount *big.Int, metadata []byte, leafType uint8, claimDestNetwork, claimOriginalNetwork uint32, err error) { + reqBridgeDeposit, err := http.Get(bridgeServiceDepositsEndpoint) if err != nil { log.Error().Err(err) return } - bodyBridgeDeposit, err := io.ReadAll(reqBridgeDeposits.Body) // Response body is []byte + bodyBridgeDeposit, err := io.ReadAll(reqBridgeDeposit.Body) // Response body is []byte if err != nil { log.Error().Err(err) return } - var bridgeDeposit BridgeDeposits + var bridgeDeposit BridgeDeposit err = json.Unmarshal(bodyBridgeDeposit, &bridgeDeposit) // Parse []byte to go struct pointer, and shadow err variable if err != nil { log.Error().Err(err).Msg("Can not unmarshal JSON") @@ -822,106 +858,335 @@ func getDeposits(bridgeServiceDepositsEndpoint string) (globalIndex *big.Int, or globalIndex = new(big.Int) amount = new(big.Int) - intClaimIndex, _ := strconv.Atoi(*ulxlyInputArgs.ClaimIndex) // Convert deposit_cnt to int - destinationNetwork, _ := strconv.Atoi(*ulxlyInputArgs.ClaimDestinationNetwork) - for index, deposit := range bridgeDeposit.Deposit { - intDepositCnt, _ := strconv.Atoi(deposit.DepositCnt) // Convert deposit_cnt to int - if intDepositCnt == intClaimIndex && destinationNetwork == deposit.DestNet { // deposit_cnt must match the user's input value - if !bridgeDeposit.Deposit[index].ReadyForClaim { - log.Error().Msg("The claim transaction is not yet ready to be claimed. Try again in a few blocks.") - return nil, common.HexToAddress("0x0"), nil, nil, errors.New("the claim transaction is not yet ready to be claimed, try again in a few blocks") - } else if bridgeDeposit.Deposit[index].ClaimTxHash != "" { - log.Info().Str("claimTxHash", bridgeDeposit.Deposit[index].ClaimTxHash).Msg("The claim transaction has already been claimed") - return nil, common.HexToAddress("0x0"), nil, nil, errors.New("the claim transaction has already been claimed") - } - originAddress = common.HexToAddress(bridgeDeposit.Deposit[index].OrigAddr) - globalIndex.SetString(bridgeDeposit.Deposit[index].GlobalIndex, 10) - amount.SetString(bridgeDeposit.Deposit[index].Amount, 10) - metadata = common.Hex2Bytes(bridgeDeposit.Deposit[index].Metadata) - return globalIndex, originAddress, amount, metadata, nil - } + defer reqBridgeDeposit.Body.Close() + if bridgeDeposit.Code != nil { + return globalIndex, originAddress, amount, metadata, leafType, claimDestNetwork, claimOriginalNetwork, fmt.Errorf("error code received getting the deposit. Code: %d, Message: %s", *bridgeDeposit.Code, *bridgeDeposit.Message) } - defer reqBridgeDeposits.Body.Close() - return nil, common.HexToAddress("0x0"), nil, nil, errors.New("failed to correctly get deposits") -} - -func checkGetDepositArgs(cmd *cobra.Command, args []string) error { - if *ulxlyInputArgs.BridgeAddress == "" { - return fmt.Errorf("please provide the bridge address") - } - if *ulxlyInputArgs.FromBlock > *ulxlyInputArgs.ToBlock { - return fmt.Errorf("the from block should be less than the to block") + if !bridgeDeposit.Deposit.ReadyForClaim { + log.Error().Msg("The claim transaction is not yet ready to be claimed. Try again in a few blocks.") + return nil, common.HexToAddress("0x0"), nil, nil, 0, 0, 0, errors.New("the claim transaction is not yet ready to be claimed, try again in a few blocks") + } else if bridgeDeposit.Deposit.ClaimTxHash != "" { + log.Info().Str("claimTxHash", bridgeDeposit.Deposit.ClaimTxHash).Msg("The claim transaction has already been claimed") + return nil, common.HexToAddress("0x0"), nil, nil, 0, 0, 0, errors.New("the claim transaction has already been claimed") } - return nil + originAddress = common.HexToAddress(bridgeDeposit.Deposit.OrigAddr) + globalIndex.SetString(bridgeDeposit.Deposit.GlobalIndex, 10) + amount.SetString(bridgeDeposit.Deposit.Amount, 10) + metadata = common.Hex2Bytes(bridgeDeposit.Deposit.Metadata) + leafType = bridgeDeposit.Deposit.LeafType + claimDestNetwork = bridgeDeposit.Deposit.DestNet + claimOriginalNetwork = bridgeDeposit.Deposit.OrigNet + return globalIndex, originAddress, amount, metadata, leafType, claimDestNetwork, claimOriginalNetwork, nil } -func checkDepositArgs(cmd *cobra.Command, args []string) error { - if *ulxlyInputArgs.DepositBridgeAddress == "" { - return fmt.Errorf("please provide the bridge address") - } - if *ulxlyInputArgs.DepositGasLimit < 130000 && *ulxlyInputArgs.DepositGasLimit != 0 { - return fmt.Errorf("the gas limit may be too low for the transaction to pass") - } - if *ulxlyInputArgs.DepositMessage && *ulxlyInputArgs.DepositWETH { - return fmt.Errorf("choose a single deposit mode (asset, message, or WETH)") - } - return nil -} +//go:embed BridgeAssetUsage.md +var bridgeAssetUsage string + +//go:embed BridgeMessageUsage.md +var bridgeMessageUsage string -func checkClaimArgs(cmd *cobra.Command, args []string) error { - if *ulxlyInputArgs.ClaimGasLimit < 150000 && *ulxlyInputArgs.ClaimGasLimit != 0 { - return fmt.Errorf("the gas limit may be too low for the transaction to pass") +//go:embed BridgeWETHMessageUsage.md +var bridgeWETHMessageUsage string + +//go:embed ClaimAssetUsage.md +var claimAssetUsage string + +//go:embed ClaimMessageUsage.md +var claimMessageUsage string + +//go:embed proofUsage.md +var proofUsage string + +//go:embed depositGetUsage.md +var depositGetUsage string + +func initCli(cmd *cobra.Command, args []string) { + app := cli.NewApp() + app.Name = "uLxLy" + app.Version = "v0.0.1" + app.Commands = []*cli.Command{ + { + Name: "ulxly", + Aliases: []string{}, + Usage: "options for ulxly", + Subcommands: []*cli.Command{ + { + Name: "bridge-asset", + Aliases: []string{}, + Usage: "Make a uLxLy bridge asset transaction", + Description: bridgeAssetUsage, + Action: bridgeAsset, + Flags: []cli.Flag{ + gasLimitFlag, + chainIDFlag, + privKeyFlag, + AmountFlag, + rpcURLFlag, + bridgeAddressFlag, + destNetworkFlag, + destAddressFlag, + tokenAddressFlag, + forceFlag, + callDataFlag, + timeoutFlag, + }, + }, + { + Name: "bridge-message", + Aliases: []string{}, + Usage: "Make a uLxLy bridge message transaction", + Description: bridgeMessageUsage, + Action: bridgeMessage, + Flags: []cli.Flag{ + gasLimitFlag, + chainIDFlag, + privKeyFlag, + AmountFlag, + rpcURLFlag, + bridgeAddressFlag, + destNetworkFlag, + destAddressFlag, + tokenAddressFlag, + forceFlag, + callDataFlag, + timeoutFlag, + }, + }, + { + Name: "bridge-message-weth", + Aliases: []string{}, + Usage: "Make a uLxLy bridge weth message transaction", + Description: bridgeWETHMessageUsage, + Action: bridgeWETHMessage, + Flags: []cli.Flag{ + gasLimitFlag, + chainIDFlag, + privKeyFlag, + AmountFlag, + rpcURLFlag, + bridgeAddressFlag, + destNetworkFlag, + destAddressFlag, + forceFlag, + callDataFlag, + timeoutFlag, + }, + }, + { + Name: "claim-asset", + Aliases: []string{}, + Usage: "Make a uLxLy claim asset transaction", + Description: claimAssetUsage, + Action: claimAsset, + Flags: []cli.Flag{ + depositCountFlag, + depositNetworkFlag, + bridgeServiceUrlFlag, + destAddressFlag, + rpcURLFlag, + privKeyFlag, + bridgeAddressFlag, + gasLimitFlag, + chainIDFlag, + timeoutFlag, + }, + }, + { + Name: "claim-message", + Aliases: []string{}, + Usage: "Make a uLxLy claim message transaction", + Description: claimMessageUsage, + Action: claimMessage, + Flags: []cli.Flag{ + depositCountFlag, + depositNetworkFlag, + bridgeServiceUrlFlag, + destAddressFlag, + rpcURLFlag, + privKeyFlag, + bridgeAddressFlag, + gasLimitFlag, + chainIDFlag, + timeoutFlag, + }, + }, + { + Name: "empty-proof", + Aliases: []string{}, + Usage: "Print an empty proof structure", + Description: "Use this command to print an empty proof response that's filled with zero-valued siblings like 0x0000000000000000000000000000000000000000000000000000000000000000. This can be useful when you need to submit a dummy proof.", + Action: emptyProof, + }, + { + Name: "zero-proof", + Aliases: []string{}, + Usage: "Print a proof structure with the zero hashes", + Description: `Use this command to print a proof response that's filled with the zero + hashes. This values are very helpful for debugging because it would + tell you how populated the tree is and roughly which leaves and + siblings are empty. It's also helpful for sanity checking a proof + response to understand if the hashed value is part of the zero hashes + or if it's actually an intermediate hash.`, + Action: zeroProof, + }, + { + Name: "proof", + Aliases: []string{}, + Usage: "Generate a merkle proof", + Description: proofUsage, + Action: proof, + Flags: []cli.Flag{depositNumberFlag}, + }, + { + Name: "deposit-get", + Aliases: []string{}, + Usage: "Get a range of deposits", + Description: depositGetUsage, + Action: readDeposit, + Flags: []cli.Flag{ + fromBlockFlag, + toBlockFlag, + filterFlag, + bridgeAddressFlag, + rpcURLFlag, + }, + }, + }, + }, } - if *ulxlyInputArgs.ClaimMessage && *ulxlyInputArgs.ClaimWETH { - return fmt.Errorf("choose a single claim mode (asset, message, or WETH)") + + err := app.Run(os.Args) + if err != nil { + fmt.Printf("\nError: %v\n", err) + os.Exit(1) } - return nil } -func init() { - ULxLyCmd.AddCommand(depositClaimCmd) - ULxLyCmd.AddCommand(depositNewCmd) - ULxLyCmd.AddCommand(depositGetCmd) - ULxLyCmd.AddCommand(ProofCmd) - ULxLyCmd.AddCommand(EmptyProofCmd) - ULxLyCmd.AddCommand(ZeroProofCmd) - - ulxlyInputArgs.ClaimIndex = depositClaimCmd.PersistentFlags().String("claim-index", "0", "The deposit count, or index to initiate a claim transaction for.") - ulxlyInputArgs.ClaimAddress = depositClaimCmd.PersistentFlags().String("claim-address", "", "The address that is receiving the bridged asset.") - ulxlyInputArgs.ClaimOriginNetwork = depositClaimCmd.PersistentFlags().String("origin-network", "0", "The network ID of the origin network.") - ulxlyInputArgs.ClaimDestinationNetwork = depositClaimCmd.PersistentFlags().String("destination-network", "1", "The network ID of the destination network.") - ulxlyInputArgs.ClaimRPCURL = depositClaimCmd.PersistentFlags().String("rpc-url", "http://127.0.0.1:8545", "The RPC endpoint of the destination network") - ulxlyInputArgs.BridgeServiceRPCURL = depositClaimCmd.PersistentFlags().String("bridge-service-url", "", "The RPC endpoint of the bridge service component.") - ulxlyInputArgs.ClaimPrivateKey = depositClaimCmd.PersistentFlags().String("private-key", "", "The private key of the sender account.") - ulxlyInputArgs.ClaimBridgeAddress = depositClaimCmd.PersistentFlags().String("bridge-address", "", "The address of the bridge contract.") - ulxlyInputArgs.ClaimGasLimit = depositClaimCmd.PersistentFlags().Uint64("gas-limit", 0, "The gas limit for the transaction. Setting this value to 0 will estimate the gas limit.") - ulxlyInputArgs.ClaimChainID = depositClaimCmd.PersistentFlags().String("chain-id", "", "The chainID.") - ulxlyInputArgs.ClaimTimeoutTxnReceipt = depositClaimCmd.PersistentFlags().Uint32("transaction-receipt-timeout", 60, "The timeout limit to check for the transaction receipt of the claim.") - ulxlyInputArgs.ClaimMessage = depositClaimCmd.PersistentFlags().Bool("claim-message", false, "Claim a message instead of an asset.") - ulxlyInputArgs.ClaimWETH = depositClaimCmd.PersistentFlags().Bool("claim-weth", false, "Claim a weth instead of an asset.") - - ulxlyInputArgs.DepositGasLimit = depositNewCmd.PersistentFlags().Uint64("gas-limit", 0, "The gas limit for the transaction. Setting this value to 0 will estimate the gas limit.") - ulxlyInputArgs.DepositChainID = depositNewCmd.PersistentFlags().String("chain-id", "", "The chainID.") - ulxlyInputArgs.DepositPrivateKey = depositNewCmd.PersistentFlags().String("private-key", "", "The private key of the sender account.") - ulxlyInputArgs.Amount = depositNewCmd.PersistentFlags().Int64("amount", 0, "The amount to send.") - ulxlyInputArgs.DepositRPCURL = depositNewCmd.PersistentFlags().String("rpc-url", "http://127.0.0.1:8545", "The RPC endpoint of the network") - ulxlyInputArgs.DepositBridgeAddress = depositNewCmd.PersistentFlags().String("bridge-address", "", "The address of the bridge contract.") - ulxlyInputArgs.DestinationNetwork = depositNewCmd.PersistentFlags().Uint32("destination-network", 1, "The destination network number.") - ulxlyInputArgs.DestinationAddress = depositNewCmd.PersistentFlags().String("destination-address", "", "The address of receiver in destination network.") - ulxlyInputArgs.TokenAddress = depositNewCmd.PersistentFlags().String("token-address", "0x0000000000000000000000000000000000000000", "The address of the token to send.") - ulxlyInputArgs.IsForced = depositNewCmd.PersistentFlags().Bool("force-update-root", true, "Force the update of the Global Exit Root.") - ulxlyInputArgs.CallData = depositNewCmd.PersistentFlags().String("call-data", "0x", "For bridging assets - raw data of the call `permit` of the token. For bridging messages - the metadata.") - ulxlyInputArgs.DepositTimeoutTxnReceipt = depositNewCmd.PersistentFlags().Uint32("transaction-receipt-timeout", 60, "The timeout limit to check for the transaction receipt of the deposit.") - ulxlyInputArgs.DepositMessage = depositNewCmd.PersistentFlags().Bool("bridge-message", false, "Bridge a message instead of an asset.") - ulxlyInputArgs.DepositWETH = depositNewCmd.PersistentFlags().Bool("bridge-weth", false, "Bridge a weth instead of an asset.") - - ulxlyInputArgs.FromBlock = depositGetCmd.PersistentFlags().Uint64("from-block", 0, "The block height to start query at.") - ulxlyInputArgs.ToBlock = depositGetCmd.PersistentFlags().Uint64("to-block", 0, "The block height to start query at.") - ulxlyInputArgs.RPCURL = depositGetCmd.PersistentFlags().String("rpc-url", "http://127.0.0.1:8545", "The RPC to query for events") - ulxlyInputArgs.FilterSize = depositGetCmd.PersistentFlags().Uint64("filter-size", 1000, "The batch size for individual filter queries") - - ulxlyInputArgs.BridgeAddress = depositGetCmd.Flags().String("bridge-address", "", "The address of the lxly bridge") - ulxlyInputArgs.InputFileName = ProofCmd.PersistentFlags().String("file-name", "", "The filename with ndjson data of deposits") - ulxlyInputArgs.DepositNum = ProofCmd.PersistentFlags().Uint32("deposit-number", 0, "The deposit that we would like to prove") +var gasLimitFlag = &cli.Uint64Flag{ + Name: "gas-limit", + Aliases: []string{"gl"}, + Usage: "This param is used to force the GasLimit", + Required: false, +} +var chainIDFlag = &cli.StringFlag{ + Name: "chain-id", + Aliases: []string{"id"}, + Usage: "This param is used to force the chainID", + Required: false, +} +var privKeyFlag = &cli.StringFlag{ + Name: "private-key", + Aliases: []string{"pk"}, + Usage: "This param is used to set the private key", + Required: true, +} +var AmountFlag = &cli.StringFlag{ + Name: "amount", + Aliases: []string{"a"}, + Usage: "This param is used to set the amount", + Required: true, +} +var rpcURLFlag = &cli.StringFlag{ + Name: "rpc-url", + Aliases: []string{"u"}, + Usage: "This param is used to set the rpc url", + Required: true, +} +var bridgeAddressFlag = &cli.StringFlag{ + Name: "bridge-address", + Aliases: []string{"b"}, + Usage: "This param is used to set the bridge address", + Required: true, +} +var destNetworkFlag = &cli.UintFlag{ + Name: "destination-network", + Aliases: []string{"dest-net"}, + Usage: "This param is used to set the destination network", + Required: true, +} +var destAddressFlag = &cli.StringFlag{ + Name: "destination-address", + Aliases: []string{"dest-addr"}, + Usage: "This param is used to set the destination address", + Required: false, +} +var tokenAddressFlag = &cli.StringFlag{ + Name: "token-address", + Aliases: []string{"token"}, + Usage: "This param is used to set the token address", + Required: false, +} +var forceFlag = &cli.BoolFlag{ + Name: "force-update-root", + Aliases: []string{"f"}, + Usage: "This param is used to force the ger update in the smc", + Required: false, + Value: true, +} +var callDataFlag = &cli.StringFlag{ + Name: "call-data", + Aliases: []string{"data"}, + Usage: "This param is used to set the callData", + Required: false, + Value: "0x", +} +var timeoutFlag = &cli.Uint64Flag{ + Name: "transaction-receipt-timeout", + Aliases: []string{"timeout"}, + Usage: "This param is used to change the timeout interval", + Value: 60, + Required: false, +} +var depositCountFlag = &cli.Uint64Flag{ + Name: "deposit-count", + Aliases: []string{"cnt"}, + Usage: "This param is used to specify the deposit counter", + Required: true, +} +var depositNetworkFlag = &cli.Uint64Flag{ + Name: "deposit-network", + Aliases: []string{"net"}, + Usage: "This param is used to specify the deposit network", + Required: true, +} +var bridgeServiceUrlFlag = &cli.StringFlag{ + Name: "bridge-service-url", + Aliases: []string{"bridge-url"}, + Usage: "This param is used to specify the bridge service url", + Required: true, +} +var inputFileNameFlag = &cli.StringFlag{ + Name: "file-name", + Aliases: []string{"file"}, + Usage: "The filename with ndjson data of deposits", + Required: true, +} +var fromBlockFlag = &cli.Uint64Flag{ + Name: "from-block", + Aliases: []string{"from"}, + Usage: "The block height to start query at.", + Value: 0, + Required: false, +} +var toBlockFlag = &cli.Uint64Flag{ + Name: "to-block", + Aliases: []string{"to"}, + Usage: "The block height to start query at.", + Value: 0, + Required: false, +} +var filterFlag = &cli.Uint64Flag{ + Name: "filter-size", + Aliases: []string{"filter"}, + Usage: "The batch size for individual filter queries.", + Value: 1000, + Required: false, +} +var depositNumberFlag = &cli.Uint64Flag{ + Name: "deposit-number", + Aliases: []string{"deposit"}, + Usage: "The deposit that we would like to prove", + Value: 0, + Required: false, } diff --git a/go.mod b/go.mod index 5357214a..1cade649 100644 --- a/go.mod +++ b/go.mod @@ -159,12 +159,16 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/urfave/cli/v2 v2.27.5 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 // indirect diff --git a/go.sum b/go.sum index 3124505b..7e618050 100644 --- a/go.sum +++ b/go.sum @@ -95,6 +95,8 @@ github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCe github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg= github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4= @@ -404,6 +406,8 @@ github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2n github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= +github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -413,6 +417,8 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= From 3f9f59eccfc428fab3b834d2eb22cf88a4def46a Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Wed, 4 Dec 2024 18:09:43 -0500 Subject: [PATCH 02/17] refactor: switching to cobra.. needs testing --- cmd/ulxly/ulxly.go | 729 +++++++++++++++++++-------------------------- 1 file changed, 306 insertions(+), 423 deletions(-) diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index 7ea04e68..6ff71888 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -27,7 +27,6 @@ import ( "github.com/0xPolygon/polygon-cli/bindings/ulxly" "github.com/rs/zerolog/log" "github.com/spf13/cobra" - "github.com/urfave/cli/v2" ) const ( @@ -38,46 +37,6 @@ const ( TreeDepth = 32 ) -type uLxLyArgs struct { - FromBlock *uint64 - ToBlock *uint64 - RPCURL *string - BridgeAddress *string - FilterSize *uint64 - - ClaimIndex *string - ClaimAddress *string - ClaimOriginNetwork *string - ClaimDestinationNetwork *string - BridgeServiceRPCURL *string - ClaimRPCURL *string - ClaimPrivateKey *string - NetworkID *uint32 - ClaimBridgeAddress *string - ClaimGasLimit *uint64 - ClaimChainID *string - ClaimTimeoutTxnReceipt *uint32 - ClaimMessage *bool - ClaimWETH *bool - - InputFileName *string - DepositNum *uint32 - DepositPrivateKey *string - DepositGasLimit *uint64 - Amount *int64 // HACK: This should be big.Int but depositNewCmd.PersistentFlags() doesn't support that type. - DestinationNetwork *uint32 - DestinationAddress *string - DepositRPCURL *string - DepositBridgeAddress *string - DepositChainID *string - TokenAddress *string - IsForced *bool - CallData *string - DepositTimeoutTxnReceipt *uint32 - DepositMessage *bool - DepositWETH *bool -} - type IMT struct { Branches map[uint32][]common.Hash Leaves map[uint32]common.Hash @@ -123,22 +82,19 @@ type BridgeDeposit struct { Message *string `json:"message"` } -var ULxLyCmd = &cobra.Command{ - Use: "ulxly", - Short: "Utilities for interacting with the lxly bridge", - Long: "These are low level tools for directly scanning bridge events and constructing proofs.", - DisableFlagParsing: true, - Run: initCli, -} +func readDeposit(cmd *cobra.Command) error { + rpcUrl, err := cmd.Flags().GetString(ArgRPCURL) + if err != nil { + return err + } + + bridgeAddress := *inputUlxlyArgs.bridgeAddress + toBlock := *inputUlxlyArgs.toBlock + fromBlock := *inputUlxlyArgs.fromBlock + filter := *inputUlxlyArgs.filterSize -func readDeposit(ctx *cli.Context) error { - bridgeAddress := ctx.String(bridgeAddressFlag.Name) - rpcUrl := ctx.String(rpcURLFlag.Name) - toBlock := ctx.Uint64(toBlockFlag.Name) - fromBlock := ctx.Uint64(fromBlockFlag.Name) - filter := ctx.Uint64(filterFlag.Name) // Dial the Ethereum RPC server. - rpc, err := ethrpc.DialContext(ctx.Context, rpcUrl) + rpc, err := ethrpc.DialContext(cmd.Context(), rpcUrl) if err != nil { log.Error().Err(err).Msg("Unable to Dial RPC") return err @@ -160,7 +116,7 @@ func readDeposit(ctx *cli.Context) error { opts := bind.FilterOpts{ Start: currentBlock, End: &endBlock, - Context: ctx.Context, + Context: cmd.Context(), } evtV2Iterator, err := bridgeV2.FilterBridgeEvent(&opts) if err != nil { @@ -187,16 +143,16 @@ func readDeposit(ctx *cli.Context) error { return nil } -func proof(ctx *cli.Context) error { - depositNumber := ctx.Uint64(depositNumberFlag.Name) - rawDepositData, err := getInputData(ctx) +func proof(args []string) error { + depositNumber := *inputUlxlyArgs.depositNumber + rawDepositData, err := getInputData(args) if err != nil { return err } return readDeposits(rawDepositData, uint32(depositNumber)) } -func emptyProof(ctx *cli.Context) error { +func emptyProof() error { p := new(Proof) e := generateEmptyHashes(TreeDepth) @@ -205,7 +161,7 @@ func emptyProof(ctx *cli.Context) error { return nil } -func zeroProof(ctx *cli.Context) error { +func zeroProof() error { p := new(Proof) e := generateZeroHashes(TreeDepth) @@ -214,29 +170,29 @@ func zeroProof(ctx *cli.Context) error { return nil } -func bridgeAsset(ctx *cli.Context) error { - bridgeAddress := ctx.String(bridgeAddressFlag.Name) - privateKey := ctx.String(privKeyFlag.Name) - gasLimit := ctx.Uint64(gasLimitFlag.Name) - destinationAddress := ctx.String(destAddressFlag.Name) - chainID := ctx.String(chainIDFlag.Name) - amount := ctx.String(AmountFlag.Name) - tokenAddr := ctx.String(tokenAddressFlag.Name) - callDataString := ctx.String(callDataFlag.Name) - destinationNetwork := uint32(ctx.Uint(destNetworkFlag.Name)) - isForced := ctx.Bool(forceFlag.Name) - timeoutTxnReceipt := ctx.Uint64(timeoutFlag.Name) - RPCURL := ctx.String(rpcURLFlag.Name) +func bridgeAsset(cmd *cobra.Command) error { + bridgeAddress := *inputUlxlyArgs.bridgeAddress + privateKey := *inputUlxlyArgs.privateKey + gasLimit := *inputUlxlyArgs.gasLimit + destinationAddress := *inputUlxlyArgs.destAddress + chainID := *inputUlxlyArgs.chainID + amount := *inputUlxlyArgs.value + tokenAddr := *inputUlxlyArgs.tokenAddress + callDataString := *inputUlxlyArgs.callData + destinationNetwork := *inputUlxlyArgs.destNetwork + isForced := *inputUlxlyArgs.forceUpdate + timeoutTxnReceipt := *inputUlxlyArgs.timeout + RPCURL := *inputUlxlyArgs.rpcURL // Dial the Ethereum RPC server. - client, err := ethclient.DialContext(ctx.Context, RPCURL) + client, err := ethclient.DialContext(cmd.Context(), RPCURL) if err != nil { log.Error().Err(err).Msg("Unable to Dial RPC") return err } defer client.Close() // Initialize and assign variables required to send transaction payload - bridgeV2, toAddress, auth, err := generateTransactionPayload(ctx.Context, client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID) + bridgeV2, toAddress, auth, err := generateTransactionPayload(cmd.Context(), client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID) if err != nil { log.Error().Err(err).Msg("error generating transaction payload") return err @@ -256,32 +212,32 @@ func bridgeAsset(ctx *cli.Context) error { return err } log.Info().Msg("bridgeTxn: " + bridgeTxn.Hash().String()) - return WaitMineTransaction(ctx.Context, client, bridgeTxn, timeoutTxnReceipt) + return WaitMineTransaction(cmd.Context(), client, bridgeTxn, timeoutTxnReceipt) } -func bridgeMessage(ctx *cli.Context) error { - bridgeAddress := ctx.String(bridgeAddressFlag.Name) - privateKey := ctx.String(privKeyFlag.Name) - gasLimit := ctx.Uint64(gasLimitFlag.Name) - destinationAddress := ctx.String(destAddressFlag.Name) - chainID := ctx.String(chainIDFlag.Name) - amount := ctx.String(AmountFlag.Name) - tokenAddr := ctx.String(tokenAddressFlag.Name) - callDataString := ctx.String(callDataFlag.Name) - destinationNetwork := uint32(ctx.Uint(destNetworkFlag.Name)) - isForced := ctx.Bool(forceFlag.Name) - timeoutTxnReceipt := ctx.Uint64(timeoutFlag.Name) - RPCURL := ctx.String(rpcURLFlag.Name) +func bridgeMessage(cmd *cobra.Command) error { + bridgeAddress := *inputUlxlyArgs.bridgeAddress + privateKey := *inputUlxlyArgs.privateKey + gasLimit := *inputUlxlyArgs.gasLimit + destinationAddress := *inputUlxlyArgs.destAddress + chainID := *inputUlxlyArgs.chainID + amount := *inputUlxlyArgs.value + tokenAddr := *inputUlxlyArgs.tokenAddress + callDataString := *inputUlxlyArgs.callData + destinationNetwork := *inputUlxlyArgs.destNetwork + isForced := *inputUlxlyArgs.forceUpdate + timeoutTxnReceipt := *inputUlxlyArgs.timeout + RPCURL := *inputUlxlyArgs.rpcURL // Dial the Ethereum RPC server. - client, err := ethclient.DialContext(ctx.Context, RPCURL) + client, err := ethclient.DialContext(cmd.Context(), RPCURL) if err != nil { log.Error().Err(err).Msg("Unable to Dial RPC") return err } defer client.Close() // Initialize and assign variables required to send transaction payload - bridgeV2, toAddress, auth, err := generateTransactionPayload(ctx.Context, client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID) + bridgeV2, toAddress, auth, err := generateTransactionPayload(cmd.Context(), client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID) if err != nil { log.Error().Err(err).Msg("error generating transaction payload") return err @@ -301,30 +257,31 @@ func bridgeMessage(ctx *cli.Context) error { return err } log.Info().Msg("bridgeTxn: " + bridgeTxn.Hash().String()) - return WaitMineTransaction(ctx.Context, client, bridgeTxn, timeoutTxnReceipt) + return WaitMineTransaction(cmd.Context(), client, bridgeTxn, timeoutTxnReceipt) } -func bridgeWETHMessage(ctx *cli.Context) error { - bridgeAddress := ctx.String(bridgeAddressFlag.Name) - privateKey := ctx.String(privKeyFlag.Name) - gasLimit := ctx.Uint64(gasLimitFlag.Name) - destinationAddress := ctx.String(destAddressFlag.Name) - chainID := ctx.String(chainIDFlag.Name) - amount := ctx.String(AmountFlag.Name) - callDataString := ctx.String(callDataFlag.Name) - destinationNetwork := uint32(ctx.Uint(destNetworkFlag.Name)) - isForced := ctx.Bool(forceFlag.Name) - timeoutTxnReceipt := ctx.Uint64(timeoutFlag.Name) - RPCURL := ctx.String(rpcURLFlag.Name) +func bridgeWETHMessage(cmd *cobra.Command) error { + bridgeAddress := *inputUlxlyArgs.bridgeAddress + privateKey := *inputUlxlyArgs.privateKey + gasLimit := *inputUlxlyArgs.gasLimit + destinationAddress := *inputUlxlyArgs.destAddress + chainID := *inputUlxlyArgs.chainID + amount := *inputUlxlyArgs.value + callDataString := *inputUlxlyArgs.callData + destinationNetwork := *inputUlxlyArgs.destNetwork + isForced := *inputUlxlyArgs.forceUpdate + timeoutTxnReceipt := *inputUlxlyArgs.timeout + RPCURL := *inputUlxlyArgs.rpcURL + // Dial the Ethereum RPC server. - client, err := ethclient.DialContext(ctx.Context, RPCURL) + client, err := ethclient.DialContext(cmd.Context(), RPCURL) if err != nil { log.Error().Err(err).Msg("Unable to Dial RPC") return err } defer client.Close() // Initialize and assign variables required to send transaction payload - bridgeV2, toAddress, auth, err := generateTransactionPayload(ctx.Context, client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID) + bridgeV2, toAddress, auth, err := generateTransactionPayload(cmd.Context(), client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID) if err != nil { log.Error().Err(err).Msg("error generating transaction payload") return err @@ -348,30 +305,30 @@ func bridgeWETHMessage(ctx *cli.Context) error { return err } log.Info().Msg("bridgeTxn: " + bridgeTxn.Hash().String()) - return WaitMineTransaction(ctx.Context, client, bridgeTxn, timeoutTxnReceipt) + return WaitMineTransaction(cmd.Context(), client, bridgeTxn, timeoutTxnReceipt) } -func claimAsset(ctx *cli.Context) error { - bridgeAddress := ctx.String(bridgeAddressFlag.Name) - privateKey := ctx.String(privKeyFlag.Name) - gasLimit := ctx.Uint64(gasLimitFlag.Name) - destinationAddress := ctx.String(destAddressFlag.Name) - chainID := ctx.String(chainIDFlag.Name) - timeoutTxnReceipt := ctx.Uint64(timeoutFlag.Name) - RPCURL := ctx.String(rpcURLFlag.Name) - depositCount := ctx.Uint64(depositCountFlag.Name) - depositNetwork := ctx.Uint64(depositNetworkFlag.Name) - bridgeServiceUrl := ctx.String(bridgeServiceUrlFlag.Name) +func claimAsset(cmd *cobra.Command) error { + bridgeAddress := *inputUlxlyArgs.bridgeAddress + privateKey := *inputUlxlyArgs.privateKey + gasLimit := *inputUlxlyArgs.gasLimit + destinationAddress := *inputUlxlyArgs.destAddress + chainID := *inputUlxlyArgs.chainID + timeoutTxnReceipt := *inputUlxlyArgs.timeout + RPCURL := *inputUlxlyArgs.rpcURL + depositCount := *inputUlxlyArgs.depositCount + depositNetwork := *inputUlxlyArgs.depositNetwork + bridgeServiceUrl := *inputUlxlyArgs.bridgeServiceURL // Dial Ethereum client - client, err := ethclient.DialContext(ctx.Context, RPCURL) + client, err := ethclient.DialContext(cmd.Context(), RPCURL) if err != nil { log.Error().Err(err).Msg("Unable to Dial RPC") return err } defer client.Close() // Initialize and assign variables required to send transaction payload - bridgeV2, toAddress, auth, err := generateTransactionPayload(ctx.Context, client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID) + bridgeV2, toAddress, auth, err := generateTransactionPayload(cmd.Context(), client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID) if err != nil { log.Error().Err(err).Msg("error generating transaction payload") return err @@ -406,30 +363,30 @@ func claimAsset(ctx *cli.Context) error { return err } log.Info().Msg("claimTxn: " + claimTxn.Hash().String()) - return WaitMineTransaction(ctx.Context, client, claimTxn, timeoutTxnReceipt) + return WaitMineTransaction(cmd.Context(), client, claimTxn, timeoutTxnReceipt) } -func claimMessage(ctx *cli.Context) error { - bridgeAddress := ctx.String(bridgeAddressFlag.Name) - privateKey := ctx.String(privKeyFlag.Name) - gasLimit := ctx.Uint64(gasLimitFlag.Name) - destinationAddress := ctx.String(destAddressFlag.Name) - chainID := ctx.String(chainIDFlag.Name) - timeoutTxnReceipt := ctx.Uint64(timeoutFlag.Name) - RPCURL := ctx.String(rpcURLFlag.Name) - depositCount := ctx.Uint64(depositCountFlag.Name) - depositNetwork := ctx.Uint64(depositNetworkFlag.Name) - bridgeServiceUrl := ctx.String(bridgeServiceUrlFlag.Name) +func claimMessage(cmd *cobra.Command) error { + bridgeAddress := *inputUlxlyArgs.bridgeAddress + privateKey := *inputUlxlyArgs.privateKey + gasLimit := *inputUlxlyArgs.gasLimit + destinationAddress := *inputUlxlyArgs.destAddress + chainID := *inputUlxlyArgs.chainID + timeoutTxnReceipt := *inputUlxlyArgs.timeout + RPCURL := *inputUlxlyArgs.rpcURL + depositCount := *inputUlxlyArgs.depositCount + depositNetwork := *inputUlxlyArgs.depositNetwork + bridgeServiceUrl := *inputUlxlyArgs.bridgeServiceURL // Dial Ethereum client - client, err := ethclient.DialContext(ctx.Context, RPCURL) + client, err := ethclient.DialContext(cmd.Context(), RPCURL) if err != nil { log.Error().Err(err).Msg("Unable to Dial RPC") return err } defer client.Close() // Initialize and assign variables required to send transaction payload - bridgeV2, toAddress, auth, err := generateTransactionPayload(ctx.Context, client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID) + bridgeV2, toAddress, auth, err := generateTransactionPayload(cmd.Context(), client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID) if err != nil { log.Error().Err(err).Msg("error generating transaction payload") return err @@ -456,7 +413,7 @@ func claimMessage(ctx *cli.Context) error { return err } log.Info().Msg("claimTxn: " + claimTxn.Hash().String()) - return WaitMineTransaction(ctx.Context, client, claimTxn, timeoutTxnReceipt) + return WaitMineTransaction(cmd.Context(), client, claimTxn, timeoutTxnReceipt) } // Wait for the transaction to be mined @@ -491,14 +448,14 @@ func WaitMineTransaction(ctx context.Context, client *ethclient.Client, tx *type } } -func getInputData(ctx *cli.Context) ([]byte, error) { - fileName := ctx.String(inputFileNameFlag.Name) +func getInputData(args []string) ([]byte, error) { + fileName := *inputUlxlyArgs.inputFileName if fileName != "" { return os.ReadFile(fileName) } - if ctx.Args().Len() > 1 { - concat := strings.Join(ctx.Args().Slice()[1:], " ") + if len(args) > 1 { + concat := strings.Join(args[1:], " ") return []byte(concat), nil } @@ -901,292 +858,218 @@ var proofUsage string //go:embed depositGetUsage.md var depositGetUsage string -func initCli(cmd *cobra.Command, args []string) { - app := cli.NewApp() - app.Name = "uLxLy" - app.Version = "v0.0.1" - app.Commands = []*cli.Command{ - { - Name: "ulxly", - Aliases: []string{}, - Usage: "options for ulxly", - Subcommands: []*cli.Command{ - { - Name: "bridge-asset", - Aliases: []string{}, - Usage: "Make a uLxLy bridge asset transaction", - Description: bridgeAssetUsage, - Action: bridgeAsset, - Flags: []cli.Flag{ - gasLimitFlag, - chainIDFlag, - privKeyFlag, - AmountFlag, - rpcURLFlag, - bridgeAddressFlag, - destNetworkFlag, - destAddressFlag, - tokenAddressFlag, - forceFlag, - callDataFlag, - timeoutFlag, - }, - }, - { - Name: "bridge-message", - Aliases: []string{}, - Usage: "Make a uLxLy bridge message transaction", - Description: bridgeMessageUsage, - Action: bridgeMessage, - Flags: []cli.Flag{ - gasLimitFlag, - chainIDFlag, - privKeyFlag, - AmountFlag, - rpcURLFlag, - bridgeAddressFlag, - destNetworkFlag, - destAddressFlag, - tokenAddressFlag, - forceFlag, - callDataFlag, - timeoutFlag, - }, - }, - { - Name: "bridge-message-weth", - Aliases: []string{}, - Usage: "Make a uLxLy bridge weth message transaction", - Description: bridgeWETHMessageUsage, - Action: bridgeWETHMessage, - Flags: []cli.Flag{ - gasLimitFlag, - chainIDFlag, - privKeyFlag, - AmountFlag, - rpcURLFlag, - bridgeAddressFlag, - destNetworkFlag, - destAddressFlag, - forceFlag, - callDataFlag, - timeoutFlag, - }, - }, - { - Name: "claim-asset", - Aliases: []string{}, - Usage: "Make a uLxLy claim asset transaction", - Description: claimAssetUsage, - Action: claimAsset, - Flags: []cli.Flag{ - depositCountFlag, - depositNetworkFlag, - bridgeServiceUrlFlag, - destAddressFlag, - rpcURLFlag, - privKeyFlag, - bridgeAddressFlag, - gasLimitFlag, - chainIDFlag, - timeoutFlag, - }, - }, - { - Name: "claim-message", - Aliases: []string{}, - Usage: "Make a uLxLy claim message transaction", - Description: claimMessageUsage, - Action: claimMessage, - Flags: []cli.Flag{ - depositCountFlag, - depositNetworkFlag, - bridgeServiceUrlFlag, - destAddressFlag, - rpcURLFlag, - privKeyFlag, - bridgeAddressFlag, - gasLimitFlag, - chainIDFlag, - timeoutFlag, - }, - }, - { - Name: "empty-proof", - Aliases: []string{}, - Usage: "Print an empty proof structure", - Description: "Use this command to print an empty proof response that's filled with zero-valued siblings like 0x0000000000000000000000000000000000000000000000000000000000000000. This can be useful when you need to submit a dummy proof.", - Action: emptyProof, - }, - { - Name: "zero-proof", - Aliases: []string{}, - Usage: "Print a proof structure with the zero hashes", - Description: `Use this command to print a proof response that's filled with the zero - hashes. This values are very helpful for debugging because it would - tell you how populated the tree is and roughly which leaves and - siblings are empty. It's also helpful for sanity checking a proof - response to understand if the hashed value is part of the zero hashes - or if it's actually an intermediate hash.`, - Action: zeroProof, - }, - { - Name: "proof", - Aliases: []string{}, - Usage: "Generate a merkle proof", - Description: proofUsage, - Action: proof, - Flags: []cli.Flag{depositNumberFlag}, - }, - { - Name: "deposit-get", - Aliases: []string{}, - Usage: "Get a range of deposits", - Description: depositGetUsage, - Action: readDeposit, - Flags: []cli.Flag{ - fromBlockFlag, - toBlockFlag, - filterFlag, - bridgeAddressFlag, - rpcURLFlag, - }, - }, - }, - }, - } - - err := app.Run(os.Args) - if err != nil { - fmt.Printf("\nError: %v\n", err) - os.Exit(1) - } -} - -var gasLimitFlag = &cli.Uint64Flag{ - Name: "gas-limit", - Aliases: []string{"gl"}, - Usage: "This param is used to force the GasLimit", - Required: false, -} -var chainIDFlag = &cli.StringFlag{ - Name: "chain-id", - Aliases: []string{"id"}, - Usage: "This param is used to force the chainID", - Required: false, -} -var privKeyFlag = &cli.StringFlag{ - Name: "private-key", - Aliases: []string{"pk"}, - Usage: "This param is used to set the private key", - Required: true, -} -var AmountFlag = &cli.StringFlag{ - Name: "amount", - Aliases: []string{"a"}, - Usage: "This param is used to set the amount", - Required: true, -} -var rpcURLFlag = &cli.StringFlag{ - Name: "rpc-url", - Aliases: []string{"u"}, - Usage: "This param is used to set the rpc url", - Required: true, -} -var bridgeAddressFlag = &cli.StringFlag{ - Name: "bridge-address", - Aliases: []string{"b"}, - Usage: "This param is used to set the bridge address", - Required: true, -} -var destNetworkFlag = &cli.UintFlag{ - Name: "destination-network", - Aliases: []string{"dest-net"}, - Usage: "This param is used to set the destination network", - Required: true, -} -var destAddressFlag = &cli.StringFlag{ - Name: "destination-address", - Aliases: []string{"dest-addr"}, - Usage: "This param is used to set the destination address", - Required: false, -} -var tokenAddressFlag = &cli.StringFlag{ - Name: "token-address", - Aliases: []string{"token"}, - Usage: "This param is used to set the token address", - Required: false, -} -var forceFlag = &cli.BoolFlag{ - Name: "force-update-root", - Aliases: []string{"f"}, - Usage: "This param is used to force the ger update in the smc", - Required: false, - Value: true, -} -var callDataFlag = &cli.StringFlag{ - Name: "call-data", - Aliases: []string{"data"}, - Usage: "This param is used to set the callData", - Required: false, - Value: "0x", -} -var timeoutFlag = &cli.Uint64Flag{ - Name: "transaction-receipt-timeout", - Aliases: []string{"timeout"}, - Usage: "This param is used to change the timeout interval", - Value: 60, - Required: false, -} -var depositCountFlag = &cli.Uint64Flag{ - Name: "deposit-count", - Aliases: []string{"cnt"}, - Usage: "This param is used to specify the deposit counter", - Required: true, -} -var depositNetworkFlag = &cli.Uint64Flag{ - Name: "deposit-network", - Aliases: []string{"net"}, - Usage: "This param is used to specify the deposit network", - Required: true, -} -var bridgeServiceUrlFlag = &cli.StringFlag{ - Name: "bridge-service-url", - Aliases: []string{"bridge-url"}, - Usage: "This param is used to specify the bridge service url", - Required: true, +var ULxLyCmd = &cobra.Command{ + Use: "ulxly", + Short: "Utilities for interacting with the lxly bridge", + Long: "These are low level tools for directly scanning bridge events and constructing proofs.", + Args: cobra.NoArgs, } -var inputFileNameFlag = &cli.StringFlag{ - Name: "file-name", - Aliases: []string{"file"}, - Usage: "The filename with ndjson data of deposits", - Required: true, +var ulxlyBridgeAndClaimCmd = &cobra.Command{ + Args: cobra.NoArgs, + Hidden: true, } -var fromBlockFlag = &cli.Uint64Flag{ - Name: "from-block", - Aliases: []string{"from"}, - Usage: "The block height to start query at.", - Value: 0, - Required: false, + +var ulxlxBridgeCmd = &cobra.Command{ + Use: "bridge", + Short: "commands for making deposits to the uLxLy bridge", + Args: cobra.NoArgs, } -var toBlockFlag = &cli.Uint64Flag{ - Name: "to-block", - Aliases: []string{"to"}, - Usage: "The block height to start query at.", - Value: 0, - Required: false, + +var ulxlyClaimCmd = &cobra.Command{ + Use: "claim", + Short: "commands for making claims of deposits from the uLxLy bridge", + Args: cobra.NoArgs, } -var filterFlag = &cli.Uint64Flag{ - Name: "filter-size", - Aliases: []string{"filter"}, - Usage: "The batch size for individual filter queries.", - Value: 1000, - Required: false, + +type ulxlyArgs struct { + gasLimit *uint64 + chainID *string + privateKey *string + value *string + rpcURL *string + bridgeAddress *string + destNetwork *uint32 + destAddress *string + tokenAddress *string + forceUpdate *bool + callData *string + timeout *uint64 + depositCount *uint64 + depositNetwork *uint64 + bridgeServiceURL *string + inputFileName *string + fromBlock *uint64 + toBlock *uint64 + filterSize *uint64 + depositNumber *uint64 } -var depositNumberFlag = &cli.Uint64Flag{ - Name: "deposit-number", - Aliases: []string{"deposit"}, - Usage: "The deposit that we would like to prove", - Value: 0, - Required: false, + +var inputUlxlyArgs = ulxlyArgs{} + +var ( + bridgeAssetCommand *cobra.Command + bridgeMessageCommand *cobra.Command + bridgeMessageWETHCommand *cobra.Command + claimAssetCommand *cobra.Command + claimMessageCommand *cobra.Command + emptyProofCommand *cobra.Command + zeroProofCommand *cobra.Command + proofCommand *cobra.Command + getDepositCommand *cobra.Command +) + +const ( + ArgGasLimit = "gas-limit" + ArgChainID = "chain-id" + ArgPrivateKey = "private-key" + ArgValue = "value" + ArgRPCURL = "rpc-url" + ArgBridgeAddress = "bridge-address" + ArgDestNetwork = "destination-network" + ArgDestAddress = "destination-address" + ArgForceUpdate = "force-update-root" + ArgCallData = "call-data" + ArgTimeout = "transaction-receipt-timeout" + ArgDepositCount = "deposit-count" + ArgDepositNetwork = "deposit-network" + ArgBridgeServiceURL = "bridge-service-url" + ArgFileName = "file-name" + ArgFromBlock = "from-block" + ArgToBlock = "to-block" + ArgFilterSize = "filter-size" +) + +func init() { + bridgeAssetCommand = &cobra.Command{ + Use: "asset", + Short: "send a single deposit of value or an ERC20 into the bridge", + RunE: func(cmd *cobra.Command, args []string) error { + return bridgeAsset(cmd) + }, + } + bridgeMessageCommand = &cobra.Command{ + Use: "message", + Short: "send some value along with call data into the bridge", + RunE: func(cmd *cobra.Command, args []string) error { + return bridgeMessage(cmd) + }, + } + bridgeMessageWETHCommand = &cobra.Command{ + Use: "weth", + Short: "send some WETH into the bridge", + RunE: func(cmd *cobra.Command, args []string) error { + return bridgeWETHMessage(cmd) + }, + } + claimAssetCommand = &cobra.Command{ + Use: "asset", + Short: "perform a claim of a given deposit in the bridge", + RunE: func(cmd *cobra.Command, args []string) error { + return claimAsset(cmd) + }, + } + claimMessageCommand = &cobra.Command{ + Use: "message", + Short: "perform a claim of a given message in the birdge", + RunE: func(cmd *cobra.Command, args []string) error { + return nil + }, + } + emptyProofCommand = &cobra.Command{ + Use: "empty-proof", + Short: "create an empty proof", + Long: "Use this command to print an empty proof response that's filled with zero-valued siblings like 0x0000000000000000000000000000000000000000000000000000000000000000. This can be useful when you need to submit a dummy proof.", + RunE: func(cmd *cobra.Command, args []string) error { + return emptyProof() + }, + } + zeroProofCommand = &cobra.Command{ + Use: "zero-proof", + Short: "create a proof that's filled with zeros", + Long: `Use this command to print a proof response that's filled with the zero + hashes. This values are very helpful for debugging because it would + tell you how populated the tree is and roughly which leaves and + siblings are empty. It's also helpful for sanity checking a proof + response to understand if the hashed value is part of the zero hashes + or if it's actually an intermediate hash.`, + RunE: func(cmd *cobra.Command, args []string) error { + return zeroProof() + }, + } + proofCommand = &cobra.Command{ + Use: "proof", + Short: "generate a proof for a given range of deposits", + RunE: func(cmd *cobra.Command, args []string) error { + return proof(args) + }, + } + getDepositCommand = &cobra.Command{ + Use: "get-deposits", + Short: "generate ndjson for each bridge deposit over a particular range of blocks", + RunE: func(cmd *cobra.Command, args []string) error { + return readDeposit(cmd) + }, + } + + // Arguments for both bridge and claim + inputUlxlyArgs.gasLimit = ulxlyBridgeAndClaimCmd.PersistentFlags().Uint64(ArgGasLimit, 0, "force a gas limit when sending a transaction") + inputUlxlyArgs.chainID = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgChainID, "", "set the chain id to be used in the transaction") + inputUlxlyArgs.privateKey = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgPrivateKey, "", "the hex encoded private key to be used when sending the tx") + inputUlxlyArgs.rpcURL = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgRPCURL, "", "the URL of the RPC to send the transaction") + inputUlxlyArgs.bridgeAddress = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgBridgeAddress, "", "the address of the lxly bridge") + inputUlxlyArgs.destAddress = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgDestAddress, "", "the address where the bridge will be sent to") + inputUlxlyArgs.callData = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgCallData, "0x", "call data to be passed directly with bridge-message or as an ERC20 Permit") + inputUlxlyArgs.timeout = ulxlyBridgeAndClaimCmd.PersistentFlags().Uint64(ArgTimeout, 60, "the amount of time to wait while trying to confirm a transaction receipt") + ulxlyBridgeAndClaimCmd.MarkFlagRequired(ArgPrivateKey) + ulxlyBridgeAndClaimCmd.MarkFlagRequired(ArgRPCURL) + ulxlyBridgeAndClaimCmd.MarkFlagRequired(ArgBridgeAddress) + ulxlyBridgeAndClaimCmd.MarkFlagRequired(ArgDestAddress) + + // bridge specific args + inputUlxlyArgs.forceUpdate = ulxlxBridgeCmd.PersistentFlags().Bool(ArgForceUpdate, true, "indicates if the new global exit root is updated or not") + inputUlxlyArgs.value = ulxlxBridgeCmd.PersistentFlags().String(ArgValue, "", "the amount in wei to be sent along with the transaction") + inputUlxlyArgs.destNetwork = ulxlxBridgeCmd.PersistentFlags().Uint32(ArgDestNetwork, 0, "the rollup id of the destination network") + ulxlxBridgeCmd.MarkFlagRequired(ArgDestNetwork) + + // Claim specific args + inputUlxlyArgs.depositCount = ulxlyClaimCmd.PersistentFlags().Uint64(ArgDepositCount, 0, "the deposit count of the bridge transaction") + inputUlxlyArgs.depositNetwork = ulxlyClaimCmd.PersistentFlags().Uint64(ArgDepositNetwork, 0, "the rollup id of the network where the bridge is being claimed") + inputUlxlyArgs.bridgeServiceURL = ulxlyClaimCmd.PersistentFlags().String(ArgBridgeServiceURL, "", "the URL of the bridge service") + ulxlyClaimCmd.MarkFlagRequired(ArgDepositCount) + ulxlyClaimCmd.MarkFlagRequired(ArgDepositNetwork) + ulxlyClaimCmd.MarkFlagRequired(ArgBridgeServiceURL) + + // Args that are just for the get deposit command + inputUlxlyArgs.inputFileName = getDepositCommand.Flags().String(ArgFileName, "", "An ndjson file with deposit data") + inputUlxlyArgs.fromBlock = getDepositCommand.Flags().Uint64(ArgFromBlock, 0, "The start of the range of blocks to retrieve") + inputUlxlyArgs.toBlock = getDepositCommand.Flags().Uint64(ArgToBlock, 0, "The end of the range of blocks to retrieve") + inputUlxlyArgs.filterSize = getDepositCommand.Flags().Uint64(ArgFilterSize, 1000, "The batch size for individual filter queries") + getDepositCommand.Flags().String(ArgRPCURL, "", "The RPC URL to read deposit data") + getDepositCommand.MarkFlagRequired(ArgFromBlock) + getDepositCommand.MarkFlagRequired(ArgToBlock) + getDepositCommand.MarkFlagRequired(ArgRPCURL) + + // Top Level + ULxLyCmd.AddCommand(ulxlyBridgeAndClaimCmd) + ULxLyCmd.AddCommand(emptyProofCommand) + ULxLyCmd.AddCommand(zeroProofCommand) + ULxLyCmd.AddCommand(proofCommand) + ULxLyCmd.AddCommand(getDepositCommand) + + ULxLyCmd.AddCommand(ulxlxBridgeCmd) + ULxLyCmd.AddCommand(ulxlyClaimCmd) + + // Bridge and Claim + ulxlyBridgeAndClaimCmd.AddCommand(ulxlxBridgeCmd) + ulxlyBridgeAndClaimCmd.AddCommand(ulxlyClaimCmd) + + // Bridge + ulxlxBridgeCmd.AddCommand(bridgeAssetCommand) + ulxlxBridgeCmd.AddCommand(bridgeMessageCommand) + ulxlxBridgeCmd.AddCommand(bridgeMessageWETHCommand) + + // Claim + ulxlyClaimCmd.AddCommand(claimAssetCommand) + ulxlyClaimCmd.AddCommand(claimMessageCommand) + } From f0e026df63f889cc2dcdd694e5a9ace829437a4c Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Wed, 4 Dec 2024 19:18:51 -0500 Subject: [PATCH 03/17] fix: minor issues related to missing field and 0x prefix --- cmd/ulxly/ulxly.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index 6ff71888..8d4e323f 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -699,6 +699,7 @@ func generateEmptyHashes(height uint8) []common.Hash { } func generateTransactionPayload(ctx context.Context, client *ethclient.Client, ulxlyInputArgBridge string, ulxlyInputArgPvtKey string, ulxlyInputArgGasLimit uint64, ulxlyInputArgDestAddr string, ulxlyInputArgChainID string) (bridgeV2 *ulxly.Ulxly, toAddress common.Address, opts *bind.TransactOpts, err error) { + ulxlyInputArgPvtKey = strings.TrimPrefix(ulxlyInputArgPvtKey, "0x") bridgeV2, err = ulxly.NewUlxly(common.HexToAddress(ulxlyInputArgBridge), client) if err != nil { return @@ -937,6 +938,7 @@ const ( ArgFromBlock = "from-block" ArgToBlock = "to-block" ArgFilterSize = "filter-size" + ArgTokenAddress = "token-address" ) func init() { @@ -1029,6 +1031,7 @@ func init() { inputUlxlyArgs.forceUpdate = ulxlxBridgeCmd.PersistentFlags().Bool(ArgForceUpdate, true, "indicates if the new global exit root is updated or not") inputUlxlyArgs.value = ulxlxBridgeCmd.PersistentFlags().String(ArgValue, "", "the amount in wei to be sent along with the transaction") inputUlxlyArgs.destNetwork = ulxlxBridgeCmd.PersistentFlags().Uint32(ArgDestNetwork, 0, "the rollup id of the destination network") + inputUlxlyArgs.tokenAddress = ulxlxBridgeCmd.PersistentFlags().String(ArgTokenAddress, "0x0000000000000000000000000000000000000000", "the address of an ERC20 token to be used") ulxlxBridgeCmd.MarkFlagRequired(ArgDestNetwork) // Claim specific args @@ -1048,7 +1051,7 @@ func init() { getDepositCommand.MarkFlagRequired(ArgFromBlock) getDepositCommand.MarkFlagRequired(ArgToBlock) getDepositCommand.MarkFlagRequired(ArgRPCURL) - + // Top Level ULxLyCmd.AddCommand(ulxlyBridgeAndClaimCmd) ULxLyCmd.AddCommand(emptyProofCommand) From 0bf4f494c67e7fed3597fe1e3d3588765c28f790 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Wed, 4 Dec 2024 20:15:19 -0500 Subject: [PATCH 04/17] fix: deposit_cnt type, missing function call, call data in wrong level --- cmd/ulxly/ulxly.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index 8d4e323f..e01dea94 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -70,7 +70,7 @@ type BridgeDeposit struct { DestNet uint32 `json:"dest_net"` DestAddr string `json:"dest_addr"` BlockNum string `json:"block_num"` - DepositCnt uint32 `json:"deposit_cnt"` + DepositCnt uint32 `json:"deposit_cnt,string"` NetworkID uint32 `json:"network_id"` TxHash string `json:"tx_hash"` ClaimTxHash string `json:"claim_tx_hash"` @@ -974,7 +974,7 @@ func init() { Use: "message", Short: "perform a claim of a given message in the birdge", RunE: func(cmd *cobra.Command, args []string) error { - return nil + return claimMessage(cmd) }, } emptyProofCommand = &cobra.Command{ @@ -1020,7 +1020,6 @@ func init() { inputUlxlyArgs.rpcURL = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgRPCURL, "", "the URL of the RPC to send the transaction") inputUlxlyArgs.bridgeAddress = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgBridgeAddress, "", "the address of the lxly bridge") inputUlxlyArgs.destAddress = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgDestAddress, "", "the address where the bridge will be sent to") - inputUlxlyArgs.callData = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgCallData, "0x", "call data to be passed directly with bridge-message or as an ERC20 Permit") inputUlxlyArgs.timeout = ulxlyBridgeAndClaimCmd.PersistentFlags().Uint64(ArgTimeout, 60, "the amount of time to wait while trying to confirm a transaction receipt") ulxlyBridgeAndClaimCmd.MarkFlagRequired(ArgPrivateKey) ulxlyBridgeAndClaimCmd.MarkFlagRequired(ArgRPCURL) @@ -1032,6 +1031,7 @@ func init() { inputUlxlyArgs.value = ulxlxBridgeCmd.PersistentFlags().String(ArgValue, "", "the amount in wei to be sent along with the transaction") inputUlxlyArgs.destNetwork = ulxlxBridgeCmd.PersistentFlags().Uint32(ArgDestNetwork, 0, "the rollup id of the destination network") inputUlxlyArgs.tokenAddress = ulxlxBridgeCmd.PersistentFlags().String(ArgTokenAddress, "0x0000000000000000000000000000000000000000", "the address of an ERC20 token to be used") + inputUlxlyArgs.callData = ulxlxBridgeCmd.PersistentFlags().String(ArgCallData, "0x", "call data to be passed directly with bridge-message or as an ERC20 Permit") ulxlxBridgeCmd.MarkFlagRequired(ArgDestNetwork) // Claim specific args From 9a6b9eb6ac6743238f4d286ae2de2a6b1aa3252b Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Wed, 4 Dec 2024 23:15:30 -0500 Subject: [PATCH 05/17] feat: adding ability to override global index while the bug is fixed --- cmd/ulxly/ulxly.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index e01dea94..5087774b 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -319,6 +319,7 @@ func claimAsset(cmd *cobra.Command) error { depositCount := *inputUlxlyArgs.depositCount depositNetwork := *inputUlxlyArgs.depositNetwork bridgeServiceUrl := *inputUlxlyArgs.bridgeServiceURL + globalIndexOverride := *inputUlxlyArgs.globalIndex // Dial Ethereum client client, err := ethclient.DialContext(cmd.Context(), RPCURL) @@ -356,7 +357,9 @@ func claimAsset(cmd *cobra.Command) error { if leafType != 0 { log.Warn().Msg("Deposit leafType is not asset") } - + if globalIndexOverride != "" { + globalIndex.SetString(globalIndexOverride, 10) + } claimTxn, err := bridgeV2.ClaimAsset(auth, merkleProofArray, rollupMerkleProofArray, globalIndex, [32]byte(mainExitRoot), [32]byte(rollupExitRoot), claimOriginalNetwork, originAddress, claimDestNetwork, toAddress, amount, metadata) if err != nil { log.Error().Err(err).Msg("Unable to interact with bridge contract") @@ -377,6 +380,7 @@ func claimMessage(cmd *cobra.Command) error { depositCount := *inputUlxlyArgs.depositCount depositNetwork := *inputUlxlyArgs.depositNetwork bridgeServiceUrl := *inputUlxlyArgs.bridgeServiceURL + globalIndexOverride := *inputUlxlyArgs.globalIndex // Dial Ethereum client client, err := ethclient.DialContext(cmd.Context(), RPCURL) @@ -406,7 +410,10 @@ func claimMessage(cmd *cobra.Command) error { if leafType != 1 { log.Warn().Msg("Deposit leafType is not message") } - + if globalIndexOverride != "" { + globalIndex.SetString(globalIndexOverride, 10) + } + //ClaimMessage(opts *bind.TransactOpts, smtProofLocalExitRoot [32][32]byte, smtProofRollupExitRoot [32][32]byte, globalIndex *big.Int, mainnetExitRoot [32]byte, rollupExitRoot [32]byte, originNetwork uint32, originAddress common.Address, destinationNetwork uint32, destinationAddress common.Address, amount *big.Int, metadata []byte) (*types.Transaction, error) { claimTxn, err := bridgeV2.ClaimMessage(auth, merkleProofArray, rollupMerkleProofArray, globalIndex, [32]byte(mainExitRoot), [32]byte(rollupExitRoot), claimOriginalNetwork, originAddress, claimDestNetwork, toAddress, amount, metadata) if err != nil { log.Error().Err(err).Msg("Unable to interact with bridge contract") @@ -903,6 +910,7 @@ type ulxlyArgs struct { toBlock *uint64 filterSize *uint64 depositNumber *uint64 + globalIndex *string } var inputUlxlyArgs = ulxlyArgs{} @@ -939,6 +947,7 @@ const ( ArgToBlock = "to-block" ArgFilterSize = "filter-size" ArgTokenAddress = "token-address" + ArgGlobalIndex = "global-index" ) func init() { @@ -1038,6 +1047,8 @@ func init() { inputUlxlyArgs.depositCount = ulxlyClaimCmd.PersistentFlags().Uint64(ArgDepositCount, 0, "the deposit count of the bridge transaction") inputUlxlyArgs.depositNetwork = ulxlyClaimCmd.PersistentFlags().Uint64(ArgDepositNetwork, 0, "the rollup id of the network where the bridge is being claimed") inputUlxlyArgs.bridgeServiceURL = ulxlyClaimCmd.PersistentFlags().String(ArgBridgeServiceURL, "", "the URL of the bridge service") + inputUlxlyArgs.globalIndex = ulxlyClaimCmd.PersistentFlags().String(ArgGlobalIndex, "", "an override of the global index value") + ulxlyClaimCmd.MarkFlagRequired(ArgDepositCount) ulxlyClaimCmd.MarkFlagRequired(ArgDepositNetwork) ulxlyClaimCmd.MarkFlagRequired(ArgBridgeServiceURL) From a99392198acacbcbfa05db2b9c5a36ccd2902a44 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Thu, 5 Dec 2024 09:29:57 -0500 Subject: [PATCH 06/17] fix: dropping string tag --- cmd/ulxly/ulxly.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index 5087774b..b4a141a3 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -70,7 +70,7 @@ type BridgeDeposit struct { DestNet uint32 `json:"dest_net"` DestAddr string `json:"dest_addr"` BlockNum string `json:"block_num"` - DepositCnt uint32 `json:"deposit_cnt,string"` + DepositCnt uint32 `json:"deposit_cnt"` NetworkID uint32 `json:"network_id"` TxHash string `json:"tx_hash"` ClaimTxHash string `json:"claim_tx_hash"` From 50349162e14335636a8109cceaad635c84dc4844 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Thu, 5 Dec 2024 19:18:46 -0500 Subject: [PATCH 07/17] feat: adding dry-run and gas-price --- cmd/ulxly/ulxly.go | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index b4a141a3..14dbd69e 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -425,6 +425,10 @@ func claimMessage(cmd *cobra.Command) error { // Wait for the transaction to be mined func WaitMineTransaction(ctx context.Context, client *ethclient.Client, tx *types.Transaction, txTimeout uint64) error { + if inputUlxlyArgs.dryRun != nil && *inputUlxlyArgs.dryRun { + log.Info().Msg("Skipping receipt check. Dry run is enabled") + return nil + } txnMinedTimer := time.NewTimer(time.Duration(txTimeout) * time.Second) defer txnMinedTimer.Stop() for { @@ -745,6 +749,14 @@ func generateTransactionPayload(ctx context.Context, client *ethclient.Client, u log.Error().Err(err).Msg("Cannot generate transactionOpts") return } + if inputUlxlyArgs.gasPrice != nil && *inputUlxlyArgs.gasPrice != "" { + gasPrice := new(big.Int) + gasPrice.SetString(*inputUlxlyArgs.gasPrice, 10) + opts.GasPrice = gasPrice + } + if inputUlxlyArgs.dryRun != nil && *inputUlxlyArgs.dryRun { + opts.NoSend = true + } opts.Context = ctx opts.GasLimit = gasLimit toAddress = common.HexToAddress(ulxlyInputArgDestAddr) @@ -911,6 +923,8 @@ type ulxlyArgs struct { filterSize *uint64 depositNumber *uint64 globalIndex *string + gasPrice *string + dryRun *bool } var inputUlxlyArgs = ulxlyArgs{} @@ -948,6 +962,8 @@ const ( ArgFilterSize = "filter-size" ArgTokenAddress = "token-address" ArgGlobalIndex = "global-index" + ArgDryRun = "dry-run" + ArgGasPrice = "gas-price" ) func init() { @@ -1030,10 +1046,12 @@ func init() { inputUlxlyArgs.bridgeAddress = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgBridgeAddress, "", "the address of the lxly bridge") inputUlxlyArgs.destAddress = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgDestAddress, "", "the address where the bridge will be sent to") inputUlxlyArgs.timeout = ulxlyBridgeAndClaimCmd.PersistentFlags().Uint64(ArgTimeout, 60, "the amount of time to wait while trying to confirm a transaction receipt") - ulxlyBridgeAndClaimCmd.MarkFlagRequired(ArgPrivateKey) - ulxlyBridgeAndClaimCmd.MarkFlagRequired(ArgRPCURL) - ulxlyBridgeAndClaimCmd.MarkFlagRequired(ArgBridgeAddress) - ulxlyBridgeAndClaimCmd.MarkFlagRequired(ArgDestAddress) + inputUlxlyArgs.gasPrice = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgGasPrice, "", "the gas price to be used") + inputUlxlyArgs.dryRun = ulxlyBridgeAndClaimCmd.PersistentFlags().Bool(ArgDryRun, false, "do all of the transaction steps but do not send the transaction") + ulxlyBridgeAndClaimCmd.MarkPersistentFlagRequired(ArgPrivateKey) + ulxlyBridgeAndClaimCmd.MarkPersistentFlagRequired(ArgRPCURL) + ulxlyBridgeAndClaimCmd.MarkPersistentFlagRequired(ArgBridgeAddress) + ulxlyBridgeAndClaimCmd.MarkPersistentFlagRequired(ArgDestAddress) // bridge specific args inputUlxlyArgs.forceUpdate = ulxlxBridgeCmd.PersistentFlags().Bool(ArgForceUpdate, true, "indicates if the new global exit root is updated or not") @@ -1041,7 +1059,7 @@ func init() { inputUlxlyArgs.destNetwork = ulxlxBridgeCmd.PersistentFlags().Uint32(ArgDestNetwork, 0, "the rollup id of the destination network") inputUlxlyArgs.tokenAddress = ulxlxBridgeCmd.PersistentFlags().String(ArgTokenAddress, "0x0000000000000000000000000000000000000000", "the address of an ERC20 token to be used") inputUlxlyArgs.callData = ulxlxBridgeCmd.PersistentFlags().String(ArgCallData, "0x", "call data to be passed directly with bridge-message or as an ERC20 Permit") - ulxlxBridgeCmd.MarkFlagRequired(ArgDestNetwork) + ulxlxBridgeCmd.MarkPersistentFlagRequired(ArgDestNetwork) // Claim specific args inputUlxlyArgs.depositCount = ulxlyClaimCmd.PersistentFlags().Uint64(ArgDepositCount, 0, "the deposit count of the bridge transaction") @@ -1049,9 +1067,9 @@ func init() { inputUlxlyArgs.bridgeServiceURL = ulxlyClaimCmd.PersistentFlags().String(ArgBridgeServiceURL, "", "the URL of the bridge service") inputUlxlyArgs.globalIndex = ulxlyClaimCmd.PersistentFlags().String(ArgGlobalIndex, "", "an override of the global index value") - ulxlyClaimCmd.MarkFlagRequired(ArgDepositCount) - ulxlyClaimCmd.MarkFlagRequired(ArgDepositNetwork) - ulxlyClaimCmd.MarkFlagRequired(ArgBridgeServiceURL) + ulxlyClaimCmd.MarkPersistentFlagRequired(ArgDepositCount) + ulxlyClaimCmd.MarkPersistentFlagRequired(ArgDepositNetwork) + ulxlyClaimCmd.MarkPersistentFlagRequired(ArgBridgeServiceURL) // Args that are just for the get deposit command inputUlxlyArgs.inputFileName = getDepositCommand.Flags().String(ArgFileName, "", "An ndjson file with deposit data") From 9006c1a6d583da159d7a4ed8391985eee69debff Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Tue, 10 Dec 2024 13:33:06 -0500 Subject: [PATCH 08/17] feat: better logging of revert data --- cmd/ulxly/ulxly.go | 50 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index 14dbd69e..684f52c3 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -170,6 +170,40 @@ func zeroProof() error { return nil } +type JsonError struct { + Code int `json:"code"` + Message string `json:"message"` + Data interface{} `json:"data"` +} + +func logAndReturnJsonError(err error) error { + if err == nil { + return nil + } + + var jsonError JsonError + jsonErrorBytes, jsErr := json.Marshal(err) + if jsErr != nil { + log.Error().Err(err).Msg("Unable to interact with the bridge contract") + return err + } + + jsErr = json.Unmarshal(jsonErrorBytes, &jsonError) + if jsErr != nil { + log.Error().Err(err).Msg("Unable to interact with the bridge contract") + return err + } + + log.Error(). + Err(err). + Str("message", jsonError.Message). + Int("code", jsonError.Code). + Interface("data", jsonError.Data). + Msg("Unable to interact with bridge contract") + + return err +} + func bridgeAsset(cmd *cobra.Command) error { bridgeAddress := *inputUlxlyArgs.bridgeAddress privateKey := *inputUlxlyArgs.privateKey @@ -208,8 +242,7 @@ func bridgeAsset(cmd *cobra.Command) error { bridgeTxn, err := bridgeV2.BridgeAsset(auth, destinationNetwork, toAddress, value, tokenAddress, isForced, callData) if err != nil { - log.Error().Err(err).Msg("Unable to interact with bridge contract") - return err + return logAndReturnJsonError(err) } log.Info().Msg("bridgeTxn: " + bridgeTxn.Hash().String()) return WaitMineTransaction(cmd.Context(), client, bridgeTxn, timeoutTxnReceipt) @@ -253,8 +286,7 @@ func bridgeMessage(cmd *cobra.Command) error { bridgeTxn, err := bridgeV2.BridgeMessage(auth, destinationNetwork, toAddress, isForced, callData) if err != nil { - log.Error().Err(err).Msg("Unable to interact with bridge contract") - return err + return logAndReturnJsonError(err) } log.Info().Msg("bridgeTxn: " + bridgeTxn.Hash().String()) return WaitMineTransaction(cmd.Context(), client, bridgeTxn, timeoutTxnReceipt) @@ -301,8 +333,7 @@ func bridgeWETHMessage(cmd *cobra.Command) error { bridgeTxn, err := bridgeV2.BridgeMessageWETH(auth, destinationNetwork, toAddress, value, isForced, callData) if err != nil { - log.Error().Err(err).Msg("Unable to interact with bridge contract") - return err + return logAndReturnJsonError(err) } log.Info().Msg("bridgeTxn: " + bridgeTxn.Hash().String()) return WaitMineTransaction(cmd.Context(), client, bridgeTxn, timeoutTxnReceipt) @@ -362,8 +393,7 @@ func claimAsset(cmd *cobra.Command) error { } claimTxn, err := bridgeV2.ClaimAsset(auth, merkleProofArray, rollupMerkleProofArray, globalIndex, [32]byte(mainExitRoot), [32]byte(rollupExitRoot), claimOriginalNetwork, originAddress, claimDestNetwork, toAddress, amount, metadata) if err != nil { - log.Error().Err(err).Msg("Unable to interact with bridge contract") - return err + return logAndReturnJsonError(err) } log.Info().Msg("claimTxn: " + claimTxn.Hash().String()) return WaitMineTransaction(cmd.Context(), client, claimTxn, timeoutTxnReceipt) @@ -416,8 +446,7 @@ func claimMessage(cmd *cobra.Command) error { //ClaimMessage(opts *bind.TransactOpts, smtProofLocalExitRoot [32][32]byte, smtProofRollupExitRoot [32][32]byte, globalIndex *big.Int, mainnetExitRoot [32]byte, rollupExitRoot [32]byte, originNetwork uint32, originAddress common.Address, destinationNetwork uint32, destinationAddress common.Address, amount *big.Int, metadata []byte) (*types.Transaction, error) { claimTxn, err := bridgeV2.ClaimMessage(auth, merkleProofArray, rollupMerkleProofArray, globalIndex, [32]byte(mainExitRoot), [32]byte(rollupExitRoot), claimOriginalNetwork, originAddress, claimDestNetwork, toAddress, amount, metadata) if err != nil { - log.Error().Err(err).Msg("Unable to interact with bridge contract") - return err + return logAndReturnJsonError(err) } log.Info().Msg("claimTxn: " + claimTxn.Hash().String()) return WaitMineTransaction(cmd.Context(), client, claimTxn, timeoutTxnReceipt) @@ -1051,6 +1080,7 @@ func init() { ulxlyBridgeAndClaimCmd.MarkPersistentFlagRequired(ArgPrivateKey) ulxlyBridgeAndClaimCmd.MarkPersistentFlagRequired(ArgRPCURL) ulxlyBridgeAndClaimCmd.MarkPersistentFlagRequired(ArgBridgeAddress) + // TODO this should be optional and we should use the private key to determine the destination address ulxlyBridgeAndClaimCmd.MarkPersistentFlagRequired(ArgDestAddress) // bridge specific args From cd1a382d655bc025682cc64551f26bee0f24e62e Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Tue, 10 Dec 2024 13:44:31 -0500 Subject: [PATCH 09/17] fix: issue with bridge address --- cmd/ulxly/ulxly.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index 684f52c3..e2b7a480 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -87,8 +87,11 @@ func readDeposit(cmd *cobra.Command) error { if err != nil { return err } + bridgeAddress, err := cmd.Flags().GetString(ArgBridgeAddress) + if err != nil { + return err + } - bridgeAddress := *inputUlxlyArgs.bridgeAddress toBlock := *inputUlxlyArgs.toBlock fromBlock := *inputUlxlyArgs.fromBlock filter := *inputUlxlyArgs.filterSize @@ -1068,11 +1071,11 @@ func init() { } // Arguments for both bridge and claim + inputUlxlyArgs.rpcURL = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgRPCURL, "", "the URL of the RPC to send the transaction") + inputUlxlyArgs.bridgeAddress = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgBridgeAddress, "", "the address of the lxly bridge") inputUlxlyArgs.gasLimit = ulxlyBridgeAndClaimCmd.PersistentFlags().Uint64(ArgGasLimit, 0, "force a gas limit when sending a transaction") inputUlxlyArgs.chainID = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgChainID, "", "set the chain id to be used in the transaction") inputUlxlyArgs.privateKey = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgPrivateKey, "", "the hex encoded private key to be used when sending the tx") - inputUlxlyArgs.rpcURL = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgRPCURL, "", "the URL of the RPC to send the transaction") - inputUlxlyArgs.bridgeAddress = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgBridgeAddress, "", "the address of the lxly bridge") inputUlxlyArgs.destAddress = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgDestAddress, "", "the address where the bridge will be sent to") inputUlxlyArgs.timeout = ulxlyBridgeAndClaimCmd.PersistentFlags().Uint64(ArgTimeout, 60, "the amount of time to wait while trying to confirm a transaction receipt") inputUlxlyArgs.gasPrice = ulxlyBridgeAndClaimCmd.PersistentFlags().String(ArgGasPrice, "", "the gas price to be used") @@ -1107,6 +1110,7 @@ func init() { inputUlxlyArgs.toBlock = getDepositCommand.Flags().Uint64(ArgToBlock, 0, "The end of the range of blocks to retrieve") inputUlxlyArgs.filterSize = getDepositCommand.Flags().Uint64(ArgFilterSize, 1000, "The batch size for individual filter queries") getDepositCommand.Flags().String(ArgRPCURL, "", "The RPC URL to read deposit data") + getDepositCommand.Flags().String(ArgBridgeAddress, "", "The address of the ulxly bridge") getDepositCommand.MarkFlagRequired(ArgFromBlock) getDepositCommand.MarkFlagRequired(ArgToBlock) getDepositCommand.MarkFlagRequired(ArgRPCURL) From d4875fc49b294a5f6e9f1868000808578ba1924e Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Tue, 10 Dec 2024 15:51:06 -0500 Subject: [PATCH 10/17] fix: token too long for large bridge messages --- cmd/ulxly/ulxly.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index e2b7a480..663c3d58 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -507,6 +507,8 @@ func getInputData(args []string) ([]byte, error) { func readDeposits(rawDeposits []byte, depositNumber uint32) error { buf := bytes.NewBuffer(rawDeposits) scanner := bufio.NewScanner(buf) + scannerBuf := make([]byte, 0) + scanner.Buffer(scannerBuf, 1024*1024) imt := new(IMT) imt.Init() seenDeposit := make(map[uint32]common.Hash, 0) @@ -539,7 +541,12 @@ func readDeposits(rawDeposits []byte, depositNumber uint32) error { break } } + if err := scanner.Err(); err != nil { + log.Error().Err(err).Msg("there was an error reading the deposit file") + return err + } + log.Info().Msg("finished") p := imt.GetProof(depositNumber) fmt.Println(p.String()) return nil @@ -1105,7 +1112,6 @@ func init() { ulxlyClaimCmd.MarkPersistentFlagRequired(ArgBridgeServiceURL) // Args that are just for the get deposit command - inputUlxlyArgs.inputFileName = getDepositCommand.Flags().String(ArgFileName, "", "An ndjson file with deposit data") inputUlxlyArgs.fromBlock = getDepositCommand.Flags().Uint64(ArgFromBlock, 0, "The start of the range of blocks to retrieve") inputUlxlyArgs.toBlock = getDepositCommand.Flags().Uint64(ArgToBlock, 0, "The end of the range of blocks to retrieve") inputUlxlyArgs.filterSize = getDepositCommand.Flags().Uint64(ArgFilterSize, 1000, "The batch size for individual filter queries") @@ -1115,6 +1121,10 @@ func init() { getDepositCommand.MarkFlagRequired(ArgToBlock) getDepositCommand.MarkFlagRequired(ArgRPCURL) + // Args for the proof command + inputUlxlyArgs.inputFileName = proofCommand.Flags().String(ArgFileName, "", "An ndjson file with deposit data") + inputUlxlyArgs.depositNumber = proofCommand.Flags().Uint64(ArgDepositCount, 0, "The deposit number to generate a proof for") + // Top Level ULxLyCmd.AddCommand(ulxlyBridgeAndClaimCmd) ULxLyCmd.AddCommand(emptyProofCommand) From 7319e96d8f472d317bbaa21ba535f0a3c26e554c Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Tue, 10 Dec 2024 18:11:27 -0500 Subject: [PATCH 11/17] fix: handling 0x prefix encoding issue --- cmd/ulxly/ulxly.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index 663c3d58..57bf52c6 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -237,7 +237,7 @@ func bridgeAsset(cmd *cobra.Command) error { value, _ := big.NewInt(0).SetString(amount, 0) tokenAddress := common.HexToAddress(tokenAddr) - callData := common.Hex2Bytes(callDataString) + callData := common.Hex2Bytes(strings.TrimPrefix(callDataString, "0x")) if tokenAddress == common.HexToAddress("0x0000000000000000000000000000000000000000") { auth.Value = value @@ -281,7 +281,7 @@ func bridgeMessage(cmd *cobra.Command) error { value, _ := big.NewInt(0).SetString(amount, 0) tokenAddress := common.HexToAddress(tokenAddr) - callData := common.Hex2Bytes(callDataString) + callData := common.Hex2Bytes(strings.TrimPrefix(callDataString, "0x")) if tokenAddress == common.HexToAddress("0x0000000000000000000000000000000000000000") { auth.Value = value @@ -332,7 +332,7 @@ func bridgeWETHMessage(cmd *cobra.Command) error { } value, _ := big.NewInt(0).SetString(amount, 0) - callData := common.Hex2Bytes(callDataString) + callData := common.Hex2Bytes(strings.TrimPrefix(callDataString, "0x")) bridgeTxn, err := bridgeV2.BridgeMessageWETH(auth, destinationNetwork, toAddress, value, isForced, callData) if err != nil { @@ -889,10 +889,20 @@ func getDeposit(bridgeServiceDepositsEndpoint string) (globalIndex *big.Int, ori originAddress = common.HexToAddress(bridgeDeposit.Deposit.OrigAddr) globalIndex.SetString(bridgeDeposit.Deposit.GlobalIndex, 10) amount.SetString(bridgeDeposit.Deposit.Amount, 10) - metadata = common.Hex2Bytes(bridgeDeposit.Deposit.Metadata) + + metadata = common.Hex2Bytes(strings.TrimPrefix(bridgeDeposit.Deposit.Metadata, "0x")) leafType = bridgeDeposit.Deposit.LeafType claimDestNetwork = bridgeDeposit.Deposit.DestNet claimOriginalNetwork = bridgeDeposit.Deposit.OrigNet + log.Info(). + Stringer("globalIndex", globalIndex). + Stringer("originAddress", originAddress). + Stringer("amount", amount). + Str("metadata", bridgeDeposit.Deposit.Metadata). + Uint8("leafType", leafType). + Uint32("claimDestNetwork", claimDestNetwork). + Uint32("claimOriginalNetwork", claimOriginalNetwork). + Msg("Got Deposit") return globalIndex, originAddress, amount, metadata, leafType, claimDestNetwork, claimOriginalNetwork, nil } From 7a3919b1fc75a29c41f2827edca214de56b94b0f Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Fri, 13 Dec 2024 16:34:57 -0500 Subject: [PATCH 12/17] feat: intial implementation for claim-everything --- cmd/ulxly/ulxly.go | 269 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 222 insertions(+), 47 deletions(-) diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index 57bf52c6..c1c0a35f 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -13,6 +13,7 @@ import ( "math/big" "net/http" "os" + "strconv" "strings" "time" @@ -60,26 +61,27 @@ type BridgeProof struct { RollupExitRoot string `json:"rollup_exit_root"` } `json:"proof"` } - type BridgeDeposit struct { - Deposit struct { - LeafType uint8 `json:"leaf_type"` - OrigNet uint32 `json:"orig_net"` - OrigAddr string `json:"orig_addr"` - Amount string `json:"amount"` - DestNet uint32 `json:"dest_net"` - DestAddr string `json:"dest_addr"` - BlockNum string `json:"block_num"` - DepositCnt uint32 `json:"deposit_cnt"` - NetworkID uint32 `json:"network_id"` - TxHash string `json:"tx_hash"` - ClaimTxHash string `json:"claim_tx_hash"` - Metadata string `json:"metadata"` - ReadyForClaim bool `json:"ready_for_claim"` - GlobalIndex string `json:"global_index"` - } `json:"deposit"` - Code *int `json:"code"` - Message *string `json:"message"` + LeafType uint8 `json:"leaf_type"` + OrigNet uint32 `json:"orig_net"` + OrigAddr string `json:"orig_addr"` + Amount string `json:"amount"` + DestNet uint32 `json:"dest_net"` + DestAddr string `json:"dest_addr"` + BlockNum string `json:"block_num"` + DepositCnt uint32 `json:"deposit_cnt"` + NetworkID uint32 `json:"network_id"` + TxHash string `json:"tx_hash"` + ClaimTxHash string `json:"claim_tx_hash"` + Metadata string `json:"metadata"` + ReadyForClaim bool `json:"ready_for_claim"` + GlobalIndex string `json:"global_index"` +} + +type BridgeDepositResponse struct { + Deposit BridgeDeposit `json:"deposit"` + Code *int `json:"code"` + Message *string `json:"message"` } func readDeposit(cmd *cobra.Command) error { @@ -455,6 +457,137 @@ func claimMessage(cmd *cobra.Command) error { return WaitMineTransaction(cmd.Context(), client, claimTxn, timeoutTxnReceipt) } +func getBridgeServiceURLs() (map[uint32]string, error) { + bridgeServiceUrls := *inputUlxlyArgs.bridgeServiceURLs + urlMap := make(map[uint32]string) + for _, mapping := range bridgeServiceUrls { + pieces := strings.Split(mapping, "=") + if len(pieces) != 2 { + return nil, fmt.Errorf("bridge service url mapping should contain a networkid and url separated by an equal sign. Got: %s", mapping) + } + networkId, err := strconv.ParseInt(pieces[0], 10, 32) + if err != nil { + return nil, err + } + urlMap[uint32(networkId)] = pieces[1] + } + return urlMap, nil +} + +func claimEverything(cmd *cobra.Command) error { + privateKey := *inputUlxlyArgs.privateKey + gasLimit := *inputUlxlyArgs.gasLimit + chainID := *inputUlxlyArgs.chainID + timeoutTxnReceipt := *inputUlxlyArgs.timeout + bridgeAddress := *inputUlxlyArgs.bridgeAddress + destinationAddress := *inputUlxlyArgs.destAddress + RPCURL := *inputUlxlyArgs.rpcURL + limit := *inputUlxlyArgs.bridgeLimit + urls, err := getBridgeServiceURLs() + if err != nil { + return err + } + + allDeposits := make([]BridgeDeposit, 0) + + for _, bridgeServiceUrl := range urls { + deposits, bErr := getDepositsForAddress(fmt.Sprintf("%s/bridges/%s?limit=%d", bridgeServiceUrl, destinationAddress, limit)) + if bErr != nil { + return bErr + } + allDeposits = append(allDeposits, deposits...) + } + + client, err := ethclient.DialContext(cmd.Context(), RPCURL) + if err != nil { + log.Error().Err(err).Msg("Unable to Dial RPC") + return err + } + defer client.Close() + + bridgeContract, _, opts, err := generateTransactionPayload(cmd.Context(), client, bridgeAddress, privateKey, gasLimit, destinationAddress, chainID) + if err != nil { + return err + } + + currentNetworkID, err := bridgeContract.NetworkID(nil) + if err != nil { + return err + } + log.Info().Uint32("networkID", currentNetworkID).Msg("detected current networkid") + for _, deposit := range allDeposits { + if deposit.DestNet != currentNetworkID { + log.Debug().Uint32("destination_network", deposit.DestNet).Msg("discarding deposit for different network") + continue + } + if deposit.ClaimTxHash != "" { + log.Info().Str("txhash", deposit.ClaimTxHash).Msg("It looks like this tx was already claimed") + } + claimTx, dErr := claimSingleDeposit(bridgeContract, opts, deposit, urls, currentNetworkID) + if dErr != nil { + continue + } + dErr = WaitMineTransaction(cmd.Context(), client, claimTx, timeoutTxnReceipt) + if dErr != nil { + log.Error().Err(dErr).Msg("error while waiting for tx to main") + } + } + + return nil +} + +func claimSingleDeposit(bridgeContract *ulxly.Ulxly, opts *bind.TransactOpts, deposit BridgeDeposit, bridgeURLs map[uint32]string, currentNetworkID uint32) (*types.Transaction, error) { + networkIDForBridgeService := deposit.NetworkID + if deposit.NetworkID == 0 { + networkIDForBridgeService = currentNetworkID + } + bridgeUrl, hasKey := bridgeURLs[networkIDForBridgeService] + if !hasKey { + return nil, fmt.Errorf("we don't have a bridge service url for network: %d", deposit.DestNet) + } + bridgeServiceProofEndpoint := fmt.Sprintf("%s/merkle-proof?deposit_cnt=%d&net_id=%d", bridgeUrl, deposit.DepositCnt, deposit.NetworkID) + merkleProofArray, rollupMerkleProofArray, mainExitRoot, rollupExitRoot := getMerkleProofsExitRoots(bridgeServiceProofEndpoint) + if len(mainExitRoot) != 32 || len(rollupExitRoot) != 32 { + log.Warn(). + Uint32("DepositCnt", deposit.DepositCnt). + Uint32("OrigNet", deposit.OrigNet). + Uint32("DestNet", deposit.DestNet). + Uint32("NetworkID", deposit.NetworkID). + Str("OrigAddr", deposit.OrigAddr). + Str("DestAddr", deposit.DestAddr). + Msg("deposit can't be claimed!") + return nil, fmt.Errorf("the exit roots from the bridse service were empty: %s", bridgeServiceProofEndpoint) + } + + globalIndex, isValid := new(big.Int).SetString(deposit.GlobalIndex, 10) + if !isValid { + return nil, fmt.Errorf("global index %s is not a valid integer", deposit.GlobalIndex) + } + amount, isValid := new(big.Int).SetString(deposit.Amount, 10) + if !isValid { + return nil, fmt.Errorf("amount %s is not a valid integer", deposit.Amount) + } + + originAddress := common.HexToAddress(deposit.OrigAddr) + toAddress := common.HexToAddress(deposit.DestAddr) + metadata := common.Hex2Bytes(strings.TrimPrefix(deposit.Metadata, "0x")) + + var claimTx *types.Transaction + var err error + if deposit.LeafType == 0 { + claimTx, err = bridgeContract.ClaimAsset(opts, merkleProofArray, rollupMerkleProofArray, globalIndex, [32]byte(mainExitRoot), [32]byte(rollupExitRoot), deposit.OrigNet, originAddress, deposit.DestNet, toAddress, amount, metadata) + } else { + claimTx, err = bridgeContract.ClaimMessage(opts, merkleProofArray, rollupMerkleProofArray, globalIndex, [32]byte(mainExitRoot), [32]byte(rollupExitRoot), deposit.OrigNet, originAddress, deposit.DestNet, toAddress, amount, metadata) + } + + if err != nil { + return nil, logAndReturnJsonError(err) + } + log.Info().Stringer("txhash", claimTx.Hash()).Msg("sent claim") + + return claimTx, nil +} + // Wait for the transaction to be mined func WaitMineTransaction(ctx context.Context, client *ethclient.Client, tx *types.Transaction, txTimeout uint64) error { if inputUlxlyArgs.dryRun != nil && *inputUlxlyArgs.dryRun { @@ -758,6 +891,7 @@ func generateTransactionPayload(ctx context.Context, client *ethclient.Client, u privateKey, err := crypto.HexToECDSA(ulxlyInputArgPvtKey) if err != nil { log.Error().Err(err).Msg("Unable to retrieve private key") + return } // value := big.NewInt(*ulxlyInputArgs.Amount) @@ -831,7 +965,7 @@ func getMerkleProofsExitRoots(bridgeServiceProofEndpoint string) (merkleProofArr merkleProof = append(merkleProof, [32]byte(byteMP)) } if len(merkleProof) == 0 { - log.Error().Msg("The Merkle Proofs cannot be retrieved, double check the input arguments and try again.") + log.Error().Str("url", bridgeServiceProofEndpoint).Msg("The Merkle Proofs cannot be retrieved, double check the input arguments and try again.") return } merkleProofArray = [32][32]byte(merkleProof) @@ -864,7 +998,7 @@ func getDeposit(bridgeServiceDepositsEndpoint string) (globalIndex *big.Int, ori log.Error().Err(err) return } - var bridgeDeposit BridgeDeposit + var bridgeDeposit BridgeDepositResponse err = json.Unmarshal(bodyBridgeDeposit, &bridgeDeposit) // Parse []byte to go struct pointer, and shadow err variable if err != nil { log.Error().Err(err).Msg("Can not unmarshal JSON") @@ -905,6 +1039,30 @@ func getDeposit(bridgeServiceDepositsEndpoint string) (globalIndex *big.Int, ori Msg("Got Deposit") return globalIndex, originAddress, amount, metadata, leafType, claimDestNetwork, claimOriginalNetwork, nil } +func getDepositsForAddress(bridgeRequestUrl string) ([]BridgeDeposit, error) { + var resp struct { + Deposits []BridgeDeposit `json:"deposits"` + Total int `json:"total_cnt,string"` + } + httpResp, err := http.Get(bridgeRequestUrl) + if err != nil { + return nil, err + } + defer httpResp.Body.Close() + respBytes, err := io.ReadAll(httpResp.Body) + if err != nil { + return nil, err + } + err = json.Unmarshal(respBytes, &resp) + if err != nil { + return nil, err + } + if len(resp.Deposits) != resp.Total { + log.Warn().Int("total_deposits", resp.Total).Int("retrieved_deposits", len(resp.Deposits)).Msg("not all deposits were retrieved") + } + + return resp.Deposits, nil +} //go:embed BridgeAssetUsage.md var bridgeAssetUsage string @@ -951,29 +1109,31 @@ var ulxlyClaimCmd = &cobra.Command{ } type ulxlyArgs struct { - gasLimit *uint64 - chainID *string - privateKey *string - value *string - rpcURL *string - bridgeAddress *string - destNetwork *uint32 - destAddress *string - tokenAddress *string - forceUpdate *bool - callData *string - timeout *uint64 - depositCount *uint64 - depositNetwork *uint64 - bridgeServiceURL *string - inputFileName *string - fromBlock *uint64 - toBlock *uint64 - filterSize *uint64 - depositNumber *uint64 - globalIndex *string - gasPrice *string - dryRun *bool + gasLimit *uint64 + chainID *string + privateKey *string + value *string + rpcURL *string + bridgeAddress *string + destNetwork *uint32 + destAddress *string + tokenAddress *string + forceUpdate *bool + callData *string + timeout *uint64 + depositCount *uint64 + depositNetwork *uint64 + bridgeServiceURL *string + inputFileName *string + fromBlock *uint64 + toBlock *uint64 + filterSize *uint64 + depositNumber *uint64 + globalIndex *string + gasPrice *string + dryRun *bool + bridgeServiceURLs *[]string + bridgeLimit *int } var inputUlxlyArgs = ulxlyArgs{} @@ -984,6 +1144,7 @@ var ( bridgeMessageWETHCommand *cobra.Command claimAssetCommand *cobra.Command claimMessageCommand *cobra.Command + claimEverythingCommand *cobra.Command emptyProofCommand *cobra.Command zeroProofCommand *cobra.Command proofCommand *cobra.Command @@ -1013,6 +1174,8 @@ const ( ArgGlobalIndex = "global-index" ArgDryRun = "dry-run" ArgGasPrice = "gas-price" + ArgBridgeMappings = "bridge-service-map" + ArgBridgeLimit = "bridge-limit" ) func init() { @@ -1046,11 +1209,18 @@ func init() { } claimMessageCommand = &cobra.Command{ Use: "message", - Short: "perform a claim of a given message in the birdge", + Short: "perform a claim of a given message in the bridge", RunE: func(cmd *cobra.Command, args []string) error { return claimMessage(cmd) }, } + claimEverythingCommand = &cobra.Command{ + Use: "claim-everything", + Short: "attempt to claim any unclaimed deposits", + RunE: func(cmd *cobra.Command, args []string) error { + return claimEverything(cmd) + }, + } emptyProofCommand = &cobra.Command{ Use: "empty-proof", Short: "create an empty proof", @@ -1116,11 +1286,15 @@ func init() { inputUlxlyArgs.depositNetwork = ulxlyClaimCmd.PersistentFlags().Uint64(ArgDepositNetwork, 0, "the rollup id of the network where the bridge is being claimed") inputUlxlyArgs.bridgeServiceURL = ulxlyClaimCmd.PersistentFlags().String(ArgBridgeServiceURL, "", "the URL of the bridge service") inputUlxlyArgs.globalIndex = ulxlyClaimCmd.PersistentFlags().String(ArgGlobalIndex, "", "an override of the global index value") - ulxlyClaimCmd.MarkPersistentFlagRequired(ArgDepositCount) ulxlyClaimCmd.MarkPersistentFlagRequired(ArgDepositNetwork) ulxlyClaimCmd.MarkPersistentFlagRequired(ArgBridgeServiceURL) + // Claim Everything Helper Command + inputUlxlyArgs.bridgeServiceURLs = claimEverythingCommand.Flags().StringSlice(ArgBridgeMappings, nil, "Mappings between network ids and bridge service urls. E.g. '1=http://network-1-bridgeurl,7=http://network-2-bridgeurl'") + inputUlxlyArgs.bridgeLimit = claimEverythingCommand.Flags().Int(ArgBridgeLimit, 25, "Limit the number or responses returned by the bridge service when claiming") + claimEverythingCommand.MarkFlagRequired(ArgBridgeMappings) + // Args that are just for the get deposit command inputUlxlyArgs.fromBlock = getDepositCommand.Flags().Uint64(ArgFromBlock, 0, "The start of the range of blocks to retrieve") inputUlxlyArgs.toBlock = getDepositCommand.Flags().Uint64(ArgToBlock, 0, "The end of the range of blocks to retrieve") @@ -1144,10 +1318,12 @@ func init() { ULxLyCmd.AddCommand(ulxlxBridgeCmd) ULxLyCmd.AddCommand(ulxlyClaimCmd) + ULxLyCmd.AddCommand(claimEverythingCommand) // Bridge and Claim ulxlyBridgeAndClaimCmd.AddCommand(ulxlxBridgeCmd) ulxlyBridgeAndClaimCmd.AddCommand(ulxlyClaimCmd) + ulxlyBridgeAndClaimCmd.AddCommand(claimEverythingCommand) // Bridge ulxlxBridgeCmd.AddCommand(bridgeAssetCommand) @@ -1157,5 +1333,4 @@ func init() { // Claim ulxlyClaimCmd.AddCommand(claimAssetCommand) ulxlyClaimCmd.AddCommand(claimMessageCommand) - } From b88ca09d89154871f430d7fd78717e62ad171575 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Wed, 18 Dec 2024 09:57:12 -0500 Subject: [PATCH 13/17] feat: adding more logging --- cmd/ulxly/ulxly.go | 52 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index c1c0a35f..9251b5c6 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -78,6 +78,11 @@ type BridgeDeposit struct { GlobalIndex string `json:"global_index"` } +type DepositID struct { + DepositCnt uint32 `json:"deposit_cnt"` + NetworkID uint32 `json:"network_id"` +} + type BridgeDepositResponse struct { Deposit BridgeDeposit `json:"deposit"` Code *int `json:"code"` @@ -483,19 +488,36 @@ func claimEverything(cmd *cobra.Command) error { destinationAddress := *inputUlxlyArgs.destAddress RPCURL := *inputUlxlyArgs.rpcURL limit := *inputUlxlyArgs.bridgeLimit + offset := *inputUlxlyArgs.bridgeOffset urls, err := getBridgeServiceURLs() if err != nil { return err } - allDeposits := make([]BridgeDeposit, 0) + depositMap := make(map[DepositID]*BridgeDeposit) for _, bridgeServiceUrl := range urls { - deposits, bErr := getDepositsForAddress(fmt.Sprintf("%s/bridges/%s?limit=%d", bridgeServiceUrl, destinationAddress, limit)) + deposits, bErr := getDepositsForAddress(fmt.Sprintf("%s/bridges/%s?offset=%d&limit=%d", bridgeServiceUrl, destinationAddress, offset, limit)) if bErr != nil { return bErr } - allDeposits = append(allDeposits, deposits...) + for idx, deposit := range deposits { + depId := DepositID{ + DepositCnt: deposit.DepositCnt, + NetworkID: deposit.NetworkID, + } + _, hasKey := depositMap[depId] + // if we haven't seen this deposit at all, we'll store it + if !hasKey { + depositMap[depId] = &deposits[idx] + continue + } + + // if this new deposit is ready for claim OR it has already been claimed we should over ride the exisitng value + if deposit.ReadyForClaim || deposit.ClaimTxHash != "" { + depositMap[depId] = &deposits[idx] + } + } } client, err := ethclient.DialContext(cmd.Context(), RPCURL) @@ -515,16 +537,24 @@ func claimEverything(cmd *cobra.Command) error { return err } log.Info().Uint32("networkID", currentNetworkID).Msg("detected current networkid") - for _, deposit := range allDeposits { + for _, deposit := range depositMap { if deposit.DestNet != currentNetworkID { log.Debug().Uint32("destination_network", deposit.DestNet).Msg("discarding deposit for different network") continue } if deposit.ClaimTxHash != "" { log.Info().Str("txhash", deposit.ClaimTxHash).Msg("It looks like this tx was already claimed") + continue } - claimTx, dErr := claimSingleDeposit(bridgeContract, opts, deposit, urls, currentNetworkID) + claimTx, dErr := claimSingleDeposit(bridgeContract, opts, *deposit, urls, currentNetworkID) if dErr != nil { + log.Warn().Err(dErr).Uint32("DepositCnt", deposit.DepositCnt). + Uint32("OrigNet", deposit.OrigNet). + Uint32("DestNet", deposit.DestNet). + Uint32("NetworkID", deposit.NetworkID). + Str("OrigAddr", deposit.OrigAddr). + Str("DestAddr", deposit.DestAddr). + Msg("There was an error claiming") continue } dErr = WaitMineTransaction(cmd.Context(), client, claimTx, timeoutTxnReceipt) @@ -581,6 +611,14 @@ func claimSingleDeposit(bridgeContract *ulxly.Ulxly, opts *bind.TransactOpts, de } if err != nil { + log.Warn(). + Uint32("DepositCnt", deposit.DepositCnt). + Uint32("OrigNet", deposit.OrigNet). + Uint32("DestNet", deposit.DestNet). + Uint32("NetworkID", deposit.NetworkID). + Str("OrigAddr", deposit.OrigAddr). + Str("DestAddr", deposit.DestAddr). + Msg("attempt to claim deposit failed") return nil, logAndReturnJsonError(err) } log.Info().Stringer("txhash", claimTx.Hash()).Msg("sent claim") @@ -1039,6 +1077,7 @@ func getDeposit(bridgeServiceDepositsEndpoint string) (globalIndex *big.Int, ori Msg("Got Deposit") return globalIndex, originAddress, amount, metadata, leafType, claimDestNetwork, claimOriginalNetwork, nil } + func getDepositsForAddress(bridgeRequestUrl string) ([]BridgeDeposit, error) { var resp struct { Deposits []BridgeDeposit `json:"deposits"` @@ -1134,6 +1173,7 @@ type ulxlyArgs struct { dryRun *bool bridgeServiceURLs *[]string bridgeLimit *int + bridgeOffset *int } var inputUlxlyArgs = ulxlyArgs{} @@ -1176,6 +1216,7 @@ const ( ArgGasPrice = "gas-price" ArgBridgeMappings = "bridge-service-map" ArgBridgeLimit = "bridge-limit" + ArgBridgeOffset = "bridge-offset" ) func init() { @@ -1293,6 +1334,7 @@ func init() { // Claim Everything Helper Command inputUlxlyArgs.bridgeServiceURLs = claimEverythingCommand.Flags().StringSlice(ArgBridgeMappings, nil, "Mappings between network ids and bridge service urls. E.g. '1=http://network-1-bridgeurl,7=http://network-2-bridgeurl'") inputUlxlyArgs.bridgeLimit = claimEverythingCommand.Flags().Int(ArgBridgeLimit, 25, "Limit the number or responses returned by the bridge service when claiming") + inputUlxlyArgs.bridgeOffset = claimEverythingCommand.Flags().Int(ArgBridgeOffset, 0, "The offset to specify for pagination of the underlying bridge service deposits") claimEverythingCommand.MarkFlagRequired(ArgBridgeMappings) // Args that are just for the get deposit command From 7de0bfb69eb4279f7505783023685d27839b6d96 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Fri, 20 Dec 2024 14:42:27 -0500 Subject: [PATCH 14/17] feat: additional logging --- cmd/ulxly/ulxly.go | 75 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index 9251b5c6..4cf98003 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -9,6 +9,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/ethereum/go-ethereum" "io" "math/big" "net/http" @@ -186,7 +187,38 @@ type JsonError struct { Data interface{} `json:"data"` } -func logAndReturnJsonError(err error) error { +func logAndReturnJsonError(cmd *cobra.Command, client *ethclient.Client, tx *types.Transaction, opts *bind.TransactOpts, err error) error { + // in case the error came down to gas estimation, we can sometimes get more information by doing a call + _, callErr := client.CallContract(cmd.Context(), ethereum.CallMsg{ + From: opts.From, + To: tx.To(), + Gas: tx.Gas(), + GasPrice: tx.GasPrice(), + GasFeeCap: tx.GasFeeCap(), + GasTipCap: tx.GasTipCap(), + Value: tx.Value(), + Data: tx.Data(), + AccessList: tx.AccessList(), + BlobGasFeeCap: tx.BlobGasFeeCap(), + BlobHashes: tx.BlobHashes(), + }, nil) + + if *inputUlxlyArgs.dryRun { + castCmd := "cast call" + castCmd += fmt.Sprintf(" --rpc-url %s", *inputUlxlyArgs.rpcURL) + castCmd += fmt.Sprintf(" --from %s", opts.From.String()) + castCmd += fmt.Sprintf(" --gas-limit %d", tx.Gas()) + if tx.Type() == types.LegacyTxType { + castCmd += fmt.Sprintf(" --gas-price %s", tx.GasPrice().String()) + } else { + castCmd += fmt.Sprintf(" --gas-price %s", tx.GasFeeCap().String()) + castCmd += fmt.Sprintf(" --priority-gas-price %s", tx.GasTipCap().String()) + } + castCmd += fmt.Sprintf(" --value %s", tx.Value().String()) + castCmd += fmt.Sprintf(" %s", tx.To().String()) + castCmd += fmt.Sprintf(" %s", common.Bytes2Hex(tx.Data())) + log.Info().Str("cmd", castCmd).Msg("use this command to replicate the call") + } if err == nil { return nil } @@ -204,12 +236,17 @@ func logAndReturnJsonError(err error) error { return err } - log.Error(). + errLog := log.Error(). Err(err). Str("message", jsonError.Message). Int("code", jsonError.Code). - Interface("data", jsonError.Data). - Msg("Unable to interact with bridge contract") + Interface("data", jsonError.Data) + + if callErr != nil { + errLog = errLog.Err(callErr) + } + + errLog.Msg("Unable to interact with bridge contract") return err } @@ -252,7 +289,7 @@ func bridgeAsset(cmd *cobra.Command) error { bridgeTxn, err := bridgeV2.BridgeAsset(auth, destinationNetwork, toAddress, value, tokenAddress, isForced, callData) if err != nil { - return logAndReturnJsonError(err) + return logAndReturnJsonError(cmd, client, bridgeTxn, auth, err) } log.Info().Msg("bridgeTxn: " + bridgeTxn.Hash().String()) return WaitMineTransaction(cmd.Context(), client, bridgeTxn, timeoutTxnReceipt) @@ -296,7 +333,7 @@ func bridgeMessage(cmd *cobra.Command) error { bridgeTxn, err := bridgeV2.BridgeMessage(auth, destinationNetwork, toAddress, isForced, callData) if err != nil { - return logAndReturnJsonError(err) + return logAndReturnJsonError(cmd, client, bridgeTxn, auth, err) } log.Info().Msg("bridgeTxn: " + bridgeTxn.Hash().String()) return WaitMineTransaction(cmd.Context(), client, bridgeTxn, timeoutTxnReceipt) @@ -343,7 +380,7 @@ func bridgeWETHMessage(cmd *cobra.Command) error { bridgeTxn, err := bridgeV2.BridgeMessageWETH(auth, destinationNetwork, toAddress, value, isForced, callData) if err != nil { - return logAndReturnJsonError(err) + return logAndReturnJsonError(cmd, client, bridgeTxn, auth, err) } log.Info().Msg("bridgeTxn: " + bridgeTxn.Hash().String()) return WaitMineTransaction(cmd.Context(), client, bridgeTxn, timeoutTxnReceipt) @@ -403,7 +440,7 @@ func claimAsset(cmd *cobra.Command) error { } claimTxn, err := bridgeV2.ClaimAsset(auth, merkleProofArray, rollupMerkleProofArray, globalIndex, [32]byte(mainExitRoot), [32]byte(rollupExitRoot), claimOriginalNetwork, originAddress, claimDestNetwork, toAddress, amount, metadata) if err != nil { - return logAndReturnJsonError(err) + return logAndReturnJsonError(cmd, client, claimTxn, auth, err) } log.Info().Msg("claimTxn: " + claimTxn.Hash().String()) return WaitMineTransaction(cmd.Context(), client, claimTxn, timeoutTxnReceipt) @@ -456,7 +493,7 @@ func claimMessage(cmd *cobra.Command) error { //ClaimMessage(opts *bind.TransactOpts, smtProofLocalExitRoot [32][32]byte, smtProofRollupExitRoot [32][32]byte, globalIndex *big.Int, mainnetExitRoot [32]byte, rollupExitRoot [32]byte, originNetwork uint32, originAddress common.Address, destinationNetwork uint32, destinationAddress common.Address, amount *big.Int, metadata []byte) (*types.Transaction, error) { claimTxn, err := bridgeV2.ClaimMessage(auth, merkleProofArray, rollupMerkleProofArray, globalIndex, [32]byte(mainExitRoot), [32]byte(rollupExitRoot), claimOriginalNetwork, originAddress, claimDestNetwork, toAddress, amount, metadata) if err != nil { - return logAndReturnJsonError(err) + return logAndReturnJsonError(cmd, client, claimTxn, auth, err) } log.Info().Msg("claimTxn: " + claimTxn.Hash().String()) return WaitMineTransaction(cmd.Context(), client, claimTxn, timeoutTxnReceipt) @@ -531,7 +568,6 @@ func claimEverything(cmd *cobra.Command) error { if err != nil { return err } - currentNetworkID, err := bridgeContract.NetworkID(nil) if err != nil { return err @@ -546,7 +582,7 @@ func claimEverything(cmd *cobra.Command) error { log.Info().Str("txhash", deposit.ClaimTxHash).Msg("It looks like this tx was already claimed") continue } - claimTx, dErr := claimSingleDeposit(bridgeContract, opts, *deposit, urls, currentNetworkID) + claimTx, dErr := claimSingleDeposit(cmd, client, bridgeContract, opts, *deposit, urls, currentNetworkID) if dErr != nil { log.Warn().Err(dErr).Uint32("DepositCnt", deposit.DepositCnt). Uint32("OrigNet", deposit.OrigNet). @@ -566,7 +602,7 @@ func claimEverything(cmd *cobra.Command) error { return nil } -func claimSingleDeposit(bridgeContract *ulxly.Ulxly, opts *bind.TransactOpts, deposit BridgeDeposit, bridgeURLs map[uint32]string, currentNetworkID uint32) (*types.Transaction, error) { +func claimSingleDeposit(cmd *cobra.Command, client *ethclient.Client, bridgeContract *ulxly.Ulxly, opts *bind.TransactOpts, deposit BridgeDeposit, bridgeURLs map[uint32]string, currentNetworkID uint32) (*types.Transaction, error) { networkIDForBridgeService := deposit.NetworkID if deposit.NetworkID == 0 { networkIDForBridgeService = currentNetworkID @@ -586,7 +622,7 @@ func claimSingleDeposit(bridgeContract *ulxly.Ulxly, opts *bind.TransactOpts, de Str("OrigAddr", deposit.OrigAddr). Str("DestAddr", deposit.DestAddr). Msg("deposit can't be claimed!") - return nil, fmt.Errorf("the exit roots from the bridse service were empty: %s", bridgeServiceProofEndpoint) + return nil, fmt.Errorf("the exit roots from the bridge service were empty: %s", bridgeServiceProofEndpoint) } globalIndex, isValid := new(big.Int).SetString(deposit.GlobalIndex, 10) @@ -619,7 +655,7 @@ func claimSingleDeposit(bridgeContract *ulxly.Ulxly, opts *bind.TransactOpts, de Str("OrigAddr", deposit.OrigAddr). Str("DestAddr", deposit.DestAddr). Msg("attempt to claim deposit failed") - return nil, logAndReturnJsonError(err) + return nil, logAndReturnJsonError(cmd, client, claimTx, opts, err) } log.Info().Stringer("txhash", claimTx.Hash()).Msg("sent claim") @@ -629,7 +665,8 @@ func claimSingleDeposit(bridgeContract *ulxly.Ulxly, opts *bind.TransactOpts, de // Wait for the transaction to be mined func WaitMineTransaction(ctx context.Context, client *ethclient.Client, tx *types.Transaction, txTimeout uint64) error { if inputUlxlyArgs.dryRun != nil && *inputUlxlyArgs.dryRun { - log.Info().Msg("Skipping receipt check. Dry run is enabled") + txJson, _ := tx.MarshalJSON() + log.Info().RawJSON("tx", txJson).Msg("Skipping receipt check. Dry run is enabled") return nil } txnMinedTimer := time.NewTimer(time.Duration(txTimeout) * time.Second) @@ -934,14 +971,6 @@ func generateTransactionPayload(ctx context.Context, client *ethclient.Client, u // value := big.NewInt(*ulxlyInputArgs.Amount) gasLimit := ulxlyInputArgGasLimit - // gasPrice, err := client.SuggestGasPrice(ctx) // This call is done automatically if it is not set - // if err != nil { - // log.Error().Err(err).Msg("Cannot get suggested gas price") - // } - // gasTipCap, err := client.SuggestGasTipCap(ctx) - // if err != nil { - // log.Error().Err(err).Msg("Cannot get suggested gas tip cap") - // } chainID := new(big.Int) // For manual input of chainID, use the user's input From cfd970c63656d6352068280a9fd3aac0762bf139 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Fri, 20 Dec 2024 14:54:17 -0500 Subject: [PATCH 15/17] feat: surpressing error logs --- cmd/ulxly/ulxly.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index 4cf98003..eabdeeba 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -246,6 +246,11 @@ func logAndReturnJsonError(cmd *cobra.Command, client *ethclient.Client, tx *typ errLog = errLog.Err(callErr) } + if errCode, isValid := jsonError.Data.(string); isValid && errCode == "0x646cf558" { + // I don't want to bother with the additional error logging for previously claimed deposits + return err + } + errLog.Msg("Unable to interact with bridge contract") return err From 971e19fc00422a16d0ac808f71403cda0b75a996 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Fri, 20 Dec 2024 15:07:59 -0500 Subject: [PATCH 16/17] fix: null tx --- cmd/ulxly/ulxly.go | 87 ++++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index eabdeeba..655b056f 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -188,37 +188,42 @@ type JsonError struct { } func logAndReturnJsonError(cmd *cobra.Command, client *ethclient.Client, tx *types.Transaction, opts *bind.TransactOpts, err error) error { - // in case the error came down to gas estimation, we can sometimes get more information by doing a call - _, callErr := client.CallContract(cmd.Context(), ethereum.CallMsg{ - From: opts.From, - To: tx.To(), - Gas: tx.Gas(), - GasPrice: tx.GasPrice(), - GasFeeCap: tx.GasFeeCap(), - GasTipCap: tx.GasTipCap(), - Value: tx.Value(), - Data: tx.Data(), - AccessList: tx.AccessList(), - BlobGasFeeCap: tx.BlobGasFeeCap(), - BlobHashes: tx.BlobHashes(), - }, nil) - - if *inputUlxlyArgs.dryRun { - castCmd := "cast call" - castCmd += fmt.Sprintf(" --rpc-url %s", *inputUlxlyArgs.rpcURL) - castCmd += fmt.Sprintf(" --from %s", opts.From.String()) - castCmd += fmt.Sprintf(" --gas-limit %d", tx.Gas()) - if tx.Type() == types.LegacyTxType { - castCmd += fmt.Sprintf(" --gas-price %s", tx.GasPrice().String()) - } else { - castCmd += fmt.Sprintf(" --gas-price %s", tx.GasFeeCap().String()) - castCmd += fmt.Sprintf(" --priority-gas-price %s", tx.GasTipCap().String()) + + var callErr error + if tx != nil { + // in case the error came down to gas estimation, we can sometimes get more information by doing a call + _, callErr = client.CallContract(cmd.Context(), ethereum.CallMsg{ + From: opts.From, + To: tx.To(), + Gas: tx.Gas(), + GasPrice: tx.GasPrice(), + GasFeeCap: tx.GasFeeCap(), + GasTipCap: tx.GasTipCap(), + Value: tx.Value(), + Data: tx.Data(), + AccessList: tx.AccessList(), + BlobGasFeeCap: tx.BlobGasFeeCap(), + BlobHashes: tx.BlobHashes(), + }, nil) + + if *inputUlxlyArgs.dryRun { + castCmd := "cast call" + castCmd += fmt.Sprintf(" --rpc-url %s", *inputUlxlyArgs.rpcURL) + castCmd += fmt.Sprintf(" --from %s", opts.From.String()) + castCmd += fmt.Sprintf(" --gas-limit %d", tx.Gas()) + if tx.Type() == types.LegacyTxType { + castCmd += fmt.Sprintf(" --gas-price %s", tx.GasPrice().String()) + } else { + castCmd += fmt.Sprintf(" --gas-price %s", tx.GasFeeCap().String()) + castCmd += fmt.Sprintf(" --priority-gas-price %s", tx.GasTipCap().String()) + } + castCmd += fmt.Sprintf(" --value %s", tx.Value().String()) + castCmd += fmt.Sprintf(" %s", tx.To().String()) + castCmd += fmt.Sprintf(" %s", common.Bytes2Hex(tx.Data())) + log.Info().Str("cmd", castCmd).Msg("use this command to replicate the call") } - castCmd += fmt.Sprintf(" --value %s", tx.Value().String()) - castCmd += fmt.Sprintf(" %s", tx.To().String()) - castCmd += fmt.Sprintf(" %s", common.Bytes2Hex(tx.Data())) - log.Info().Str("cmd", castCmd).Msg("use this command to replicate the call") } + if err == nil { return nil } @@ -293,8 +298,8 @@ func bridgeAsset(cmd *cobra.Command) error { } bridgeTxn, err := bridgeV2.BridgeAsset(auth, destinationNetwork, toAddress, value, tokenAddress, isForced, callData) - if err != nil { - return logAndReturnJsonError(cmd, client, bridgeTxn, auth, err) + if err = logAndReturnJsonError(cmd, client, bridgeTxn, auth, err); err != nil { + return err } log.Info().Msg("bridgeTxn: " + bridgeTxn.Hash().String()) return WaitMineTransaction(cmd.Context(), client, bridgeTxn, timeoutTxnReceipt) @@ -337,8 +342,8 @@ func bridgeMessage(cmd *cobra.Command) error { } bridgeTxn, err := bridgeV2.BridgeMessage(auth, destinationNetwork, toAddress, isForced, callData) - if err != nil { - return logAndReturnJsonError(cmd, client, bridgeTxn, auth, err) + if err = logAndReturnJsonError(cmd, client, bridgeTxn, auth, err); err != nil { + return err } log.Info().Msg("bridgeTxn: " + bridgeTxn.Hash().String()) return WaitMineTransaction(cmd.Context(), client, bridgeTxn, timeoutTxnReceipt) @@ -384,8 +389,8 @@ func bridgeWETHMessage(cmd *cobra.Command) error { callData := common.Hex2Bytes(strings.TrimPrefix(callDataString, "0x")) bridgeTxn, err := bridgeV2.BridgeMessageWETH(auth, destinationNetwork, toAddress, value, isForced, callData) - if err != nil { - return logAndReturnJsonError(cmd, client, bridgeTxn, auth, err) + if err = logAndReturnJsonError(cmd, client, bridgeTxn, auth, err); err != nil { + return err } log.Info().Msg("bridgeTxn: " + bridgeTxn.Hash().String()) return WaitMineTransaction(cmd.Context(), client, bridgeTxn, timeoutTxnReceipt) @@ -444,8 +449,8 @@ func claimAsset(cmd *cobra.Command) error { globalIndex.SetString(globalIndexOverride, 10) } claimTxn, err := bridgeV2.ClaimAsset(auth, merkleProofArray, rollupMerkleProofArray, globalIndex, [32]byte(mainExitRoot), [32]byte(rollupExitRoot), claimOriginalNetwork, originAddress, claimDestNetwork, toAddress, amount, metadata) - if err != nil { - return logAndReturnJsonError(cmd, client, claimTxn, auth, err) + if err = logAndReturnJsonError(cmd, client, claimTxn, auth, err); err != nil { + return err } log.Info().Msg("claimTxn: " + claimTxn.Hash().String()) return WaitMineTransaction(cmd.Context(), client, claimTxn, timeoutTxnReceipt) @@ -497,8 +502,8 @@ func claimMessage(cmd *cobra.Command) error { } //ClaimMessage(opts *bind.TransactOpts, smtProofLocalExitRoot [32][32]byte, smtProofRollupExitRoot [32][32]byte, globalIndex *big.Int, mainnetExitRoot [32]byte, rollupExitRoot [32]byte, originNetwork uint32, originAddress common.Address, destinationNetwork uint32, destinationAddress common.Address, amount *big.Int, metadata []byte) (*types.Transaction, error) { claimTxn, err := bridgeV2.ClaimMessage(auth, merkleProofArray, rollupMerkleProofArray, globalIndex, [32]byte(mainExitRoot), [32]byte(rollupExitRoot), claimOriginalNetwork, originAddress, claimDestNetwork, toAddress, amount, metadata) - if err != nil { - return logAndReturnJsonError(cmd, client, claimTxn, auth, err) + if err = logAndReturnJsonError(cmd, client, claimTxn, auth, err); err != nil { + return err } log.Info().Msg("claimTxn: " + claimTxn.Hash().String()) return WaitMineTransaction(cmd.Context(), client, claimTxn, timeoutTxnReceipt) @@ -651,7 +656,7 @@ func claimSingleDeposit(cmd *cobra.Command, client *ethclient.Client, bridgeCont claimTx, err = bridgeContract.ClaimMessage(opts, merkleProofArray, rollupMerkleProofArray, globalIndex, [32]byte(mainExitRoot), [32]byte(rollupExitRoot), deposit.OrigNet, originAddress, deposit.DestNet, toAddress, amount, metadata) } - if err != nil { + if err = logAndReturnJsonError(cmd, client, claimTx, opts, err); err != nil { log.Warn(). Uint32("DepositCnt", deposit.DepositCnt). Uint32("OrigNet", deposit.OrigNet). @@ -660,7 +665,7 @@ func claimSingleDeposit(cmd *cobra.Command, client *ethclient.Client, bridgeCont Str("OrigAddr", deposit.OrigAddr). Str("DestAddr", deposit.DestAddr). Msg("attempt to claim deposit failed") - return nil, logAndReturnJsonError(cmd, client, claimTx, opts, err) + return nil, err } log.Info().Stringer("txhash", claimTx.Hash()).Msg("sent claim") From 7fa6ed290e13bd027424524737c4b6ea278d9ce8 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Fri, 20 Dec 2024 15:24:43 -0500 Subject: [PATCH 17/17] feat: better defaults for dry running --- cmd/ulxly/ulxly.go | 109 +++++++++++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 38 deletions(-) diff --git a/cmd/ulxly/ulxly.go b/cmd/ulxly/ulxly.go index 655b056f..5830a7f3 100644 --- a/cmd/ulxly/ulxly.go +++ b/cmd/ulxly/ulxly.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "context" + "crypto/ecdsa" _ "embed" "encoding/binary" "encoding/json" @@ -1187,32 +1188,33 @@ var ulxlyClaimCmd = &cobra.Command{ } type ulxlyArgs struct { - gasLimit *uint64 - chainID *string - privateKey *string - value *string - rpcURL *string - bridgeAddress *string - destNetwork *uint32 - destAddress *string - tokenAddress *string - forceUpdate *bool - callData *string - timeout *uint64 - depositCount *uint64 - depositNetwork *uint64 - bridgeServiceURL *string - inputFileName *string - fromBlock *uint64 - toBlock *uint64 - filterSize *uint64 - depositNumber *uint64 - globalIndex *string - gasPrice *string - dryRun *bool - bridgeServiceURLs *[]string - bridgeLimit *int - bridgeOffset *int + gasLimit *uint64 + chainID *string + privateKey *string + addressOfPrivateKey string + value *string + rpcURL *string + bridgeAddress *string + destNetwork *uint32 + destAddress *string + tokenAddress *string + forceUpdate *bool + callData *string + timeout *uint64 + depositCount *uint64 + depositNetwork *uint64 + bridgeServiceURL *string + inputFileName *string + fromBlock *uint64 + toBlock *uint64 + filterSize *uint64 + depositNumber *uint64 + globalIndex *string + gasPrice *string + dryRun *bool + bridgeServiceURLs *[]string + bridgeLimit *int + bridgeOffset *int } var inputUlxlyArgs = ulxlyArgs{} @@ -1258,45 +1260,76 @@ const ( ArgBridgeOffset = "bridge-offset" ) +func prepInputs(cmd *cobra.Command, args []string) error { + if *inputUlxlyArgs.dryRun && *inputUlxlyArgs.gasLimit == 0 { + dryRunGasLimit := uint64(10_000_000) + inputUlxlyArgs.gasLimit = &dryRunGasLimit + } + pvtKey := strings.TrimPrefix(*inputUlxlyArgs.privateKey, "0x") + + privateKey, err := crypto.HexToECDSA(pvtKey) + if err != nil { + return err + } + publicKey := privateKey.Public() + + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return fmt.Errorf("cannot assert type: publicKey is not of type *ecdsa.PublicKey") + } + fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA) + inputUlxlyArgs.addressOfPrivateKey = fromAddress.String() + if *inputUlxlyArgs.destAddress == "" { + *inputUlxlyArgs.destAddress = fromAddress.String() + } + return nil +} + func init() { bridgeAssetCommand = &cobra.Command{ - Use: "asset", - Short: "send a single deposit of value or an ERC20 into the bridge", + Use: "asset", + Short: "send a single deposit of value or an ERC20 into the bridge", + PreRunE: prepInputs, RunE: func(cmd *cobra.Command, args []string) error { return bridgeAsset(cmd) }, } bridgeMessageCommand = &cobra.Command{ - Use: "message", - Short: "send some value along with call data into the bridge", + Use: "message", + Short: "send some value along with call data into the bridge", + PreRunE: prepInputs, RunE: func(cmd *cobra.Command, args []string) error { return bridgeMessage(cmd) }, } bridgeMessageWETHCommand = &cobra.Command{ - Use: "weth", - Short: "send some WETH into the bridge", + Use: "weth", + Short: "send some WETH into the bridge", + PreRunE: prepInputs, RunE: func(cmd *cobra.Command, args []string) error { return bridgeWETHMessage(cmd) }, } claimAssetCommand = &cobra.Command{ - Use: "asset", - Short: "perform a claim of a given deposit in the bridge", + Use: "asset", + Short: "perform a claim of a given deposit in the bridge", + PreRunE: prepInputs, RunE: func(cmd *cobra.Command, args []string) error { return claimAsset(cmd) }, } claimMessageCommand = &cobra.Command{ - Use: "message", - Short: "perform a claim of a given message in the bridge", + Use: "message", + Short: "perform a claim of a given message in the bridge", + PreRunE: prepInputs, RunE: func(cmd *cobra.Command, args []string) error { return claimMessage(cmd) }, } claimEverythingCommand = &cobra.Command{ - Use: "claim-everything", - Short: "attempt to claim any unclaimed deposits", + Use: "claim-everything", + Short: "attempt to claim any unclaimed deposits", + PreRunE: prepInputs, RunE: func(cmd *cobra.Command, args []string) error { return claimEverything(cmd) },