From 5ed5f5d5665c09708ac7821964a6d5af73b0280a Mon Sep 17 00:00:00 2001 From: thanhpp Date: Wed, 15 May 2024 15:19:34 +0700 Subject: [PATCH] =?UTF-8?q?feat:=20=E2=9C=A8=20eth/trace:=20traceTx=20&=20?= =?UTF-8?q?traceCall?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: thanhpp --- .github/workflows/ci.yml | 4 +- go.mod | 2 +- pkg/eth/trace.go | 178 +++++++++++++++++++++++++++++++++++++++ pkg/eth/trace_test.go | 31 +++++++ 4 files changed, 212 insertions(+), 3 deletions(-) create mode 100644 pkg/eth/trace.go create mode 100644 pkg/eth/trace_test.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6986aa2..2ac9ddc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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: @@ -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 ./... diff --git a/go.mod b/go.mod index e83968e..07edeab 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/pkg/eth/trace.go b/pkg/eth/trace.go new file mode 100644 index 0000000..5abe50e --- /dev/null +++ b/pkg/eth/trace.go @@ -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 +} diff --git a/pkg/eth/trace_test.go b/pkg/eth/trace_test.go new file mode 100644 index 0000000..cdea38d --- /dev/null +++ b/pkg/eth/trace_test.go @@ -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) +}