github.com/lmittmann/w3@v0.20.0/func_test.go (about)

     1  package w3_test
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"math/big"
     7  	"strconv"
     8  	"testing"
     9  
    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"
    14  	"github.com/lmittmann/w3/internal"
    15  	"github.com/lmittmann/w3/w3types"
    16  )
    17  
    18  func TestNewFunc(t *testing.T) {
    19  	tests := []struct {
    20  		Signature string
    21  		Returns   string
    22  		Tuples    []any
    23  		WantFunc  *w3.Func
    24  	}{
    25  		{
    26  			Signature: "transfer(address,uint256)",
    27  			Returns:   "bool",
    28  			WantFunc: &w3.Func{
    29  				Signature: "transfer(address,uint256)",
    30  				Selector:  [4]byte{0xa9, 0x05, 0x9c, 0xbb},
    31  			},
    32  		},
    33  		{
    34  			Signature: "transfer(address recipient, uint256 amount)",
    35  			Returns:   "bool success",
    36  			WantFunc: &w3.Func{
    37  				Signature: "transfer(address,uint256)",
    38  				Selector:  [4]byte{0xa9, 0x05, 0x9c, 0xbb},
    39  			},
    40  		},
    41  		{
    42  			Signature: "testTuple(tuple)",
    43  			Returns:   "bool",
    44  			Tuples:    []any{tuple{}},
    45  			WantFunc: &w3.Func{
    46  				Signature: "testTuple((address,uint256))",
    47  				Selector:  [4]byte{0xa0, 0x54, 0xdf, 0xd5},
    48  			},
    49  		},
    50  		{
    51  			Signature: "testTuple(tupleWithTag)",
    52  			Returns:   "bool",
    53  			Tuples:    []any{tupleWithTag{}},
    54  			WantFunc: &w3.Func{
    55  				Signature: "testTuple((address,uint128))",
    56  				Selector:  [4]byte{0xa8, 0x97, 0xff, 0xb3},
    57  			},
    58  		},
    59  		{
    60  			Signature: "testTuple(tupleWithNesting)",
    61  			Returns:   "bool",
    62  			Tuples:    []any{tupleWithNesting{}},
    63  			WantFunc: &w3.Func{
    64  				Signature: "testTuple((address,(address,uint256)))",
    65  				Selector:  [4]byte{0xff, 0x4c, 0x07, 0xd1},
    66  			},
    67  		},
    68  	}
    69  
    70  	for i, test := range tests {
    71  		t.Run(strconv.Itoa(i), func(t *testing.T) {
    72  			gotFunc, err := w3.NewFunc(test.Signature, test.Returns, test.Tuples...)
    73  			if err != nil {
    74  				t.Fatalf("Failed to create new FUnc: %v", err)
    75  			}
    76  
    77  			if diff := cmp.Diff(test.WantFunc, gotFunc,
    78  				cmpopts.IgnoreFields(w3.Func{}, "Args", "Returns", "name"),
    79  			); diff != "" {
    80  				t.Fatalf("(-want, +got)\n%s", diff)
    81  			}
    82  		})
    83  	}
    84  }
    85  
    86  func TestFuncEncodeArgs(t *testing.T) {
    87  	tests := []struct {
    88  		Func w3types.Func
    89  		Args []any
    90  		Want []byte
    91  	}{
    92  		{
    93  			Func: w3.MustNewFunc("balanceOf(address who)", "uint256 balance"),
    94  			Args: []any{w3.A("0x000000000000000000000000000000000000dEaD")},
    95  			Want: w3.B("0x70a08231000000000000000000000000000000000000000000000000000000000000dEaD"),
    96  		},
    97  		{
    98  			Func: w3.MustNewFunc("transfer(address recipient, uint256 amount)", "bool success"),
    99  			Args: []any{w3.A("0x000000000000000000000000000000000000dEaD"), big.NewInt(1)},
   100  			Want: w3.B("0xa9059cbb000000000000000000000000000000000000000000000000000000000000dEaD0000000000000000000000000000000000000000000000000000000000000001"),
   101  		},
   102  		{
   103  			Func: w3.MustNewFunc("name()", "string"),
   104  			Args: []any{},
   105  			Want: w3.B("0x06fdde03"),
   106  		},
   107  		{
   108  			Func: w3.MustNewFunc("withdraw(uint256)", ""),
   109  			Args: []any{big.NewInt(1)},
   110  			Want: w3.B("0x2e1a7d4d0000000000000000000000000000000000000000000000000000000000000001"),
   111  		},
   112  		{
   113  			Func: w3.MustNewFunc("getAmountsOut(uint256,address[])", "uint256[]"),
   114  			Args: []any{big.NewInt(1), []common.Address{w3.A("0x1111111111111111111111111111111111111111"), w3.A("0x2222222222222222222222222222222222222222")}},
   115  			Want: w3.B("0xd06ca61f00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000011111111111111111111111111111111111111110000000000000000000000002222222222222222222222222222222222222222"),
   116  		},
   117  		{
   118  			Func: w3.MustNewFunc("test((address arg0, uint256 arg1))", ""),
   119  			Args: []any{&tuple{
   120  				Arg0: w3.A("0x000000000000000000000000000000000000c0Fe"),
   121  				Arg1: big.NewInt(42),
   122  			}},
   123  			Want: w3.B("0xba71720c000000000000000000000000000000000000000000000000000000000000c0fe000000000000000000000000000000000000000000000000000000000000002a"),
   124  		},
   125  		{
   126  			Func: w3.MustNewFunc("test((address arg0, uint256 arg1))", ""),
   127  			Args: []any{&tupleWithWrongOrder{
   128  				Arg1: big.NewInt(42),
   129  				Arg0: w3.A("0x000000000000000000000000000000000000c0Fe"),
   130  			}},
   131  			Want: w3.B("0xba71720c000000000000000000000000000000000000000000000000000000000000c0fe000000000000000000000000000000000000000000000000000000000000002a"),
   132  		},
   133  		{
   134  			Func: w3.MustNewFunc("test((address arg0, uint256 arg1))", ""),
   135  			Args: []any{&tupleWithMoreArgs{
   136  				Arg0: w3.A("0x000000000000000000000000000000000000c0Fe"),
   137  				Arg1: big.NewInt(42),
   138  				Arg2: big.NewInt(7),
   139  			}},
   140  			Want: w3.B("0xba71720c000000000000000000000000000000000000000000000000000000000000c0fe000000000000000000000000000000000000000000000000000000000000002a"),
   141  		},
   142  		{
   143  			Func: w3.MustNewFunc("test((address arg0, uint256 arg1)[] args)", ""),
   144  			Args: []any{
   145  				[]tuple{
   146  					{Arg0: w3.A("0x1111111111111111111111111111111111111111"), Arg1: big.NewInt(7)},
   147  					{Arg0: w3.A("0x2222222222222222222222222222222222222222"), Arg1: big.NewInt(42)},
   148  				},
   149  			},
   150  			Want: w3.B("0xae4f5efa00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000070000000000000000000000002222222222222222222222222222222222222222000000000000000000000000000000000000000000000000000000000000002a"),
   151  		},
   152  		{
   153  			Func: w3.MustNewFunc("test((address arg0, bytes arg1)[] calls)", ""),
   154  			Args: []any{
   155  				[]tupleWithBytes{
   156  					{Arg0: w3.A("0x1111111111111111111111111111111111111111"), Arg1: w3.B("0xc0fe")},
   157  					{Arg0: w3.A("0x2222222222222222222222222222222222222222"), Arg1: w3.B("0xdeadbeef")},
   158  				},
   159  			},
   160  			Want: w3.B("0x3a91207700000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002c0fe000000000000000000000000000000000000000000000000000000000000000000000000000000000000222222222222222222222222222222222222222200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000004deadbeef00000000000000000000000000000000000000000000000000000000"),
   161  		},
   162  		{
   163  			Func: w3.MustNewFunc("test(tupleWithBytes[] calls)", "", tupleWithBytes{}),
   164  			Args: []any{
   165  				[]tupleWithBytes{
   166  					{Arg0: w3.A("0x1111111111111111111111111111111111111111"), Arg1: w3.B("0xc0fe")},
   167  					{Arg0: w3.A("0x2222222222222222222222222222222222222222"), Arg1: w3.B("0xdeadbeef")},
   168  				},
   169  			},
   170  			Want: w3.B("0x3a91207700000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002c0fe000000000000000000000000000000000000000000000000000000000000000000000000000000000000222222222222222222222222222222222222222200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000004deadbeef00000000000000000000000000000000000000000000000000000000"),
   171  		},
   172  		{
   173  			Func: w3.MustNewFunc("test(uint[])", ""),
   174  			Args: []any{
   175  				[]*big.Int{big.NewInt(0xdead), big.NewInt(0xbeef)},
   176  			},
   177  			Want: w3.B("0xca16068400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000dead000000000000000000000000000000000000000000000000000000000000beef"),
   178  		},
   179  		{
   180  			Func: w3.MustNewFunc("test(uint[2])", ""),
   181  			Args: []any{
   182  				[2]*big.Int{big.NewInt(0xdead), big.NewInt(0xbeef)},
   183  			},
   184  			Want: w3.B("0xf1635056000000000000000000000000000000000000000000000000000000000000dead000000000000000000000000000000000000000000000000000000000000beef"),
   185  		},
   186  		{
   187  			Func: w3.MustNewFunc("test(uint64[])", ""),
   188  			Args: []any{
   189  				[]uint64{0xdead, 0xbeef},
   190  			},
   191  			Want: w3.B("0xd3469fbd00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000dead000000000000000000000000000000000000000000000000000000000000beef"),
   192  		},
   193  		{
   194  			Func: w3.MustNewFunc("test(uint64[2])", ""),
   195  			Args: []any{
   196  				[2]uint64{0xdead, 0xbeef},
   197  			},
   198  			Want: w3.B("0x533d6285000000000000000000000000000000000000000000000000000000000000dead000000000000000000000000000000000000000000000000000000000000beef"),
   199  		},
   200  		{
   201  			Func: w3.MustNewFunc("testTuple(tuple)", "bool", tuple{}),
   202  			Args: []any{&tuple{
   203  				Arg0: w3.A("0x000000000000000000000000000000000000c0Fe"),
   204  				Arg1: big.NewInt(42),
   205  			}},
   206  			Want: w3.B("0xa054dfd5000000000000000000000000000000000000000000000000000000000000c0fe000000000000000000000000000000000000000000000000000000000000002a"),
   207  		},
   208  		{ // https://github.com/lmittmann/w3/issues/35
   209  			Func: w3.MustNewFunc("test(((address to)[] recipients) param)", ""),
   210  			Args: []any{
   211  				&tupleIssue35{Recipients: []struct {
   212  					To common.Address
   213  				}{
   214  					{To: w3.A("0x1111111111111111111111111111111111111111")},
   215  					{To: w3.A("0x2222222222222222222222222222222222222222")},
   216  				}},
   217  			},
   218  			Want: w3.B("0xf61d1a2a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000011111111111111111111111111111111111111110000000000000000000000002222222222222222222222222222222222222222"),
   219  		},
   220  	}
   221  
   222  	for i, test := range tests {
   223  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   224  			encodedInput, err := test.Func.EncodeArgs(test.Args...)
   225  			if err != nil {
   226  				t.Fatalf("Failed to encode args: %v", err)
   227  			}
   228  
   229  			if !bytes.Equal(test.Want, encodedInput) {
   230  				t.Fatalf("(-want, +got)\n- 0x%x\n+ 0x%x", test.Want, encodedInput)
   231  			}
   232  		})
   233  	}
   234  }
   235  
   236  func TestFuncDecodeArgs(t *testing.T) {
   237  	tests := []struct {
   238  		Func     w3types.Func
   239  		Input    []byte
   240  		Args     []any
   241  		WantArgs []any
   242  		WantErr  error
   243  	}{
   244  		{
   245  			Func:     w3.MustNewFunc("test(address)", ""),
   246  			Input:    w3.B("0xbb29998e000000000000000000000000000000000000000000000000000000000000c0fe"),
   247  			Args:     []any{new(common.Address)},
   248  			WantArgs: []any{w3.APtr("0x000000000000000000000000000000000000c0Fe")},
   249  		},
   250  		{
   251  			Func:     w3.MustNewFunc("test(uint256)", ""),
   252  			Input:    w3.B("0x29e99f07000000000000000000000000000000000000000000000000000000000000002a"),
   253  			Args:     []any{new(big.Int)},
   254  			WantArgs: []any{big.NewInt(42)},
   255  		},
   256  		{
   257  			Func:     w3.MustNewFunc("test(bool)", ""),
   258  			Input:    w3.B("0x36091dff0000000000000000000000000000000000000000000000000000000000000001"),
   259  			Args:     []any{ptr(false)},
   260  			WantArgs: []any{ptr(true)},
   261  		},
   262  		{
   263  			Func:     w3.MustNewFunc("test(bytes32)", ""),
   264  			Input:    w3.B("0x993723210102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"),
   265  			Args:     []any{&[32]byte{}},
   266  			WantArgs: []any{&[32]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}},
   267  		},
   268  		{
   269  			Func:     w3.MustNewFunc("test(bytes32)", ""),
   270  			Input:    w3.B("0x993723210102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"),
   271  			Args:     []any{new(common.Hash)},
   272  			WantArgs: []any{ptr(w3.H("0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"))},
   273  		},
   274  		{
   275  			Func:     w3.MustNewFunc("test(bytes)", ""),
   276  			Input:    w3.B("0x2f570a23000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030102030000000000000000000000000000000000000000000000000000000000"),
   277  			Args:     []any{&[]byte{}},
   278  			WantArgs: []any{&[]byte{1, 2, 3}},
   279  		},
   280  		{
   281  			Func:  w3.MustNewFunc("test((address arg0, uint256 arg1))", ""),
   282  			Input: w3.B("0xba71720c000000000000000000000000000000000000000000000000000000000000c0fe000000000000000000000000000000000000000000000000000000000000002a"),
   283  			Args:  []any{new(tuple)},
   284  			WantArgs: []any{&tuple{
   285  				Arg0: w3.A("0x000000000000000000000000000000000000c0Fe"),
   286  				Arg1: big.NewInt(42),
   287  			}},
   288  		},
   289  		{
   290  			Func:  w3.MustNewFunc("test(tuple)", "", tuple{}),
   291  			Input: w3.B("0xba71720c000000000000000000000000000000000000000000000000000000000000c0fe000000000000000000000000000000000000000000000000000000000000002a"),
   292  			Args:  []any{new(tuple)},
   293  			WantArgs: []any{&tuple{
   294  				Arg0: w3.A("0x000000000000000000000000000000000000c0Fe"),
   295  				Arg1: big.NewInt(42),
   296  			}},
   297  		},
   298  		{
   299  			// https://github.com/lmittmann/w3/issues/67
   300  			Func:     w3.MustNewFunc("test((address, uint256))", ""),
   301  			Input:    w3.B("0xba71720c000000000000000000000000000000000000000000000000000000000000c0fe000000000000000000000000000000000000000000000000000000000000002a"),
   302  			Args:     []any{nil},
   303  			WantArgs: []any{nil},
   304  		},
   305  		{
   306  			// https://github.com/lmittmann/w3/issues/67
   307  			Func:  w3.MustNewFunc("test((address, uint256))", ""),
   308  			Input: w3.B("0xba71720c000000000000000000000000000000000000000000000000000000000000c0fe000000000000000000000000000000000000000000000000000000000000002a"),
   309  			Args:  []any{new(tuple)},
   310  			WantArgs: []any{&tuple{
   311  				Arg0: w3.A("0x000000000000000000000000000000000000c0Fe"),
   312  				Arg1: big.NewInt(42),
   313  			}},
   314  		},
   315  		{
   316  			// https://github.com/lmittmann/w3/issues/67
   317  			Func:  w3.MustNewFunc("test((address, (address, uint256)))", ""),
   318  			Input: w3.B("0x1a68b84c000000000000000000000000000000000000000000000000000000000000c0fe000000000000000000000000000000000000000000000000000000000000dead000000000000000000000000000000000000000000000000000000000000002a"),
   319  			Args:  []any{new(tupleNested)},
   320  			WantArgs: []any{&tupleNested{
   321  				Arg0: w3.A("0x000000000000000000000000000000000000c0Fe"),
   322  				Arg1: tuple{
   323  					Arg0: w3.A("0x000000000000000000000000000000000000dEaD"),
   324  					Arg1: big.NewInt(42),
   325  				},
   326  			}},
   327  		},
   328  		{
   329  			Func:  w3.MustNewFunc("test((address arg0, uint256 arg1))", ""),
   330  			Input: w3.B("0xba71720c000000000000000000000000000000000000000000000000000000000000c0fe000000000000000000000000000000000000000000000000000000000000002a"),
   331  			Args:  []any{new(tupleWithWrongOrder)},
   332  			WantArgs: []any{&tupleWithWrongOrder{
   333  				Arg1: big.NewInt(42),
   334  				Arg0: w3.A("0x000000000000000000000000000000000000c0Fe"),
   335  			}},
   336  		},
   337  		{
   338  			Func:  w3.MustNewFunc("test((address arg0, uint256 arg1))", ""),
   339  			Input: w3.B("0xba71720c000000000000000000000000000000000000000000000000000000000000c0fe000000000000000000000000000000000000000000000000000000000000002a"),
   340  			Args:  []any{new(tupleWithMoreArgs)},
   341  			WantArgs: []any{&tupleWithMoreArgs{
   342  				Arg0: w3.A("0x000000000000000000000000000000000000c0Fe"),
   343  				Arg1: big.NewInt(42),
   344  			}},
   345  		},
   346  		{ // https://github.com/lmittmann/w3/issues/22
   347  			Func:    w3.MustNewFunc("transfer(address recipient, uint256 amount)", "bool success"),
   348  			Input:   w3.B("0x"),
   349  			Args:    []any{new(common.Address), new(big.Int)},
   350  			WantErr: errors.New("w3: insufficient input length"),
   351  		},
   352  		{
   353  			Func:  w3.MustNewFunc("test((address arg0, uint256 arg1))", ""),
   354  			Input: w3.B("0xba71720c000000000000000000000000000000000000000000000000000000000000c0fe000000000000000000000000000000000000000000000000000000000000002a"),
   355  			Args:  []any{new(tupleWithUnexportedProperty)},
   356  			WantArgs: []any{&tupleWithUnexportedProperty{
   357  				Arg1: big.NewInt(42),
   358  			}},
   359  		},
   360  		{
   361  			Func:  w3.MustNewFunc("test((address arg0, bytes arg1)[] calls)", ""),
   362  			Input: w3.B("0x3a91207700000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002c0fe000000000000000000000000000000000000000000000000000000000000000000000000000000000000222222222222222222222222222222222222222200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000004deadbeef00000000000000000000000000000000000000000000000000000000"),
   363  			Args:  []any{&[]*tupleWithBytes{}},
   364  			WantArgs: []any{
   365  				&[]*tupleWithBytes{
   366  					{Arg0: w3.A("0x1111111111111111111111111111111111111111"), Arg1: w3.B("0xc0fe")},
   367  					{Arg0: w3.A("0x2222222222222222222222222222222222222222"), Arg1: w3.B("0xdeadbeef")},
   368  				},
   369  			},
   370  		},
   371  		{
   372  			Func:  w3.MustNewFunc("test(uint[])", ""),
   373  			Input: w3.B("0xca16068400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000dead000000000000000000000000000000000000000000000000000000000000beef"),
   374  			Args:  []any{new([]*big.Int)},
   375  			WantArgs: []any{
   376  				&[]*big.Int{big.NewInt(0xdead), big.NewInt(0xbeef)},
   377  			},
   378  		},
   379  		{
   380  			Func:  w3.MustNewFunc("test(uint[2])", ""),
   381  			Input: w3.B("0xf1635056000000000000000000000000000000000000000000000000000000000000dead000000000000000000000000000000000000000000000000000000000000beef"),
   382  			Args:  []any{new([2]*big.Int)},
   383  			WantArgs: []any{
   384  				&[2]*big.Int{big.NewInt(0xdead), big.NewInt(0xbeef)},
   385  			},
   386  		},
   387  		{
   388  			Func:  w3.MustNewFunc("test(uint64[])", ""),
   389  			Input: w3.B("0xd3469fbd00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000dead000000000000000000000000000000000000000000000000000000000000beef"),
   390  			Args:  []any{new([]uint64)},
   391  			WantArgs: []any{
   392  				&[]uint64{0xdead, 0xbeef},
   393  			},
   394  		},
   395  		{
   396  			Func:  w3.MustNewFunc("test(uint64[2])", ""),
   397  			Input: w3.B("0x533d6285000000000000000000000000000000000000000000000000000000000000dead000000000000000000000000000000000000000000000000000000000000beef"),
   398  			Args:  []any{new([2]uint64)},
   399  			WantArgs: []any{
   400  				&[2]uint64{0xdead, 0xbeef},
   401  			},
   402  		},
   403  		{
   404  			Func:  w3.MustNewFunc("testTuple(tuple)", "bool", tuple{}),
   405  			Input: w3.B("0xa054dfd5000000000000000000000000000000000000000000000000000000000000c0fe000000000000000000000000000000000000000000000000000000000000002a"),
   406  			Args:  []any{new(tuple)},
   407  			WantArgs: []any{&tuple{
   408  				Arg0: w3.A("0x000000000000000000000000000000000000c0Fe"),
   409  				Arg1: big.NewInt(42),
   410  			}},
   411  		},
   412  		{ // https://github.com/lmittmann/w3/issues/35
   413  			Func:  w3.MustNewFunc("test(((address to)[] recipients) param)", ""),
   414  			Input: w3.B("0xf61d1a2a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000011111111111111111111111111111111111111110000000000000000000000002222222222222222222222222222222222222222"),
   415  			Args:  []any{new(tupleIssue35)},
   416  			WantArgs: []any{
   417  				&tupleIssue35{Recipients: []struct {
   418  					To common.Address
   419  				}{
   420  					{To: w3.A("0x1111111111111111111111111111111111111111")},
   421  					{To: w3.A("0x2222222222222222222222222222222222222222")},
   422  				}},
   423  			},
   424  		},
   425  		{
   426  			Func:    w3.MustNewFunc("test(address)", ""),
   427  			Input:   w3.B("0xffffffff000000000000000000000000000000000000000000000000000000000000c0fe"),
   428  			Args:    []any{new(common.Address)},
   429  			WantErr: errors.New("w3: input does not match selector"),
   430  		},
   431  	}
   432  
   433  	for i, test := range tests {
   434  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   435  			err := test.Func.DecodeArgs(test.Input, test.Args...)
   436  			if diff := cmp.Diff(test.WantErr, err, internal.EquateErrors()); diff != "" {
   437  				t.Fatalf("Err: (-want, +got)\n%s", diff)
   438  			}
   439  			if err != nil {
   440  				return
   441  			}
   442  
   443  			if diff := cmp.Diff(test.WantArgs, test.Args,
   444  				cmp.AllowUnexported(big.Int{}, tupleWithUnexportedProperty{}),
   445  			); diff != "" {
   446  				t.Fatalf("Args: (-want, +got)\n%s", diff)
   447  			}
   448  		})
   449  	}
   450  }
   451  
   452  func TestFuncDecodeReturns(t *testing.T) {
   453  	tests := []struct {
   454  		Func        w3types.Func
   455  		Output      []byte
   456  		Returns     []any
   457  		WantReturns []any
   458  	}{
   459  		{
   460  			Func:        w3.MustNewFunc("test()", "address"),
   461  			Output:      w3.B("0x000000000000000000000000000000000000000000000000000000000000c0fe"),
   462  			Returns:     []any{new(common.Address)},
   463  			WantReturns: []any{w3.APtr("0x000000000000000000000000000000000000c0Fe")},
   464  		},
   465  		{
   466  			Func:        w3.MustNewFunc("test()", "uint256"),
   467  			Output:      w3.B("0x000000000000000000000000000000000000000000000000000000000000002a"),
   468  			Returns:     []any{new(big.Int)},
   469  			WantReturns: []any{big.NewInt(42)},
   470  		},
   471  		{
   472  			Func:        w3.MustNewFunc("test()", "bool"),
   473  			Output:      w3.B("0x0000000000000000000000000000000000000000000000000000000000000001"),
   474  			Returns:     []any{ptr(false)},
   475  			WantReturns: []any{ptr(true)},
   476  		},
   477  		{
   478  			Func:        w3.MustNewFunc("test()", "bytes32"),
   479  			Output:      w3.B("0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"),
   480  			Returns:     []any{&[32]byte{}},
   481  			WantReturns: []any{&[32]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}},
   482  		},
   483  		{
   484  			Func:        w3.MustNewFunc("test()", "bytes32"),
   485  			Output:      w3.B("0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"),
   486  			Returns:     []any{new(common.Hash)},
   487  			WantReturns: []any{ptr(w3.H("0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"))},
   488  		},
   489  		{
   490  			Func:        w3.MustNewFunc("test()", "bytes"),
   491  			Output:      w3.B("0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030102030000000000000000000000000000000000000000000000000000000000"),
   492  			Returns:     []any{&[]byte{}},
   493  			WantReturns: []any{&[]byte{1, 2, 3}},
   494  		},
   495  		{ // https://github.com/lmittmann/w3/issues/25
   496  			Func:    w3.MustNewFunc("test()", "(address arg0, uint256 arg1)"),
   497  			Output:  w3.B("0x000000000000000000000000000000000000000000000000000000000000c0fe000000000000000000000000000000000000000000000000000000000000002a"),
   498  			Returns: []any{new(tuple)},
   499  			WantReturns: []any{&tuple{
   500  				Arg0: w3.A("0x000000000000000000000000000000000000c0Fe"),
   501  				Arg1: big.NewInt(42),
   502  			}},
   503  		},
   504  		{
   505  			Func:    w3.MustNewFunc("test()", "tuple", tuple{}),
   506  			Output:  w3.B("0x000000000000000000000000000000000000000000000000000000000000c0fe000000000000000000000000000000000000000000000000000000000000002a"),
   507  			Returns: []any{new(tuple)},
   508  			WantReturns: []any{&tuple{
   509  				Arg0: w3.A("0x000000000000000000000000000000000000c0Fe"),
   510  				Arg1: big.NewInt(42),
   511  			}},
   512  		},
   513  	}
   514  
   515  	for i, test := range tests {
   516  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   517  			if err := test.Func.DecodeReturns(test.Output, test.Returns...); err != nil {
   518  				t.Fatalf("Failed to decode returns: %v", err)
   519  			}
   520  			if diff := cmp.Diff(test.WantReturns, test.Returns, cmp.AllowUnexported(big.Int{})); diff != "" {
   521  				t.Fatalf("(-want, +got)\n%s", diff)
   522  			}
   523  		})
   524  	}
   525  }
   526  
   527  func ptr[T any](v T) *T { return &v }
   528  
   529  type tuple struct {
   530  	Arg0 common.Address
   531  	Arg1 *big.Int
   532  }
   533  
   534  type tupleWithTag struct {
   535  	Arg0 common.Address `abitype:"address"`
   536  	Arg1 *big.Int       `abitype:"uint128"`
   537  }
   538  
   539  type tupleWithNesting struct {
   540  	Arg0 common.Address
   541  	Arg1 tuple
   542  }
   543  
   544  type tupleWithBytes struct {
   545  	Arg0 common.Address
   546  	Arg1 []byte
   547  }
   548  
   549  type tupleWithUnexportedProperty struct {
   550  	//lint:ignore U1000 ignore unused field
   551  	arg0 common.Address
   552  	Arg1 *big.Int
   553  }
   554  
   555  type tupleWithWrongOrder struct {
   556  	Arg1 *big.Int
   557  	Arg0 common.Address
   558  }
   559  
   560  type tupleWithMoreArgs struct {
   561  	Arg0 common.Address
   562  	Arg1 *big.Int
   563  	Arg2 *big.Int // Arg that is missing in func signature
   564  }
   565  
   566  type tupleIssue35 struct {
   567  	Recipients []struct {
   568  		To common.Address
   569  	}
   570  }
   571  
   572  type tupleNested struct {
   573  	Arg0 common.Address
   574  	Arg1 tuple
   575  }
   576  
   577  func BenchmarkFuncEncode(b *testing.B) {
   578  	var (
   579  		funcSwap   = w3.MustNewFunc("swap(uint amount0Out, uint amount1Out, address to, bytes data)", "")
   580  		amount0Out = big.NewInt(1000000000000000000) // 1 ETH
   581  		amount1Out = big.NewInt(0)                   // 0 token
   582  		to         = w3.A("0x000000000000000000000000000000000000c0Fe")
   583  		data       = []byte{}
   584  	)
   585  
   586  	b.ReportAllocs()
   587  	b.ResetTimer()
   588  	for range b.N {
   589  		funcSwap.EncodeArgs(amount0Out, amount1Out, to, data)
   590  	}
   591  }