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 }