2022-08-24 12:42:41 +00:00
package abispec
import (
"encoding/hex"
"encoding/json"
"fmt"
"math/big"
"reflect"
"regexp"
"strings"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
)
var methodPattern = regexp . MustCompile ( ` (^[a-zA-Z].*)\((.*)\) ` )
var maxSafeInteger = big . NewInt ( int64 ( 9007199254740991 ) )
const transferInputs = ` [ { "type":"address"}, { "type":"uint256"}] `
const transferFunctionName = "transfer"
var transferInDef = fmt . Sprintf ( ` [ { "name" : "%s", "type": "function", "inputs": %s}] ` , transferFunctionName , transferInputs )
var transferAbi , _ = abi . JSON ( strings . NewReader ( transferInDef ) )
func EncodeTransfer ( to string , value string ) ( string , error ) {
amount , success := big . NewInt ( 0 ) . SetString ( value , 0 )
if ! success {
return "" , fmt . Errorf ( "failed to convert %s to big.Int" , value )
}
address := common . HexToAddress ( to )
result , err := transferAbi . Pack ( transferFunctionName , address , amount )
if err != nil {
return "" , fmt . Errorf ( "pack failed: %v" , err )
}
return "0x" + hex . EncodeToString ( result ) , nil
}
func Encode ( method string , paramsJSON string ) ( string , error ) {
matches := methodPattern . FindStringSubmatch ( method )
if len ( matches ) != 3 {
return "" , fmt . Errorf ( "unrecognized method: %s" , method )
}
methodName := matches [ 1 ]
paramTypesString := strings . TrimSpace ( matches [ 2 ] )
// value of inputs looks like: `[{ "type": "uint32" },{ "type": "bool" }]`
inputs := "["
var params [ ] interface { }
if len ( paramTypesString ) > 0 {
var paramsRaw [ ] json . RawMessage
if err := json . Unmarshal ( [ ] byte ( paramsJSON ) , & paramsRaw ) ; err != nil {
return "" , fmt . Errorf ( "unable to unmarshal params: %v" , err )
}
types := strings . Split ( paramTypesString , "," )
if len ( paramsRaw ) != len ( types ) {
return "" , fmt . Errorf ( "num of param type should equal to num of param value, actual value: %d, %d" , len ( types ) , len ( paramsRaw ) )
}
for i , typeName := range types {
if i != 0 {
inputs += ","
}
inputs += fmt . Sprintf ( ` { "type":"%s"} ` , typeName )
param , err := toGoTypeValue ( typeName , paramsRaw [ i ] )
if err != nil {
return "" , err
}
params = append ( params , param . Interface ( ) )
}
}
inputs += "]"
inDef := fmt . Sprintf ( ` [ { "name" : "%s", "type": "function", "inputs": %s}] ` , methodName , inputs )
inAbi , err := abi . JSON ( strings . NewReader ( inDef ) )
if err != nil {
return "" , fmt . Errorf ( "invalid ABI definition %s, %v" , inDef , err )
}
var result [ ] byte
result , err = inAbi . Pack ( methodName , params ... )
if err != nil {
return "" , fmt . Errorf ( "Pack failed: %v" , err )
}
return "0x" + hex . EncodeToString ( result ) , nil
}
// override result to make it looks like what status-mobile need
func overrideResult ( out [ ] interface { } ) [ ] interface { } {
for i , v := range out {
outType := reflect . TypeOf ( v )
switch outType . String ( ) {
case "[]uint8" :
out [ i ] = "0x" + common . Bytes2Hex ( v . ( [ ] uint8 ) )
case bigIntType :
vv := v . ( * big . Int )
if vv . Cmp ( maxSafeInteger ) == 1 {
out [ i ] = vv . String ( )
}
}
if outType . Kind ( ) == reflect . Array && outType . Elem ( ) . Kind ( ) == reflect . Array && outType . Elem ( ) . Elem ( ) . Kind ( ) == reflect . Uint8 { //case e.g. [2][3]uint8
val := reflect . ValueOf ( v )
rowNum := val . Len ( )
colNum := val . Index ( 0 ) . Len ( )
var ss = make ( [ ] string , rowNum )
for i := 0 ; i < rowNum ; i ++ {
bytes := make ( [ ] uint8 , colNum )
for j := 0 ; j < colNum ; j ++ {
bytes [ j ] = uint8 ( val . Index ( i ) . Index ( j ) . Uint ( ) )
}
ss [ i ] = common . Bytes2Hex ( bytes )
}
out [ i ] = ss
} else if outType . String ( ) != "common.Address" && outType . Kind ( ) == reflect . Array && outType . Elem ( ) . Kind ( ) == reflect . Uint8 {
val := reflect . ValueOf ( v )
len := val . Len ( )
bytes := make ( [ ] uint8 , len )
for i := 0 ; i < len ; i ++ {
bytes [ i ] = uint8 ( val . Index ( i ) . Uint ( ) )
}
out [ i ] = common . Bytes2Hex ( bytes )
}
}
return out
}
// bytesString e.g. 0x000000000000000000000000000000000000000000000000000000005bc741cd00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000013b86dbf1a83c9e6a492914a0ee39e8a5b7eb60700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e516d533152484e4a57414b356e426f6f57454d34654d644268707a35666e325764557473457357754a4b79356147000000000000000000000000000000000000
// types e.g. []string{"uint256","bytes","address","uint256","uint256"}
func Decode ( bytesString string , types [ ] string ) ( [ ] interface { } , error ) {
outputs := "["
for i , typeName := range types {
if i != 0 {
outputs += ","
}
outputs += fmt . Sprintf ( ` { "type":"%s"} ` , typeName )
}
outputs += "]"
def := fmt . Sprintf ( ` [ { "name" : "method", "type": "function", "outputs": %s}] ` , outputs )
abi , err := abi . JSON ( strings . NewReader ( def ) )
if err != nil {
return nil , fmt . Errorf ( "invalid ABI definition %s: %v" , def , err )
}
2023-01-13 17:12:46 +00:00
bytesString = strings . TrimPrefix ( bytesString , "0x" )
2022-08-24 12:42:41 +00:00
bytes , err := hex . DecodeString ( bytesString )
if err != nil {
return nil , fmt . Errorf ( "invalid hex %s: %v" , bytesString , err )
}
out , err := abi . Unpack ( "method" , bytes )
if err != nil {
return nil , fmt . Errorf ( "unpack failed: %v" , err )
}
return overrideResult ( out ) , nil
}