Skip to content

Commit 607e7b9

Browse files
authored
Preprocess contract call with no method sig (#111)
1 parent 565925e commit 607e7b9

File tree

2 files changed

+117
-1
lines changed

2 files changed

+117
-1
lines changed

services/construction_service.go

+36-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ const (
4949
// TokenContractAddressKey is the key in the currency metadata map
5050
// that represents the contract address of a token
5151
TokenContractAddressKey = "token_address"
52+
// NoMethodSig is the method signature name when a contract call does not have one
53+
NoMethodSig = "NO-METHOD-SIG"
5254
)
5355

5456
var (
@@ -958,8 +960,14 @@ func constructERC20VotesDelegateData(to string) []byte {
958960
func constructContractCallData(methodSig string, methodArgsGeneric interface{}) ([]byte, error) {
959961
data := contractCallMethodID(methodSig)
960962

963+
// preprocess method args for contract call with no method signature
964+
args, err := preprocessArgs(methodSig, methodArgsGeneric)
965+
if err != nil {
966+
return nil, err
967+
}
968+
961969
// switch on the type of the method args. method args can come in from json as either a string or list of strings
962-
switch methodArgs := methodArgsGeneric.(type) {
970+
switch methodArgs := args.(type) {
963971
// case 0: no method arguments, return the selector
964972
case nil:
965973
return data, nil
@@ -1262,11 +1270,38 @@ func rosettaOperations(
12621270
// contractCallMethodID calculates the first 4 bytes of the method
12631271
// signature for function call on contract
12641272
func contractCallMethodID(methodSig string) []byte {
1273+
if methodSig == "" || methodSig == NoMethodSig {
1274+
// contract call without method signature (fallback pattern)
1275+
return []byte{}
1276+
}
1277+
12651278
fnSignature := []byte(methodSig)
12661279
hash := crypto.Keccak256(fnSignature)
12671280
return hash[:4]
12681281
}
12691282

1283+
// preprocessArgs converts methodArgs to a string value if a contract call has no method signature.
1284+
// In this case, methodArgs contains the pre-compiled ABI data.
1285+
func preprocessArgs(methodSig string, methodArgs interface{}) (interface{}, error) {
1286+
if methodSig == "" || methodSig == NoMethodSig {
1287+
switch args := methodArgs.(type) {
1288+
case []interface{}:
1289+
if len(args) == 1 {
1290+
if argStr, ok := args[0].(string); ok {
1291+
return argStr, nil
1292+
}
1293+
return nil, fmt.Errorf("failed to convert method arg \"%T\" to string", args[0])
1294+
}
1295+
case []string:
1296+
if len(args) == 1 {
1297+
return args[0], nil
1298+
}
1299+
}
1300+
}
1301+
1302+
return methodArgs, nil
1303+
}
1304+
12701305
func bigIntMax(a *big.Int, b *big.Int) *big.Int {
12711306
if a.Cmp(b) == -1 {
12721307
return b

services/construction_service_test.go

+81
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"encoding/hex"
2020
"encoding/json"
21+
"errors"
2122
"fmt"
2223
"math"
2324
"math/big"
@@ -1330,6 +1331,26 @@ func TestConstructContractCallData(t *testing.T) {
13301331
},
13311332
expectedResult: "cf9d137c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000deaddeaddeaddeaddeaddeaddeaddeaddead0000000000000000000000000000b0935a466e6fa8fda8143c7f4a8c149ca56d06fe",
13321333
},
1334+
"nil args": {
1335+
methodSig: "deposit()",
1336+
methodArgs: nil,
1337+
expectedResult: "d0e30db0",
1338+
},
1339+
"list of non string args": {
1340+
methodSig: "register(string,address,bool)",
1341+
methodArgs: []interface{}{"bool abc", "0x0000000000000000000000000000000000000000", "true"},
1342+
expectedResult: "60d7a2780000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000008626f6f6c20616263000000000000000000000000000000000000000000000000",
1343+
},
1344+
"method sig is an empty string and args is a list of interface": {
1345+
methodSig: "",
1346+
methodArgs: []interface{}{"0xabcde12345"},
1347+
expectedResult: "abcde12345",
1348+
},
1349+
"method sig is NO-METHOD-SIG and args is a list of interface": {
1350+
methodSig: NoMethodSig,
1351+
methodArgs: []interface{}{"0xaabbcc112233"},
1352+
expectedResult: "aabbcc112233",
1353+
},
13331354
"invalid bytes format": {
13341355
methodSig: "deploy(bytes32,address)",
13351356
methodArgs: []string{
@@ -1377,3 +1398,63 @@ func TestConstructContractCallData(t *testing.T) {
13771398
})
13781399
}
13791400
}
1401+
1402+
func TestConstruction_preprocessArgs(t *testing.T) {
1403+
tests := map[string]struct {
1404+
methodSig string
1405+
methodArgs interface{}
1406+
1407+
expectedResponse interface{}
1408+
expectedError error
1409+
}{
1410+
"happy path: method sig is function name": {
1411+
methodSig: "withdraw(address,uint256,uint32,bytes)",
1412+
methodArgs: []interface{}{
1413+
"0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22",
1414+
"32941055343948244352",
1415+
"0",
1416+
"0x"},
1417+
expectedResponse: []interface{}{
1418+
"0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22",
1419+
"32941055343948244352",
1420+
"0",
1421+
"0x"},
1422+
},
1423+
"happy path: method sig is empty and args is nil": {
1424+
methodSig: "",
1425+
methodArgs: nil,
1426+
expectedResponse: nil,
1427+
},
1428+
"happy path: method sig is NO-METHOD-SIG and args is a single string": {
1429+
methodSig: NoMethodSig,
1430+
methodArgs: "0x12345",
1431+
expectedResponse: "0x12345",
1432+
},
1433+
"happy path: method sig is empty and args is a list of interface": {
1434+
methodSig: "",
1435+
methodArgs: []interface{}{"0xabcde"},
1436+
expectedResponse: "0xabcde",
1437+
},
1438+
"happy path: method sig is NO-METHOD-SIG and args is a list of strings": {
1439+
methodSig: NoMethodSig,
1440+
methodArgs: []string{"0x1a2b3c"},
1441+
expectedResponse: "0x1a2b3c",
1442+
},
1443+
"unhappy path: args is a list of interface and cannot be converted to strings": {
1444+
methodSig: "",
1445+
methodArgs: []interface{}{34567},
1446+
expectedError: errors.New("failed to convert method arg \"int\" to string"),
1447+
},
1448+
}
1449+
1450+
for name, test := range tests {
1451+
t.Run(name, func(t *testing.T) {
1452+
argsReturned, err := preprocessArgs(test.methodSig, test.methodArgs)
1453+
if err != nil {
1454+
assert.EqualError(t, err, test.expectedError.Error())
1455+
} else {
1456+
assert.Equal(t, test.expectedResponse, argsReturned)
1457+
}
1458+
})
1459+
}
1460+
}

0 commit comments

Comments
 (0)