Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
pkieltyka committed Oct 21, 2024
1 parent 82a5295 commit ec96376
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 445 deletions.
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
github.com/0xsequence/ethkit v1.27.7 h1:0g2CWWS+OYdhf6gfDlAjU566UIoiGWhZICjijPZtMjA=
github.com/0xsequence/ethkit v1.27.7/go.mod h1:rv0FAIyEyN0hhwGefbduAz4ujmyjyJXhCd6a0/yF3tk=
github.com/0xsequence/go-ethauth v0.13.0 h1:ZaqFEEqy574A2b1P7vjpcy5tb4W/izn+A3swwOYi9wA=
github.com/0xsequence/go-ethauth v0.13.0/go.mod h1:f3kx39S9F+W+qvZEB6bkKKbpUstmyB7goUntO3wvlhg=
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
Expand Down
2 changes: 2 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -390,6 +391,7 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
Expand Down
269 changes: 7 additions & 262 deletions intents/intent_data_transaction_contract_abi.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
package intents

import (
"encoding/json"
"errors"
"fmt"
"reflect"
"strings"

"github.com/0xsequence/ethkit/ethcoder"
"github.com/0xsequence/ethkit/go-ethereum/accounts/abi"
"github.com/0xsequence/ethkit/go-ethereum/common"
"github.com/davecgh/go-spew/spew"
)

