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

     1  package parser
     2  
     3  import (
     4  	"errors"
     5  	"reflect"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
    10  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/utils"
    11  	"github.com/ethereum/go-ethereum/accounts/abi"
    12  	"github.com/ethereum/go-ethereum/common"
    13  )
    14  
    15  func TestParse_Encoding(t *testing.T) {
    16  	// Short
    17  	if p, err := ParseCall(`0xcdba2fd40000000000000000000000000000000000000000000000000000000000007a69`); err != nil {
    18  		t.Fatal(err)
    19  	} else {
    20  		if s := p.Encoded; s != `0xcdba2fd40000000000000000000000000000000000000000000000000000000000007a69` {
    21  			t.Fatal("wrong encoded value", s)
    22  		}
    23  	}
    24  
    25  	// Long one
    26  	long := "0x7087e4bd000000000000000000000000f503017d7baf7fbc0fff7492b751025c6a78179b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000076d61696e6e657400000000000000000000000000000000000000000000000000"
    27  	if p, err := ParseCall(long); err != nil {
    28  		t.Fatal(err)
    29  	} else {
    30  		if s := p.Encoded; s != long {
    31  			t.Fatal("wrong encoded value", s)
    32  		}
    33  	}
    34  }
    35  
    36  func TestParse_Selector(t *testing.T) {
    37  	// Invalid selector
    38  	if _, err := ParseCall(`0xcdba()`); !strings.Contains(err.Error(), errInvalidSelector.Error()) {
    39  		t.Fatal("expected errInvalidSelector, got:", err)
    40  	}
    41  
    42  	// No arguments
    43  	if parsed, err := ParseCall(`0xcdba2fd4()`); err != nil {
    44  		t.Fatal(err)
    45  	} else {
    46  		if value := parsed.SelectorCall.Selector.Value; value != `0xcdba2fd4` {
    47  			t.Fatal("wrong selector", value)
    48  		}
    49  		if argsLen := len(parsed.SelectorCall.Arguments); argsLen != 0 {
    50  			t.Fatal("wrong number of arguments", argsLen)
    51  		}
    52  	}
    53  
    54  	if parsed, err := ParseCall(`0x7087e4bd(trueblocks.eth,"mainnet")`); err != nil {
    55  		t.Fatal(err)
    56  	} else {
    57  		if value := parsed.SelectorCall.Selector.Value; value != `0x7087e4bd` {
    58  			t.Fatal("wrong selector", value)
    59  		}
    60  		if argsLen := len(parsed.SelectorCall.Arguments); argsLen != 2 {
    61  			t.Fatal("wrong number of arguments", argsLen)
    62  		}
    63  	}
    64  
    65  	// Arguments
    66  	if parsed, err := ParseCall(`0xcdba2fd4(1, true, false, 0xbeef, "string")`); err != nil {
    67  		t.Fatal(err)
    68  	} else {
    69  		if value := parsed.SelectorCall.Selector.Value; value != `0xcdba2fd4` {
    70  			t.Fatal("wrong selector", value)
    71  		}
    72  		if argValue := *parsed.SelectorCall.Arguments[0].Number.Int; argValue != 1 {
    73  			t.Fatal("wrong #1 input value", argValue)
    74  		}
    75  		if argValue := *parsed.SelectorCall.Arguments[1].Boolean; argValue != true {
    76  			t.Fatal("wrong #2 input value", argValue)
    77  		}
    78  		if argValue := *parsed.SelectorCall.Arguments[2].Boolean; argValue != false {
    79  			t.Fatal("wrong #3 input value", argValue)
    80  		}
    81  		if argValue := *parsed.SelectorCall.Arguments[3].Hex.String; argValue != "0xbeef" {
    82  			t.Fatal("wrong #4 input value", argValue)
    83  		}
    84  		if argValue := *parsed.SelectorCall.Arguments[4].String; argValue != "string" {
    85  			t.Fatal("wrong #5 input value", argValue)
    86  		}
    87  	}
    88  
    89  	// Hex parsing
    90  	if parsed, err := ParseCall(`0xcdba2fd4(0xdeadbeef, 0x6982508145454ce325ddbe47a25d4ec3d23119a1)`); err != nil {
    91  		t.Fatal(err)
    92  	} else {
    93  		if argsLen := len(parsed.SelectorCall.Arguments); argsLen != 2 {
    94  			t.Fatal("wrong inputs length:", argsLen)
    95  		}
    96  
    97  		if *parsed.SelectorCall.Arguments[0].Hex.String != "0xdeadbeef" {
    98  			t.Fatal("should parse into Hex.String")
    99  		}
   100  		if parsed.SelectorCall.Arguments[1].Hex.Address.Hex() != "0x6982508145454ce325ddbe47a25d4ec3d23119a1" {
   101  			t.Fatal("should parse into Hex.Address")
   102  		}
   103  	}
   104  }
   105  
   106  func TestParse_Function(t *testing.T) {
   107  	// Invalid selector
   108  	if _, err := ParseCall(`111()`); err == nil {
   109  		t.Fatal("expected parsing error")
   110  	}
   111  
   112  	// No arguments
   113  	if parsed, err := ParseCall(`transfer()`); err != nil {
   114  		t.Fatal(err)
   115  	} else {
   116  		if value := parsed.FunctionNameCall.Name; value != `transfer` {
   117  			t.Fatal("wrong function name", value)
   118  		}
   119  		if argsLen := len(parsed.FunctionNameCall.Arguments); argsLen != 0 {
   120  			t.Fatal("wrong number of arguments", argsLen)
   121  		}
   122  	}
   123  
   124  	// Correct Solidity identifiers
   125  	if parsed, err := ParseCall(`$dollar$_underscoreCamelCase125__()`); err != nil {
   126  		t.Fatal(err)
   127  	} else {
   128  		if value := parsed.FunctionNameCall.Name; value != `$dollar$_underscoreCamelCase125__` {
   129  			t.Fatal("wrong function name", value)
   130  		}
   131  	}
   132  
   133  	// Arguments
   134  	if parsed, err := ParseCall(`something(1, true, false, 0xbeef, "string")`); err != nil {
   135  		t.Fatal(err)
   136  	} else {
   137  		if value := parsed.FunctionNameCall.Name; value != `something` {
   138  			t.Fatal("wrong selector", value)
   139  		}
   140  		if argValue := *parsed.FunctionNameCall.Arguments[0].Number.Int; argValue != 1 {
   141  			t.Fatal("wrong #1 input value", argValue)
   142  		}
   143  		if argValue := *parsed.FunctionNameCall.Arguments[1].Boolean; argValue != true {
   144  			t.Fatal("wrong #2 input value", argValue)
   145  		}
   146  		if argValue := *parsed.FunctionNameCall.Arguments[2].Boolean; argValue != false {
   147  			t.Fatal("wrong #3 input value", argValue)
   148  		}
   149  		if argValue := *parsed.FunctionNameCall.Arguments[3].Hex.String; argValue != "0xbeef" {
   150  			t.Fatal("wrong #4 input value", argValue)
   151  		}
   152  		if argValue := *parsed.FunctionNameCall.Arguments[4].String; argValue != "string" {
   153  			t.Fatal("wrong #5 input value", argValue)
   154  		}
   155  	}
   156  
   157  	// Hex parsing
   158  	if parsed, err := ParseCall(`somethingElse(0xdeadbeef, 0x6982508145454ce325ddbe47a25d4ec3d23119a1)`); err != nil {
   159  		t.Fatal(err)
   160  	} else {
   161  		if argsLen := len(parsed.FunctionNameCall.Arguments); argsLen != 2 {
   162  			t.Fatal("wrong inputs length:", argsLen)
   163  		}
   164  
   165  		if *parsed.FunctionNameCall.Arguments[0].Hex.String != "0xdeadbeef" {
   166  			t.Fatal("should parse into Hex.String")
   167  		}
   168  		if parsed.FunctionNameCall.Arguments[1].Hex.Address.Hex() != "0x6982508145454ce325ddbe47a25d4ec3d23119a1" {
   169  			t.Fatal("should parse into Hex.Address")
   170  		}
   171  	}
   172  }
   173  
   174  func TestParse_Numbers(t *testing.T) {
   175  	if parsed, err := ParseCall(`doSomething(1, -2, 115792089237316195423570985008687907853269984665640564039457584007913129639935)`); err != nil {
   176  		t.Fatal(err)
   177  	} else {
   178  		if argsLen := len(parsed.FunctionNameCall.Arguments); argsLen != 3 {
   179  			t.Fatal("wrong inputs length:", argsLen)
   180  		}
   181  
   182  		if value := *parsed.FunctionNameCall.Arguments[0].Number.Int; value != 1 {
   183  			t.Fatal("wrong uint value:", value)
   184  		}
   185  		if value := *parsed.FunctionNameCall.Arguments[1].Number.Int; value != -2 {
   186  			t.Fatal("wrong int value:", value)
   187  		}
   188  		if value := parsed.FunctionNameCall.Arguments[2].Number.Big.String(); value != "115792089237316195423570985008687907853269984665640564039457584007913129639935" {
   189  			t.Fatal("wrong big value:", value)
   190  		}
   191  	}
   192  }
   193  
   194  // This comes from go-ethereum's abi_test.go, we will use it to
   195  // test type conversion (our types -> go-ethereum)
   196  const packTestAbiSource = `
   197  [
   198  	{ "type" : "function", "name" : ""},
   199  	{ "type" : "function", "name" : "string", "inputs" : [ { "name" : "inputs", "type" : "string" } ] },
   200  	{ "type" : "function", "name" : "bool", "inputs" : [ { "name" : "inputs", "type" : "bool" } ] },
   201  	{ "type" : "function", "name" : "address", "inputs" : [ { "name" : "inputs", "type" : "address" } ] },
   202  	{ "type" : "function", "name" : "uint64", "inputs" : [ { "name" : "inputs", "type" : "uint64" } ] },
   203  	{ "type" : "function", "name" : "uint256", "inputs" : [ { "name" : "inputs", "type" : "uint256" } ] },
   204  	{ "type" : "function", "name" : "int8", "inputs" : [ { "name" : "inputs", "type" : "int8" } ] },
   205  	{ "type" : "function", "name" : "int64", "inputs" : [ { "name" : "inputs", "type" : "int64" } ] },
   206  	{ "type" : "function", "name" : "int256", "inputs" : [ { "name" : "inputs", "type" : "int256" } ] },
   207  	{ "type" : "function", "name" : "bytes4", "inputs" : [ { "name" : "inputs", "type" : "bytes4" } ] },
   208  	{ "type" : "function", "name" : "bytes32", "inputs" : [ { "name" : "inputs", "type" : "bytes32" } ] }
   209  ]`
   210  
   211  func TestArgument_AbiType(t *testing.T) {
   212  	testAbi, err := abi.JSON(strings.NewReader(packTestAbiSource))
   213  	if err != nil {
   214  		panic(err)
   215  	}
   216  	int256 := base.NewWei(0)
   217  	_, ok := int256.SetString("-57896044618658097711785492504343953926634992332820282019728792003956564819968", 0)
   218  	if !ok {
   219  		t.Fatal("cannot set int256 value")
   220  	}
   221  
   222  	type fields struct {
   223  		String  *ArgString
   224  		Number  *ArgNumber
   225  		Boolean *ArgBool
   226  		Hex     *ArgHex
   227  	}
   228  	type args struct {
   229  		abiType *abi.Type
   230  	}
   231  	tests := []struct {
   232  		name    string
   233  		fields  fields
   234  		args    args
   235  		want    any
   236  		wantErr bool
   237  	}{
   238  		{
   239  			name: "bytes4 argument",
   240  			fields: fields{
   241  				Hex: &ArgHex{
   242  					String: utils.PointerOf("0x80ac58cd"),
   243  				},
   244  			},
   245  			args: args{
   246  				abiType: &testAbi.Methods["bytes4"].Inputs[0].Type,
   247  			},
   248  			want: [4]byte{128, 172, 88, 205},
   249  		},
   250  		{
   251  			name: "int8 argument",
   252  			fields: fields{
   253  				Number: &ArgNumber{
   254  					Int: utils.PointerOf(int64(-128)),
   255  				},
   256  			},
   257  			args: args{
   258  				abiType: &testAbi.Methods["int8"].Inputs[0].Type,
   259  			},
   260  			want: int8(-128),
   261  		},
   262  		{
   263  			name: "int64 argument",
   264  			fields: fields{
   265  				Number: &ArgNumber{
   266  					Int: utils.PointerOf(int64(-9223372036854775808)),
   267  				},
   268  			},
   269  			args: args{
   270  				abiType: &testAbi.Methods["int64"].Inputs[0].Type,
   271  			},
   272  			want: int64(-9223372036854775808),
   273  		},
   274  		{
   275  			name: "int256 argument",
   276  			fields: fields{
   277  				Number: &ArgNumber{
   278  					Big: int256,
   279  				},
   280  			},
   281  			args: args{
   282  				abiType: &testAbi.Methods["int256"].Inputs[0].Type,
   283  			},
   284  			want: int256,
   285  		},
   286  		{
   287  			name: "address argument",
   288  			fields: fields{
   289  				Hex: &ArgHex{
   290  					Address: utils.PointerOf(base.HexToAddress("0xf503017d7baf7fbc0fff7492b751025c6a78179b")),
   291  				},
   292  			},
   293  			args: args{
   294  				abiType: &testAbi.Methods["address"].Inputs[0].Type,
   295  			},
   296  			want: common.HexToAddress("0xf503017d7baf7fbc0fff7492b751025c6a78179b"),
   297  		},
   298  	}
   299  	for _, tt := range tests {
   300  		t.Run(tt.name, func(t *testing.T) {
   301  			a := &ContractArgument{
   302  				String:  tt.fields.String,
   303  				Number:  tt.fields.Number,
   304  				Boolean: tt.fields.Boolean,
   305  				Hex:     tt.fields.Hex,
   306  			}
   307  			got, err := a.AbiType(tt.args.abiType)
   308  			if (err != nil) != tt.wantErr {
   309  				t.Errorf("Argument.ToAbiType() error = %v, wantErr %v", err, tt.wantErr)
   310  				return
   311  			}
   312  			if !reflect.DeepEqual(got, tt.want) {
   313  				t.Errorf("Argument.ToAbiType() = %v, want %v", got, tt.want)
   314  			}
   315  		})
   316  	}
   317  }
   318  
   319  func TestArgument_AbiType_Errors(t *testing.T) {
   320  	testAbi, err := abi.JSON(strings.NewReader(packTestAbiSource))
   321  	if err != nil {
   322  		panic(err)
   323  	}
   324  
   325  	// the second argument is string instead of address
   326  	parsed, err := ParseCall(`someFunc(0x6982508145454ce325ddbe47a25d4ec3d23119a1, "0x6982508145454ce325ddbe47a25d4ec3d23119a1")`)
   327  	if err != nil {
   328  		t.Fatal(err)
   329  	}
   330  	if _, err := parsed.FunctionNameCall.Arguments[0].AbiType(&testAbi.Methods["address"].Inputs[0].Type); err != nil {
   331  		t.Fatal(err)
   332  	}
   333  	_, err = parsed.FunctionNameCall.Arguments[1].AbiType(&testAbi.Methods["address"].Inputs[0].Type)
   334  	expected := errors.New(`expected address, but got string "0x6982508145454ce325ddbe47a25d4ec3d23119a1"`)
   335  	if err.Error() != expected.Error() {
   336  		t.Fatal("got wrong error:", err, "expected:", expected)
   337  	}
   338  
   339  	parsed, err = ParseCall(`someBool(111)`)
   340  	if err != nil {
   341  		t.Fatal(err)
   342  	}
   343  	_, err = parsed.FunctionNameCall.Arguments[0].AbiType(&testAbi.Methods["bool"].Inputs[0].Type)
   344  	expected = errors.New(`expected bool, but got integer "111"`)
   345  	if err.Error() != expected.Error() {
   346  		t.Fatal("got wrong error:", err, "expected:", expected)
   347  	}
   348  
   349  	parsed, err = ParseCall(`someBytes32("hello")`)
   350  	if err != nil {
   351  		t.Fatal(err)
   352  	}
   353  	_, err = parsed.FunctionNameCall.Arguments[0].AbiType(&testAbi.Methods["bytes32"].Inputs[0].Type)
   354  	expected = errors.New(`expected hash, but got string "hello"`)
   355  	if err.Error() != expected.Error() {
   356  		t.Fatal("got wrong error:", err, "expected:", expected)
   357  	}
   358  }