code.vegaprotocol.io/vega@v0.79.0/core/datasource/external/ethcall/json_args.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package ethcall
    17  
    18  import (
    19  	"bytes"
    20  	"encoding/json"
    21  	"fmt"
    22  	"reflect"
    23  	"unsafe"
    24  
    25  	"github.com/ethereum/go-ethereum/accounts/abi"
    26  )
    27  
    28  // JsonArgsToAny takes a list of arguments marshalled as JSON strings.
    29  // It then uses the ethereum ABI to convert each JSON argument into the go type
    30  // which corresponds to the ethereum type defined in the ABI for that argument.
    31  func JsonArgsToAny(methodName string, jsonArgs []string, abiJSON []byte) ([]any, error) {
    32  	abi, err := abi.JSON(bytes.NewReader(abiJSON))
    33  	if err != nil {
    34  		return nil, fmt.Errorf("unable to parse abi json: %w", err)
    35  	}
    36  
    37  	methodAbi, ok := abi.Methods[methodName]
    38  	if !ok {
    39  		return nil, fmt.Errorf("method %s not found in abi", methodName)
    40  	}
    41  
    42  	inputsAbi := methodAbi.Inputs
    43  	if len(inputsAbi) != len(jsonArgs) {
    44  		return nil, fmt.Errorf("expected %v arguments for method %s, got %v", len(inputsAbi), methodName, len(jsonArgs))
    45  	}
    46  
    47  	args := []any{}
    48  	for i, jsonArg := range jsonArgs {
    49  		argType := inputsAbi[i].Type.GetType()
    50  		argIsPointer := argType.Kind() == reflect.Pointer
    51  
    52  		if argIsPointer {
    53  			argType = argType.Elem()
    54  		}
    55  
    56  		newArgValue := reflect.New(argType) // A reflect.Value of kind 'Pointer' to new instance of argType
    57  
    58  		// here we handle specifically this type because the type returned by GetType() method
    59  		// is a [32]uint8, which is not assignable by the json marshaller
    60  		// we then instantiate specifically a []byte
    61  		// then set the reflect instatiated type with unsafe (...) by addressing to the first
    62  		// element of the byte slice
    63  		if argType.String() == "[32]uint8" {
    64  			b := []byte{}
    65  			err := json.Unmarshal([]byte(jsonArg), &b)
    66  			if err != nil {
    67  				return nil, fmt.Errorf("unable to unmarshal json argument %s: %w", jsonArg, err)
    68  			}
    69  			newArgValue = reflect.NewAt(argType, unsafe.Pointer(&b[0]))
    70  		} else {
    71  			err := json.Unmarshal([]byte(jsonArg), newArgValue.Interface())
    72  			if err != nil {
    73  				return nil, fmt.Errorf("unable to unmarshal json argument %s: %w", jsonArg, err)
    74  			}
    75  		}
    76  
    77  		if argIsPointer {
    78  			args = append(args, newArgValue.Interface())
    79  		} else {
    80  			args = append(args, newArgValue.Elem().Interface())
    81  		}
    82  	}
    83  	return args, nil
    84  }
    85  
    86  // AnyArgsToJson does the inverse of the JsonArgsToAny; takes a list of arguments in go types
    87  // and marshals them to a list of JSON strings.
    88  func AnyArgsToJson(args []any) ([]string, error) {
    89  	result := make([]string, 0, len(args))
    90  	for _, arg := range args {
    91  		argJSON, err := json.Marshal(arg)
    92  		if err != nil {
    93  			return []string{}, fmt.Errorf("failed to json marshall args to JSON: %w", err)
    94  		}
    95  		result = append(result, string(argJSON))
    96  	}
    97  
    98  	return result, nil
    99  }
   100  
   101  func CanonicalizeJSON(in []byte) ([]byte, error) {
   102  	var data interface{}
   103  	if err := json.Unmarshal(in, &data); err != nil {
   104  		return nil, fmt.Errorf("failed to unmarshal json: %w", err)
   105  	}
   106  
   107  	out, err := json.Marshal(data)
   108  	if err != nil {
   109  		return nil, fmt.Errorf("failed to marshal json: %w", err)
   110  	}
   111  	return out, nil
   112  }