github.com/lmittmann/w3@v0.20.0/internal/abi/parser_test.go (about)

     1  package abi
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"math/big"
     7  	"testing"
     8  
     9  	"github.com/ethereum/go-ethereum/accounts/abi"
    10  	"github.com/ethereum/go-ethereum/common"
    11  	"github.com/google/go-cmp/cmp"
    12  	"github.com/google/go-cmp/cmp/cmpopts"
    13  	"github.com/lmittmann/w3/internal"
    14  )
    15  
    16  var (
    17  	typeAddress = abi.Type{T: abi.AddressTy, Size: 20}
    18  	typeBool    = abi.Type{T: abi.BoolTy}
    19  	typeUint24  = abi.Type{T: abi.UintTy, Size: 24}
    20  	typeUint160 = abi.Type{T: abi.UintTy, Size: 160}
    21  	typeUint256 = abi.Type{T: abi.UintTy, Size: 256}
    22  )
    23  
    24  func TestParseArgs(t *testing.T) {
    25  	tests := []struct {
    26  		Input    string
    27  		Tuples   []any
    28  		WantArgs Arguments
    29  		WantErr  error
    30  	}{
    31  		{
    32  			Input:    "",
    33  			WantArgs: Arguments{},
    34  		},
    35  		{
    36  			Input:   "xxx",
    37  			WantErr: errors.New(`syntax error: unexpected "xxx", expecting type`),
    38  		},
    39  		{
    40  			Input:    "uint256",
    41  			WantArgs: Arguments{{Type: typeUint256}},
    42  		},
    43  		{
    44  			Input:    "uint",
    45  			WantArgs: Arguments{{Type: typeUint256}},
    46  		},
    47  		{
    48  			Input:    "uint256 balance",
    49  			WantArgs: Arguments{{Type: typeUint256, Name: "balance"}},
    50  		},
    51  		{
    52  			Input:    "uint256 indexed balance",
    53  			WantArgs: Arguments{{Type: typeUint256, Indexed: true, Name: "balance"}},
    54  		},
    55  		{
    56  			Input:    "uint256 indexed",
    57  			WantArgs: Arguments{{Type: typeUint256, Indexed: true}},
    58  		},
    59  		{
    60  			Input:    "uint256[]",
    61  			WantArgs: Arguments{{Type: abi.Type{Elem: &typeUint256, T: abi.SliceTy}}},
    62  		},
    63  		{
    64  			Input:    "uint256[3]",
    65  			WantArgs: Arguments{{Type: abi.Type{Elem: &typeUint256, T: abi.ArrayTy, Size: 3}}},
    66  		},
    67  		{
    68  			Input: "uint256[][]",
    69  			WantArgs: Arguments{{
    70  				Type: abi.Type{
    71  					Elem: &abi.Type{Elem: &typeUint256, T: abi.SliceTy},
    72  					T:    abi.SliceTy,
    73  				},
    74  			}},
    75  		},
    76  		{
    77  			Input: "uint256[][3]",
    78  			WantArgs: Arguments{{
    79  				Type: abi.Type{
    80  					Elem: &abi.Type{Elem: &typeUint256, T: abi.SliceTy},
    81  					T:    abi.ArrayTy,
    82  					Size: 3,
    83  				},
    84  			}},
    85  		},
    86  		{
    87  			Input: "uint256[3][]",
    88  			WantArgs: Arguments{{
    89  				Type: abi.Type{
    90  					Elem: &abi.Type{Elem: &typeUint256, T: abi.ArrayTy, Size: 3},
    91  					T:    abi.SliceTy,
    92  				},
    93  			}},
    94  		},
    95  		{
    96  			Input: "uint256[],uint256[3],uint256[][3],uint256[3][]",
    97  			WantArgs: Arguments{
    98  				{Type: abi.Type{T: abi.SliceTy, Elem: &typeUint256}},
    99  				{Type: abi.Type{T: abi.ArrayTy, Size: 3, Elem: &typeUint256}},
   100  				{Type: abi.Type{T: abi.ArrayTy, Size: 3, Elem: &abi.Type{T: abi.SliceTy, Elem: &typeUint256}}},
   101  				{Type: abi.Type{T: abi.SliceTy, Elem: &abi.Type{T: abi.ArrayTy, Size: 3, Elem: &typeUint256}}},
   102  			},
   103  		},
   104  		{
   105  			Input:   "uint256[",
   106  			WantErr: errors.New(`syntax error: unexpected EOF, expecting "]"`),
   107  		},
   108  		{
   109  			Input:   "uint256[3",
   110  			WantErr: errors.New(`syntax error: unexpected EOF, expecting "]"`),
   111  		},
   112  		{
   113  			Input: "(uint256 arg0)",
   114  			WantArgs: Arguments{{
   115  				Type: abi.Type{
   116  					T:             abi.TupleTy,
   117  					TupleElems:    []*abi.Type{&typeUint256},
   118  					TupleRawNames: []string{"arg0"},
   119  				},
   120  			}},
   121  		},
   122  		{
   123  			Input: "(uint256 arg0)[]",
   124  			WantArgs: Arguments{{
   125  				Type: abi.Type{
   126  					Elem: &abi.Type{
   127  						T:             abi.TupleTy,
   128  						TupleElems:    []*abi.Type{&typeUint256},
   129  						TupleRawNames: []string{"arg0"},
   130  					},
   131  					T: abi.SliceTy,
   132  				},
   133  			}},
   134  		},
   135  		{
   136  			Input: "(uint256 arg0)[3]",
   137  			WantArgs: Arguments{{
   138  				Type: abi.Type{
   139  					Elem: &abi.Type{
   140  						T:             abi.TupleTy,
   141  						TupleElems:    []*abi.Type{&typeUint256},
   142  						TupleRawNames: []string{"arg0"},
   143  					},
   144  					T:    abi.ArrayTy,
   145  					Size: 3,
   146  				},
   147  			}},
   148  		},
   149  		{
   150  			Input: "uint256,(uint256 v0,uint256 v1),((uint256 v00,uint256 v01) v0,uint256 v1)",
   151  			WantArgs: Arguments{
   152  				{Type: typeUint256},
   153  				{Type: abi.Type{T: abi.TupleTy, TupleElems: []*abi.Type{&typeUint256, &typeUint256}, TupleRawNames: []string{"v0", "v1"}}},
   154  				{Type: abi.Type{T: abi.TupleTy, TupleElems: []*abi.Type{
   155  					{T: abi.TupleTy, TupleElems: []*abi.Type{&typeUint256, &typeUint256}, TupleRawNames: []string{"v00", "v01"}},
   156  					&typeUint256,
   157  				}, TupleRawNames: []string{"v0", "v1"}}},
   158  			},
   159  		},
   160  		{
   161  			Input:  "simpleStruct",
   162  			Tuples: []any{simpleStruct{}},
   163  			WantArgs: Arguments{{
   164  				Type: abi.Type{
   165  					T:             abi.TupleTy,
   166  					TupleRawName:  "simpleStruct",
   167  					TupleElems:    []*abi.Type{&typeUint256, &typeAddress},
   168  					TupleRawNames: []string{"amount", "token"},
   169  				},
   170  			}},
   171  		},
   172  		{
   173  			Input:  "simpleStructWithoutTags",
   174  			Tuples: []any{simpleStructWithoutTags{}},
   175  			WantArgs: Arguments{{
   176  				Type: abi.Type{
   177  					T:             abi.TupleTy,
   178  					TupleRawName:  "simpleStructWithoutTags",
   179  					TupleElems:    []*abi.Type{&typeUint256, &typeAddress},
   180  					TupleRawNames: []string{"amount", "token"},
   181  				},
   182  			}},
   183  		},
   184  		{
   185  			Input:  "simpleStruct[]",
   186  			Tuples: []any{simpleStruct{}},
   187  			WantArgs: Arguments{{
   188  				Type: abi.Type{
   189  					T: abi.SliceTy,
   190  					Elem: &abi.Type{
   191  						T:             abi.TupleTy,
   192  						TupleRawName:  "simpleStruct",
   193  						TupleElems:    []*abi.Type{&typeUint256, &typeAddress},
   194  						TupleRawNames: []string{"amount", "token"},
   195  					},
   196  				},
   197  			}},
   198  		},
   199  		{
   200  			Input:  "simpleStruct[3]",
   201  			Tuples: []any{simpleStruct{}},
   202  			WantArgs: Arguments{{
   203  				Type: abi.Type{
   204  					T:    abi.ArrayTy,
   205  					Size: 3,
   206  					Elem: &abi.Type{
   207  						T:             abi.TupleTy,
   208  						TupleRawName:  "simpleStruct",
   209  						TupleElems:    []*abi.Type{&typeUint256, &typeAddress},
   210  						TupleRawNames: []string{"amount", "token"},
   211  					},
   212  				},
   213  			}},
   214  		},
   215  		{
   216  			Input:  "uint256,simpleStruct,address",
   217  			Tuples: []any{simpleStruct{}},
   218  			WantArgs: Arguments{
   219  				{Type: typeUint256},
   220  				{
   221  					Type: abi.Type{
   222  						T:             abi.TupleTy,
   223  						TupleRawName:  "simpleStruct",
   224  						TupleElems:    []*abi.Type{&typeUint256, &typeAddress},
   225  						TupleRawNames: []string{"amount", "token"},
   226  					},
   227  				},
   228  				{Type: typeAddress},
   229  			},
   230  		},
   231  		{
   232  			Input:  "emptyStruct",
   233  			Tuples: []any{emptyStruct{}},
   234  			WantArgs: Arguments{{
   235  				Type: abi.Type{
   236  					T:             abi.TupleTy,
   237  					TupleRawName:  "emptyStruct",
   238  					TupleElems:    []*abi.Type{},
   239  					TupleRawNames: []string{},
   240  				},
   241  			}},
   242  		},
   243  		{
   244  			Input:   "unknownStruct",
   245  			Tuples:  []any{simpleStruct{}},
   246  			WantErr: errors.New(`syntax error: unexpected "unknownStruct", expecting type`),
   247  		},
   248  		{
   249  			Input:   "simpleStruct",
   250  			Tuples:  []any{}, // no tuples provided
   251  			WantErr: errors.New(`syntax error: unexpected "simpleStruct", expecting type`),
   252  		},
   253  		{
   254  			Input:   "simpleStruct",
   255  			Tuples:  []any{simpleStruct{}, simpleStruct{}},
   256  			WantErr: errors.New(`syntax error: duplicate tuple definition: simpleStruct`),
   257  		},
   258  		{
   259  			Input:  "nestedStruct",
   260  			Tuples: []any{nestedStruct{}},
   261  			WantArgs: Arguments{{
   262  				Type: abi.Type{
   263  					T:            abi.TupleTy,
   264  					TupleRawName: "nestedStruct",
   265  					TupleElems: []*abi.Type{
   266  						{
   267  							T:             abi.TupleTy,
   268  							TupleRawName:  "simpleStruct",
   269  							TupleElems:    []*abi.Type{&typeUint256, &typeAddress},
   270  							TupleRawNames: []string{"amount", "token"},
   271  						},
   272  						&typeBool,
   273  					},
   274  					TupleRawNames: []string{"inner", "active"},
   275  				},
   276  			}},
   277  		},
   278  		{
   279  			Input:  "nestedStruct[]",
   280  			Tuples: []any{nestedStruct{}},
   281  			WantArgs: Arguments{{
   282  				Type: abi.Type{
   283  					T: abi.SliceTy,
   284  					Elem: &abi.Type{
   285  						T:            abi.TupleTy,
   286  						TupleRawName: "nestedStruct",
   287  						TupleElems: []*abi.Type{
   288  							{
   289  								T:             abi.TupleTy,
   290  								TupleRawName:  "simpleStruct",
   291  								TupleElems:    []*abi.Type{&typeUint256, &typeAddress},
   292  								TupleRawNames: []string{"amount", "token"},
   293  							},
   294  							&typeBool,
   295  						},
   296  						TupleRawNames: []string{"inner", "active"},
   297  					},
   298  				},
   299  			}},
   300  		},
   301  		{
   302  			Input:  "complexStruct",
   303  			Tuples: []any{complexStruct{}},
   304  			WantArgs: Arguments{{
   305  				Type: abi.Type{
   306  					T:            abi.TupleTy,
   307  					TupleRawName: "complexStruct",
   308  					TupleElems: []*abi.Type{
   309  						&typeUint256,
   310  						&typeAddress,
   311  						{T: abi.BytesTy},
   312  						{
   313  							T:            abi.TupleTy,
   314  							TupleRawName: "nestedStruct",
   315  							TupleElems: []*abi.Type{
   316  								{
   317  									T:             abi.TupleTy,
   318  									TupleRawName:  "simpleStruct",
   319  									TupleElems:    []*abi.Type{&typeUint256, &typeAddress},
   320  									TupleRawNames: []string{"amount", "token"},
   321  								},
   322  								&typeBool,
   323  							},
   324  							TupleRawNames: []string{"inner", "active"},
   325  						},
   326  					},
   327  					TupleRawNames: []string{"id", "owner", "data", "metadata"},
   328  				},
   329  			}},
   330  		},
   331  		{
   332  			Input:  "complexStruct[2]",
   333  			Tuples: []any{complexStruct{}},
   334  			WantArgs: Arguments{{
   335  				Type: abi.Type{
   336  					T:    abi.ArrayTy,
   337  					Size: 2,
   338  					Elem: &abi.Type{
   339  						T:            abi.TupleTy,
   340  						TupleRawName: "complexStruct",
   341  						TupleElems: []*abi.Type{
   342  							&typeUint256,
   343  							&typeAddress,
   344  							{T: abi.BytesTy},
   345  							{
   346  								T:            abi.TupleTy,
   347  								TupleRawName: "nestedStruct",
   348  								TupleElems: []*abi.Type{
   349  									{
   350  										T:             abi.TupleTy,
   351  										TupleRawName:  "simpleStruct",
   352  										TupleElems:    []*abi.Type{&typeUint256, &typeAddress},
   353  										TupleRawNames: []string{"amount", "token"},
   354  									},
   355  									&typeBool,
   356  								},
   357  								TupleRawNames: []string{"inner", "active"},
   358  							},
   359  						},
   360  						TupleRawNames: []string{"id", "owner", "data", "metadata"},
   361  					},
   362  				},
   363  			}},
   364  		},
   365  		{
   366  			Input:  "arrayStruct",
   367  			Tuples: []any{arrayStruct{}},
   368  			WantArgs: Arguments{{
   369  				Type: abi.Type{
   370  					T:            abi.TupleTy,
   371  					TupleRawName: "arrayStruct",
   372  					TupleElems: []*abi.Type{
   373  						{T: abi.SliceTy, Elem: &typeUint256},
   374  						&typeUint256,
   375  					},
   376  					TupleRawNames: []string{"values", "count"},
   377  				},
   378  			}},
   379  		},
   380  		{
   381  			Input:  "arrayStruct[]",
   382  			Tuples: []any{arrayStruct{}},
   383  			WantArgs: Arguments{{
   384  				Type: abi.Type{
   385  					T: abi.SliceTy,
   386  					Elem: &abi.Type{
   387  						T:            abi.TupleTy,
   388  						TupleRawName: "arrayStruct",
   389  						TupleElems: []*abi.Type{
   390  							{T: abi.SliceTy, Elem: &typeUint256},
   391  							&typeUint256,
   392  						},
   393  						TupleRawNames: []string{"values", "count"},
   394  					},
   395  				},
   396  			}},
   397  		},
   398  		{
   399  			Input:  "uint256,nestedStruct,complexStruct,arrayStruct",
   400  			Tuples: []any{nestedStruct{}, complexStruct{}, arrayStruct{}},
   401  			WantArgs: Arguments{
   402  				{Type: typeUint256},
   403  				{
   404  					Type: abi.Type{
   405  						T:            abi.TupleTy,
   406  						TupleRawName: "nestedStruct",
   407  						TupleElems: []*abi.Type{
   408  							{
   409  								T:             abi.TupleTy,
   410  								TupleRawName:  "simpleStruct",
   411  								TupleElems:    []*abi.Type{&typeUint256, &typeAddress},
   412  								TupleRawNames: []string{"amount", "token"},
   413  							},
   414  							&typeBool,
   415  						},
   416  						TupleRawNames: []string{"inner", "active"},
   417  					},
   418  				},
   419  				{
   420  					Type: abi.Type{
   421  						T:            abi.TupleTy,
   422  						TupleRawName: "complexStruct",
   423  						TupleElems: []*abi.Type{
   424  							&typeUint256,
   425  							&typeAddress,
   426  							{T: abi.BytesTy},
   427  							{
   428  								T:            abi.TupleTy,
   429  								TupleRawName: "nestedStruct",
   430  								TupleElems: []*abi.Type{
   431  									{
   432  										T:             abi.TupleTy,
   433  										TupleRawName:  "simpleStruct",
   434  										TupleElems:    []*abi.Type{&typeUint256, &typeAddress},
   435  										TupleRawNames: []string{"amount", "token"},
   436  									},
   437  									&typeBool,
   438  								},
   439  								TupleRawNames: []string{"inner", "active"},
   440  							},
   441  						},
   442  						TupleRawNames: []string{"id", "owner", "data", "metadata"},
   443  					},
   444  				},
   445  				{
   446  					Type: abi.Type{
   447  						T:            abi.TupleTy,
   448  						TupleRawName: "arrayStruct",
   449  						TupleElems: []*abi.Type{
   450  							{T: abi.SliceTy, Elem: &typeUint256},
   451  							&typeUint256,
   452  						},
   453  						TupleRawNames: []string{"values", "count"},
   454  					},
   455  				},
   456  			},
   457  		},
   458  		{
   459  			Input:   "@invalid",
   460  			WantErr: errors.New(`syntax error: unexpected character: @`),
   461  		},
   462  		{
   463  			Input:   "func(uint256 arg0",
   464  			WantErr: errors.New(`syntax error: unexpected "func", expecting type`),
   465  		},
   466  		{
   467  			Input:   "(:uint256)",
   468  			WantErr: errors.New(`syntax error: unexpected character: :`),
   469  		},
   470  		{
   471  			Input:   "(uint256 arg0 extra)",
   472  			WantErr: errors.New(`syntax error: unexpected "extra", expecting "," or ")"`),
   473  		},
   474  		{
   475  			Input:   "(uint256",
   476  			WantErr: errors.New(`syntax error: unexpected EOF, expecting "," or ")"`),
   477  		},
   478  		{
   479  			Input:   "(uint256 @)",
   480  			WantErr: errors.New(`syntax error: unexpected character: @`),
   481  		},
   482  		{
   483  			Input:   "uint256 arg0 extra",
   484  			WantErr: errors.New(`syntax error: unexpected "extra", want "," or EOF`),
   485  		},
   486  		{
   487  			Input:   "uint256 @",
   488  			WantErr: errors.New(`syntax error: unexpected character: @`),
   489  		},
   490  		{
   491  			Input:   "uint256,@",
   492  			WantErr: errors.New(`syntax error: unexpected character: @`),
   493  		},
   494  		{
   495  			Input:   "uint256[@",
   496  			WantErr: errors.New(`syntax error: unexpected character: @`),
   497  		},
   498  		{
   499  			Input:   "nonStruct",
   500  			Tuples:  []any{123},
   501  			WantErr: errors.New(`syntax error: expected struct, got int`),
   502  		},
   503  		{
   504  			Input:   "stringType",
   505  			Tuples:  []any{"hello"},
   506  			WantErr: errors.New(`syntax error: expected struct, got string`),
   507  		},
   508  		{
   509  			Input:   "structWithInvalidField",
   510  			Tuples:  []any{structWithInvalidField{}},
   511  			WantErr: errors.New(`syntax error: unknown type "func()"`),
   512  		},
   513  	}
   514  
   515  	for i, test := range tests {
   516  		t.Run(fmt.Sprintf("%d_%s", i, test.Input), func(t *testing.T) {
   517  			gotArgs, gotErr := Parse(test.Input, test.Tuples...)
   518  			if diff := cmp.Diff(test.WantErr, gotErr,
   519  				internal.EquateErrors(),
   520  			); diff != "" {
   521  				t.Fatalf("Err (-want, +got):\n%s", diff)
   522  			}
   523  			if diff := cmp.Diff(test.WantArgs, gotArgs,
   524  				cmpopts.EquateEmpty(),
   525  				cmpopts.IgnoreUnexported(abi.Type{}),
   526  				cmpopts.IgnoreFields(abi.Type{}, "TupleType")); diff != "" {
   527  				t.Errorf("Args (-want, +got):\n%s", diff)
   528  			}
   529  		})
   530  	}
   531  }
   532  
   533  func TestParseArgsWithName(t *testing.T) {
   534  	tests := []struct {
   535  		Input    string
   536  		Tuples   []any
   537  		WantArgs Arguments
   538  		WantName string
   539  		WantErr  error
   540  	}{
   541  		{
   542  			Input:   "",
   543  			WantErr: errors.New(`syntax error: unexpected EOF, expecting name`),
   544  		},
   545  		{
   546  			Input:   "uint",
   547  			WantErr: errors.New(`syntax error: unexpected EOF, expecting "("`),
   548  		},
   549  		{
   550  			Input:    "f()",
   551  			WantName: "f",
   552  		},
   553  		{
   554  			Input:   "f(",
   555  			WantErr: errors.New(`syntax error: unexpected EOF, expecting type`),
   556  		},
   557  		{
   558  			Input:   "f(uint256",
   559  			WantErr: errors.New(`syntax error: unexpected EOF, want "," or ")"`),
   560  		},
   561  		{
   562  			Input:   "f(uint256 indexed",
   563  			WantErr: errors.New(`syntax error: unexpected EOF, want "," or ")"`),
   564  		},
   565  		{
   566  			Input:   "f(uint256 arg0",
   567  			WantErr: errors.New(`syntax error: unexpected EOF, want "," or ")"`),
   568  		},
   569  		{
   570  			Input:   "f(uint256 indexed arg0",
   571  			WantErr: errors.New(`syntax error: unexpected EOF, want "," or ")"`),
   572  		},
   573  		{
   574  			Input:   "f(uint256,",
   575  			WantErr: errors.New(`syntax error: unexpected EOF, expecting type`),
   576  		},
   577  		{
   578  			Input:    "transfer(address,uint256)",
   579  			WantArgs: Arguments{{Type: typeAddress}, {Type: typeUint256}},
   580  			WantName: "transfer",
   581  		},
   582  		{
   583  			Input:    "transfer(address recipient, uint256 amount)",
   584  			WantArgs: Arguments{{Type: typeAddress, Name: "recipient"}, {Type: typeUint256, Name: "amount"}},
   585  			WantName: "transfer",
   586  		},
   587  		{
   588  			Input: "exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 deadline, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96) params)",
   589  			WantArgs: Arguments{{
   590  				Type: abi.Type{
   591  					T:             abi.TupleTy,
   592  					TupleElems:    []*abi.Type{&typeAddress, &typeAddress, &typeUint24, &typeAddress, &typeUint256, &typeUint256, &typeUint256, &typeUint160},
   593  					TupleRawNames: []string{"tokenIn", "tokenOut", "fee", "recipient", "deadline", "amountIn", "amountOutMinimum", "sqrtPriceLimitX96"},
   594  				},
   595  				Name: "params",
   596  			}},
   597  			WantName: "exactInputSingle",
   598  		},
   599  		{
   600  			Input:    "transfer(simpleStruct)",
   601  			Tuples:   []any{simpleStruct{}},
   602  			WantName: "transfer",
   603  			WantArgs: Arguments{{
   604  				Type: abi.Type{
   605  					T:             abi.TupleTy,
   606  					TupleRawName:  "simpleStruct",
   607  					TupleElems:    []*abi.Type{&typeUint256, &typeAddress},
   608  					TupleRawNames: []string{"amount", "token"},
   609  				},
   610  			}},
   611  		},
   612  		// Test cases for error paths in ParseWithName
   613  		{
   614  			Input:   "f(uint256) extra",
   615  			WantErr: errors.New(`syntax error: unexpected "extra", expecting EOF`),
   616  		},
   617  		{
   618  			Input:   "f(uint256,",
   619  			WantErr: errors.New(`syntax error: unexpected EOF, expecting type`),
   620  		},
   621  		{
   622  			Input:   "f(uint256 arg0",
   623  			WantErr: errors.New(`syntax error: unexpected EOF, want "," or ")"`),
   624  		},
   625  		{
   626  			Input:   "f(uint256 indexed arg0",
   627  			WantErr: errors.New(`syntax error: unexpected EOF, want "," or ")"`),
   628  		},
   629  		{
   630  			Input:   "f((uint256",
   631  			WantErr: errors.New(`syntax error: unexpected EOF, expecting "," or ")"`),
   632  		},
   633  		{
   634  			Input:   "f(uint256 extra",
   635  			WantErr: errors.New(`syntax error: unexpected EOF, want "," or ")"`),
   636  		},
   637  	}
   638  
   639  	for i, test := range tests {
   640  		t.Run(fmt.Sprintf("%d_%s", i, test.Input), func(t *testing.T) {
   641  			gotName, gotArgs, gotErr := ParseWithName(test.Input, test.Tuples...)
   642  			if diff := cmp.Diff(test.WantErr, gotErr,
   643  				internal.EquateErrors(),
   644  			); diff != "" {
   645  				t.Fatalf("Err (-want, +got):\n%s", diff)
   646  			}
   647  			if test.WantName != gotName {
   648  				t.Errorf("Name want: %s, got: %s", test.WantName, gotName)
   649  			}
   650  			if diff := cmp.Diff(test.WantArgs, gotArgs,
   651  				cmpopts.EquateEmpty(),
   652  				cmpopts.IgnoreUnexported(abi.Type{}),
   653  				cmpopts.IgnoreFields(abi.Type{}, "TupleType")); diff != "" {
   654  				t.Errorf("Args (-want, +got):\n%s", diff)
   655  			}
   656  		})
   657  	}
   658  }
   659  
   660  func BenchmarkParseArgsWithName(b *testing.B) {
   661  	benchmarks := []struct {
   662  		Name  string
   663  		Input string
   664  	}{
   665  		{Name: "symbol", Input: "symbol()"},
   666  		{Name: "transfer", Input: "transfer(address,uint256)"},
   667  		{Name: "exactInputSingle", Input: "exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 deadline, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96) params)"},
   668  		{Name: "swap", Input: `swap(
   669  			(address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks) key,
   670  			(bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96) params,
   671  			bytes hookData
   672  		)`},
   673  		{Name: "settle", Input: `settle(
   674  			address[] tokens,
   675  			uint256[] clearingPrices,
   676  			(
   677  				uint256 sellTokenIndex,
   678  				uint256 buyTokenIndex,
   679  				address receiver,
   680  				uint256 sellAmount,
   681  				uint256 buyAmount,
   682  				uint32 validTo,
   683  				bytes32 appData,
   684  				uint256 feeAmount,
   685  				uint256 flags,
   686  				uint256 executedAmount,
   687  				bytes signature
   688  			)[] trades,
   689  			(address target, uint256 value, bytes callData)[][3] interactions
   690  		)`},
   691  	}
   692  
   693  	for _, bench := range benchmarks {
   694  		b.Run(bench.Name, func(b *testing.B) {
   695  			b.ReportAllocs()
   696  			for range b.N {
   697  				ParseWithName(bench.Input)
   698  			}
   699  		})
   700  	}
   701  }
   702  
   703  // test structs for tuple functionality
   704  type simpleStruct struct {
   705  	Amount *big.Int       `abitype:"uint256"`
   706  	Token  common.Address `abitype:"address"`
   707  }
   708  
   709  type simpleStructWithoutTags struct {
   710  	Amount *big.Int
   711  	Token  common.Address
   712  }
   713  
   714  type nestedStruct struct {
   715  	Inner  simpleStruct
   716  	Active bool
   717  }
   718  
   719  type complexStruct struct {
   720  	ID       *big.Int `abitype:"uint256"`
   721  	Owner    common.Address
   722  	Data     []byte
   723  	Metadata nestedStruct
   724  }
   725  
   726  type arrayStruct struct {
   727  	Values []*big.Int `abitype:"uint256"`
   728  	Count  *big.Int   `abitype:"uint256"`
   729  }
   730  
   731  type emptyStruct struct{}
   732  
   733  // Test struct with invalid field type for coverage
   734  type structWithInvalidField struct {
   735  	InvalidFunc func() // This will cause typeOf to fail
   736  }