type contractCallType struct {
Expand All @@ -19,259 +10,13 @@ type contractCallType struct {
Args []any `json:"args"`
}

// EncodeContractCall encodes a contract call as a hex encoded calldata.
// NOTE: see ethcoder.EncodeContractCall for more details.
func EncodeContractCall(data *contractCallType) (string, error) {
data.Abi = strings.TrimSpace(data.Abi)

// Get the method from the abi
method, _, argTypes, err := getMethodFromAbi(data.Abi, data.Func)
if err != nil {
return "", err
}

// Prepare the arguments, which may be nested
argStringValues, err := prepareContractCallArgs(data.Args)
if err != nil {
return "", err
}

spew.Dump("method", method)
spew.Dump("enc", argStringValues)
spew.Dump("argTypes", argTypes)

// Encode the method call
// TODO: pass argTypes so we dont have to decode again... we can just copy what we do inside of `ethcoder.AbiEncodeMethodCalldataFromStringValuesAny`
// res, err := ethcoder.AbiEncodeMethodCalldataFromStringValuesAny(method, enc)
// if err != nil {
// return "", err
// }

// TODO: we need to fix up and update ethcoder.AbiEncodeMethodCalldata to upgrade the method parsing functions... etc...

// TODO: we are calling ParseEventDef multiple times..

argValues, err := ethcoder.ABIUnmarshalStringValuesAny(argTypes, argStringValues)
if err != nil {
return "", err
}
// return AbiEncodeMethodCalldata(methodExpr, argValues)

var mabi abi.ABI
var methodName string

if len(data.Abi) > 0 && strings.Contains(data.Abi, "(") && data.Abi[len(data.Abi)-1] == ')' {
abiSig, err := ethcoder.ParseABISignature(data.Abi)
if err != nil {
return "", err
}
mabi, err = ethcoder.EventDefToABI(abiSig, true)
if err != nil {
return "", err
}
methodName = abiSig.Name
spew.Dump(mabi)
spew.Dump(argValues)
} else {
mabi, err = abi.JSON(strings.NewReader(data.Abi))
if err != nil {
return "", err
}
methodName = data.Func
}

args, err := packableArgValues(mabi, methodName, argValues)
if err != nil {
return "", err
}

packed, err := mabi.Pack(methodName, args...)
if err != nil {
return "", err
}

return "0x" + common.Bytes2Hex(packed), nil
}

func packableArgValues(mabi abi.ABI, method string, argValues []any) ([]any, error) {
m, ok := mabi.Methods[method]
if !ok {
return nil, errors.New("method not found in abi")
}

if len(m.Inputs) != len(argValues) {
return nil, errors.New("method inputs length does not match arg values length")
}

fmt.Println("$$$$$$$$$$$$$$$$$$$")
spew.Dump(m.Inputs)

out := make([]any, len(argValues))

for i, input := range m.Inputs {
isTuple := false
typ := input.Type.String()
if len(typ) >= 2 && typ[0] == '(' && typ[len(typ)-1] == ')' {
isTuple = true
}

if !isTuple {
out[i] = argValues[i]
} else {
// build struct for the tuple, as that is what the geth abi encoder expects
// NOTE: in future we could fork or modify it if we want to avoid the need for this,
// as it means decoding tuples will be more intensive the necessary.

spew.Dump(input)

fields := []reflect.StructField{}

v, ok := argValues[i].([]any)
if !ok {
vv, ok := argValues[i].([]string)
if !ok {
return nil, errors.New("tuple arg values must be an array")
}
v = make([]any, len(vv))
for j, x := range vv {
v[j] = x
}
}

for j, vv := range v {
fields = append(fields, reflect.StructField{
Name: fmt.Sprintf("Name%d", j),
Type: reflect.TypeOf(vv),
})
}

structType := reflect.StructOf(fields)
instance := reflect.New(structType).Elem()

for j, vv := range v {
instance.Field(j).Set(reflect.ValueOf(vv))
}

spew.Dump(instance.Interface())

out[i] = instance.Interface()
}
}

return out, nil
}

func prepareContractCallArgs(args []any) ([]any, error) {
var err error
out := make([]any, len(args))

for i, arg := range args {
switch arg := arg.(type) {
case string, []string, []any:
out[i] = arg

case map[string]interface{}:
nst := arg

var funcName string
if v, ok := nst["func"].(string); ok {
funcName = v
}

args, ok := nst["args"].([]interface{})
if !ok {
return nil, fmt.Errorf("nested encode expects the 'args' field to be an array")
}

abi, ok := nst["abi"].(string)
if !ok {
return nil, fmt.Errorf("nested encode expects an 'abi' field")
}

out[i], err = EncodeContractCall(&contractCallType{
Abi: abi,
Func: funcName,
Args: args,
})
if err != nil {
return nil, err
}

default:
return nil, fmt.Errorf("abi encoding fail due to invalid arg type, '%T'", arg)
}
}

return out, nil
}

// The abi may be a:
// - already encoded method abi: transferFrom(address,address,uint256)
// - already encoded named method: transferFrom(address from,address to,uint256 val)
// - an array of function abis: "[{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_orderId\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"_maxCost\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_fees\",\"type\":\"address\"}],\"name\":\"fillOrKillOrder\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"}]"
// - or a single function abi: "{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_orderId\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"_maxCost\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_fees\",\"type\":\"address\"}],\"name\":\"fillOrKillOrder\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"}"
// And it must always return it encoded, like this:
// - transferFrom(address,address,uint256)
// making sure that the method matches the returned one
func getMethodFromAbi(abi string, method string) (string, []string, []string, error) {
//
// First attempt to parse `abi` string as a plain method abi
// ie. transferFrom(address,address,uint256)
//

// Handle the case for already encoded method abi.
// NOTE: we do not need the know the `method` argument here.
abi = strings.TrimSpace(abi)
if len(abi) > 0 && strings.Contains(abi, "(") && abi[len(abi)-1] == ')' {
// NOTE: even though the ethcoder function is `ParseEventDef`, designed for event type parsing
// the abi format for a single function structure is the same, so it works. Perhaps we will rename
// `ParseEventDef` in the future, or just add another method with a different name.
abiSig, err := ethcoder.ParseABISignature(abi)
if err != nil {
return "", nil, nil, err
}
return abiSig.Signature, abiSig.ArgNames, abiSig.ArgTypes, nil
callDef := ethcoder.ContractCallDef{
ABI: data.Abi,
Func: data.Func,
Args: data.Args,
}

//
// If above didn't work, attempt to parse `abi` string as
// a JSON object of the full abi definition
//

type FunctionAbi struct {
Name string `json:"name"`
Type string `json:"type"`
Inputs []struct {
InternalType string `json:"internalType"`
Name string `json:"name"`
Type string `json:"type"`
} `json:"inputs"`
}

// Handle array of function abis and single function abi
var abis []FunctionAbi
if strings.HasPrefix(abi, "[") {
if err := json.Unmarshal([]byte(abi), &abis); err != nil {
return "", nil, nil, err
}
} else {
var singleAbi FunctionAbi
if err := json.Unmarshal([]byte(abi), &singleAbi); err != nil {
return "", nil, nil, err
}
abis = append(abis, singleAbi)
}

// Find the correct method and encode it
for _, fnAbi := range abis {
if fnAbi.Name == method {
var paramTypes []string
order := make([]string, len(fnAbi.Inputs))
for i, input := range fnAbi.Inputs {
paramTypes = append(paramTypes, input.Type)
order[i] = input.Name
}
return method + "(" + strings.Join(paramTypes, ",") + ")", order, paramTypes, nil
}
}

return "", nil, nil, fmt.Errorf("method not found in abi")
return ethcoder.EncodeContractCall(callDef)
}
Loading

0 comments on commit ec96376

Please sign in to comment.