Skip to content

Instantly share code, notes, and snippets.

@toidv
Created April 2, 2024 10:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save toidv/18b6705ae0f3ef37ad256436106c4597 to your computer and use it in GitHub Desktop.
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.
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