Skip to content

Commit

Permalink
feat: ✨ eth/trace: traceTx & traceCall
Browse files Browse the repository at this point in the history
Signed-off-by: thanhpp <[email protected]>
  • Loading branch information
thanhpp committed May 15, 2024
1 parent 28275d7 commit 5ed5f5d
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 3 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: "1.21.x"
go-version: "1.22.x"
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
Expand Down Expand Up @@ -67,6 +67,6 @@ jobs:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: "1.21.x"
go-version: "1.22.x"
- name: Run test
run: go test -race -v ./...
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/KyberNetwork/tradinglib

go 1.21
go 1.22

require (
github.com/duoxehyon/mev-share-go v0.3.0
Expand Down
178 changes: 178 additions & 0 deletions pkg/eth/trace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package eth

import (
"bytes"
"cmp"
"encoding/json"
"io"
"math/big"
"net/http"

"github.com/duoxehyon/mev-share-go/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)

type TraceClient struct {
httpClient *http.Client
rpcURL string
}

func NewTraceClient(httpClient *http.Client, rpcURL string) *TraceClient {
return &TraceClient{
httpClient: httpClient,
rpcURL: rpcURL,
}
}

func (c *TraceClient) DebugTraceTransaction(txHash string) (CallFrame, error) {
payload := map[string]interface{}{
"method": "debug_traceTransaction",
"id": 1,
"jsonrpc": "2.0",
"params": []interface{}{
txHash,
map[string]interface{}{
"tracer": "callTracer",
"tracerConfig": map[string]interface{}{
"withLog": true,
},
},
},
}

b, err := json.Marshal(payload)
if err != nil {
return CallFrame{}, err
}

req, err := http.NewRequest(http.MethodPost, c.rpcURL, bytes.NewBuffer(b))
if err != nil {
return CallFrame{}, err
}
req.Header.Add("Content-Type", "application/json")

res, err := c.httpClient.Do(req)
if err != nil {
return CallFrame{}, err
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
return CallFrame{}, err
}

var rpcResponse CommomTraceResponse[CallFrame]
if err := json.Unmarshal(body, &rpcResponse); err != nil {
return CallFrame{}, err
}

return rpcResponse.Result, err
}

type CommomTraceResponse[T any] struct {
Jsonrpc string `json:"jsonrpc"`
ID int `json:"id"`
Result T `json:"result"`
}

type CallLog struct {
Address common.Address `json:"address"`
Topics []common.Hash `json:"topics"`
Data string `json:"data"`
}

func (l CallLog) ToEthereumLog() types.Log {
return types.Log{
Address: l.Address,
Topics: l.Topics,
Data: common.Hex2Bytes(l.Data),
}
}

type CallFrame struct {
From string `json:"from"`
Gas string `json:"gas"`
GasUsed string `json:"gasUsed"`
To string `json:"to"`
Input string `json:"input"`
Output string `json:"output"`
Calls []CallFrame `json:"calls"`
Value string `json:"value"`
Type string `json:"type"`
Logs []CallLog `json:"logs"`
}

func (c *TraceClient) DebugTraceCall(
from, to string,
gas uint64,
gasPrice *big.Int,
value *big.Int,
encodedData string,
block *big.Int,
) (CallFrame, error) {
paramData := map[string]any{
"from": cmp.Or(from, "null"),
"to": to,
"data": cmp.Or(encodedData, "null"),
}
if gas != 0 {
paramData["gas"] = hexutil.EncodeUint64(gas)
}
if gasPrice != nil {
paramData["gasPrice"] = hexutil.EncodeBig(gasPrice)
}
if value != nil {
paramData["value"] = hexutil.EncodeBig(value)
}
blockStr := "latest"
if block != nil {
blockStr = hexutil.EncodeBig(block)
}

payload := map[string]any{
"method": "debug_traceCall",
"id": 1,
"jsonrpc": "2.0",
"params": []any{
paramData,
blockStr,
map[string]any{
"tracer": "callTracer",
"tracerConfig": map[string]interface{}{
"onlyTopCall": false,
},
},
},
}

b, err := json.Marshal(payload)
if err != nil {
return CallFrame{}, err
}

req, err := http.NewRequest(http.MethodPost, c.rpcURL, bytes.NewBuffer(b))
if err != nil {
return CallFrame{}, err
}
req.Header.Add("Content-Type", "application/json")

res, err := c.httpClient.Do(req)
if err != nil {
return CallFrame{}, err
}
defer res.Body.Close()

text, err := io.ReadAll(res.Body)
if err != nil {
return CallFrame{}, err
}

var rpcResponse CommomTraceResponse[CallFrame]
if err := json.Unmarshal(text, &rpcResponse); err != nil {
return CallFrame{}, err
}

return rpcResponse.Result, err
}
31 changes: 31 additions & 0 deletions pkg/eth/trace_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package eth_test

import (
"math/big"
"net/http"
"testing"

"github.com/KyberNetwork/tradinglib/pkg/eth"
"github.com/stretchr/testify/require"
)

func TestTraceCall(t *testing.T) {
t.Skip()

nodeRPC := "...."

traceClient := eth.NewTraceClient(http.DefaultClient, nodeRPC)

resp, err := traceClient.DebugTraceCall(
"0x8686301A430112D76FeF8331821aEf42c8c48686",
"0x807cF9A772d5a3f9CeFBc1192e939D62f0D9bD38",
407047,
nil,
big.NewInt(0),
"0x7507bd7a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008800000000000000000000000008686301a430112d76fef8331821aef42c8c486860000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049c00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000764e21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000004e000000000000000000000000000000000000000000000000000000000000007200000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000807cf9a772d5a3f9cefbc1192e939d62f0d9bd38000000000000000000000000000000000000000000000000000000006644581a00000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000004063407a490000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000fe0df74636bc25c7f2400f22fe7dae32d39443d2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006123b0049f904d730db3c36a31167d9d4121fa6b00000000000000000000000000000000000000000000000000000000b2c059df00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004063407a490000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd6700000000000000000000000094981f69f7483af3ae218cbfe65233cc3c60d93a0000000000000000000000006123b0049f904d730db3c36a31167d9d4121fa6b000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000a4cc6f4162480b7c24000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000807cf9a772d5a3f9cefbc1192e939d62f0d9bd3800000000000000000000000000000000000000000000000000000000b2c059df0000000000000000000000000000000000000000000000000e6b8af58ea04f8f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000b2c059df0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a19520ccf6ebf03bdf1c42fade9aa702ef33dc9a842da9e1489eebbd22de1e295e4138d1778b6166f612de774b947d53466c8c89601d37df95eb20744f7fc7651b00000000000000000000000000000000000000000000000000000000000000", // nolint: lll
nil,
)
require.NoError(t, err)

t.Logf("%+v", resp)
}

0 comments on commit 5ed5f5d

Please sign in to comment.