-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'ccip-develop' into usdc-token-pool-support
- Loading branch information
Showing
25 changed files
with
789 additions
and
398 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,173 +1,71 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"encoding/hex" | ||
"encoding/json" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/ethereum/go-ethereum" | ||
"github.com/ethereum/go-ethereum/accounts/abi" | ||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/ethereum/go-ethereum/ethclient" | ||
"github.com/joho/godotenv" | ||
|
||
"github.com/smartcontractkit/chainlink/core/scripts/ccip/revert-reason/handler" | ||
"github.com/smartcontractkit/chainlink/core/scripts/ccip/secrets" | ||
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/arm_contract" | ||
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/burn_mint_token_pool" | ||
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" | ||
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" | ||
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" | ||
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/lock_release_token_pool" | ||
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry" | ||
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" | ||
) | ||
|
||
func panicErr(err error) { | ||
// How to use | ||
// Set either an error code string OR set the chainId, txHash and txRequester. | ||
// Setting an error code allows the script to run offline and doesn't require any RPC | ||
// endpoint. Using the chainId, txHash and txRequester requires an RPC endpoint, and if | ||
// the tx is old, the node needs to run in archive mode. | ||
// | ||
// Set the variable(s) and run main.go. The script will try to match the error code to the | ||
// ABIs of various CCIP contracts. If it finds a match, it will check if it's a CCIP wrapped error | ||
// like ExecutionError and TokenRateLimitError, and if so, it will decode the inner error. | ||
// | ||
// To configure an RPC endpoint, set the RPC_<chain_id> environment variable to the RPC endpoint. | ||
// e.g. RPC_420=https://rpc.<chain_id>.com | ||
const ( | ||
ErrorCodeString = "0x4e487b710000000000000000000000000000000000000000000000000000000000000032" | ||
|
||
// The following inputs are only used if ERROR_CODE_STRING is empty | ||
// Need a node URL | ||
// NOTE: this node needs to run in archive mode if the tx is old | ||
ChainId = uint64(420) | ||
TxHash = "0x97be8559164442595aba46b5f849c23257905b78e72ee43d9b998b28eee78b84" | ||
TxRequester = "0xe88ff73814fb891bb0e149f5578796fa41f20242" | ||
EnvFileName = ".env" | ||
) | ||
|
||
func main() { | ||
errorString, err := getErrorString() | ||
if err != nil { | ||
panic(err) | ||
} | ||
} | ||
|
||
// You can either add an error string (like "0x4e487b710000000000000000000000000000000000000000000000000000000000000032") | ||
// or you can specify an ethURL, txHash and requester. | ||
func main() { | ||
errorCodeString := "" | ||
|
||
if errorCodeString == "" { | ||
// Need a node URL | ||
// NOTE: this node needs to run in archive mode | ||
ethUrl := secrets.GetRPC(420) | ||
txHash := "0x97be8559164442595aba46b5f849c23257905b78e72ee43d9b998b28eee78b84" | ||
requester := "0xe88ff73814fb891bb0e149f5578796fa41f20242" | ||
|
||
ec, ethErr := ethclient.Dial(ethUrl) | ||
panicErr(ethErr) | ||
errorString, _ := getErrorForTx(ec, txHash, requester) | ||
// Some nodes prepend "Reverted " and we also remove the 0x | ||
trimmed := strings.TrimPrefix(errorString, "Reverted ")[2:] | ||
|
||
contractABIs := getAllABIs() | ||
|
||
DecodeErrorStringFromABI(trimmed, contractABIs) | ||
} else { | ||
errorCodeString = strings.TrimPrefix(errorCodeString, "0x") | ||
DecodeErrorStringFromABI(errorCodeString, getAllABIs()) | ||
decodedError, err := handler.DecodeErrorStringFromABI(errorString) | ||
if err != nil { | ||
panic(err) | ||
} | ||
} | ||
|
||
func DecodeErrorStringFromABI(errorString string, contractABIs []string) { | ||
data, err := hex.DecodeString(errorString) | ||
panicErr(err) | ||
|
||
for _, contractABI := range contractABIs { | ||
parsedAbi, err2 := abi.JSON(strings.NewReader(contractABI)) | ||
panicErr(err2) | ||
fmt.Println(decodedError) | ||
} | ||
|
||
for errorName, abiError := range parsedAbi.Errors { | ||
if bytes.Equal(data[:4], abiError.ID.Bytes()[:4]) { | ||
// Found a matching error | ||
v, err3 := abiError.Unpack(data) | ||
panicErr(err3) | ||
func getErrorString() (string, error) { | ||
errorCodeString := ErrorCodeString | ||
|
||
// If exec error, the actual error is within the revert reason | ||
if errorName == "ExecutionError" { | ||
// Get the inner type, which is `bytes` | ||
fmt.Printf("Error is \"%v\" inner error: ", errorName) | ||
errorBytes := v.([]interface{})[0].([]byte) | ||
DecodeErrorStringFromABI(hex.EncodeToString(errorBytes), contractABIs) | ||
return | ||
} | ||
fmt.Printf("Error is \"%v\" args %v\n", errorName, v) | ||
return | ||
} | ||
if errorCodeString == "" { | ||
// Try to load env vars from .env file | ||
err := godotenv.Load(EnvFileName) | ||
if err != nil { | ||
fmt.Println("No .env file found, using env vars from shell") | ||
} | ||
} | ||
|
||
if len(errorString) > 8 && errorString[:8] == "4e487b71" { | ||
fmt.Println("Assertion failure") | ||
indicator := errorString[len(errorString)-2:] | ||
switch indicator { | ||
case "01": | ||
fmt.Printf("If you call assert with an argument that evaluates to false.") | ||
case "11": | ||
fmt.Printf("If an arithmetic operation results in underflow or overflow outside of an unchecked { ... } block.") | ||
case "12": | ||
fmt.Printf("If you divide or modulo by zero (e.g. 5 / 0 or 23 modulo 0).") | ||
case "21": | ||
fmt.Printf("If you convert a value that is too big or negative into an enum type.") | ||
case "31": | ||
fmt.Printf("If you call .pop() on an empty array.") | ||
case "32": | ||
fmt.Printf("If you access an array, bytesN or an array slice at an out-of-bounds or negative index (i.e. x[i] where i >= x.length or i < 0).") | ||
case "41": | ||
fmt.Printf("If you allocate too much memory or create an array that is too large.") | ||
case "51": | ||
fmt.Printf("If you call a zero-initialized variable of internal function type.") | ||
default: | ||
fmt.Printf("This is a revert produced by an assertion failure. Exact code not found \"%s\"", indicator) | ||
ec, err := ethclient.Dial(secrets.GetRPC(ChainId)) | ||
if err != nil { | ||
return "", err | ||
} | ||
errorCodeString, err = handler.GetErrorForTx(ec, TxHash, TxRequester) | ||
if err != nil { | ||
return "", err | ||
} | ||
return | ||
} | ||
|
||
stringErr, err := abi.UnpackRevert(data) | ||
if err == nil { | ||
fmt.Print("string error: ") | ||
fmt.Printf("%s\n", stringErr) | ||
return | ||
} | ||
|
||
fmt.Printf("Cannot match error with contract ABI. Error code \"%v\"\n", errorString) | ||
} | ||
|
||
func getAllABIs() []string { | ||
return []string{ | ||
arm_contract.ARMContractABI, | ||
lock_release_token_pool.LockReleaseTokenPoolABI, | ||
burn_mint_token_pool.BurnMintTokenPoolABI, | ||
commit_store.CommitStoreABI, | ||
price_registry.PriceRegistryABI, | ||
evm_2_evm_onramp.EVM2EVMOnRampABI, | ||
evm_2_evm_offramp.EVM2EVMOffRampABI, | ||
router.RouterABI, | ||
} | ||
} | ||
|
||
func getErrorForTx(client *ethclient.Client, txHash string, requester string) (string, common.Address) { | ||
tx, _, err := client.TransactionByHash(context.Background(), common.HexToHash(txHash)) | ||
panicErr(err) | ||
re, err := client.TransactionReceipt(context.Background(), common.HexToHash(txHash)) | ||
panicErr(err) | ||
|
||
call := ethereum.CallMsg{ | ||
From: common.HexToAddress(requester), | ||
To: tx.To(), | ||
Data: tx.Data(), | ||
Gas: tx.Gas(), | ||
GasPrice: tx.GasPrice(), | ||
} | ||
_, err = client.CallContract(context.Background(), call, re.BlockNumber) | ||
if err == nil { | ||
panic("no error calling contract") | ||
} | ||
|
||
return parseError(err), *tx.To() | ||
} | ||
|
||
func parseError(txError error) string { | ||
b, err := json.Marshal(txError) | ||
panicErr(err) | ||
var callErr struct { | ||
Code int | ||
Data string `json:"data"` | ||
Message string `json:"message"` | ||
} | ||
err = json.Unmarshal(b, &callErr) | ||
panicErr(err) | ||
|
||
if callErr.Data == "" && strings.Contains(callErr.Message, "missing trie node") { | ||
panic("Use an archive node") | ||
} | ||
return callErr.Data | ||
return errorCodeString, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.