Created
April 2, 2024 10:19
-
-
Save toidv/18b6705ae0f3ef37ad256436106c4597 to your computer and use it in GitHub Desktop.
This is a script designed to debug the reason for a failed transaction. It utilizes the Tenderly API.
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
package main | |
import ( | |
"bufio" | |
"encoding/json" | |
"fmt" | |
"io/ioutil" | |
"log" | |
"net/http" | |
"os" | |
"sort" | |
"strconv" | |
"strings" | |
"sync" | |
"time" | |
"github.com/jedib0t/go-pretty/v6/table" | |
"golang.org/x/sync/errgroup" | |
) | |
type Txn struct { | |
Hash string `json:"hash"` | |
BlockHash string `json:"block_hash"` | |
BlockNumber int `json:"block_number"` | |
From string `json:"from"` | |
Gas int `json:"gas"` | |
GasPrice int64 `json:"gas_price"` | |
GasFeeCap int `json:"gas_fee_cap"` | |
GasTipCap int `json:"gas_tip_cap"` | |
CumulativeGasUsed int `json:"cumulative_gas_used"` | |
GasUsed int `json:"gas_used"` | |
EffectiveGasPrice int64 `json:"effective_gas_price"` | |
Input string `json:"input"` | |
Nonce int `json:"nonce"` | |
To string `json:"to"` | |
Index int `json:"index"` | |
Value string `json:"value"` | |
AccessList any `json:"access_list"` | |
Status bool `json:"status"` | |
Addresses []string `json:"addresses"` | |
ContractIds []string `json:"contract_ids"` | |
NetworkID string `json:"network_id"` | |
Timestamp time.Time `json:"timestamp"` | |
FunctionSelector string `json:"function_selector"` | |
L1BlockNumber int `json:"l1_block_number"` | |
L1Timestamp int `json:"l1_timestamp"` | |
DepositTx bool `json:"deposit_tx"` | |
SystemTx bool `json:"system_tx"` | |
Mint int `json:"mint"` | |
Sig Sig `json:"sig"` | |
ErrorMessage string `json:"error_message"` | |
Method string `json:"method"` | |
DecodedInput any `json:"decoded_input"` | |
CallTrace any `json:"call_trace"` | |
} | |
type Sig struct { | |
V string `json:"v"` | |
R string `json:"r"` | |
S string `json:"s"` | |
} | |
func main() { | |
readFile("./test.csv") | |
} | |
var lock sync.Mutex | |
func getTransactionDetail(chainId string, txHash string, errorMap map[string]int) { | |
var url = fmt.Sprintf("https://api.tenderly.co/api/v1/public-contract/%s/tx/%s", chainId, txHash) | |
r, err := http.Get(url) | |
if err != nil { | |
fmt.Print(err.Error()) | |
os.Exit(1) | |
} | |
if r.StatusCode != 200 { | |
error := fmt.Sprintf("error code %d ", r.StatusCode) | |
fmt.Println(error) | |
errorMap[error] += 1 | |
// log.Default("status code is not 200") | |
} | |
rd, err := ioutil.ReadAll(r.Body) | |
if err != nil { | |
log.Fatal(err) | |
} | |
var responseObject Txn | |
json.Unmarshal(rd, &responseObject) | |
error := responseObject.ErrorMessage | |
if len(strings.TrimSpace(error)) > 0 { | |
lock.Lock() | |
errorMap[error] += 1 | |
lock.Unlock() | |
} | |
} | |
func readFile(fileName string) { | |
//var m map[string]int | |
errorCount := make(map[string]int) | |
txnCount := make(map[string]int) | |
stats := make(map[string]map[string]int) | |
f, err := os.Open(fileName) | |
if err != nil { | |
log.Fatal(err) | |
} | |
defer f.Close() | |
scanner := bufio.NewScanner(f) | |
var eg = &errgroup.Group{} | |
eg.SetLimit(3) | |
for scanner.Scan() { | |
txInfo := scanner.Text() | |
parseTxn := strings.Split(txInfo, ",") | |
if len(parseTxn) > 2 && strings.HasPrefix(parseTxn[1], "0x") { | |
var m map[string]int | |
if _, ok := stats[parseTxn[0]]; ok { | |
m = stats[parseTxn[0]] | |
} else { | |
m = make(map[string]int) | |
stats[parseTxn[0]] = m | |
} | |
i, err := strconv.Atoi(parseTxn[4]) | |
if err != nil { | |
panic(err) | |
} | |
errorCount[parseTxn[0]] = i | |
t, err := strconv.Atoi(parseTxn[5]) | |
if err != nil { | |
panic(err) | |
} | |
txnCount[parseTxn[0]] = t | |
time.Sleep(1000 * time.Millisecond) | |
eg.Go(func() error { | |
getTransactionDetail(parseTxn[2], parseTxn[1], m) | |
return nil | |
}) | |
// stats[parseTxn[0]] = m | |
} | |
} | |
eg.Wait() | |
if err := scanner.Err(); err != nil { | |
log.Fatal(err) | |
} | |
t := table.NewWriter() | |
t.SetOutputMirror(os.Stdout) | |
rowConfigAutoMerge := table.RowConfig{AutoMerge: true} | |
t.AppendHeader(table.Row{"Date", "Error", "Error/TotalTxn(%)", "Error/TotalError(%)", "Count Error", "Total Non-Success", "Total Txn"}, rowConfigAutoMerge) | |
keys := make([]string, 0, len(stats)) | |
for k := range stats { | |
keys = append(keys, k) | |
} | |
sort.Slice(keys, func(i, j int) bool { | |
return keys[i] > keys[j] | |
}) | |
for _, k := range keys { | |
v := stats[k] | |
for k1, v1 := range v { | |
t.AppendRow(table.Row{k, k1, fmt.Sprintf("%.2f", float32(v1*100)/float32(txnCount[k])), fmt.Sprintf("%.2f", float32(v1*100)/float32(errorCount[k])), v1, errorCount[k], txnCount[k]}, rowConfigAutoMerge) | |
} | |
} | |
t.SetColumnConfigs([]table.ColumnConfig{ | |
{Number: 1, AutoMerge: true}, | |
{Number: 6, AutoMerge: true}, | |
{Number: 7, AutoMerge: true}, | |
}) | |
t.Style().Options.SeparateRows = true | |
t.Render() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment