github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/parser/contract_argument.go (about)

     1  package parser
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"reflect"
     7  
     8  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
     9  	"github.com/alecthomas/participle/v2/lexer"
    10  	"github.com/ethereum/go-ethereum/accounts/abi"
    11  	"github.com/ethereum/go-ethereum/common"
    12  )
    13  
    14  // ContractArgument represents input to the smart contract method call, e.g.
    15  // `true` in `setSomething(true)`
    16  type ContractArgument struct {
    17  	Tokens  []lexer.Token // the token that was parsed for this argument
    18  	String  *ArgString    `parser:"@String"`             // the value if it's a string
    19  	Number  *ArgNumber    `parser:"| @Decimal"`          // the value if it's a number
    20  	Boolean *ArgBool      `parser:"| @('true'|'false')"` // the value if it's a boolean
    21  	Hex     *ArgHex       `parser:"| @Hex"`              // the value if it's a hex string
    22  	EnsAddr *ArgEnsDomain `parser:"| @EnsDomain"`        // the value if it's an ENS domain
    23  }
    24  
    25  // Interface returns the value as interface{} (any)
    26  func (a *ContractArgument) Interface() any {
    27  	if a.EnsAddr != nil {
    28  		return *a.EnsAddr
    29  	}
    30  
    31  	if a.String != nil {
    32  		value := *a.String
    33  		return string(value)
    34  	}
    35  
    36  	if a.Number != nil {
    37  		return a.Number.Interface()
    38  	}
    39  
    40  	if a.Boolean != nil {
    41  		return *a.Boolean
    42  	}
    43  
    44  	if a.Hex != nil {
    45  		if a.Hex.Address != nil {
    46  			return *a.Hex.Address
    47  		}
    48  		return *a.Hex.String
    49  	}
    50  
    51  	return nil
    52  }
    53  
    54  func (a *ContractArgument) AbiType(abiType *abi.Type) (any, error) {
    55  	if abiType.T == abi.FixedBytesTy {
    56  		// We only support fixed bytes as hashes
    57  		if a.Hex == nil {
    58  			return nil, wrongTypeError("hash", a.Tokens[0], a.Interface())
    59  		}
    60  		hex := *a.Hex.String
    61  		if len(hex) == 0 {
    62  			return nil, errors.New("no value for fixed-size bytes argument")
    63  		}
    64  
    65  		arrayInterface, err := abi.ReadFixedBytes(*abiType, base.Hex2Bytes(hex[2:]))
    66  		if err != nil {
    67  			return nil, err
    68  		}
    69  		return arrayInterface, nil
    70  	}
    71  
    72  	if abiType.T == abi.IntTy {
    73  		if a.Number == nil {
    74  			return nil, wrongTypeError(abiType.String(), a.Tokens[0], a.Interface())
    75  		}
    76  		// We have to convert int64 to a correct int type, otherwise go-ethereum will
    77  		// return an error. It's not needed for uints, because they handle them differently.
    78  		if a.Number.Big != nil {
    79  			return a.Number.Interface(), nil
    80  		}
    81  
    82  		converted, err := a.Number.Convert(abiType)
    83  		if err != nil {
    84  			return nil, err
    85  		}
    86  		return converted, nil
    87  	}
    88  
    89  	if abiType.T == abi.AddressTy {
    90  		if a.EnsAddr != nil {
    91  			return a.EnsAddr, nil
    92  		}
    93  
    94  		// We need go-ethereum's Address type, not ours
    95  		if a.Hex == nil {
    96  			return nil, wrongTypeError(abiType.String(), a.Tokens[0], a.Interface())
    97  		}
    98  		address := a.Hex.Address
    99  		if address == nil {
   100  			return nil, errors.New("expected address, but got hash instead (check length)")
   101  		}
   102  
   103  		addressHex := common.HexToAddress(address.Hex())
   104  		return addressHex, nil
   105  	}
   106  
   107  	// Below checks are for nice errors only
   108  	if (abiType.T == abi.UintTy && a.Number == nil) ||
   109  		(abiType.T == abi.StringTy && a.String == nil) ||
   110  		(abiType.T == abi.BoolTy && a.Boolean == nil) {
   111  		return nil, wrongTypeError(abiType.String(), a.Tokens[0], a.Interface())
   112  	}
   113  
   114  	return a.Interface(), nil
   115  }
   116  
   117  // wrongTypeError returns user-friendly errors
   118  func wrongTypeError(expectedType string, token lexer.Token, value any) error {
   119  	t := reflect.TypeOf(value)
   120  	typeName := t.String()
   121  	kind := t.Kind()
   122  	// kinds between this range are all (u)int, called "integer" in Solidity
   123  	if kind > 1 && kind < 12 {
   124  		typeName = "integer"
   125  	}
   126  	return fmt.Errorf("expected %s, but got %s \"%s\"", expectedType, typeName, token)
   127  }