From 7ca40306af9da68a0d31439117246de8247f99d6 Mon Sep 17 00:00:00 2001
From: gary rong <garyrong0905@gmail.com>
Date: Thu, 10 Jan 2019 16:59:37 +0800
Subject: [PATCH] accounts/abi: tuple support (#18406)

---
 accounts/abi/abi.go         |   2 -
 accounts/abi/abi_test.go    |  57 +++--
 accounts/abi/argument.go    | 187 ++++++++++------
 accounts/abi/pack_test.go   | 302 ++++++++++++++++++++++++-
 accounts/abi/reflect.go     |  63 +++---
 accounts/abi/type.go        | 141 ++++++++++--
 accounts/abi/type_test.go   | 433 +++++++++++++++++++-----------------
 accounts/abi/unpack.go      |  92 +++++---
 accounts/abi/unpack_test.go | 119 +++++++++-
 9 files changed, 1003 insertions(+), 393 deletions(-)

diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
index 4d29814b2176..08d5db979861 100644
--- a/accounts/abi/abi.go
+++ b/accounts/abi/abi.go
@@ -58,13 +58,11 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
 			return nil, err
 		}
 		return arguments, nil
-
 	}
 	method, exist := abi.Methods[name]
 	if !exist {
 		return nil, fmt.Errorf("method '%s' not found", name)
 	}
