Skip to content

Commit

Permalink
Use special strings for methodSig to call fallback contract
Browse files Browse the repository at this point in the history
  • Loading branch information
songge-cb committed Jul 19, 2024
1 parent 845a4ce commit d1b0d80
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 33 deletions.
24 changes: 11 additions & 13 deletions services/construction/contract_call_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import (
"golang.org/x/crypto/sha3"
)

const NoMethodSig = "NO-METHOD-SIG"

// ConstructContractCallDataGeneric constructs the data field of a transaction.
// The methodArgs can be already in ABI encoded format in case of a single string
// It can also be passed in as a slice of args, which requires further encoding.
Expand All @@ -38,7 +40,7 @@ func ConstructContractCallDataGeneric(methodSig string, methodArgs interface{})
return nil, err
}

// preprocess method args
// preprocess method args for fallback pattern contract call
args, err := preprocessArgs(methodSig, methodArgs)
if err != nil {
return nil, err
Expand Down Expand Up @@ -87,16 +89,16 @@ func ConstructContractCallDataGeneric(methodSig string, methodArgs interface{})
}
}

// preprocessArgs converts methodArgs to a string value if methodSig is a 4-byte hex string.
// In this case, we expect methodArgs containing the pre-compiled ABI data.
// preprocessArgs converts methodArgs to a string value if methodSig is an empty string.
// We are calling a contract written with fallback pattern, which has no method signature.
func preprocessArgs(methodSig string, methodArgs interface{}) (interface{}, error) {
if !strings.HasPrefix(methodSig, "0x") || len(methodSig) != 10 {
if methodSig != "" && methodSig != NoMethodSig {
return methodArgs, nil
}

switch args := methodArgs.(type) {
case []interface{}:
if len(args) > 0 {
if len(args) == 1 {
argStr, isStrVal := args[0].(string)
if !isStrVal {
return nil, fmt.Errorf("failed to convert method arg \"%T\" to string", args[0])
Expand All @@ -105,7 +107,7 @@ func preprocessArgs(methodSig string, methodArgs interface{}) (interface{}, erro
}
return methodArgs, nil
case []string:
if len(args) > 0 {
if len(args) == 1 {
return args[0], nil
}
return methodArgs, nil
Expand Down Expand Up @@ -219,13 +221,9 @@ func encodeMethodArgsStrings(methodID []byte, methodSig string, methodArgs []str
// contractCallMethodID calculates the first 4 bytes of the method
// signature for function call on contract
func contractCallMethodID(methodSig string) ([]byte, error) {
if strings.HasPrefix(methodSig, "0x") && len(methodSig) == 10 {
// method signature is already a 4 byte hex string
result, err := hex.DecodeString(methodSig[2:])
if err != nil {
return nil, err
}
return result, nil
if methodSig == "" || methodSig == NoMethodSig {
// contract call without method signature (fallback pattern)
return []byte{}, nil
}

fnSignature := []byte(methodSig)
Expand Down
45 changes: 25 additions & 20 deletions services/construction/contract_call_data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,15 @@ func TestConstruction_ContractCallData(t *testing.T) {
methodArgs: []interface{}{"bool abc", "0x0000000000000000000000000000000000000000", "true"},
expectedResponse: "0x60d7a2780000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000008626f6f6c20616263000000000000000000000000000000000000000000000000",
},
"happy path: method sig is hex string and args is a list of interface": {
methodSig: "0xabcdef12",
methodArgs: []interface{}{"34567890"},
expectedResponse: "0xabcdef1234567890",
"happy path: method sig is an empty string and args is a list of interface": {
methodSig: "",
methodArgs: []interface{}{"0xabcde12345"},
expectedResponse: "0xabcde12345",
},
"happy path: method sig is NO-METHOD-SIG and args is a list of interface": {
methodSig: NoMethodSig,
methodArgs: []interface{}{"0xaabbcc112233"},
expectedResponse: "0xaabbcc112233",
},
"error: case string: invalid method args hex data": {
methodSig: "attest((bytes32,(address,uint64,bool,bytes32,bytes,uint256)))",
Expand Down Expand Up @@ -102,29 +107,29 @@ func TestConstruction_preprocessArgs(t *testing.T) {
"0",
"0x"},
},
"happy path: method sig is hex string and args is nil": {
methodSig: "0xabcdef12",
"happy path: method sig is empty and args is nil": {
methodSig: "",
methodArgs: nil,
expectedResponse: nil,
},
"happy path: method sig is hex string and args is a single string": {
methodSig: "0xabcdef12",
methodArgs: "34567890",
expectedResponse: "34567890",
"happy path: method sig is NO-METHOD-SIG and args is a single string": {
methodSig: NoMethodSig,
methodArgs: "0x12345",
expectedResponse: "0x12345",
},
"happy path: method sig is hex string and args is a list of interface": {
methodSig: "0xabcdef12",
methodArgs: []interface{}{"34567890"},
expectedResponse: "34567890",
"happy path: method sig is empty and args is a list of interface": {
methodSig: "",
methodArgs: []interface{}{"0xabcde"},
expectedResponse: "0xabcde",
},
"happy path: method sig is hex string and args is a list of strings": {
methodSig: "0xabcdef12",
methodArgs: []string{"34567890"},
expectedResponse: "34567890",
"happy path: method sig is NO-METHOD-SIG and args is a list of strings": {
methodSig: NoMethodSig,
methodArgs: []string{"0x1a2b3c"},
expectedResponse: "0x1a2b3c",
},
"unhappy path: args is a list of interface and cannot be converted to strings": {
methodSig: "0xabcdef12",
methodArgs: []interface{}{34567890},
methodSig: "",
methodArgs: []interface{}{34567},
expectedError: errors.New("failed to convert method arg \"int\" to string"),
},
}
Expand Down

0 comments on commit d1b0d80

Please sign in to comment.