Skip to content

Commit 5554ff3

Browse files
authored
feature: support generate parse transaction input bytes codes in abigen (#7593)
feature: support generate parse transaction input bytes codes in `abigen`. It's easy to use abigen to generate code that help you parse the event log and call the payable method with the transactor. But parsing the transaction call data is missing. You can only send a transaction with a transactor. This PR aims to generate codes that help developer parse transaction calldata. abigen can generate code to parse the transaction call data. With this PR, transaction parse codes are generated like this: ```go // CollectParams is an auto generated read-only Go binding of transcaction calldata params type CollectParams struct { Param_params INonfungiblePositionManagerCollectParams } // Parse Collect method from calldata of a transaction // // Solidity: function collect((uint256,address,uint128,uint128) params) payable returns(uint256 amount0, uint256 amount1) func ParseCollect(calldata []byte) (*CollectParams, error) { if len(calldata) <= 4 { return nil, fmt.Errorf("invalid calldata input") } _abi, err := abi.JSON(strings.NewReader(UniswapABI)) if err != nil { return nil, fmt.Errorf("failed to get abi of registry metadata: %w", err) } out, err := _abi.Methods["collect"].Inputs.Unpack(calldata[4:]) if err != nil { return nil, fmt.Errorf("failed to unpack collect params data: %w", err) } var paramsResult = new(CollectParams) value := reflect.ValueOf(paramsResult).Elem() if value.NumField() != len(out) { return nil, fmt.Errorf("failed to match calldata with param field number") } out0 := *abi.ConvertType(out[0], new(INonfungiblePositionManagerCollectParams)).(*INonfungiblePositionManagerCollectParams) return &CollectParams{ Param_params: out0, }, nil } ``` Example of using `Parse` Function above: ```go package bin import ( "context" "fmt" "testing" "github.com/ledgerwatch/erigon/common/hexutil" "github.com/ledgerwatch/erigon/rpc" "github.com/ledgerwatch/log/v3" ) func TestOnParse(t *testing.T) { cli, err := rpc.Dial("https://rpc.ankr.com/polygon", log.New(context.Background())) if err != nil { t.Fatal(err) } var collectTx struct { Input string `json:"input"` } err = cli.CallContext(context.Background(), &collectTx, "eth_getTransactionByHash", "0x741146cce64d873cfe82ade413651de355a6db92f992e5bfc4e1c58d92f5dd5b") if err != nil { t.Fatal(err) } var increaseLiquidityTx struct { Input string `json:"input"` } err = cli.CallContext(context.Background(), &increaseLiquidityTx, "eth_getTransactionByHash", "0x645ff650d7bfb9a74f573af474b9ebee48c3fadc7dac67257a8da6b55c71f338") if err != nil { t.Fatal(err) } fmt.Println(collectTx.Input) fmt.Println(increaseLiquidityTx.Input) collectBytes, _ := hexutil.Decode(collectTx.Input) increaseLiquidityBytes, _ := hexutil.Decode(increaseLiquidityTx.Input) collectInfo, err := ParseCollect(collectBytes) if err != nil { t.Fatal(err) } increaseLiquidityInfo, err := ParseIncreaseLiquidity(increaseLiquidityBytes) if err != nil { t.Fatal(err) } fmt.Printf("%+v\n", collectInfo) fmt.Printf("%+v\n", increaseLiquidityInfo) } ``` Output of code above: &{Param_params:{TokenId:+894123 Recipient:0x48B8e4ed457da9B64c33Ee50Fd5490614833A37D Amount0Max:+340282366920938463463374607431768211455 Amount1Max:+340282366920938463463374607431768211455}} &{Param_params:{TokenId:+891904 Amount0Desired:+331092 Amount1Desired:+0 Amount0Min:+331092 Amount1Min:+0 Deadline:+1685112789}}
1 parent 3c2b99e commit 5554ff3

File tree

1 file changed

+49
-1
lines changed

1 file changed

+49
-1
lines changed

accounts/abi/bind/template.go

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ package {{.Package}}
9090
import (
9191
"math/big"
9292
"strings"
93+
"fmt"
94+
"reflect"
9395
9496
ethereum "github.com/ledgerwatch/erigon"
9597
"github.com/ledgerwatch/erigon/accounts/abi"
@@ -335,7 +337,7 @@ var (
335337
func (_{{$contract.Type}} *{{$contract.Type}}CallerSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
336338
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
337339
}
338-
{{end}}
340+
{{end}}
339341
340342
{{range .Transacts}}
341343
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
@@ -360,6 +362,52 @@ var (
360362
}
361363
{{end}}
362364
365+
{{$metaType := .Type}}
366+
{{range .Transacts}}
367+
{{if ne (len .Normalized.Inputs) 0}}
368+
369+
// {{.Normalized.Name}}Params is an auto generated read-only Go binding of transcaction calldata params
370+
type {{.Normalized.Name}}Params struct {
371+
{{range $i, $_ := .Normalized.Inputs}} Param_{{.Name}} {{bindtype .Type $structs}}
372+
{{end}}
373+
}
374+
375+
// Parse {{.Normalized.Name}} method from calldata of a transaction
376+
//
377+
// Solidity: {{.Original.String}}
378+
func Parse{{.Normalized.Name}}(calldata []byte) (*{{.Normalized.Name}}Params, error) {
379+
if len(calldata) <= 4 {
380+
return nil, fmt.Errorf("invalid calldata input")
381+
}
382+
383+
_abi, err := abi.JSON(strings.NewReader({{$metaType}}ABI))
384+
if err != nil {
385+
return nil, fmt.Errorf("failed to get abi of registry metadata: %w", err)
386+
}
387+
388+
out, err := _abi.Methods["{{.Original.Name}}"].Inputs.Unpack(calldata[4:])
389+
if err != nil {
390+
return nil, fmt.Errorf("failed to unpack {{.Original.Name}} params data: %w", err)
391+
}
392+
393+
var paramsResult = new({{.Normalized.Name}}Params)
394+
value := reflect.ValueOf(paramsResult).Elem()
395+
396+
if value.NumField() != len(out) {
397+
return nil, fmt.Errorf("failed to match calldata with param field number")
398+
}
399+
400+
{{range $i, $t := .Normalized.Inputs}}
401+
out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
402+
403+
return &{{.Normalized.Name}}Params{
404+
{{range $i, $_ := .Normalized.Inputs}} Param_{{.Name}} : out{{$i}},{{end}}
405+
}, nil
406+
}
407+
408+
{{end}}
409+
{{end}}
410+
363411
{{if .Fallback}}
364412
// Fallback is a paid mutator transaction binding the contract fallback function.
365413
//

0 commit comments

Comments
 (0)