-
 	arguments, err := method.Inputs.Pack(args...)
 	if err != nil {
 		return nil, err
diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go
index 59ba79cb6d3b..b9444f9f0d94 100644
--- a/accounts/abi/abi_test.go
+++ b/accounts/abi/abi_test.go
@@ -22,11 +22,10 @@ import (
 	"fmt"
 	"log"
 	"math/big"
+	"reflect"
 	"strings"
 	"testing"
 
-	"reflect"
-
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/crypto"
 )
@@ -52,11 +51,14 @@ const jsondata2 = `
 	{ "type" : "function", "name" : "slice", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] },
 	{ "type" : "function", "name" : "slice256", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] },
 	{ "type" : "function", "name" : "sliceAddress", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] },
-	{ "type" : "function", "name" : "sliceMultiAddress", "constant" : false, "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] }
+	{ "type" : "function", "name" : "sliceMultiAddress", "constant" : false, "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] },
+	{ "type" : "function", "name" : "nestedArray", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint256[2][2]" }, { "name" : "b", "type" : "address[]" } ] },
+	{ "type" : "function", "name" : "nestedArray2", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint8[][2]" } ] },
+	{ "type" : "function", "name" : "nestedSlice", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint8[][]" } ] }
 ]`
 
 func TestReader(t *testing.T) {
-	Uint256, _ := NewType("uint256")
+	Uint256, _ := NewType("uint256", nil)
 	exp := ABI{
 		Methods: map[string]Method{
 			"balance": {
@@ -177,7 +179,7 @@ func TestTestSlice(t *testing.T) {
 }
 
 func TestMethodSignature(t *testing.T) {
-	String, _ := NewType("string")
+	String, _ := NewType("string", nil)
 	m := Method{"foo", false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil}
 	exp := "foo(string,string)"
 	if m.Sig() != exp {
@@ -189,12 +191,31 @@ func TestMethodSignature(t *testing.T) {
 		t.Errorf("expected ids to match %x != %x", m.Id(), idexp)
 	}
 
-	uintt, _ := NewType("uint256")
+	uintt, _ := NewType("uint256", nil)
 	m = Method{"foo", false, []Argument{{"bar", uintt, false}}, nil}
 	exp = "foo(uint256)"
 	if m.Sig() != exp {
 		t.Error("signature mismatch", exp, "!=", m.Sig())
 	}
+
+	// Method with tuple arguments
+	s, _ := NewType("tuple", []ArgumentMarshaling{
+		{Name: "a", Type: "int256"},
+		{Name: "b", Type: "int256[]"},
+		{Name: "c", Type: "tuple[]", Components: []ArgumentMarshaling{
+			{Name: "x", Type: "int256"},
+			{Name: "y", Type: "int256"},
+		}},
+		{Name: "d", Type: "tuple[2]", Components: []ArgumentMarshaling{
+			{Name: "x", Type: "int256"},
+			{Name: "y", Type: "int256"},
+		}},
+	})
+	m = Method{"foo", false, []Argument{{"s", s, false}, {"bar", String, false}}, nil}
+	exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)"
+	if m.Sig() != exp {
+		t.Error("signature mismatch", exp, "!=", m.Sig())
+	}
 }
 
 func TestMultiPack(t *testing.T) {
@@ -564,11 +585,13 @@ func TestBareEvents(t *testing.T) {
 	const definition = `[
 	{ "type" : "event", "name" : "balance" },
 	{ "type" : "event", "name" : "anon", "anonymous" : true},
-	{ "type" : "event", "name" : "args", "inputs" : [{ "indexed":false, "name":"arg0", "type":"uint256" }, { "indexed":true, "name":"arg1", "type":"address" }] }
+	{ "type" : "event", "name" : "args", "inputs" : [{ "indexed":false, "name":"arg0", "type":"uint256" }, { "indexed":true, "name":"arg1", "type":"address" }] },
+	{ "type" : "event", "name" : "tuple", "inputs" : [{ "indexed":false, "name":"t", "type":"tuple", "components":[{"name":"a", "type":"uint256"}] }, { "indexed":true, "name":"arg1", "type":"address" }] }
 	]`
 
-	arg0, _ := NewType("uint256")
-	arg1, _ := NewType("address")
+	arg0, _ := NewType("uint256", nil)
+	arg1, _ := NewType("address", nil)
+	tuple, _ := NewType("tuple", []ArgumentMarshaling{{Name: "a", Type: "uint256"}})
 
 	expectedEvents := map[string]struct {
 		Anonymous bool
@@ -580,6 +603,10 @@ func TestBareEvents(t *testing.T) {
 			{Name: "arg0", Type: arg0, Indexed: false},
 			{Name: "arg1", Type: arg1, Indexed: true},
 		}},
+		"tuple": {false, []Argument{
+			{Name: "t", Type: tuple, Indexed: false},
+			{Name: "arg1", Type: arg1, Indexed: true},
+		}},
 	}
 
 	abi, err := JSON(strings.NewReader(definition))
@@ -646,28 +673,24 @@ func TestUnpackEvent(t *testing.T) {
 	}
 
 	type ReceivedEvent struct {
-		Address common.Address
-		Amount  *big.Int
-		Memo    []byte
+		Sender common.Address
+		Amount *big.Int
+		Memo   []byte
 	}
 	var ev ReceivedEvent
 
 	err = abi.Unpack(&ev, "received", data)
 	if err != nil {
 		t.Error(err)
-	} else {
-		t.Logf("len(data): %d; received event: %+v", len(data), ev)
 	}
 
 	type ReceivedAddrEvent struct {
-		Address common.Address
+		Sender common.Address
 	}
 	var receivedAddrEv ReceivedAddrEvent
 	err = abi.Unpack(&receivedAddrEv, "receivedAddr", data)
 	if err != nil {
 		t.Error(err)
-	} else {
-		t.Logf("len(data): %d; received event: %+v", len(data), receivedAddrEv)
 	}
 }
 
diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go
index fdc6ff164806..d0a6b035c66a 100644
--- a/accounts/abi/argument.go
+++ b/accounts/abi/argument.go
@@ -33,24 +33,27 @@ type Argument struct {
 
 type Arguments []Argument
 
+type ArgumentMarshaling struct {
+	Name       string
+	Type       string
+	Components []ArgumentMarshaling
+	Indexed    bool
+}
+
 // UnmarshalJSON implements json.Unmarshaler interface
 func (argument *Argument) UnmarshalJSON(data []byte) error {
-	var extarg struct {
-		Name    string
-		Type    string
-		Indexed bool
-	}
-	err := json.Unmarshal(data, &extarg)
+	var arg ArgumentMarshaling
+	err := json.Unmarshal(data, &arg)
 	if err != nil {
 		return fmt.Errorf("argument json err: %v", err)
 	}
 
-	argument.Type, err = NewType(extarg.Type)
+	argument.Type, err = NewType(arg.Type, arg.Components)
 	if err != nil {
 		return err
 	}
-	argument.Name = extarg.Name
-	argument.Indexed = extarg.Indexed
+	argument.Name = arg.Name
+	argument.Indexed = arg.Indexed
 
 	return nil
 }
@@ -85,7 +88,6 @@ func (arguments Arguments) isTuple() bool {
 
 // Unpack performs the operation hexdata -> Go format
 func (arguments Arguments) Unpack(v interface{}, data []byte) error {
-
 	// make sure the passed value is arguments pointer
 	if reflect.Ptr != reflect.ValueOf(v).Kind() {
 		return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
@@ -97,52 +99,134 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error {
 	if arguments.isTuple() {
 		return arguments.unpackTuple(v, marshalledValues)
 	}
-	return arguments.unpackAtomic(v, marshalledValues)
+	return arguments.unpackAtomic(v, marshalledValues[0])
 }
 
-func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {
+// unpack sets the unmarshalled value to go format.
+// Note the dst here must be settable.
+func unpack(t *Type, dst interface{}, src interface{}) error {
+	var (
+		dstVal = reflect.ValueOf(dst).Elem()
+		srcVal = reflect.ValueOf(src)
+	)
+
+	if t.T != TupleTy && !((t.T == SliceTy || t.T == ArrayTy) && t.Elem.T == TupleTy) {
+		return set(dstVal, srcVal)
+	}
+
+	switch t.T {
+	case TupleTy:
+		if dstVal.Kind() != reflect.Struct {
+			return fmt.Errorf("abi: invalid dst value for unpack, want struct, got %s", dstVal.Kind())
+		}
+		fieldmap, err := mapArgNamesToStructFields(t.TupleRawNames, dstVal)
+		if err != nil {
+			return err
+		}
+		for i, elem := range t.TupleElems {
+			fname := fieldmap[t.TupleRawNames[i]]
+			field := dstVal.FieldByName(fname)
+			if !field.IsValid() {
+				return fmt.Errorf("abi: field %s can't found in the given value", t.TupleRawNames[i])
+			}
+			if err := unpack(elem, field.Addr().Interface(), srcVal.Field(i).Interface()); err != nil {
+				return err
+			}
+		}
+		return nil
+	case SliceTy:
+		if dstVal.Kind() != reflect.Slice {
+			return fmt.Errorf("abi: invalid dst value for unpack, want slice, got %s", dstVal.Kind())
+		}
+		slice := reflect.MakeSlice(dstVal.Type(), srcVal.Len(), srcVal.Len())
+		for i := 0; i < slice.Len(); i++ {
+			if err := unpack(t.Elem, slice.Index(i).Addr().Interface(), srcVal.Index(i).Interface()); err != nil {
+				return err
+			}
+		}
+		dstVal.Set(slice)
+	case ArrayTy:
+		if dstVal.Kind() != reflect.Array {
+			return fmt.Errorf("abi: invalid dst value for unpack, want array, got %s", dstVal.Kind())
+		}
+		array := reflect.New(dstVal.Type()).Elem()
+		for i := 0; i < array.Len(); i++ {
+			if err := unpack(t.Elem, array.Index(i).Addr().Interface(), srcVal.Index(i).Interface()); err != nil {
+				return err
+			}
+		}
+		dstVal.Set(array)
+	}
+	return nil
+}
+
+// unpackAtomic unpacks ( hexdata -> go ) a single value
+func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interface{}) error {
+	if arguments.LengthNonIndexed() == 0 {
+		return nil
+	}
+	argument := arguments.NonIndexed()[0]
+	elem := reflect.ValueOf(v).Elem()
 
+	if elem.Kind() == reflect.Struct {
+		fieldmap, err := mapArgNamesToStructFields([]string{argument.Name}, elem)
+		if err != nil {
+			return err
+		}
+		field := elem.FieldByName(fieldmap[argument.Name])
+		if !field.IsValid() {
+			return fmt.Errorf("abi: field %s can't be found in the given value", argument.Name)
+		}
+		return unpack(&argument.Type, field.Addr().Interface(), marshalledValues)
+	}
+	return unpack(&argument.Type, elem.Addr().Interface(), marshalledValues)
+}
+
+// unpackTuple unpacks ( hexdata -> go ) a batch of values.
+func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {
 	var (
 		value = reflect.ValueOf(v).Elem()
 		typ   = value.Type()
 		kind  = value.Kind()
 	)
-
 	if err := requireUnpackKind(value, typ, kind, arguments); err != nil {
 		return err
 	}
 
 	// If the interface is a struct, get of abi->struct_field mapping
-
 	var abi2struct map[string]string
 	if kind == reflect.Struct {
-		var err error
-		abi2struct, err = mapAbiToStructFields(arguments, value)
+		var (
+			argNames []string
+			err      error
+		)
+		for _, arg := range arguments.NonIndexed() {
+			argNames = append(argNames, arg.Name)
+		}
+		abi2struct, err = mapArgNamesToStructFields(argNames, value)
 		if err != nil {
 			return err
 		}
 	}
 	for i, arg := range arguments.NonIndexed() {
-
-		reflectValue := reflect.ValueOf(marshalledValues[i])
-
 		switch kind {
 		case reflect.Struct:
-			if structField, ok := abi2struct[arg.Name]; ok {
-				if err := set(value.FieldByName(structField), reflectValue, arg); err != nil {
-					return err
-				}
+			field := value.FieldByName(abi2struct[arg.Name])
+			if !field.IsValid() {
+				return fmt.Errorf("abi: field %s can't be found in the given value", arg.Name)
+			}
+			if err := unpack(&arg.Type, field.Addr().Interface(), marshalledValues[i]); err != nil {
+				return err
 			}
 		case reflect.Slice, reflect.Array:
 			if value.Len() < i {
 				return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
 			}
 			v := value.Index(i)
-			if err := requireAssignable(v, reflectValue); err != nil {
+			if err := requireAssignable(v, reflect.ValueOf(marshalledValues[i])); err != nil {
 				return err
 			}
-
-			if err := set(v.Elem(), reflectValue, arg); err != nil {
+			if err := unpack(&arg.Type, v.Addr().Interface(), marshalledValues[i]); err != nil {
 				return err
 			}
 		default:
@@ -150,48 +234,7 @@ func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interfa
 		}
 	}
 	return nil
-}
 
-// unpackAtomic unpacks ( hexdata -> go ) a single value
-func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues []interface{}) error {
-	if len(marshalledValues) != 1 {
-		return fmt.Errorf("abi: wrong length, expected single value, got %d", len(marshalledValues))
-	}
-
-	elem := reflect.ValueOf(v).Elem()
-	kind := elem.Kind()
-	reflectValue := reflect.ValueOf(marshalledValues[0])
-
-	var abi2struct map[string]string
-	if kind == reflect.Struct {
-		var err error
-		if abi2struct, err = mapAbiToStructFields(arguments, elem); err != nil {
-			return err
-		}
-		arg := arguments.NonIndexed()[0]
-		if structField, ok := abi2struct[arg.Name]; ok {
-			return set(elem.FieldByName(structField), reflectValue, arg)
-		}
-		return nil
-	}
-
-	return set(elem, reflectValue, arguments.NonIndexed()[0])
-
-}
-
-// Computes the full size of an array;
-// i.e. counting nested arrays, which count towards size for unpacking.
-func getArraySize(arr *Type) int {
-	size := arr.Size
-	// Arrays can be nested, with each element being the same size
-	arr = arr.Elem
-	for arr.T == ArrayTy {
-		// Keep multiplying by elem.Size while the elem is an array.
-		size *= arr.Size
-		arr = arr.Elem
-	}
-	// Now we have the full array size, including its children.
-	return size
 }
 
 // UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
@@ -202,7 +245,7 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
 	virtualArgs := 0
 	for index, arg := range arguments.NonIndexed() {
 		marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
-		if arg.Type.T == ArrayTy && (*arg.Type.Elem).T != StringTy {
+		if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) {
 			// If we have a static array, like [3]uint256, these are coded as
 			// just like uint256,uint256,uint256.
 			// This means that we need to add two 'virtual' arguments when
@@ -213,7 +256,11 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
 			//
 			// Calculate the full array size to get the correct offset for the next argument.
 			// Decrement it by 1, as the normal index increment is still applied.
-			virtualArgs += getArraySize(&arg.Type) - 1
+			virtualArgs += getTypeSize(arg.Type)/32 - 1
+		} else if arg.Type.T == TupleTy && !isDynamicType(arg.Type) {
+			// If we have a static tuple, like (uint256, bool, uint256), these are
+			// coded as just like uint256,bool,uint256
+			virtualArgs += getTypeSize(arg.Type)/32 - 1
 		}
 		if err != nil {
 			return nil, err
@@ -243,7 +290,7 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
 	// input offset is the bytes offset for packed output
 	inputOffset := 0
 	for _, abiArg := range abiArgs {
-		inputOffset += getDynamicTypeOffset(abiArg.Type)
+		inputOffset += getTypeSize(abiArg.Type)
 	}
 	var ret []byte
 	for i, a := range args {
diff --git a/accounts/abi/pack_test.go b/accounts/abi/pack_test.go
index ddd2b7362d5a..10cd3a396274 100644
--- a/accounts/abi/pack_test.go
+++ b/accounts/abi/pack_test.go
@@ -29,303 +29,356 @@ import (
 
 func TestPack(t *testing.T) {
 	for i, test := range []struct {
-		typ string
-
-		input  interface{}
-		output []byte
+		typ        string
+		components []ArgumentMarshaling
+		input      interface{}
+		output     []byte
 	}{
 		{
 			"uint8",
+			nil,
 			uint8(2),
 			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"uint8[]",
+			nil,
 			[]uint8{1, 2},
 			common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"uint16",
+			nil,
 			uint16(2),
 			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"uint16[]",
+			nil,
 			[]uint16{1, 2},
 			common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"uint32",
+			nil,
 			uint32(2),
 			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"uint32[]",
+			nil,
 			[]uint32{1, 2},
 			common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"uint64",
+			nil,
 			uint64(2),
 			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"uint64[]",
+			nil,
 			[]uint64{1, 2},
 			common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"uint256",
+			nil,
 			big.NewInt(2),
 			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"uint256[]",
+			nil,
 			[]*big.Int{big.NewInt(1), big.NewInt(2)},
 			common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"int8",
+			nil,
 			int8(2),
 			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"int8[]",
+			nil,
 			[]int8{1, 2},
 			common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"int16",
+			nil,
 			int16(2),
 			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"int16[]",
+			nil,
 			[]int16{1, 2},
 			common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"int32",
+			nil,
 			int32(2),
 			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"int32[]",
+			nil,
 			[]int32{1, 2},
 			common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"int64",
+			nil,
 			int64(2),
 			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"int64[]",
+			nil,
 			[]int64{1, 2},
 			common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"int256",
+			nil,
 			big.NewInt(2),
 			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"int256[]",
+			nil,
 			[]*big.Int{big.NewInt(1), big.NewInt(2)},
 			common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
 		},
 		{
 			"bytes1",
+			nil,
 			[1]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes2",
+			nil,
 			[2]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes3",
+			nil,
 			[3]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes4",
+			nil,
 			[4]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes5",
+			nil,
 			[5]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes6",
+			nil,
 			[6]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes7",
+			nil,
 			[7]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes8",
+			nil,
 			[8]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes9",
+			nil,
 			[9]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes10",
+			nil,
 			[10]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes11",
+			nil,
 			[11]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes12",
+			nil,
 			[12]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes13",
+			nil,
 			[13]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes14",
+			nil,
 			[14]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes15",
+			nil,
 			[15]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes16",
+			nil,
 			[16]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes17",
+			nil,
 			[17]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes18",
+			nil,
 			[18]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes19",
+			nil,
 			[19]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes20",
+			nil,
 			[20]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes21",
+			nil,
 			[21]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes22",
+			nil,
 			[22]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes23",
+			nil,
 			[23]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes24",
-			[24]byte{1},
-			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
-		},
-		{
-			"bytes24",
+			nil,
 			[24]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes25",
+			nil,
 			[25]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes26",
+			nil,
 			[26]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes27",
+			nil,
 			[27]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes28",
+			nil,
 			[28]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes29",
+			nil,
 			[29]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes30",
+			nil,
 			[30]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes31",
+			nil,
 			[31]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"bytes32",
+			nil,
 			[32]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"uint32[2][3][4]",
+			nil,
 			[4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}},
 			common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018"),
 		},
 		{
 			"address[]",
+			nil,
 			[]common.Address{{1}, {2}},
 			common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000"),
 		},
 		{
 			"bytes32[]",
+			nil,
 			[]common.Hash{{1}, {2}},
 			common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000201000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"function",
+			nil,
 			[24]byte{1},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"string",
+			nil,
 			"foobar",
 			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000006666f6f6261720000000000000000000000000000000000000000000000000000"),
 		},
 		{
 			"string[]",
+			nil,
 			[]string{"hello", "foobar"},
 			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
 				"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
@@ -337,6 +390,7 @@ func TestPack(t *testing.T) {
 		},
 		{
 			"string[2]",
+			nil,
 			[]string{"hello", "foobar"},
 			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset to i = 0
 				"0000000000000000000000000000000000000000000000000000000000000080" + // offset to i = 1
@@ -347,6 +401,7 @@ func TestPack(t *testing.T) {
 		},
 		{
 			"bytes32[][]",
+			nil,
 			[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
 			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
 				"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
@@ -362,6 +417,7 @@ func TestPack(t *testing.T) {
 
 		{
 			"bytes32[][2]",
+			nil,
 			[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
 			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
 				"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
@@ -376,6 +432,7 @@ func TestPack(t *testing.T) {
 
 		{
 			"bytes32[3][2]",
+			nil,
 			[][]common.Hash{{{1}, {2}, {3}}, {{3}, {4}, {5}}},
 			common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
 				"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
@@ -384,12 +441,182 @@ func TestPack(t *testing.T) {
 				"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
 				"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
 		},
+		{
+			// static tuple
+			"tuple",
+			[]ArgumentMarshaling{
+				{Name: "a", Type: "int64"},
+				{Name: "b", Type: "int256"},
+				{Name: "c", Type: "int256"},
+				{Name: "d", Type: "bool"},
+				{Name: "e", Type: "bytes32[3][2]"},
+			},
+			struct {
+				A int64
+				B *big.Int
+				C *big.Int
+				D bool
+				E [][]common.Hash
+			}{1, big.NewInt(1), big.NewInt(-1), true, [][]common.Hash{{{1}, {2}, {3}}, {{3}, {4}, {5}}}},
+			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001" + // struct[a]
+				"0000000000000000000000000000000000000000000000000000000000000001" + // struct[b]
+				"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // struct[c]
+				"0000000000000000000000000000000000000000000000000000000000000001" + // struct[d]
+				"0100000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][0]
+				"0200000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][1]
+				"0300000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][2]
+				"0300000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[1][0]
+				"0400000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[1][1]
+				"0500000000000000000000000000000000000000000000000000000000000000"), // struct[e] array[1][2]
+		},
+		{
+			// dynamic tuple
+			"tuple",
+			[]ArgumentMarshaling{
+				{Name: "a", Type: "string"},
+				{Name: "b", Type: "int64"},
+				{Name: "c", Type: "bytes"},
+				{Name: "d", Type: "string[]"},
+				{Name: "e", Type: "int256[]"},
+				{Name: "f", Type: "address[]"},
+			},
+			struct {
+				FieldA string `abi:"a"` // Test whether abi tag works
+				FieldB int64  `abi:"b"`
+				C      []byte
+				D      []string
+				E      []*big.Int
+				F      []common.Address
+			}{"foobar", 1, []byte{1}, []string{"foo", "bar"}, []*big.Int{big.NewInt(1), big.NewInt(-1)}, []common.Address{{1}, {2}}},
+			common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000c0" + // struct[a] offset
+				"0000000000000000000000000000000000000000000000000000000000000001" + // struct[b]
+				"0000000000000000000000000000000000000000000000000000000000000100" + // struct[c] offset
+				"0000000000000000000000000000000000000000000000000000000000000140" + // struct[d] offset
+				"0000000000000000000000000000000000000000000000000000000000000220" + // struct[e] offset
+				"0000000000000000000000000000000000000000000000000000000000000280" + // struct[f] offset
+				"0000000000000000000000000000000000000000000000000000000000000006" + // struct[a] length
+				"666f6f6261720000000000000000000000000000000000000000000000000000" + // struct[a] "foobar"
+				"0000000000000000000000000000000000000000000000000000000000000001" + // struct[c] length
+				"0100000000000000000000000000000000000000000000000000000000000000" + // []byte{1}
+				"0000000000000000000000000000000000000000000000000000000000000002" + // struct[d] length
+				"0000000000000000000000000000000000000000000000000000000000000040" + // foo offset
+				"0000000000000000000000000000000000000000000000000000000000000080" + // bar offset
+				"0000000000000000000000000000000000000000000000000000000000000003" + // foo length
+				"666f6f0000000000000000000000000000000000000000000000000000000000" + // foo
+				"0000000000000000000000000000000000000000000000000000000000000003" + // bar offset
+				"6261720000000000000000000000000000000000000000000000000000000000" + // bar
+				"0000000000000000000000000000000000000000000000000000000000000002" + // struct[e] length
+				"0000000000000000000000000000000000000000000000000000000000000001" + // 1
+				"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // -1
+				"0000000000000000000000000000000000000000000000000000000000000002" + // struct[f] length
+				"0000000000000000000000000100000000000000000000000000000000000000" + // common.Address{1}
+				"0000000000000000000000000200000000000000000000000000000000000000"), // common.Address{2}
+		},
+		{
+			// nested tuple
+			"tuple",
+			[]ArgumentMarshaling{
+				{Name: "a", Type: "tuple", Components: []ArgumentMarshaling{{Name: "a", Type: "uint256"}, {Name: "b", Type: "uint256[]"}}},
+				{Name: "b", Type: "int256[]"},
+			},
+			struct {
+				A struct {
+					FieldA *big.Int `abi:"a"`
+					B      []*big.Int
+				}
+				B []*big.Int
+			}{
+				A: struct {
+					FieldA *big.Int `abi:"a"` // Test whether abi tag works for nested tuple
+					B      []*big.Int
+				}{big.NewInt(1), []*big.Int{big.NewInt(1), big.NewInt(0)}},
+				B: []*big.Int{big.NewInt(1), big.NewInt(0)}},
+			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // a offset
+				"00000000000000000000000000000000000000000000000000000000000000e0" + // b offset
+				"0000000000000000000000000000000000000000000000000000000000000001" + // a.a value
+				"0000000000000000000000000000000000000000000000000000000000000040" + // a.b offset
+				"0000000000000000000000000000000000000000000000000000000000000002" + // a.b length
+				"0000000000000000000000000000000000000000000000000000000000000001" + // a.b[0] value
+				"0000000000000000000000000000000000000000000000000000000000000000" + // a.b[1] value
+				"0000000000000000000000000000000000000000000000000000000000000002" + // b length
+				"0000000000000000000000000000000000000000000000000000000000000001" + // b[0] value
+				"0000000000000000000000000000000000000000000000000000000000000000"), // b[1] value
+		},
+		{
+			// tuple slice
+			"tuple[]",
+			[]ArgumentMarshaling{
+				{Name: "a", Type: "int256"},
+				{Name: "b", Type: "int256[]"},
+			},
+			[]struct {
+				A *big.Int
+				B []*big.Int
+			}{
+				{big.NewInt(-1), []*big.Int{big.NewInt(1), big.NewInt(0)}},
+				{big.NewInt(1), []*big.Int{big.NewInt(2), big.NewInt(-1)}},
+			},
+			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // tuple length
+				"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0] offset
+				"00000000000000000000000000000000000000000000000000000000000000e0" + // tuple[1] offset
+				"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].A
+				"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0].B offset
+				"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[0].B length
+				"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].B[0] value
+				"0000000000000000000000000000000000000000000000000000000000000000" + // tuple[0].B[1] value
+				"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].A
+				"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[1].B offset
+				"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].B length
+				"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].B[0] value
+				"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].B[1] value
+		},
+		{
+			// static tuple array
+			"tuple[2]",
+			[]ArgumentMarshaling{
+				{Name: "a", Type: "int256"},
+				{Name: "b", Type: "int256"},
+			},
+			[2]struct {
+				A *big.Int
+				B *big.Int
+			}{
+				{big.NewInt(-1), big.NewInt(1)},
+				{big.NewInt(1), big.NewInt(-1)},
+			},
+			common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].a
+				"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].b
+				"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].a
+				"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].b
+		},
+		{
+			// dynamic tuple array
+			"tuple[2]",
+			[]ArgumentMarshaling{
+				{Name: "a", Type: "int256[]"},
+			},
+			[2]struct {
+				A []*big.Int
+			}{
+				{[]*big.Int{big.NewInt(-1), big.NewInt(1)}},
+				{[]*big.Int{big.NewInt(1), big.NewInt(-1)}},
+			},
+			common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0] offset
+				"00000000000000000000000000000000000000000000000000000000000000c0" + // tuple[1] offset
+				"0000000000000000000000000000000000000000000000000000000000000020" + // tuple[0].A offset
+				"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[0].A length
+				"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].A[0]
+				"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].A[1]
+				"0000000000000000000000000000000000000000000000000000000000000020" + // tuple[1].A offset
+				"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].A length
+				"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].A[0]
+				"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].A[1]
+		},
 	} {
-		typ, err := NewType(test.typ)
+		typ, err := NewType(test.typ, test.components)
 		if err != nil {
 			t.Fatalf("%v failed. Unexpected parse error: %v", i, err)
 		}
-
 		output, err := typ.pack(reflect.ValueOf(test.input))
 		if err != nil {
 			t.Fatalf("%v failed. Unexpected pack error: %v", i, err)
@@ -466,6 +693,59 @@ func TestMethodPack(t *testing.T) {
 	if !bytes.Equal(packed, sig) {
 		t.Errorf("expected %x got %x", sig, packed)
 	}
+
+	a := [2][2]*big.Int{{big.NewInt(1), big.NewInt(1)}, {big.NewInt(2), big.NewInt(0)}}
+	sig = abi.Methods["nestedArray"].Id()
+	sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{0}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{0xa0}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
+	sig = append(sig, common.LeftPadBytes(addrC[:], 32)...)
+	sig = append(sig, common.LeftPadBytes(addrD[:], 32)...)
+	packed, err = abi.Pack("nestedArray", a, []common.Address{addrC, addrD})
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !bytes.Equal(packed, sig) {
+		t.Errorf("expected %x got %x", sig, packed)
+	}
+
+	sig = abi.Methods["nestedArray2"].Id()
+	sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{0x80}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
+	packed, err = abi.Pack("nestedArray2", [2][]uint8{{1}, {1}})
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !bytes.Equal(packed, sig) {
+		t.Errorf("expected %x got %x", sig, packed)
+	}
+
+	sig = abi.Methods["nestedSlice"].Id()
+	sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{0x02}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{0xa0}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
+	sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
+	packed, err = abi.Pack("nestedSlice", [][]uint8{{1, 2}, {1, 2}})
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !bytes.Equal(packed, sig) {
+		t.Errorf("expected %x got %x", sig, packed)
+	}
 }
 
 func TestPackNumber(t *testing.T) {
diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go
index f541cf3bfe8f..1b0bb0049625 100644
--- a/accounts/abi/reflect.go
+++ b/accounts/abi/reflect.go
@@ -71,18 +71,17 @@ func mustArrayToByteSlice(value reflect.Value) reflect.Value {
 //
 // set is a bit more lenient when it comes to assignment and doesn't force an as
 // strict ruleset as bare `reflect` does.
-func set(dst, src reflect.Value, output Argument) error {
-	dstType := dst.Type()
-	srcType := src.Type()
+func set(dst, src reflect.Value) error {
+	dstType, srcType := dst.Type(), src.Type()
 	switch {
-	case dstType.AssignableTo(srcType):
-		dst.Set(src)
-	case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice:
-		return setSlice(dst, src, output)
 	case dstType.Kind() == reflect.Interface:
+		return set(dst.Elem(), src)
+	case dstType.Kind() == reflect.Ptr && dstType.Elem() != derefbigT:
+		return set(dst.Elem(), src)
+	case srcType.AssignableTo(dstType) && dst.CanSet():
 		dst.Set(src)
-	case dstType.Kind() == reflect.Ptr:
-		return set(dst.Elem(), src, output)
+	case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice:
+		return setSlice(dst, src)
 	default:
 		return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
 	}
@@ -91,7 +90,7 @@ func set(dst, src reflect.Value, output Argument) error {
 
 // setSlice attempts to assign src to dst when slices are not assignable by default
 // e.g. src: [][]byte -> dst: [][15]byte
-func setSlice(dst, src reflect.Value, output Argument) error {
+func setSlice(dst, src reflect.Value) error {
 	slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len())
 	for i := 0; i < src.Len(); i++ {
 		v := src.Index(i)
@@ -127,14 +126,14 @@ func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind,
 	return nil
 }
 
-// mapAbiToStringField maps abi to struct fields.
+// mapArgNamesToStructFields maps a slice of argument names to struct fields.
 // first round: for each Exportable field that contains a `abi:""` tag
-//   and this field name exists in the arguments, pair them together.
-// second round: for each argument field that has not been already linked,
+//   and this field name exists in the given argument name list, pair them together.
+// second round: for each argument name that has not been already linked,
 //   find what variable is expected to be mapped into, if it exists and has not been
 //   used, pair them.
-func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]string, error) {
-
+// Note this function assumes the given value is a struct value.
+func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[string]string, error) {
 	typ := value.Type()
 
 	abi2struct := make(map[string]string)
@@ -148,45 +147,39 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
 		if structFieldName[:1] != strings.ToUpper(structFieldName[:1]) {
 			continue
 		}
-
 		// skip fields that have no abi:"" tag.
 		var ok bool
 		var tagName string
 		if tagName, ok = typ.Field(i).Tag.Lookup("abi"); !ok {
 			continue
 		}
-
 		// check if tag is empty.
 		if tagName == "" {
 			return nil, fmt.Errorf("struct: abi tag in '%s' is empty", structFieldName)
 		}
-
 		// check which argument field matches with the abi tag.
 		found := false
-		for _, abiField := range args.NonIndexed() {
-			if abiField.Name == tagName {
-				if abi2struct[abiField.Name] != "" {
+		for _, arg := range argNames {
+			if arg == tagName {
+				if abi2struct[arg] != "" {
 					return nil, fmt.Errorf("struct: abi tag in '%s' already mapped", structFieldName)
 				}
 				// pair them
-				abi2struct[abiField.Name] = structFieldName
-				struct2abi[structFieldName] = abiField.Name
+				abi2struct[arg] = structFieldName
+				struct2abi[structFieldName] = arg
 				found = true
 			}
 		}
-
 		// check if this tag has been mapped.
 		if !found {
 			return nil, fmt.Errorf("struct: abi tag '%s' defined but not found in abi", tagName)
 		}
-
 	}
 
 	// second round ~~~
-	for _, arg := range args {
+	for _, argName := range argNames {
 
-		abiFieldName := arg.Name
-		structFieldName := ToCamelCase(abiFieldName)
+		structFieldName := ToCamelCase(argName)
 
 		if structFieldName == "" {
 			return nil, fmt.Errorf("abi: purely underscored output cannot unpack to struct")
@@ -196,11 +189,11 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
 		// struct field with the same field name. If so, raise an error:
 		//    abi: [ { "name": "value" } ]
 		//    struct { Value  *big.Int , Value1 *big.Int `abi:"value"`}
-		if abi2struct[abiFieldName] != "" {
-			if abi2struct[abiFieldName] != structFieldName &&
+		if abi2struct[argName] != "" {
+			if abi2struct[argName] != structFieldName &&
 				struct2abi[structFieldName] == "" &&
 				value.FieldByName(structFieldName).IsValid() {
-				return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", abiFieldName)
+				return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", argName)
 			}
 			continue
 		}
@@ -212,16 +205,14 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
 
 		if value.FieldByName(structFieldName).IsValid() {
 			// pair them
-			abi2struct[abiFieldName] = structFieldName
-			struct2abi[structFieldName] = abiFieldName
+			abi2struct[argName] = structFieldName
+			struct2abi[structFieldName] = argName
 		} else {
 			// not paired, but annotate as used, to detect cases like
 			//   abi : [ { "name": "value" }, { "name": "_value" } ]
 			//   struct { Value *big.Int }
-			struct2abi[structFieldName] = abiFieldName
+			struct2abi[structFieldName] = argName
 		}
-
 	}
-
 	return abi2struct, nil
 }
diff --git a/accounts/abi/type.go b/accounts/abi/type.go
index 6bfaabf5a5b1..26151dbd3e7e 100644
--- a/accounts/abi/type.go
+++ b/accounts/abi/type.go
@@ -17,6 +17,7 @@
 package abi
 
 import (
+	"errors"
 	"fmt"
 	"reflect"
 	"regexp"
@@ -32,6 +33,7 @@ const (
 	StringTy
 	SliceTy
 	ArrayTy
+	TupleTy
 	AddressTy
 	FixedBytesTy
 	BytesTy
@@ -43,13 +45,16 @@ const (
 // Type is the reflection of the supported argument type
 type Type struct {
 	Elem *Type
-
 	Kind reflect.Kind
 	Type reflect.Type
 	Size int
 	T    byte // Our own type checking
 
 	stringKind string // holds the unparsed string for deriving signatures
+
+	// Tuple relative fields
+	TupleElems    []*Type  // Type information of all tuple fields
+	TupleRawNames []string // Raw field name of all tuple fields
 }
 
 var (
@@ -58,7 +63,7 @@ var (
 )
 
 // NewType creates a new reflection type of abi type given in t.
-func NewType(t string) (typ Type, err error) {
+func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) {
 	// check that array brackets are equal if they exist
 	if strings.Count(t, "[") != strings.Count(t, "]") {
 		return Type{}, fmt.Errorf("invalid arg type in abi")
@@ -71,7 +76,7 @@ func NewType(t string) (typ Type, err error) {
 	if strings.Count(t, "[") != 0 {
 		i := strings.LastIndex(t, "[")
 		// recursively embed the type
-		embeddedType, err := NewType(t[:i])
+		embeddedType, err := NewType(t[:i], components)
 		if err != nil {
 			return Type{}, err
 		}
@@ -87,6 +92,9 @@ func NewType(t string) (typ Type, err error) {
 			typ.Kind = reflect.Slice
 			typ.Elem = &embeddedType
 			typ.Type = reflect.SliceOf(embeddedType.Type)
+			if embeddedType.T == TupleTy {
+				typ.stringKind = embeddedType.stringKind + sliced
+			}
 		} else if len(intz) == 1 {
 			// is a array
 			typ.T = ArrayTy
@@ -97,6 +105,9 @@ func NewType(t string) (typ Type, err error) {
 				return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
 			}
 			typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type)
+			if embeddedType.T == TupleTy {
+				typ.stringKind = embeddedType.stringKind + sliced
+			}
 		} else {
 			return Type{}, fmt.Errorf("invalid formatting of array type")
 		}
@@ -158,6 +169,40 @@ func NewType(t string) (typ Type, err error) {
 			typ.Size = varSize
 			typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0)))
 		}
+	case "tuple":
+		var (
+			fields     []reflect.StructField
+			elems      []*Type
+			names      []string
+			expression string // canonical parameter expression
+		)
+		expression += "("
+		for idx, c := range components {
+			cType, err := NewType(c.Type, c.Components)
+			if err != nil {
+				return Type{}, err
+			}
+			if ToCamelCase(c.Name) == "" {
+				return Type{}, errors.New("abi: purely anonymous or underscored field is not supported")
+			}
+			fields = append(fields, reflect.StructField{
+				Name: ToCamelCase(c.Name), // reflect.StructOf will panic for any exported field.
+				Type: cType.Type,
+			})
+			elems = append(elems, &cType)
+			names = append(names, c.Name)
+			expression += cType.stringKind
+			if idx != len(components)-1 {
+				expression += ","
+			}
+		}
+		expression += ")"
+		typ.Kind = reflect.Struct
+		typ.Type = reflect.StructOf(fields)
+		typ.TupleElems = elems
+		typ.TupleRawNames = names
+		typ.T = TupleTy
+		typ.stringKind = expression
 	case "function":
 		typ.Kind = reflect.Array
 		typ.T = FunctionTy
@@ -178,7 +223,6 @@ func (t Type) String() (out string) {
 func (t Type) pack(v reflect.Value) ([]byte, error) {
 	// dereference pointer first if it's a pointer
 	v = indirect(v)
-
 	if err := typeCheck(t, v); err != nil {
 		return nil, err
 	}
@@ -196,7 +240,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
 		offset := 0
 		offsetReq := isDynamicType(*t.Elem)
 		if offsetReq {
-			offset = getDynamicTypeOffset(*t.Elem) * v.Len()
+			offset = getTypeSize(*t.Elem) * v.Len()
 		}
 		var tail []byte
 		for i := 0; i < v.Len(); i++ {
@@ -213,6 +257,45 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
 			tail = append(tail, val...)
 		}
 		return append(ret, tail...), nil
+	case TupleTy:
+		// (T1,...,Tk) for k >= 0 and any types T1, …, Tk
+		// enc(X) = head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(k))
+		// where X = (X(1), ..., X(k)) and head and tail are defined for Ti being a static
+		// type as
+		//     head(X(i)) = enc(X(i)) and tail(X(i)) = "" (the empty string)
+		// and as
+		//     head(X(i)) = enc(len(head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(i-1))))
+		//     tail(X(i)) = enc(X(i))
+		// otherwise, i.e. if Ti is a dynamic type.
+		fieldmap, err := mapArgNamesToStructFields(t.TupleRawNames, v)
+		if err != nil {
+			return nil, err
+		}
+		// Calculate prefix occupied size.
+		offset := 0
+		for _, elem := range t.TupleElems {
+			offset += getTypeSize(*elem)
+		}
+		var ret, tail []byte
+		for i, elem := range t.TupleElems {
+			field := v.FieldByName(fieldmap[t.TupleRawNames[i]])
+			if !field.IsValid() {
+				return nil, fmt.Errorf("field %s for tuple not found in the given struct", t.TupleRawNames[i])
+			}
+			val, err := elem.pack(field)
+			if err != nil {
+				return nil, err
+			}
+			if isDynamicType(*elem) {
+				ret = append(ret, packNum(reflect.ValueOf(offset))...)
+				tail = append(tail, val...)
+				offset += len(val)
+			} else {
+				ret = append(ret, val...)
+			}
+		}
+		return append(ret, tail...), nil
+
 	default:
 		return packElement(t, v), nil
 	}
@@ -225,25 +308,45 @@ func (t Type) requiresLengthPrefix() bool {
 }
 
 // isDynamicType returns true if the type is dynamic.
-// StringTy, BytesTy, and SliceTy(irrespective of slice element type) are dynamic types
-// ArrayTy is considered dynamic if and only if the Array element is a dynamic type.
-// This function recursively checks the type for slice and array elements.
+// The following types are called “dynamic”:
+// * bytes
+// * string
+// * T[] for any T
+// * T[k] for any dynamic T and any k >= 0
+// * (T1,...,Tk) if Ti is dynamic for some 1 <= i <= k
 func isDynamicType(t Type) bool {
-	// dynamic types
-	// array is also a dynamic type if the array type is dynamic
+	if t.T == TupleTy {
+		for _, elem := range t.TupleElems {
+			if isDynamicType(*elem) {
+				return true
+			}
+		}
+		return false
+	}
 	return t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && isDynamicType(*t.Elem))
 }
 
-// getDynamicTypeOffset returns the offset for the type.
-// See `isDynamicType` to know which types are considered dynamic.
-// If the type t is an array and element type is not a dynamic type, then we consider it a static type and
-// return 32 * size of array since length prefix is not required.
-// If t is a dynamic type or element type(for slices and arrays) is dynamic, then we simply return 32 as offset.
-func getDynamicTypeOffset(t Type) int {
-	// if it is an array and there are no dynamic types
-	// then the array is static type
+// getTypeSize returns the size that this type needs to occupy.
+// We distinguish static and dynamic types. Static types are encoded in-place
+// and dynamic types are encoded at a separately allocated location after the
+// current block.
+// So for a static variable, the size returned represents the size that the
+// variable actually occupies.
+// For a dynamic variable, the returned size is fixed 32 bytes, which is used
+// to store the location reference for actual value storage.
+func getTypeSize(t Type) int {
 	if t.T == ArrayTy && !isDynamicType(*t.Elem) {
-		return 32 * t.Size
+		// Recursively calculate type size if it is a nested array
+		if t.Elem.T == ArrayTy {
+			return t.Size * getTypeSize(*t.Elem)
+		}
+		return t.Size * 32
+	} else if t.T == TupleTy && !isDynamicType(t) {
+		total := 0
+		for _, elem := range t.TupleElems {
+			total += getTypeSize(*elem)
+		}
+		return total
 	}
 	return 32
 }
diff --git a/accounts/abi/type_test.go b/accounts/abi/type_test.go
index f6b36f18fd54..7ef47330dc52 100644
--- a/accounts/abi/type_test.go
+++ b/accounts/abi/type_test.go
@@ -32,72 +32,75 @@ type typeWithoutStringer Type
 // Tests that all allowed types get recognized by the type parser.
 func TestTypeRegexp(t *testing.T) {
 	tests := []struct {
-		blob string
-		kind Type
+		blob       string
+		components []ArgumentMarshaling
+		kind       Type
 	}{
-		{"bool", Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}},
-		{"bool[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool(nil)), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}},
-		{"bool[2]", Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}},
-		{"bool[2][]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}},
-		{"bool[][]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}},
-		{"bool[][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}},
-		{"bool[2][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}},
-		{"bool[2][][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][][2]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}, stringKind: "bool[2][][2]"}},
-		{"bool[2][2][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}, stringKind: "bool[2][2][2]"}},
-		{"bool[][][]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}, stringKind: "bool[][][]"}},
-		{"bool[][2][]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][2][]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}, stringKind: "bool[][2][]"}},
-		{"int8", Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}},
-		{"int16", Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}},
-		{"int32", Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}},
-		{"int64", Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}},
-		{"int256", Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}},
-		{"int8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}},
-		{"int8[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}},
-		{"int16[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}},
-		{"int16[2]", Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}},
-		{"int32[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
-		{"int32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
-		{"int64[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}},
-		{"int64[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}},
-		{"int256[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
-		{"int256[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
-		{"uint8", Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}},
-		{"uint16", Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}},
-		{"uint32", Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}},
-		{"uint64", Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}},
-		{"uint256", Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}},
-		{"uint8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}},
-		{"uint8[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}},
-		{"uint16[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}},
-		{"uint16[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}},
-		{"uint32[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
-		{"uint32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
-		{"uint64[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}},
-		{"uint64[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}},
-		{"uint256[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
-		{"uint256[2]", Type{Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]*big.Int{}), Size: 2, Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
-		{"bytes32", Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}},
-		{"bytes[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]byte{}), Elem: &Type{Kind: reflect.Slice, Type: reflect.TypeOf([]byte{}), T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
-		{"bytes[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]byte{}), Elem: &Type{T: BytesTy, Type: reflect.TypeOf([]byte{}), Kind: reflect.Slice, stringKind: "bytes"}, stringKind: "bytes[2]"}},
-		{"bytes32[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][32]byte{}), Elem: &Type{Kind: reflect.Array, Type: reflect.TypeOf([32]byte{}), T: FixedBytesTy, Size: 32, stringKind: "bytes32"}, stringKind: "bytes32[]"}},
-		{"bytes32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][32]byte{}), Elem: &Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}, stringKind: "bytes32[2]"}},
-		{"string", Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}},
-		{"string[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]string{}), Elem: &Type{Kind: reflect.String, Type: reflect.TypeOf(""), T: StringTy, stringKind: "string"}, stringKind: "string[]"}},
-		{"string[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]string{}), Elem: &Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}, stringKind: "string[2]"}},
-		{"address", Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}},
-		{"address[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
-		{"address[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
+		{"bool", nil, Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}},
+		{"bool[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool(nil)), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}},
+		{"bool[2]", nil, Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}},
+		{"bool[2][]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}},
+		{"bool[][]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}},
+		{"bool[][2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}},
+		{"bool[2][2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}},
+		{"bool[2][][2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][][2]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}, stringKind: "bool[2][][2]"}},
+		{"bool[2][2][2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}, stringKind: "bool[2][2][2]"}},
+		{"bool[][][]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}, stringKind: "bool[][][]"}},
+		{"bool[][2][]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][2][]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}, stringKind: "bool[][2][]"}},
+		{"int8", nil, Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}},
+		{"int16", nil, Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}},
+		{"int32", nil, Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}},
+		{"int64", nil, Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}},
+		{"int256", nil, Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}},
+		{"int8[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}},
+		{"int8[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}},
+		{"int16[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}},
+		{"int16[2]", nil, Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}},
+		{"int32[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
+		{"int32[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
+		{"int64[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}},
+		{"int64[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}},
+		{"int256[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
+		{"int256[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
+		{"uint8", nil, Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}},
+		{"uint16", nil, Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}},
+		{"uint32", nil, Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}},
+		{"uint64", nil, Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}},
+		{"uint256", nil, Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}},
+		{"uint8[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}},
+		{"uint8[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}},
+		{"uint16[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}},
+		{"uint16[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}},
+		{"uint32[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
+		{"uint32[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
+		{"uint64[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}},
+		{"uint64[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}},
+		{"uint256[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
+		{"uint256[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]*big.Int{}), Size: 2, Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
+		{"bytes32", nil, Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}},
+		{"bytes[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]byte{}), Elem: &Type{Kind: reflect.Slice, Type: reflect.TypeOf([]byte{}), T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
+		{"bytes[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]byte{}), Elem: &Type{T: BytesTy, Type: reflect.TypeOf([]byte{}), Kind: reflect.Slice, stringKind: "bytes"}, stringKind: "bytes[2]"}},
+		{"bytes32[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][32]byte{}), Elem: &Type{Kind: reflect.Array, Type: reflect.TypeOf([32]byte{}), T: FixedBytesTy, Size: 32, stringKind: "bytes32"}, stringKind: "bytes32[]"}},
+		{"bytes32[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][32]byte{}), Elem: &Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}, stringKind: "bytes32[2]"}},
+		{"string", nil, Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}},
+		{"string[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]string{}), Elem: &Type{Kind: reflect.String, Type: reflect.TypeOf(""), T: StringTy, stringKind: "string"}, stringKind: "string[]"}},
+		{"string[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]string{}), Elem: &Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}, stringKind: "string[2]"}},
+		{"address", nil, Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}},
+		{"address[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
+		{"address[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
 		// TODO when fixed types are implemented properly
-		// {"fixed", Type{}},
-		// {"fixed128x128", Type{}},
-		// {"fixed[]", Type{}},
-		// {"fixed[2]", Type{}},
-		// {"fixed128x128[]", Type{}},
-		// {"fixed128x128[2]", Type{}},
+		// {"fixed", nil, Type{}},
+		// {"fixed128x128", nil, Type{}},
+		// {"fixed[]", nil, Type{}},
+		// {"fixed[2]", nil, Type{}},
+		// {"fixed128x128[]", nil, Type{}},
+		// {"fixed128x128[2]", nil, Type{}},
+		{"tuple", []ArgumentMarshaling{{Name: "a", Type: "int64"}}, Type{Kind: reflect.Struct, T: TupleTy, Type: reflect.TypeOf(struct{ A int64 }{}), stringKind: "(int64)",
+			TupleElems: []*Type{{Kind: reflect.Int64, T: IntTy, Type: reflect.TypeOf(int64(0)), Size: 64, stringKind: "int64"}}, TupleRawNames: []string{"a"}}},
 	}
 
 	for _, tt := range tests {
-		typ, err := NewType(tt.blob)
+		typ, err := NewType(tt.blob, tt.components)
 		if err != nil {
 			t.Errorf("type %q: failed to parse type string: %v", tt.blob, err)
 		}
@@ -109,154 +112,170 @@ func TestTypeRegexp(t *testing.T) {
 
 func TestTypeCheck(t *testing.T) {
 	for i, test := range []struct {
-		typ   string
-		input interface{}
-		err   string
+		typ        string
+		components []ArgumentMarshaling
+		input      interface{}
+		err        string
 	}{
-		{"uint", big.NewInt(1), "unsupported arg type: uint"},
-		{"int", big.NewInt(1), "unsupported arg type: int"},
-		{"uint256", big.NewInt(1), ""},
-		{"uint256[][3][]", [][3][]*big.Int{{{}}}, ""},
-		{"uint256[][][3]", [3][][]*big.Int{{{}}}, ""},
-		{"uint256[3][][]", [][][3]*big.Int{{{}}}, ""},
-		{"uint256[3][3][3]", [3][3][3]*big.Int{{{}}}, ""},
-		{"uint8[][]", [][]uint8{}, ""},
-		{"int256", big.NewInt(1), ""},
-		{"uint8", uint8(1), ""},
-		{"uint16", uint16(1), ""},
-		{"uint32", uint32(1), ""},
-		{"uint64", uint64(1), ""},
-		{"int8", int8(1), ""},
-		{"int16", int16(1), ""},
-		{"int32", int32(1), ""},
-		{"int64", int64(1), ""},
-		{"uint24", big.NewInt(1), ""},
-		{"uint40", big.NewInt(1), ""},
-		{"uint48", big.NewInt(1), ""},
-		{"uint56", big.NewInt(1), ""},
-		{"uint72", big.NewInt(1), ""},
-		{"uint80", big.NewInt(1), ""},
-		{"uint88", big.NewInt(1), ""},
-		{"uint96", big.NewInt(1), ""},
-		{"uint104", big.NewInt(1), ""},
-		{"uint112", big.NewInt(1), ""},
-		{"uint120", big.NewInt(1), ""},
-		{"uint128", big.NewInt(1), ""},
-		{"uint136", big.NewInt(1), ""},
-		{"uint144", big.NewInt(1), ""},
-		{"uint152", big.NewInt(1), ""},
-		{"uint160", big.NewInt(1), ""},
-		{"uint168", big.NewInt(1), ""},
-		{"uint176", big.NewInt(1), ""},
-		{"uint184", big.NewInt(1), ""},
-		{"uint192", big.NewInt(1), ""},
-		{"uint200", big.NewInt(1), ""},
-		{"uint208", big.NewInt(1), ""},
-		{"uint216", big.NewInt(1), ""},
-		{"uint224", big.NewInt(1), ""},
-		{"uint232", big.NewInt(1), ""},
-		{"uint240", big.NewInt(1), ""},
-		{"uint248", big.NewInt(1), ""},
-		{"int24", big.NewInt(1), ""},
-		{"int40", big.NewInt(1), ""},
-		{"int48", big.NewInt(1), ""},
-		{"int56", big.NewInt(1), ""},
-		{"int72", big.NewInt(1), ""},
-		{"int80", big.NewInt(1), ""},
-		{"int88", big.NewInt(1), ""},
-		{"int96", big.NewInt(1), ""},
-		{"int104", big.NewInt(1), ""},
-		{"int112", big.NewInt(1), ""},
-		{"int120", big.NewInt(1), ""},
-		{"int128", big.NewInt(1), ""},
-		{"int136", big.NewInt(1), ""},
-		{"int144", big.NewInt(1), ""},
-		{"int152", big.NewInt(1), ""},
-		{"int160", big.NewInt(1), ""},
-		{"int168", big.NewInt(1), ""},
-		{"int176", big.NewInt(1), ""},
-		{"int184", big.NewInt(1), ""},
-		{"int192", big.NewInt(1), ""},
-		{"int200", big.NewInt(1), ""},
-		{"int208", big.NewInt(1), ""},
-		{"int216", big.NewInt(1), ""},
-		{"int224", big.NewInt(1), ""},
-		{"int232", big.NewInt(1), ""},
-		{"int240", big.NewInt(1), ""},
-		{"int248", big.NewInt(1), ""},
-		{"uint30", uint8(1), "abi: cannot use uint8 as type ptr as argument"},
-		{"uint8", uint16(1), "abi: cannot use uint16 as type uint8 as argument"},
-		{"uint8", uint32(1), "abi: cannot use uint32 as type uint8 as argument"},
-		{"uint8", uint64(1), "abi: cannot use uint64 as type uint8 as argument"},
-		{"uint8", int8(1), "abi: cannot use int8 as type uint8 as argument"},
-		{"uint8", int16(1), "abi: cannot use int16 as type uint8 as argument"},
-		{"uint8", int32(1), "abi: cannot use int32 as type uint8 as argument"},
-		{"uint8", int64(1), "abi: cannot use int64 as type uint8 as argument"},
-		{"uint16", uint16(1), ""},
-		{"uint16", uint8(1), "abi: cannot use uint8 as type uint16 as argument"},
-		{"uint16[]", []uint16{1, 2, 3}, ""},
-		{"uint16[]", [3]uint16{1, 2, 3}, ""},
-		{"uint16[]", []uint32{1, 2, 3}, "abi: cannot use []uint32 as type [0]uint16 as argument"},
-		{"uint16[3]", [3]uint32{1, 2, 3}, "abi: cannot use [3]uint32 as type [3]uint16 as argument"},
-		{"uint16[3]", [4]uint16{1, 2, 3}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
-		{"uint16[3]", []uint16{1, 2, 3}, ""},
-		{"uint16[3]", []uint16{1, 2, 3, 4}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
-		{"address[]", []common.Address{{1}}, ""},
-		{"address[1]", []common.Address{{1}}, ""},
-		{"address[1]", [1]common.Address{{1}}, ""},
-		{"address[2]", [1]common.Address{{1}}, "abi: cannot use [1]array as type [2]array as argument"},
-		{"bytes32", [32]byte{}, ""},
-		{"bytes31", [31]byte{}, ""},
-		{"bytes30", [30]byte{}, ""},
-		{"bytes29", [29]byte{}, ""},
-		{"bytes28", [28]byte{}, ""},
-		{"bytes27", [27]byte{}, ""},
-		{"bytes26", [26]byte{}, ""},
-		{"bytes25", [25]byte{}, ""},
-		{"bytes24", [24]byte{}, ""},
-		{"bytes23", [23]byte{}, ""},
-		{"bytes22", [22]byte{}, ""},
-		{"bytes21", [21]byte{}, ""},
-		{"bytes20", [20]byte{}, ""},
-		{"bytes19", [19]byte{}, ""},
-		{"bytes18", [18]byte{}, ""},
-		{"bytes17", [17]byte{}, ""},
-		{"bytes16", [16]byte{}, ""},
-		{"bytes15", [15]byte{}, ""},
-		{"bytes14", [14]byte{}, ""},
-		{"bytes13", [13]byte{}, ""},
-		{"bytes12", [12]byte{}, ""},
-		{"bytes11", [11]byte{}, ""},
-		{"bytes10", [10]byte{}, ""},
-		{"bytes9", [9]byte{}, ""},
-		{"bytes8", [8]byte{}, ""},
-		{"bytes7", [7]byte{}, ""},
-		{"bytes6", [6]byte{}, ""},
-		{"bytes5", [5]byte{}, ""},
-		{"bytes4", [4]byte{}, ""},
-		{"bytes3", [3]byte{}, ""},
-		{"bytes2", [2]byte{}, ""},
-		{"bytes1", [1]byte{}, ""},
-		{"bytes32", [33]byte{}, "abi: cannot use [33]uint8 as type [32]uint8 as argument"},
-		{"bytes32", common.Hash{1}, ""},
-		{"bytes31", common.Hash{1}, "abi: cannot use common.Hash as type [31]uint8 as argument"},
-		{"bytes31", [32]byte{}, "abi: cannot use [32]uint8 as type [31]uint8 as argument"},
-		{"bytes", []byte{0, 1}, ""},
-		{"bytes", [2]byte{0, 1}, "abi: cannot use array as type slice as argument"},
-		{"bytes", common.Hash{1}, "abi: cannot use array as type slice as argument"},
-		{"string", "hello world", ""},
-		{"string", string(""), ""},
-		{"string", []byte{}, "abi: cannot use slice as type string as argument"},
-		{"bytes32[]", [][32]byte{{}}, ""},
-		{"function", [24]byte{}, ""},
-		{"bytes20", common.Address{}, ""},
-		{"address", [20]byte{}, ""},
-		{"address", common.Address{}, ""},
-		{"bytes32[]]", "", "invalid arg type in abi"},
-		{"invalidType", "", "unsupported arg type: invalidType"},
-		{"invalidSlice[]", "", "unsupported arg type: invalidSlice"},
+		{"uint", nil, big.NewInt(1), "unsupported arg type: uint"},
+		{"int", nil, big.NewInt(1), "unsupported arg type: int"},
+		{"uint256", nil, big.NewInt(1), ""},
+		{"uint256[][3][]", nil, [][3][]*big.Int{{{}}}, ""},
+		{"uint256[][][3]", nil, [3][][]*big.Int{{{}}}, ""},
+		{"uint256[3][][]", nil, [][][3]*big.Int{{{}}}, ""},
+		{"uint256[3][3][3]", nil, [3][3][3]*big.Int{{{}}}, ""},
+		{"uint8[][]", nil, [][]uint8{}, ""},
+		{"int256", nil, big.NewInt(1), ""},
+		{"uint8", nil, uint8(1), ""},
+		{"uint16", nil, uint16(1), ""},
+		{"uint32", nil, uint32(1), ""},
+		{"uint64", nil, uint64(1), ""},
+		{"int8", nil, int8(1), ""},
+		{"int16", nil, int16(1), ""},
+		{"int32", nil, int32(1), ""},
+		{"int64", nil, int64(1), ""},
+		{"uint24", nil, big.NewInt(1), ""},
+		{"uint40", nil, big.NewInt(1), ""},
+		{"uint48", nil, big.NewInt(1), ""},
+		{"uint56", nil, big.NewInt(1), ""},
+		{"uint72", nil, big.NewInt(1), ""},
+		{"uint80", nil, big.NewInt(1), ""},
+		{"uint88", nil, big.NewInt(1), ""},
+		{"uint96", nil, big.NewInt(1), ""},
+		{"uint104", nil, big.NewInt(1), ""},
+		{"uint112", nil, big.NewInt(1), ""},
+		{"uint120", nil, big.NewInt(1), ""},
+		{"uint128", nil, big.NewInt(1), ""},
+		{"uint136", nil, big.NewInt(1), ""},
+		{"uint144", nil, big.NewInt(1), ""},
+		{"uint152", nil, big.NewInt(1), ""},
+		{"uint160", nil, big.NewInt(1), ""},
+		{"uint168", nil, big.NewInt(1), ""},
+		{"uint176", nil, big.NewInt(1), ""},
+		{"uint184", nil, big.NewInt(1), ""},
+		{"uint192", nil, big.NewInt(1), ""},
+		{"uint200", nil, big.NewInt(1), ""},
+		{"uint208", nil, big.NewInt(1), ""},
+		{"uint216", nil, big.NewInt(1), ""},
+		{"uint224", nil, big.NewInt(1), ""},
+		{"uint232", nil, big.NewInt(1), ""},
+		{"uint240", nil, big.NewInt(1), ""},
+		{"uint248", nil, big.NewInt(1), ""},
+		{"int24", nil, big.NewInt(1), ""},
+		{"int40", nil, big.NewInt(1), ""},
+		{"int48", nil, big.NewInt(1), ""},
+		{"int56", nil, big.NewInt(1), ""},
+		{"int72", nil, big.NewInt(1), ""},
+		{"int80", nil, big.NewInt(1), ""},
+		{"int88", nil, big.NewInt(1), ""},
+		{"int96", nil, big.NewInt(1), ""},
+		{"int104", nil, big.NewInt(1), ""},
+		{"int112", nil, big.NewInt(1), ""},
+		{"int120", nil, big.NewInt(1), ""},
+		{"int128", nil, big.NewInt(1), ""},
+		{"int136", nil, big.NewInt(1), ""},
+		{"int144", nil, big.NewInt(1), ""},
+		{"int152", nil, big.NewInt(1), ""},
+		{"int160", nil, big.NewInt(1), ""},
+		{"int168", nil, big.NewInt(1), ""},
+		{"int176", nil, big.NewInt(1), ""},
+		{"int184", nil, big.NewInt(1), ""},
+		{"int192", nil, big.NewInt(1), ""},
+		{"int200", nil, big.NewInt(1), ""},
+		{"int208", nil, big.NewInt(1), ""},
+		{"int216", nil, big.NewInt(1), ""},
+		{"int224", nil, big.NewInt(1), ""},
+		{"int232", nil, big.NewInt(1), ""},
+		{"int240", nil, big.NewInt(1), ""},
+		{"int248", nil, big.NewInt(1), ""},
+		{"uint30", nil, uint8(1), "abi: cannot use uint8 as type ptr as argument"},
+		{"uint8", nil, uint16(1), "abi: cannot use uint16 as type uint8 as argument"},
+		{"uint8", nil, uint32(1), "abi: cannot use uint32 as type uint8 as argument"},
+		{"uint8", nil, uint64(1), "abi: cannot use uint64 as type uint8 as argument"},
+		{"uint8", nil, int8(1), "abi: cannot use int8 as type uint8 as argument"},
+		{"uint8", nil, int16(1), "abi: cannot use int16 as type uint8 as argument"},
+		{"uint8", nil, int32(1), "abi: cannot use int32 as type uint8 as argument"},
+		{"uint8", nil, int64(1), "abi: cannot use int64 as type uint8 as argument"},
+		{"uint16", nil, uint16(1), ""},
+		{"uint16", nil, uint8(1), "abi: cannot use uint8 as type uint16 as argument"},
+		{"uint16[]", nil, []uint16{1, 2, 3}, ""},
+		{"uint16[]", nil, [3]uint16{1, 2, 3}, ""},
+		{"uint16[]", nil, []uint32{1, 2, 3}, "abi: cannot use []uint32 as type [0]uint16 as argument"},
+		{"uint16[3]", nil, [3]uint32{1, 2, 3}, "abi: cannot use [3]uint32 as type [3]uint16 as argument"},
+		{"uint16[3]", nil, [4]uint16{1, 2, 3}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
+		{"uint16[3]", nil, []uint16{1, 2, 3}, ""},
+		{"uint16[3]", nil, []uint16{1, 2, 3, 4}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
+		{"address[]", nil, []common.Address{{1}}, ""},
+		{"address[1]", nil, []common.Address{{1}}, ""},
+		{"address[1]", nil, [1]common.Address{{1}}, ""},
+		{"address[2]", nil, [1]common.Address{{1}}, "abi: cannot use [1]array as type [2]array as argument"},
+		{"bytes32", nil, [32]byte{}, ""},
+		{"bytes31", nil, [31]byte{}, ""},
+		{"bytes30", nil, [30]byte{}, ""},
+		{"bytes29", nil, [29]byte{}, ""},
+		{"bytes28", nil, [28]byte{}, ""},
+		{"bytes27", nil, [27]byte{}, ""},
+		{"bytes26", nil, [26]byte{}, ""},
+		{"bytes25", nil, [25]byte{}, ""},
+		{"bytes24", nil, [24]byte{}, ""},
+		{"bytes23", nil, [23]byte{}, ""},
+		{"bytes22", nil, [22]byte{}, ""},
+		{"bytes21", nil, [21]byte{}, ""},
+		{"bytes20", nil, [20]byte{}, ""},
+		{"bytes19", nil, [19]byte{}, ""},
+		{"bytes18", nil, [18]byte{}, ""},
+		{"bytes17", nil, [17]byte{}, ""},
+		{"bytes16", nil, [16]byte{}, ""},
+		{"bytes15", nil, [15]byte{}, ""},
+		{"bytes14", nil, [14]byte{}, ""},
+		{"bytes13", nil, [13]byte{}, ""},
+		{"bytes12", nil, [12]byte{}, ""},
+		{"bytes11", nil, [11]byte{}, ""},
+		{"bytes10", nil, [10]byte{}, ""},
+		{"bytes9", nil, [9]byte{}, ""},
+		{"bytes8", nil, [8]byte{}, ""},
+		{"bytes7", nil, [7]byte{}, ""},
+		{"bytes6", nil, [6]byte{}, ""},
+		{"bytes5", nil, [5]byte{}, ""},
+		{"bytes4", nil, [4]byte{}, ""},
+		{"bytes3", nil, [3]byte{}, ""},
+		{"bytes2", nil, [2]byte{}, ""},
+		{"bytes1", nil, [1]byte{}, ""},
+		{"bytes32", nil, [33]byte{}, "abi: cannot use [33]uint8 as type [32]uint8 as argument"},
+		{"bytes32", nil, common.Hash{1}, ""},
+		{"bytes31", nil, common.Hash{1}, "abi: cannot use common.Hash as type [31]uint8 as argument"},
+		{"bytes31", nil, [32]byte{}, "abi: cannot use [32]uint8 as type [31]uint8 as argument"},
+		{"bytes", nil, []byte{0, 1}, ""},
+		{"bytes", nil, [2]byte{0, 1}, "abi: cannot use array as type slice as argument"},
+		{"bytes", nil, common.Hash{1}, "abi: cannot use array as type slice as argument"},
+		{"string", nil, "hello world", ""},
+		{"string", nil, string(""), ""},
+		{"string", nil, []byte{}, "abi: cannot use slice as type string as argument"},
+		{"bytes32[]", nil, [][32]byte{{}}, ""},
+		{"function", nil, [24]byte{}, ""},
+		{"bytes20", nil, common.Address{}, ""},
+		{"address", nil, [20]byte{}, ""},
+		{"address", nil, common.Address{}, ""},
+		{"bytes32[]]", nil, "", "invalid arg type in abi"},
+		{"invalidType", nil, "", "unsupported arg type: invalidType"},
+		{"invalidSlice[]", nil, "", "unsupported arg type: invalidSlice"},
+		// simple tuple
+		{"tuple", []ArgumentMarshaling{{Name: "a", Type: "uint256"}, {Name: "b", Type: "uint256"}}, struct {
+			A *big.Int
+			B *big.Int
+		}{}, ""},
+		// tuple slice
+		{"tuple[]", []ArgumentMarshaling{{Name: "a", Type: "uint256"}, {Name: "b", Type: "uint256"}}, []struct {
+			A *big.Int
+			B *big.Int
+		}{}, ""},
+		// tuple array
+		{"tuple[2]", []ArgumentMarshaling{{Name: "a", Type: "uint256"}, {Name: "b", Type: "uint256"}}, []struct {
+			A *big.Int
+			B *big.Int
+		}{{big.NewInt(0), big.NewInt(0)}, {big.NewInt(0), big.NewInt(0)}}, ""},
 	} {
-		typ, err := NewType(test.typ)
+		typ, err := NewType(test.typ, test.components)
 		if err != nil && len(test.err) == 0 {
 			t.Fatal("unexpected parse error:", err)
 		} else if err != nil && len(test.err) != 0 {
diff --git a/accounts/abi/unpack.go b/accounts/abi/unpack.go
index 04716f7a2a1a..8406b09c801d 100644
--- a/accounts/abi/unpack.go
+++ b/accounts/abi/unpack.go
@@ -115,17 +115,6 @@ func readFixedBytes(t Type, word []byte) (interface{}, error) {
 
 }
 
-func getFullElemSize(elem *Type) int {
-	//all other should be counted as 32 (slices have pointers to respective elements)
-	size := 32
-	//arrays wrap it, each element being the same size
-	for elem.T == ArrayTy {
-		size *= elem.Size
-		elem = elem.Elem
-	}
-	return size
-}
-
 // iteratively unpack elements
 func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
 	if size < 0 {
@@ -150,13 +139,9 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
 
 	// Arrays have packed elements, resulting in longer unpack steps.
 	// Slices have just 32 bytes per element (pointing to the contents).
-	elemSize := 32
-	if t.T == ArrayTy || t.T == SliceTy {
-		elemSize = getFullElemSize(t.Elem)
-	}
+	elemSize := getTypeSize(*t.Elem)
 
 	for i, j := start, 0; j < size; i, j = i+elemSize, j+1 {
-
 		inter, err := toGoType(i, *t.Elem, output)
 		if err != nil {
 			return nil, err
@@ -170,6 +155,36 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
 	return refSlice.Interface(), nil
 }
 
+func forTupleUnpack(t Type, output []byte) (interface{}, error) {
+	retval := reflect.New(t.Type).Elem()
+	virtualArgs := 0
+	for index, elem := range t.TupleElems {
+		marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
+		if elem.T == ArrayTy && !isDynamicType(*elem) {
+			// If we have a static array, like [3]uint256, these are coded as
+			// just like uint256,uint256,uint256.
+			// This means that we need to add two 'virtual' arguments when
+			// we count the index from now on.
+			//
+			// Array values nested multiple levels deep are also encoded inline:
+			// [2][3]uint256: uint256,uint256,uint256,uint256,uint256,uint256
+			//
+			// Calculate the full array size to get the correct offset for the next argument.
+			// Decrement it by 1, as the normal index increment is still applied.
+			virtualArgs += getTypeSize(*elem)/32 - 1
+		} else if elem.T == TupleTy && !isDynamicType(*elem) {
+			// If we have a static tuple, like (uint256, bool, uint256), these are
+			// coded as just like uint256,bool,uint256
+			virtualArgs += getTypeSize(*elem)/32 - 1
+		}
+		if err != nil {
+			return nil, err
+		}
+		retval.Field(index).Set(reflect.ValueOf(marshalledValue))
+	}
+	return retval.Interface(), nil
+}
+
 // toGoType parses the output bytes and recursively assigns the value of these bytes
 // into a go type with accordance with the ABI spec.
 func toGoType(index int, t Type, output []byte) (interface{}, error) {
@@ -178,14 +193,14 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
 	}
 
 	var (
-		returnOutput []byte
-		begin, end   int
-		err          error
+		returnOutput  []byte
+		begin, length int
+		err           error
 	)
 
 	// if we require a length prefix, find the beginning word and size returned.
 	if t.requiresLengthPrefix() {
-		begin, end, err = lengthPrefixPointsTo(index, output)
+		begin, length, err = lengthPrefixPointsTo(index, output)
 		if err != nil {
 			return nil, err
 		}
@@ -194,19 +209,26 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
 	}
 
 	switch t.T {
-	case SliceTy:
-		if (*t.Elem).T == StringTy {
-			return forEachUnpack(t, output[begin:], 0, end)
+	case TupleTy:
+		if isDynamicType(t) {
+			begin, err := tuplePointsTo(index, output)
+			if err != nil {
+				return nil, err
+			}
+			return forTupleUnpack(t, output[begin:])
+		} else {
+			return forTupleUnpack(t, output[index:])
 		}
-		return forEachUnpack(t, output, begin, end)
+	case SliceTy:
+		return forEachUnpack(t, output[begin:], 0, length)
 	case ArrayTy:
-		if (*t.Elem).T == StringTy {
+		if isDynamicType(*t.Elem) {
 			offset := int64(binary.BigEndian.Uint64(returnOutput[len(returnOutput)-8:]))
 			return forEachUnpack(t, output[offset:], 0, t.Size)
 		}
-		return forEachUnpack(t, output, index, t.Size)
+		return forEachUnpack(t, output[index:], 0, t.Size)
 	case StringTy: // variable arrays are written at the end of the return bytes
-		return string(output[begin : begin+end]), nil
+		return string(output[begin : begin+length]), nil
 	case IntTy, UintTy:
 		return readInteger(t.T, t.Kind, returnOutput), nil
 	case BoolTy:
@@ -216,7 +238,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
 	case HashTy:
 		return common.BytesToHash(returnOutput), nil
 	case BytesTy:
-		return output[begin : begin+end], nil
+		return output[begin : begin+length], nil
 	case FixedBytesTy:
 		return readFixedBytes(t, returnOutput)
 	case FunctionTy:
@@ -257,3 +279,17 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
 	length = int(lengthBig.Uint64())
 	return
 }
+
+// tuplePointsTo resolves the location reference for dynamic tuple.
+func tuplePointsTo(index int, output []byte) (start int, err error) {
+	offset := big.NewInt(0).SetBytes(output[index : index+32])
+	outputLen := big.NewInt(int64(len(output)))
+
+	if offset.Cmp(big.NewInt(int64(len(output)))) > 0 {
+		return 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", offset, outputLen)
+	}
+	if offset.BitLen() > 63 {
+		return 0, fmt.Errorf("abi offset larger than int64: %v", offset)
+	}
+	return int(offset.Uint64()), nil
+}
diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go
index 3873414c9f8c..5ece81ef2823 100644
--- a/accounts/abi/unpack_test.go
+++ b/accounts/abi/unpack_test.go
@@ -173,7 +173,7 @@ var unpackTests = []unpackTest{
 	// multi dimensional, if these pass, all types that don't require length prefix should pass
 	{
 		def:  `[{"type": "uint8[][]"}]`,
-		enc:  "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000E0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+		enc:  "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
 		want: [][]uint8{{1, 2}, {1, 2}},
 	},
 	{
@@ -183,7 +183,7 @@ var unpackTests = []unpackTest{
 	},
 	{
 		def:  `[{"type": "uint8[][2]"}]`,
-		enc:  "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001",
+		enc:  "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001",
 		want: [2][]uint8{{1}, {1}},
 	},
 	{
@@ -610,7 +610,18 @@ func TestMultiReturnWithStringSlice(t *testing.T) {
 		t.Fatal(err)
 	}
 	buff := new(bytes.Buffer)
-	buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008657468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b676f2d657468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000065"))
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // output[0] offset
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000120")) // output[1] offset
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // output[0] length
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // output[0][0] offset
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // output[0][1] offset
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000008")) // output[0][0] length
+	buff.Write(common.Hex2Bytes("657468657265756d000000000000000000000000000000000000000000000000")) // output[0][0] value
+	buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000b")) // output[0][1] length
+	buff.Write(common.Hex2Bytes("676f2d657468657265756d000000000000000000000000000000000000000000")) // output[0][1] value
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // output[1] length
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000064")) // output[1][0] value
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000065")) // output[1][1] value
 	ret1, ret1Exp := new([]string), []string{"ethereum", "go-ethereum"}
 	ret2, ret2Exp := new([]*big.Int), []*big.Int{big.NewInt(100), big.NewInt(101)}
 	if err := abi.Unpack(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
@@ -913,6 +924,108 @@ func TestUnmarshal(t *testing.T) {
 	}
 }
 
+func TestUnpackTuple(t *testing.T) {
+	const simpleTuple = `[{"name":"tuple","constant":false,"outputs":[{"type":"tuple","name":"ret","components":[{"type":"int256","name":"a"},{"type":"int256","name":"b"}]}]}]`
+	abi, err := JSON(strings.NewReader(simpleTuple))
+	if err != nil {
+		t.Fatal(err)
+	}
+	buff := new(bytes.Buffer)
+
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // ret[a] = 1
+	buff.Write(common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) // ret[b] = -1
+
+	v := struct {
+		Ret struct {
+			A *big.Int
+			B *big.Int
+		}
+	}{Ret: struct {
+		A *big.Int
+		B *big.Int
+	}{new(big.Int), new(big.Int)}}
+
+	err = abi.Unpack(&v, "tuple", buff.Bytes())
+	if err != nil {
+		t.Error(err)
+	} else {
+		if v.Ret.A.Cmp(big.NewInt(1)) != 0 {
+			t.Errorf("unexpected value unpacked: want %x, got %x", 1, v.Ret.A)
+		}
+		if v.Ret.B.Cmp(big.NewInt(-1)) != 0 {
+			t.Errorf("unexpected value unpacked: want %x, got %x", v.Ret.B, -1)
+		}
+	}
+
+	// Test nested tuple
+	const nestedTuple = `[{"name":"tuple","constant":false,"outputs":[
+		{"type":"tuple","name":"s","components":[{"type":"uint256","name":"a"},{"type":"uint256[]","name":"b"},{"type":"tuple[]","name":"c","components":[{"name":"x", "type":"uint256"},{"name":"y","type":"uint256"}]}]},
+		{"type":"tuple","name":"t","components":[{"name":"x", "type":"uint256"},{"name":"y","type":"uint256"}]},
+		{"type":"uint256","name":"a"}
+	]}]`
+
+	abi, err = JSON(strings.NewReader(nestedTuple))
+	if err != nil {
+		t.Fatal(err)
+	}
+	buff.Reset()
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // s offset
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")) // t.X = 0
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // t.Y = 1
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // a = 1
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // s.A = 1
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000060")) // s.B offset
+	buff.Write(common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000c0")) // s.C offset
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // s.B length
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // s.B[0] = 1
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // s.B[0] = 2
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // s.C length
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // s.C[0].X
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // s.C[0].Y
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // s.C[1].X
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // s.C[1].Y
+
+	type T struct {
+		X *big.Int `abi:"x"`
+		Z *big.Int `abi:"y"` // Test whether the abi tag works.
+	}
+
+	type S struct {
+		A *big.Int
+		B []*big.Int
+		C []T
+	}
+
+	type Ret struct {
+		FieldS S `abi:"s"`
+		FieldT T `abi:"t"`
+		A      *big.Int
+	}
+	var ret Ret
+	var expected = Ret{
+		FieldS: S{
+			A: big.NewInt(1),
+			B: []*big.Int{big.NewInt(1), big.NewInt(2)},
+			C: []T{
+				{big.NewInt(1), big.NewInt(2)},
+				{big.NewInt(2), big.NewInt(1)},
+			},
+		},
+		FieldT: T{
+			big.NewInt(0), big.NewInt(1),
+		},
+		A: big.NewInt(1),
+	}
+
+	err = abi.Unpack(&ret, "tuple", buff.Bytes())
+	if err != nil {
+		t.Error(err)
+	}
+	if reflect.DeepEqual(ret, expected) {
+		t.Error("unexpected unpack value")
+	}
+}
+
 func TestOOMMaliciousInput(t *testing.T) {
 	oomTests := []unpackTest{
 		{