diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
index 02b4fa472cb80d43aa7de6f115e64066940beaf9..cbcf4ca924eb440e97effdb8a12184d92a642829 100644
--- a/accounts/abi/abi.go
+++ b/accounts/abi/abi.go
@@ -17,6 +17,7 @@
 package abi
 
 import (
+	"bytes"
 	"encoding/json"
 	"fmt"
 	"io"
@@ -50,25 +51,25 @@ func JSON(reader io.Reader) (ABI, error) {
 // methods string signature. (signature = baz(uint32,string32))
 func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
 	// Fetch the ABI of the requested method
-	var method Method
-
 	if name == "" {
-		method = abi.Constructor
-	} else {
-		m, exist := abi.Methods[name]
-		if !exist {
-			return nil, fmt.Errorf("method '%s' not found", name)
+		// constructor
+		arguments, err := abi.Constructor.Inputs.Pack(args...)
+		if err != nil {
+			return nil, err
 		}
-		method = m
+		return arguments, nil
+
+	}
+	method, exist := abi.Methods[name]
+	if !exist {
+		return nil, fmt.Errorf("method '%s' not found", name)
 	}
-	arguments, err := method.pack(args...)
+
+	arguments, err := method.Inputs.Pack(args...)
 	if err != nil {
 		return nil, err
 	}
 	// Pack up the method ID too if not a constructor and return
-	if name == "" {
-		return arguments, nil
-	}
 	return append(method.Id(), arguments...), nil
 }
 
@@ -77,28 +78,20 @@ func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) {
 	if len(output) == 0 {
 		return fmt.Errorf("abi: unmarshalling empty output")
 	}
-
 	// since there can't be naming collisions with contracts and events,
 	// we need to decide whether we're calling a method or an event
-	var unpack unpacker
 	if method, ok := abi.Methods[name]; ok {
 		if len(output)%32 != 0 {
 			return fmt.Errorf("abi: improperly formatted output")
 		}
-		unpack = method
+		return method.Outputs.Unpack(v, output)
 	} else if event, ok := abi.Events[name]; ok {
-		unpack = event
-	} else {
-		return fmt.Errorf("abi: could not locate named method or event.")
-	}
-
-	// requires a struct to unpack into for a tuple return...
-	if unpack.isTupleReturn() {
-		return unpack.tupleUnpack(v, output)
+		return event.Inputs.Unpack(v, output)
 	}
-	return unpack.singleUnpack(v, output)
+	return fmt.Errorf("abi: could not locate named method or event")
 }
 
+// UnmarshalJSON implements json.Unmarshaler interface
 func (abi *ABI) UnmarshalJSON(data []byte) error {
 	var fields []struct {
 		Type      string
@@ -141,3 +134,14 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
 
 	return nil
 }
+
+// MethodById looks up a method by the 4-byte id
+// returns nil if none found
+func (abi *ABI) MethodById(sigdata []byte) *Method {
+	for _, method := range abi.Methods {
+		if bytes.Equal(method.Id(), sigdata[:4]) {
+			return &method
+		}
+	}
+	return nil
+}
diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go
index 644a388e3b90e9c82ca8df1f79755c1b80f6cebc..2d43b631c2adf37ca9961700b2566c415f2303e0 100644
--- a/accounts/abi/abi_test.go
+++ b/accounts/abi/abi_test.go
@@ -22,10 +22,11 @@ import (
 	"fmt"
 	"log"
 	"math/big"
-	"reflect"
 	"strings"
 	"testing"
 
+	"reflect"
+
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/crypto"
 )
@@ -75,9 +76,24 @@ func TestReader(t *testing.T) {
 	}
 
 	// deep equal fails for some reason
-	t.Skip()
-	if !reflect.DeepEqual(abi, exp) {
-		t.Errorf("\nabi: %v\ndoes not match exp: %v", abi, exp)
+	for name, expM := range exp.Methods {
+		gotM, exist := abi.Methods[name]
+		if !exist {
+			t.Errorf("Missing expected method %v", name)
+		}
+		if !reflect.DeepEqual(gotM, expM) {
+			t.Errorf("\nGot abi method: \n%v\ndoes not match expected method\n%v", gotM, expM)
+		}
+	}
+
+	for name, gotM := range abi.Methods {
+		expM, exist := exp.Methods[name]
+		if !exist {
+			t.Errorf("Found extra method %v", name)
+		}
+		if !reflect.DeepEqual(gotM, expM) {
+			t.Errorf("\nGot abi method: \n%v\ndoes not match expected method\n%v", gotM, expM)
+		}
 	}
 }
 
@@ -641,3 +657,42 @@ func TestUnpackEvent(t *testing.T) {
 		t.Logf("len(data): %d; received event: %+v", len(data), ev)
 	}
 }
+
+func TestABI_MethodById(t *testing.T) {
+	const abiJSON = `[
+		{"type":"function","name":"receive","constant":false,"inputs":[{"name":"memo","type":"bytes"}],"outputs":[],"payable":true,"stateMutability":"payable"},
+		{"type":"event","name":"received","anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}]},
+		{"type":"function","name":"fixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr","type":"uint256[2]"}]},
+		{"type":"function","name":"fixedArrBytes","constant":true,"inputs":[{"name":"str","type":"bytes"},{"name":"fixedArr","type":"uint256[2]"}]},
+		{"type":"function","name":"mixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr","type":"uint256[2]"},{"name":"dynArr","type":"uint256[]"}]},
+		{"type":"function","name":"doubleFixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr1","type":"uint256[2]"},{"name":"fixedArr2","type":"uint256[3]"}]},
+		{"type":"function","name":"multipleMixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr1","type":"uint256[2]"},{"name":"dynArr","type":"uint256[]"},{"name":"fixedArr2","type":"uint256[3]"}]},
+		{"type":"function","name":"balance","constant":true},
+		{"type":"function","name":"send","constant":false,"inputs":[{"name":"amount","type":"uint256"}]},
+		{"type":"function","name":"test","constant":false,"inputs":[{"name":"number","type":"uint32"}]},
+		{"type":"function","name":"string","constant":false,"inputs":[{"name":"inputs","type":"string"}]},
+		{"type":"function","name":"bool","constant":false,"inputs":[{"name":"inputs","type":"bool"}]},
+		{"type":"function","name":"address","constant":false,"inputs":[{"name":"inputs","type":"address"}]},
+		{"type":"function","name":"uint64[2]","constant":false,"inputs":[{"name":"inputs","type":"uint64[2]"}]},
+		{"type":"function","name":"uint64[]","constant":false,"inputs":[{"name":"inputs","type":"uint64[]"}]},
+		{"type":"function","name":"foo","constant":false,"inputs":[{"name":"inputs","type":"uint32"}]},
+		{"type":"function","name":"bar","constant":false,"inputs":[{"name":"inputs","type":"uint32"},{"name":"string","type":"uint16"}]},
+		{"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[]"}]}
+	]
+`
+	abi, err := JSON(strings.NewReader(abiJSON))
+	if err != nil {
+		t.Fatal(err)
+	}
+	for name, m := range abi.Methods {
+		a := fmt.Sprintf("%v", m)
+		b := fmt.Sprintf("%v", abi.MethodById(m.Id()))
+		if a != b {
+			t.Errorf("Method %v (id %v) not 'findable' by id in ABI", name, common.ToHex(m.Id()))
+		}
+	}
+
+}
diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go
index 4691318ce7292b69045914087bfb24aa76b80f8b..ad17fbf2b1a90dfbe59df9a13fe302beae4fae72 100644
--- a/accounts/abi/argument.go
+++ b/accounts/abi/argument.go
@@ -19,6 +19,8 @@ package abi
 import (
 	"encoding/json"
 	"fmt"
+	"reflect"
+	"strings"
 )
 
 // Argument holds the name of the argument and the corresponding type.
@@ -29,7 +31,10 @@ type Argument struct {
 	Indexed bool // indexed is only used by events
 }
 
-func (a *Argument) UnmarshalJSON(data []byte) error {
+type Arguments []Argument
+
+// UnmarshalJSON implements json.Unmarshaler interface
+func (argument *Argument) UnmarshalJSON(data []byte) error {
 	var extarg struct {
 		Name    string
 		Type    string
@@ -40,12 +45,180 @@ func (a *Argument) UnmarshalJSON(data []byte) error {
 		return fmt.Errorf("argument json err: %v", err)
 	}
 
-	a.Type, err = NewType(extarg.Type)
+	argument.Type, err = NewType(extarg.Type)
 	if err != nil {
 		return err
 	}
-	a.Name = extarg.Name
-	a.Indexed = extarg.Indexed
+	argument.Name = extarg.Name
+	argument.Indexed = extarg.Indexed
 
 	return nil
 }
+
+// LengthNonIndexed returns the number of arguments when not counting 'indexed' ones. Only events
+// can ever have 'indexed' arguments, it should always be false on arguments for method input/output
+func (arguments Arguments) LengthNonIndexed() int {
+	out := 0
+	for _, arg := range arguments {
+		if !arg.Indexed {
+			out++
+		}
+	}
+	return out
+}
+
+// isTuple returns true for non-atomic constructs, like (uint,uint) or uint[]
+func (arguments Arguments) isTuple() bool {
+	return len(arguments) > 1
+}
+
+// Unpack performs the operation hexdata -> Go format
+func (arguments Arguments) Unpack(v interface{}, data []byte) error {
+	if arguments.isTuple() {
+		return arguments.unpackTuple(v, data)
+	}
+	return arguments.unpackAtomic(v, data)
+}
+
+func (arguments Arguments) unpackTuple(v interface{}, output []byte) error {
+	// make sure the passed value is arguments pointer
+	valueOf := reflect.ValueOf(v)
+	if reflect.Ptr != valueOf.Kind() {
+		return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
+	}
+
+	var (
+		value = valueOf.Elem()
+		typ   = value.Type()
+		kind  = value.Kind()
+	)
+
+	if err := requireUnpackKind(value, typ, kind, arguments); err != nil {
+		return err
+	}
+	// `i` counts the nonindexed arguments.
+	// `j` counts the number of complex types.
+	// both `i` and `j` are used to to correctly compute `data` offset.
+
+	i, j := -1, 0
+	for _, arg := range arguments {
+
+		if arg.Indexed {
+			// can't read, continue
+			continue
+		}
+		i++
+		marshalledValue, err := toGoType((i+j)*32, arg.Type, output)
+		if err != nil {
+			return err
+		}
+
+		if arg.Type.T == ArrayTy {
+			// combined index ('i' + 'j') need to be adjusted only by size of array, thus
+			// we need to decrement 'j' because 'i' was incremented
+			j += arg.Type.Size - 1
+		}
+
+		reflectValue := reflect.ValueOf(marshalledValue)
+
+		switch kind {
+		case reflect.Struct:
+			for j := 0; j < typ.NumField(); j++ {
+				field := typ.Field(j)
+				// TODO read tags: `abi:"fieldName"`
+				if field.Name == strings.ToUpper(arg.Name[:1])+arg.Name[1:] {
+					if err := set(value.Field(j), reflectValue, arg); 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 {
+				return err
+			}
+
+			if err := set(v.Elem(), reflectValue, arg); err != nil {
+				return err
+			}
+		default:
+			return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", typ)
+		}
+	}
+	return nil
+}
+
+// unpackAtomic unpacks ( hexdata -> go ) a single value
+func (arguments Arguments) unpackAtomic(v interface{}, output []byte) error {
+	// make sure the passed value is arguments pointer
+	valueOf := reflect.ValueOf(v)
+	if reflect.Ptr != valueOf.Kind() {
+		return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
+	}
+	arg := arguments[0]
+	if arg.Indexed {
+		return fmt.Errorf("abi: attempting to unpack indexed variable into element.")
+	}
+
+	value := valueOf.Elem()
+
+	marshalledValue, err := toGoType(0, arg.Type, output)
+	if err != nil {
+		return err
+	}
+	return set(value, reflect.ValueOf(marshalledValue), arg)
+}
+
+// Unpack performs the operation Go format -> Hexdata
+func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
+	// Make sure arguments match up and pack them
+	abiArgs := arguments
+	if len(args) != len(abiArgs) {
+		return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(abiArgs))
+	}
+
+	// variable input is the output appended at the end of packed
+	// output. This is used for strings and bytes types input.
+	var variableInput []byte
+
+	// input offset is the bytes offset for packed output
+	inputOffset := 0
+	for _, abiArg := range abiArgs {
+		if abiArg.Type.T == ArrayTy {
+			inputOffset += (32 * abiArg.Type.Size)
+		} else {
+			inputOffset += 32
+		}
+	}
+
+	var ret []byte
+	for i, a := range args {
+		input := abiArgs[i]
+		// pack the input
+		packed, err := input.Type.pack(reflect.ValueOf(a))
+		if err != nil {
+			return nil, err
+		}
+
+		// check for a slice type (string, bytes, slice)
+		if input.Type.requiresLengthPrefix() {
+			// calculate the offset
+			offset := inputOffset + len(variableInput)
+			// set the offset
+			ret = append(ret, packNum(reflect.ValueOf(offset))...)
+			// Append the packed output to the variable input. The variable input
+			// will be appended at the end of the input.
+			variableInput = append(variableInput, packed...)
+		} else {
+			// append the packed value to the input
+			ret = append(ret, packed...)
+		}
+	}
+	// append the variable input at the end of the packed input
+	ret = append(ret, variableInput...)
+
+	return ret, nil
+}
diff --git a/accounts/abi/event.go b/accounts/abi/event.go
index bd1098d878e3ef66895dfa69cc3f6d0941f0a4ed..726bac90e9992933f4e907b0e70721f23c344a6a 100644
--- a/accounts/abi/event.go
+++ b/accounts/abi/event.go
@@ -18,7 +18,6 @@ package abi
 
 import (
 	"fmt"
-	"reflect"
 	"strings"
 
 	"github.com/ethereum/go-ethereum/common"
@@ -31,7 +30,7 @@ import (
 type Event struct {
 	Name      string
 	Anonymous bool
-	Inputs    []Argument
+	Inputs    Arguments
 }
 
 // Id returns the canonical representation of the event's signature used by the
@@ -45,95 +44,3 @@ func (e Event) Id() common.Hash {
 	}
 	return common.BytesToHash(crypto.Keccak256([]byte(fmt.Sprintf("%v(%v)", e.Name, strings.Join(types, ",")))))
 }
-
-// unpacks an event return tuple into a struct of corresponding go types
-//
-// Unpacking can be done into a struct or a slice/array.
-func (e Event) tupleUnpack(v interface{}, output []byte) error {
-	// make sure the passed value is a pointer
-	valueOf := reflect.ValueOf(v)
-	if reflect.Ptr != valueOf.Kind() {
-		return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
-	}
-
-	var (
-		value = valueOf.Elem()
-		typ   = value.Type()
-	)
-
-	if value.Kind() != reflect.Struct {
-		return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ)
-	}
-
-	j := 0
-	for i := 0; i < len(e.Inputs); i++ {
-		input := e.Inputs[i]
-		if input.Indexed {
-			// can't read, continue
-			continue
-		}
-		marshalledValue, err := toGoType((i+j)*32, input.Type, output)
-		if err != nil {
-			return err
-		}
-		if input.Type.T == ArrayTy {
-			// combined index ('i' + 'j') need to be adjusted only by size of array, thus
-			// we need to decrement 'j' because 'i' was incremented
-			j += input.Type.Size - 1
-		}
-		reflectValue := reflect.ValueOf(marshalledValue)
-
-		switch value.Kind() {
-		case reflect.Struct:
-			for j := 0; j < typ.NumField(); j++ {
-				field := typ.Field(j)
-				// TODO read tags: `abi:"fieldName"`
-				if field.Name == strings.ToUpper(e.Inputs[i].Name[:1])+e.Inputs[i].Name[1:] {
-					if err := set(value.Field(j), reflectValue, e.Inputs[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(e.Inputs), value.Len())
-			}
-			v := value.Index(i)
-			if v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface {
-				return fmt.Errorf("abi: cannot unmarshal %v in to %v", v.Type(), reflectValue.Type())
-			}
-			reflectValue := reflect.ValueOf(marshalledValue)
-			if err := set(v.Elem(), reflectValue, e.Inputs[i]); err != nil {
-				return err
-			}
-		default:
-			return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ)
-		}
-	}
-	return nil
-}
-
-func (e Event) isTupleReturn() bool { return len(e.Inputs) > 1 }
-
-func (e Event) singleUnpack(v interface{}, output []byte) error {
-	// make sure the passed value is a pointer
-	valueOf := reflect.ValueOf(v)
-	if reflect.Ptr != valueOf.Kind() {
-		return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
-	}
-
-	if e.Inputs[0].Indexed {
-		return fmt.Errorf("abi: attempting to unpack indexed variable into element.")
-	}
-
-	value := valueOf.Elem()
-
-	marshalledValue, err := toGoType(0, e.Inputs[0].Type, output)
-	if err != nil {
-		return err
-	}
-	if err := set(value, reflect.ValueOf(marshalledValue), e.Inputs[0]); err != nil {
-		return err
-	}
-	return nil
-}
diff --git a/accounts/abi/event_test.go b/accounts/abi/event_test.go
index a3899b4a6feebb0e29c63d56baab73db0a3fd70b..cca61e433dd610adf45ce643c33a5c33cf3cde34 100644
--- a/accounts/abi/event_test.go
+++ b/accounts/abi/event_test.go
@@ -18,15 +18,52 @@ package abi
 
 import (
 	"bytes"
+	"encoding/hex"
+	"encoding/json"
+	"math/big"
 	"reflect"
 	"strings"
 	"testing"
 
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/crypto"
+	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
 
+var jsonEventTransfer = []byte(`{
+  "anonymous": false,
+  "inputs": [
+    {
+      "indexed": true, "name": "from", "type": "address"
+    }, {
+      "indexed": true, "name": "to", "type": "address"
+    }, {
+      "indexed": false, "name": "value", "type": "uint256"
+  }],
+  "name": "Transfer",
+  "type": "event"
+}`)
+
+var jsonEventPledge = []byte(`{
+  "anonymous": false,
+  "inputs": [{
+      "indexed": false, "name": "who", "type": "address"
+    }, {
+      "indexed": false, "name": "wad", "type": "uint128"
+    }, {
+      "indexed": false, "name": "currency", "type": "bytes3"
+  }],
+  "name": "Pledge",
+  "type": "event"
+}`)
+
+// 1000000
+var transferData1 = "00000000000000000000000000000000000000000000000000000000000f4240"
+
+// "0x00Ce0d46d924CC8437c806721496599FC3FFA268", 2218516807680, "usd"
+var pledgeData1 = "00000000000000000000000000ce0d46d924cc8437c806721496599fc3ffa2680000000000000000000000000000000000000000000000000000020489e800007573640000000000000000000000000000000000000000000000000000000000"
+
 func TestEventId(t *testing.T) {
 	var table = []struct {
 		definition   string
@@ -77,3 +114,203 @@ func TestEventMultiValueWithArrayUnpack(t *testing.T) {
 	require.Equal(t, [2]uint8{1, 2}, rst.Value1)
 	require.Equal(t, uint8(3), rst.Value2)
 }
+
+func TestEventTupleUnpack(t *testing.T) {
+
+	type EventTransfer struct {
+		Value *big.Int
+	}
+
+	type EventPledge struct {
+		Who      common.Address
+		Wad      *big.Int
+		Currency [3]byte
+	}
+
+	type BadEventPledge struct {
+		Who      string
+		Wad      int
+		Currency [3]byte
+	}
+
+	bigint := new(big.Int)
+	bigintExpected := big.NewInt(1000000)
+	bigintExpected2 := big.NewInt(2218516807680)
+	addr := common.HexToAddress("0x00Ce0d46d924CC8437c806721496599FC3FFA268")
+	var testCases = []struct {
+		data     string
+		dest     interface{}
+		expected interface{}
+		jsonLog  []byte
+		error    string
+		name     string
+	}{{
+		transferData1,
+		&EventTransfer{},
+		&EventTransfer{Value: bigintExpected},
+		jsonEventTransfer,
+		"",
+		"Can unpack ERC20 Transfer event into structure",
+	}, {
+		transferData1,
+		&[]interface{}{&bigint},
+		&[]interface{}{&bigintExpected},
+		jsonEventTransfer,
+		"",
+		"Can unpack ERC20 Transfer event into slice",
+	}, {
+		pledgeData1,
+		&EventPledge{},
+		&EventPledge{
+			addr,
+			bigintExpected2,
+			[3]byte{'u', 's', 'd'}},
+		jsonEventPledge,
+		"",
+		"Can unpack Pledge event into structure",
+	}, {
+		pledgeData1,
+		&[]interface{}{&common.Address{}, &bigint, &[3]byte{}},
+		&[]interface{}{
+			&addr,
+			&bigintExpected2,
+			&[3]byte{'u', 's', 'd'}},
+		jsonEventPledge,
+		"",
+		"Can unpack Pledge event into slice",
+	}, {
+		pledgeData1,
+		&[3]interface{}{&common.Address{}, &bigint, &[3]byte{}},
+		&[3]interface{}{
+			&addr,
+			&bigintExpected2,
+			&[3]byte{'u', 's', 'd'}},
+		jsonEventPledge,
+		"",
+		"Can unpack Pledge event into an array",
+	}, {
+		pledgeData1,
+		&[]interface{}{new(int), 0, 0},
+		&[]interface{}{},
+		jsonEventPledge,
+		"abi: cannot unmarshal common.Address in to int",
+		"Can not unpack Pledge event into slice with wrong types",
+	}, {
+		pledgeData1,
+		&BadEventPledge{},
+		&BadEventPledge{},
+		jsonEventPledge,
+		"abi: cannot unmarshal common.Address in to string",
+		"Can not unpack Pledge event into struct with wrong filed types",
+	}, {
+		pledgeData1,
+		&[]interface{}{common.Address{}, new(big.Int)},
+		&[]interface{}{},
+		jsonEventPledge,
+		"abi: insufficient number of elements in the list/array for unpack, want 3, got 2",
+		"Can not unpack Pledge event into too short slice",
+	}, {
+		pledgeData1,
+		new(map[string]interface{}),
+		&[]interface{}{},
+		jsonEventPledge,
+		"abi: cannot unmarshal tuple into map[string]interface {}",
+		"Can not unpack Pledge event into map",
+	}}
+
+	for _, tc := range testCases {
+		assert := assert.New(t)
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			err := unpackTestEventData(tc.dest, tc.data, tc.jsonLog, assert)
+			if tc.error == "" {
+				assert.Nil(err, "Should be able to unpack event data.")
+				assert.Equal(tc.expected, tc.dest, tc.name)
+			} else {
+				assert.EqualError(err, tc.error)
+			}
+		})
+	}
+}
+
+func unpackTestEventData(dest interface{}, hexData string, jsonEvent []byte, assert *assert.Assertions) error {
+	data, err := hex.DecodeString(hexData)
+	assert.NoError(err, "Hex data should be a correct hex-string")
+	var e Event
+	assert.NoError(json.Unmarshal(jsonEvent, &e), "Should be able to unmarshal event ABI")
+	a := ABI{Events: map[string]Event{"e": e}}
+	return a.Unpack(dest, "e", data)
+}
+
+/*
+Taken from
+https://github.com/ethereum/go-ethereum/pull/15568
+*/
+
+type testResult struct {
+	Values [2]*big.Int
+	Value1 *big.Int
+	Value2 *big.Int
+}
+
+type testCase struct {
+	definition string
+	want       testResult
+}
+
+func (tc testCase) encoded(intType, arrayType Type) []byte {
+	var b bytes.Buffer
+	if tc.want.Value1 != nil {
+		val, _ := intType.pack(reflect.ValueOf(tc.want.Value1))
+		b.Write(val)
+	}
+
+	if !reflect.DeepEqual(tc.want.Values, [2]*big.Int{nil, nil}) {
+		val, _ := arrayType.pack(reflect.ValueOf(tc.want.Values))
+		b.Write(val)
+	}
+	if tc.want.Value2 != nil {
+		val, _ := intType.pack(reflect.ValueOf(tc.want.Value2))
+		b.Write(val)
+	}
+	return b.Bytes()
+}
+
+// TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder.
+func TestEventUnpackIndexed(t *testing.T) {
+	definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
+	type testStruct struct {
+		Value1 uint8
+		Value2 uint8
+	}
+	abi, err := JSON(strings.NewReader(definition))
+	require.NoError(t, err)
+	var b bytes.Buffer
+	b.Write(packNum(reflect.ValueOf(uint8(8))))
+	var rst testStruct
+	require.NoError(t, abi.Unpack(&rst, "test", b.Bytes()))
+	require.Equal(t, uint8(0), rst.Value1)
+	require.Equal(t, uint8(8), rst.Value2)
+}
+
+// TestEventIndexedWithArrayUnpack verifies that decoder will not overlow when static array is indexed input.
+func TestEventIndexedWithArrayUnpack(t *testing.T) {
+	definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"string"}]}]`
+	type testStruct struct {
+		Value1 [2]uint8
+		Value2 string
+	}
+	abi, err := JSON(strings.NewReader(definition))
+	require.NoError(t, err)
+	var b bytes.Buffer
+	stringOut := "abc"
+	// number of fields that will be encoded * 32
+	b.Write(packNum(reflect.ValueOf(32)))
+	b.Write(packNum(reflect.ValueOf(len(stringOut))))
+	b.Write(common.RightPadBytes([]byte(stringOut), 32))
+
+	var rst testStruct
+	require.NoError(t, abi.Unpack(&rst, "test", b.Bytes()))
+	require.Equal(t, [2]uint8{0, 0}, rst.Value1)
+	require.Equal(t, stringOut, rst.Value2)
+}
diff --git a/accounts/abi/method.go b/accounts/abi/method.go
index 66e8751f3d7ccb6ceb35db8cf3c21c5be7f34320..f434ffdbef6c1d6f0ecc900573017e7c05777982 100644
--- a/accounts/abi/method.go
+++ b/accounts/abi/method.go
@@ -18,13 +18,12 @@ package abi
 
 import (
 	"fmt"
-	"reflect"
 	"strings"
 
 	"github.com/ethereum/go-ethereum/crypto"
 )
 
-// Callable method given a `Name` and whether the method is a constant.
+// Method represents a callable given a `Name` and whether the method is a constant.
 // If the method is `Const` no transaction needs to be created for this
 // particular Method call. It can easily be simulated using a local VM.
 // For example a `Balance()` method only needs to retrieve something
@@ -35,137 +34,8 @@ import (
 type Method struct {
 	Name    string
 	Const   bool
-	Inputs  []Argument
-	Outputs []Argument
-}
-
-func (method Method) pack(args ...interface{}) ([]byte, error) {
-	// Make sure arguments match up and pack them
-	if len(args) != len(method.Inputs) {
-		return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Inputs))
-	}
-	// variable input is the output appended at the end of packed
-	// output. This is used for strings and bytes types input.
-	var variableInput []byte
-
-	// input offset is the bytes offset for packed output
-	inputOffset := 0
-	for _, input := range method.Inputs {
-		if input.Type.T == ArrayTy {
-			inputOffset += (32 * input.Type.Size)
-		} else {
-			inputOffset += 32
-		}
-	}
-
-	var ret []byte
-	for i, a := range args {
-		input := method.Inputs[i]
-		// pack the input
-		packed, err := input.Type.pack(reflect.ValueOf(a))
-		if err != nil {
-			return nil, fmt.Errorf("`%s` %v", method.Name, err)
-		}
-
-		// check for a slice type (string, bytes, slice)
-		if input.Type.requiresLengthPrefix() {
-			// calculate the offset
-			offset := inputOffset + len(variableInput)
-
-			// set the offset
-			ret = append(ret, packNum(reflect.ValueOf(offset))...)
-			// Append the packed output to the variable input. The variable input
-			// will be appended at the end of the input.
-			variableInput = append(variableInput, packed...)
-		} else {
-			// append the packed value to the input
-			ret = append(ret, packed...)
-		}
-	}
-	// append the variable input at the end of the packed input
-	ret = append(ret, variableInput...)
-
-	return ret, nil
-}
-
-// unpacks a method return tuple into a struct of corresponding go types
-//
-// Unpacking can be done into a struct or a slice/array.
-func (method Method) tupleUnpack(v interface{}, output []byte) error {
-	// make sure the passed value is a pointer
-	valueOf := reflect.ValueOf(v)
-	if reflect.Ptr != valueOf.Kind() {
-		return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
-	}
-
-	var (
-		value = valueOf.Elem()
-		typ   = value.Type()
-	)
-
-	j := 0
-	for i := 0; i < len(method.Outputs); i++ {
-		toUnpack := method.Outputs[i]
-		marshalledValue, err := toGoType((i+j)*32, toUnpack.Type, output)
-		if err != nil {
-			return err
-		}
-		if toUnpack.Type.T == ArrayTy {
-			// combined index ('i' + 'j') need to be adjusted only by size of array, thus
-			// we need to decrement 'j' because 'i' was incremented
-			j += toUnpack.Type.Size - 1
-		}
-		reflectValue := reflect.ValueOf(marshalledValue)
-
-		switch value.Kind() {
-		case reflect.Struct:
-			for j := 0; j < typ.NumField(); j++ {
-				field := typ.Field(j)
-				// TODO read tags: `abi:"fieldName"`
-				if field.Name == strings.ToUpper(method.Outputs[i].Name[:1])+method.Outputs[i].Name[1:] {
-					if err := set(value.Field(j), reflectValue, method.Outputs[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(method.Outputs), value.Len())
-			}
-			v := value.Index(i)
-			if v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface {
-				return fmt.Errorf("abi: cannot unmarshal %v in to %v", v.Type(), reflectValue.Type())
-			}
-			reflectValue := reflect.ValueOf(marshalledValue)
-			if err := set(v.Elem(), reflectValue, method.Outputs[i]); err != nil {
-				return err
-			}
-		default:
-			return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ)
-		}
-	}
-	return nil
-}
-
-func (method Method) isTupleReturn() bool { return len(method.Outputs) > 1 }
-
-func (method Method) singleUnpack(v interface{}, output []byte) error {
-	// make sure the passed value is a pointer
-	valueOf := reflect.ValueOf(v)
-	if reflect.Ptr != valueOf.Kind() {
-		return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
-	}
-
-	value := valueOf.Elem()
-
-	marshalledValue, err := toGoType(0, method.Outputs[0].Type, output)
-	if err != nil {
-		return err
-	}
-	if err := set(value, reflect.ValueOf(marshalledValue), method.Outputs[0]); err != nil {
-		return err
-	}
-	return nil
+	Inputs  Arguments
+	Outputs Arguments
 }
 
 // Sig returns the methods string signature according to the ABI spec.
@@ -175,35 +45,35 @@ func (method Method) singleUnpack(v interface{}, output []byte) error {
 //     function foo(uint32 a, int b)    =    "foo(uint32,int256)"
 //
 // Please note that "int" is substitute for its canonical representation "int256"
-func (m Method) Sig() string {
-	types := make([]string, len(m.Inputs))
+func (method Method) Sig() string {
+	types := make([]string, len(method.Inputs))
 	i := 0
-	for _, input := range m.Inputs {
+	for _, input := range method.Inputs {
 		types[i] = input.Type.String()
 		i++
 	}
-	return fmt.Sprintf("%v(%v)", m.Name, strings.Join(types, ","))
+	return fmt.Sprintf("%v(%v)", method.Name, strings.Join(types, ","))
 }
 
-func (m Method) String() string {
-	inputs := make([]string, len(m.Inputs))
-	for i, input := range m.Inputs {
+func (method Method) String() string {
+	inputs := make([]string, len(method.Inputs))
+	for i, input := range method.Inputs {
 		inputs[i] = fmt.Sprintf("%v %v", input.Name, input.Type)
 	}
-	outputs := make([]string, len(m.Outputs))
-	for i, output := range m.Outputs {
+	outputs := make([]string, len(method.Outputs))
+	for i, output := range method.Outputs {
 		if len(output.Name) > 0 {
 			outputs[i] = fmt.Sprintf("%v ", output.Name)
 		}
 		outputs[i] += output.Type.String()
 	}
 	constant := ""
-	if m.Const {
+	if method.Const {
 		constant = "constant "
 	}
-	return fmt.Sprintf("function %v(%v) %sreturns(%v)", m.Name, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", "))
+	return fmt.Sprintf("function %v(%v) %sreturns(%v)", method.Name, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", "))
 }
 
-func (m Method) Id() []byte {
-	return crypto.Keccak256([]byte(m.Sig()))[:4]
+func (method Method) Id() []byte {
+	return crypto.Keccak256([]byte(method.Sig()))[:4]
 }
diff --git a/accounts/abi/pack.go b/accounts/abi/pack.go
index 072e805368d1f73f4d3baf90c219adf1b9116975..36c58265bd4b5388f428acb64ab0f4ef2903a7e4 100644
--- a/accounts/abi/pack.go
+++ b/accounts/abi/pack.go
@@ -48,9 +48,8 @@ func packElement(t Type, reflectValue reflect.Value) []byte {
 	case BoolTy:
 		if reflectValue.Bool() {
 			return math.PaddedBigBytes(common.Big1, 32)
-		} else {
-			return math.PaddedBigBytes(common.Big0, 32)
 		}
+		return math.PaddedBigBytes(common.Big0, 32)
 	case BytesTy:
 		if reflectValue.Kind() == reflect.Array {
 			reflectValue = mustArrayToByteSlice(reflectValue)
diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go
index e953b77c184070c0db2c27e36a198362918f9622..7a9cdacd54ab504bf9823b40b49a944d8be60b97 100644
--- a/accounts/abi/reflect.go
+++ b/accounts/abi/reflect.go
@@ -85,3 +85,28 @@ func set(dst, src reflect.Value, output Argument) error {
 	}
 	return nil
 }
+
+// requireAssignable assures that `dest` is a pointer and it's not an interface.
+func requireAssignable(dst, src reflect.Value) error {
+	if dst.Kind() != reflect.Ptr && dst.Kind() != reflect.Interface {
+		return fmt.Errorf("abi: cannot unmarshal %v into %v", src.Type(), dst.Type())
+	}
+	return nil
+}
+
+// requireUnpackKind verifies preconditions for unpacking `args` into `kind`
+func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind,
+	args Arguments) error {
+
+	switch k {
+	case reflect.Struct:
+	case reflect.Slice, reflect.Array:
+		if minLen := args.LengthNonIndexed(); v.Len() < minLen {
+			return fmt.Errorf("abi: insufficient number of elements in the list/array for unpack, want %d, got %d",
+				minLen, v.Len())
+		}
+	default:
+		return fmt.Errorf("abi: cannot unmarshal tuple into %v", t)
+	}
+	return nil
+}
diff --git a/accounts/abi/type.go b/accounts/abi/type.go
index fba10b96d2fb9c3c9706089efab97c110d904c75..a1f13ffa29f38ecf6cf076074f7ae28f4e2b463c 100644
--- a/accounts/abi/type.go
+++ b/accounts/abi/type.go
@@ -24,6 +24,7 @@ import (
 	"strings"
 )
 
+// Type enumerator
 const (
 	IntTy byte = iota
 	UintTy
@@ -100,68 +101,65 @@ func NewType(t string) (typ Type, err error) {
 			return Type{}, fmt.Errorf("invalid formatting of array type")
 		}
 		return typ, err
+	}
+	// parse the type and size of the abi-type.
+	parsedType := typeRegex.FindAllStringSubmatch(t, -1)[0]
+	// varSize is the size of the variable
+	var varSize int
+	if len(parsedType[3]) > 0 {
+		var err error
+		varSize, err = strconv.Atoi(parsedType[2])
+		if err != nil {
+			return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
+		}
 	} else {
-		// parse the type and size of the abi-type.
-		parsedType := typeRegex.FindAllStringSubmatch(t, -1)[0]
-		// varSize is the size of the variable
-		var varSize int
-		if len(parsedType[3]) > 0 {
-			var err error
-			varSize, err = strconv.Atoi(parsedType[2])
-			if err != nil {
-				return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
-			}
-		} else {
-			if parsedType[0] == "uint" || parsedType[0] == "int" {
-				// this should fail because it means that there's something wrong with
-				// the abi type (the compiler should always format it to the size...always)
-				return Type{}, fmt.Errorf("unsupported arg type: %s", t)
-			}
+		if parsedType[0] == "uint" || parsedType[0] == "int" {
+			// this should fail because it means that there's something wrong with
+			// the abi type (the compiler should always format it to the size...always)
+			return Type{}, fmt.Errorf("unsupported arg type: %s", t)
 		}
-		// varType is the parsed abi type
-		varType := parsedType[1]
-
-		switch varType {
-		case "int":
-			typ.Kind, typ.Type = reflectIntKindAndType(false, varSize)
-			typ.Size = varSize
-			typ.T = IntTy
-		case "uint":
-			typ.Kind, typ.Type = reflectIntKindAndType(true, varSize)
-			typ.Size = varSize
-			typ.T = UintTy
-		case "bool":
-			typ.Kind = reflect.Bool
-			typ.T = BoolTy
-			typ.Type = reflect.TypeOf(bool(false))
-		case "address":
-			typ.Kind = reflect.Array
-			typ.Type = address_t
-			typ.Size = 20
-			typ.T = AddressTy
-		case "string":
-			typ.Kind = reflect.String
-			typ.Type = reflect.TypeOf("")
-			typ.T = StringTy
-		case "bytes":
-			if varSize == 0 {
-				typ.T = BytesTy
-				typ.Kind = reflect.Slice
-				typ.Type = reflect.SliceOf(reflect.TypeOf(byte(0)))
-			} else {
-				typ.T = FixedBytesTy
-				typ.Kind = reflect.Array
-				typ.Size = varSize
-				typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0)))
-			}
-		case "function":
+	}
+	// varType is the parsed abi type
+	switch varType := parsedType[1]; varType {
+	case "int":
+		typ.Kind, typ.Type = reflectIntKindAndType(false, varSize)
+		typ.Size = varSize
+		typ.T = IntTy
+	case "uint":
+		typ.Kind, typ.Type = reflectIntKindAndType(true, varSize)
+		typ.Size = varSize
+		typ.T = UintTy
+	case "bool":
+		typ.Kind = reflect.Bool
+		typ.T = BoolTy
+		typ.Type = reflect.TypeOf(bool(false))
+	case "address":
+		typ.Kind = reflect.Array
+		typ.Type = address_t
+		typ.Size = 20
+		typ.T = AddressTy
+	case "string":
+		typ.Kind = reflect.String
+		typ.Type = reflect.TypeOf("")
+		typ.T = StringTy
+	case "bytes":
+		if varSize == 0 {
+			typ.T = BytesTy
+			typ.Kind = reflect.Slice
+			typ.Type = reflect.SliceOf(reflect.TypeOf(byte(0)))
+		} else {
+			typ.T = FixedBytesTy
 			typ.Kind = reflect.Array
-			typ.T = FunctionTy
-			typ.Size = 24
-			typ.Type = reflect.ArrayOf(24, reflect.TypeOf(byte(0)))
-		default:
-			return Type{}, fmt.Errorf("unsupported arg type: %s", t)
+			typ.Size = varSize
+			typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0)))
 		}
+	case "function":
+		typ.Kind = reflect.Array
+		typ.T = FunctionTy
+		typ.Size = 24
+		typ.Type = reflect.ArrayOf(24, reflect.TypeOf(byte(0)))
+	default:
+		return Type{}, fmt.Errorf("unsupported arg type: %s", t)
 	}
 
 	return
diff --git a/accounts/abi/unpack.go b/accounts/abi/unpack.go
index 051fc1916fa4925e785d3c52c8b862d7943385c1..80efb3f7eaecbba2f5a8f6f32d76572f9abdd59a 100644
--- a/accounts/abi/unpack.go
+++ b/accounts/abi/unpack.go
@@ -25,15 +25,6 @@ import (
 	"github.com/ethereum/go-ethereum/common"
 )
 
-// unpacker is a utility interface that enables us to have
-// abstraction between events and methods and also to properly
-// "unpack" them; e.g. events use Inputs, methods use Outputs.
-type unpacker interface {
-	tupleUnpack(v interface{}, output []byte) error
-	singleUnpack(v interface{}, output []byte) error
-	isTupleReturn() bool
-}
-
 // reads the integer based on its kind
 func readInteger(kind reflect.Kind, b []byte) interface{} {
 	switch kind {
@@ -79,7 +70,7 @@ func readBool(word []byte) (bool, error) {
 // This enforces that standard by always presenting it as a 24-array (address + sig = 24 bytes)
 func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
 	if t.T != FunctionTy {
-		return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array.")
+		return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array")
 	}
 	if garbage := binary.BigEndian.Uint64(word[24:32]); garbage != 0 {
 		err = fmt.Errorf("abi: got improperly encoded function type, got %v", word)
@@ -92,7 +83,7 @@ func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
 // through reflection, creates a fixed array to be read from
 func readFixedBytes(t Type, word []byte) (interface{}, error) {
 	if t.T != FixedBytesTy {
-		return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array.")
+		return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array")
 	}
 	// convert
 	array := reflect.New(t.Type).Elem()
diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go
index 14393d2309abf58964267dad8c3c209ff695cfe4..39109507326f25c8af1d67cd1c21f49b1de78c6c 100644
--- a/accounts/abi/unpack_test.go
+++ b/accounts/abi/unpack_test.go
@@ -27,6 +27,7 @@ import (
 	"testing"
 
 	"github.com/ethereum/go-ethereum/common"
+	"github.com/stretchr/testify/require"
 )
 
 type unpackTest struct {
@@ -286,56 +287,83 @@ func TestUnpack(t *testing.T) {
 	}
 }
 
-func TestMultiReturnWithStruct(t *testing.T) {
+type methodMultiOutput struct {
+	Int    *big.Int
+	String string
+}
+
+func methodMultiReturn(require *require.Assertions) (ABI, []byte, methodMultiOutput) {
 	const definition = `[
 	{ "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
+	var expected = methodMultiOutput{big.NewInt(1), "hello"}
 
 	abi, err := JSON(strings.NewReader(definition))
-	if err != nil {
-		t.Fatal(err)
-	}
-
+	require.NoError(err)
 	// using buff to make the code readable
 	buff := new(bytes.Buffer)
 	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
 	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
 	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005"))
-	stringOut := "hello"
-	buff.Write(common.RightPadBytes([]byte(stringOut), 32))
-
-	var inter struct {
-		Int    *big.Int
-		String string
-	}
-	err = abi.Unpack(&inter, "multi", buff.Bytes())
-	if err != nil {
-		t.Error(err)
-	}
-
-	if inter.Int == nil || inter.Int.Cmp(big.NewInt(1)) != 0 {
-		t.Error("expected Int to be 1 got", inter.Int)
-	}
-
-	if inter.String != stringOut {
-		t.Error("expected String to be", stringOut, "got", inter.String)
-	}
+	buff.Write(common.RightPadBytes([]byte(expected.String), 32))
+	return abi, buff.Bytes(), expected
+}
 
-	var reversed struct {
+func TestMethodMultiReturn(t *testing.T) {
+	type reversed struct {
 		String string
 		Int    *big.Int
 	}
 
-	err = abi.Unpack(&reversed, "multi", buff.Bytes())
-	if err != nil {
-		t.Error(err)
-	}
-
-	if reversed.Int == nil || reversed.Int.Cmp(big.NewInt(1)) != 0 {
-		t.Error("expected Int to be 1 got", reversed.Int)
-	}
-
-	if reversed.String != stringOut {
-		t.Error("expected String to be", stringOut, "got", reversed.String)
+	abi, data, expected := methodMultiReturn(require.New(t))
+	bigint := new(big.Int)
+	var testCases = []struct {
+		dest     interface{}
+		expected interface{}
+		error    string
+		name     string
+	}{{
+		&methodMultiOutput{},
+		&expected,
+		"",
+		"Can unpack into structure",
+	}, {
+		&reversed{},
+		&reversed{expected.String, expected.Int},
+		"",
+		"Can unpack into reversed structure",
+	}, {
+		&[]interface{}{&bigint, new(string)},
+		&[]interface{}{&expected.Int, &expected.String},
+		"",
+		"Can unpack into a slice",
+	}, {
+		&[2]interface{}{&bigint, new(string)},
+		&[2]interface{}{&expected.Int, &expected.String},
+		"",
+		"Can unpack into an array",
+	}, {
+		&[]interface{}{new(int), new(int)},
+		&[]interface{}{&expected.Int, &expected.String},
+		"abi: cannot unmarshal *big.Int in to int",
+		"Can not unpack into a slice with wrong types",
+	}, {
+		&[]interface{}{new(int)},
+		&[]interface{}{},
+		"abi: insufficient number of elements in the list/array for unpack, want 2, got 1",
+		"Can not unpack into a slice with wrong types",
+	}}
+	for _, tc := range testCases {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			require := require.New(t)
+			err := abi.Unpack(tc.dest, "multi", data)
+			if tc.error == "" {
+				require.Nil(err, "Should be able to unpack method outputs.")
+				require.Equal(tc.expected, tc.dest)
+			} else {
+				require.EqualError(err, tc.error)
+			}
+		})
 	}
 }