diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
index ba1774c6470e3986d60610a7cecc487098202198..c5fbc1e77e9f070ccd722d8d9fbfb759fe09a1db 100644
--- a/accounts/abi/abi.go
+++ b/accounts/abi/abi.go
@@ -72,19 +72,39 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
 }
 
 // Unpack output in v according to the abi specification
-func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) {
-	if len(output) == 0 {
+func (abi ABI) Unpack(v interface{}, name string, data []byte) (err error) {
+	if len(data) == 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
 	if method, ok := abi.Methods[name]; ok {
-		if len(output)%32 != 0 {
-			return fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(output), output)
+		if len(data)%32 != 0 {
+			return fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data)
 		}
-		return method.Outputs.Unpack(v, output)
-	} else if event, ok := abi.Events[name]; ok {
-		return event.Inputs.Unpack(v, output)
+		return method.Outputs.Unpack(v, data)
+	}
+	if event, ok := abi.Events[name]; ok {
+		return event.Inputs.Unpack(v, data)
+	}
+	return fmt.Errorf("abi: could not locate named method or event")
+}
+
+// UnpackIntoMap unpacks a log into the provided map[string]interface{}
+func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) {
+	if len(data) == 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
+	if method, ok := abi.Methods[name]; ok {
+		if len(data)%32 != 0 {
+			return fmt.Errorf("abi: improperly formatted output")
+		}
+		return method.Outputs.UnpackIntoMap(v, data)
+	}
+	if event, ok := abi.Events[name]; ok {
+		return event.Inputs.UnpackIntoMap(v, data)
 	}
 	return fmt.Errorf("abi: could not locate named method or event")
 }
diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go
index b9444f9f0d94f738c8760917695f7f6c48b37539..42b60a6395d05f5d9cde2cbfd9e80629f4081cc3 100644
--- a/accounts/abi/abi_test.go
+++ b/accounts/abi/abi_test.go
@@ -694,6 +694,192 @@ func TestUnpackEvent(t *testing.T) {
 	}
 }
 
+func TestUnpackEventIntoMap(t *testing.T) {
+	const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
+	abi, err := JSON(strings.NewReader(abiJSON))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	const hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
+	data, err := hex.DecodeString(hexdata)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(data)%32 == 0 {
+		t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
+	}
+
+	receivedMap := map[string]interface{}{}
+	expectedReceivedMap := map[string]interface{}{
+		"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
+		"amount": big.NewInt(1),
+		"memo":   []byte{88},
+	}
+	if err := abi.UnpackIntoMap(receivedMap, "received", data); err != nil {
+		t.Error(err)
+	}
+	if len(receivedMap) != 3 {
+		t.Error("unpacked `received` map expected to have length 3")
+	}
+	if receivedMap["sender"] != expectedReceivedMap["sender"] {
+		t.Error("unpacked `received` map does not match expected map")
+	}
+	if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
+		t.Error("unpacked `received` map does not match expected map")
+	}
+	if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
+		t.Error("unpacked `received` map does not match expected map")
+	}
+
+	receivedAddrMap := map[string]interface{}{}
+	if err = abi.UnpackIntoMap(receivedAddrMap, "receivedAddr", data); err != nil {
+		t.Error(err)
+	}
+	if len(receivedAddrMap) != 1 {
+		t.Error("unpacked `receivedAddr` map expected to have length 1")
+	}
+	if receivedAddrMap["sender"] != expectedReceivedMap["sender"] {
+		t.Error("unpacked `receivedAddr` map does not match expected map")
+	}
+}
+
+func TestUnpackMethodIntoMap(t *testing.T) {
+	const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"send","outputs":[{"name":"amount","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"get","outputs":[{"name":"hash","type":"bytes"}],"payable":true,"stateMutability":"payable","type":"function"}]`
+	abi, err := JSON(strings.NewReader(abiJSON))
+	if err != nil {
+		t.Fatal(err)
+	}
+	const hexdata = `00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000015800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000158000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001580000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000015800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000158`
+	data, err := hex.DecodeString(hexdata)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(data)%32 != 0 {
+		t.Errorf("len(data) is %d, want a multiple of 32", len(data))
+	}
+
+	// Tests a method with no outputs
+	receiveMap := map[string]interface{}{}
+	if err = abi.UnpackIntoMap(receiveMap, "receive", data); err != nil {
+		t.Error(err)
+	}
+	if len(receiveMap) > 0 {
+		t.Error("unpacked `receive` map expected to have length 0")
+	}
+
+	// Tests a method with only outputs
+	sendMap := map[string]interface{}{}
+	if err = abi.UnpackIntoMap(sendMap, "send", data); err != nil {
+		t.Error(err)
+	}
+	if len(sendMap) != 1 {
+		t.Error("unpacked `send` map expected to have length 1")
+	}
+	if sendMap["amount"].(*big.Int).Cmp(big.NewInt(1)) != 0 {
+		t.Error("unpacked `send` map expected `amount` value of 1")
+	}
+
+	// Tests a method with outputs and inputs
+	getMap := map[string]interface{}{}
+	if err = abi.UnpackIntoMap(getMap, "get", data); err != nil {
+		t.Error(err)
+	}
+	if len(sendMap) != 1 {
+		t.Error("unpacked `get` map expected to have length 1")
+	}
+	expectedBytes := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 88, 0}
+	if !bytes.Equal(getMap["hash"].([]byte), expectedBytes) {
+		t.Errorf("unpacked `get` map expected `hash` value of %v", expectedBytes)
+	}
+}
+
+func TestUnpackIntoMapNamingConflict(t *testing.T) {
+	// Two methods have the same name
+	var abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"get","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"send","outputs":[{"name":"amount","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"get","outputs":[{"name":"hash","type":"bytes"}],"payable":true,"stateMutability":"payable","type":"function"}]`
+	abi, err := JSON(strings.NewReader(abiJSON))
+	if err != nil {
+		t.Fatal(err)
+	}
+	var hexdata = `00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
+	data, err := hex.DecodeString(hexdata)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(data)%32 == 0 {
+		t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
+	}
+	getMap := map[string]interface{}{}
+	if err = abi.UnpackIntoMap(getMap, "get", data); err == nil {
+		t.Error("naming conflict between two methods; error expected")
+	}
+
+	// Two events have the same name
+	abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"received","type":"event"}]`
+	abi, err = JSON(strings.NewReader(abiJSON))
+	if err != nil {
+		t.Fatal(err)
+	}
+	hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
+	data, err = hex.DecodeString(hexdata)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(data)%32 == 0 {
+		t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
+	}
+	receivedMap := map[string]interface{}{}
+	if err = abi.UnpackIntoMap(receivedMap, "received", data); err != nil {
+		t.Error("naming conflict between two events; no error expected")
+	}
+	if len(receivedMap) != 1 {
+		t.Error("naming conflict between two events; event defined latest in the abi expected to be used")
+	}
+
+	// Method and event have the same name
+	abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"received","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
+	abi, err = JSON(strings.NewReader(abiJSON))
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(data)%32 == 0 {
+		t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
+	}
+	if err = abi.UnpackIntoMap(receivedMap, "received", data); err == nil {
+		t.Error("naming conflict between an event and a method; error expected")
+	}
+
+	// Conflict is case sensitive
+	abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"received","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"Received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
+	abi, err = JSON(strings.NewReader(abiJSON))
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(data)%32 == 0 {
+		t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
+	}
+	expectedReceivedMap := map[string]interface{}{
+		"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
+		"amount": big.NewInt(1),
+		"memo":   []byte{88},
+	}
+	if err = abi.UnpackIntoMap(receivedMap, "Received", data); err != nil {
+		t.Error(err)
+	}
+	if len(receivedMap) != 3 {
+		t.Error("unpacked `received` map expected to have length 3")
+	}
+	if receivedMap["sender"] != expectedReceivedMap["sender"] {
+		t.Error("unpacked `received` map does not match expected map")
+	}
+	if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
+		t.Error("unpacked `received` map does not match expected map")
+	}
+	if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
+		t.Error("unpacked `received` map does not match expected map")
+	}
+}
+
 func TestABI_MethodById(t *testing.T) {
 	const abiJSON = `[
 		{"type":"function","name":"receive","constant":false,"inputs":[{"name":"memo","type":"bytes"}],"outputs":[],"payable":true,"stateMutability":"payable"},
diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go
index d0a6b035c66a0d45ce30fc280d3e2d67d1094bf4..501cb1621397898eb0fc053a2ca1d54916fbefa3 100644
--- a/accounts/abi/argument.go
+++ b/accounts/abi/argument.go
@@ -102,6 +102,16 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error {
 	return arguments.unpackAtomic(v, marshalledValues[0])
 }
 
+// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value
+func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
+	marshalledValues, err := arguments.UnpackValues(data)
+	if err != nil {
+		return err
+	}
+
+	return arguments.unpackIntoMap(v, marshalledValues)
+}
+
 // unpack sets the unmarshalled value to go format.
 // Note the dst here must be settable.
 func unpack(t *Type, dst interface{}, src interface{}) error {
@@ -160,6 +170,19 @@ func unpack(t *Type, dst interface{}, src interface{}) error {
 	return nil
 }
 
+// unpackIntoMap unpacks marshalledValues into the provided map[string]interface{}
+func (arguments Arguments) unpackIntoMap(v map[string]interface{}, marshalledValues []interface{}) error {
+	// Make sure map is not nil
+	if v == nil {
+		return fmt.Errorf("abi: cannot unpack into a nil map")
+	}
+
+	for i, arg := range arguments.NonIndexed() {
+		v[arg.Name] = marshalledValues[i]
+	}
+	return nil
+}
+
 // unpackAtomic unpacks ( hexdata -> go ) a single value
 func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interface{}) error {
 	if arguments.LengthNonIndexed() == 0 {
diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go
index c37bdf11d53ac865f7de0d00a9827cf2ab309a69..f70f911d37b2bae9344245be9adaae0631bf1a70 100644
--- a/accounts/abi/bind/base.go
+++ b/accounts/abi/bind/base.go
@@ -340,6 +340,22 @@ func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log)
 	return parseTopics(out, indexed, log.Topics[1:])
 }
 
+// UnpackLogIntoMap unpacks a retrieved log into the provided map.
+func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error {
+	if len(log.Data) > 0 {
+		if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil {
+			return err
+		}
+	}
+	var indexed abi.Arguments
+	for _, arg := range c.abi.Events[event].Inputs {
+		if arg.Indexed {
+			indexed = append(indexed, arg)
+		}
+	}
+	return parseTopicsIntoMap(out, indexed, log.Topics[1:])
+}
+
 // ensureContext is a helper method to ensure a context is not nil, even if the
 // user specified it as such.
 func ensureContext(ctx context.Context) context.Context {
diff --git a/accounts/abi/bind/base_test.go b/accounts/abi/bind/base_test.go
index 02caf457a444e6207b438f92069653e18e0cf33b..f65c9e9b496375f9ce067eee4a1ac538001cc126 100644
--- a/accounts/abi/bind/base_test.go
+++ b/accounts/abi/bind/base_test.go
@@ -17,14 +17,20 @@
 package bind_test
 
 import (
+	"bytes"
 	"context"
 	"math/big"
+	"strings"
 	"testing"
 
-	ethereum "github.com/ethereum/go-ethereum"
+	"github.com/ethereum/go-ethereum"
 	"github.com/ethereum/go-ethereum/accounts/abi"
 	"github.com/ethereum/go-ethereum/accounts/abi/bind"
 	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/hexutil"
+	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/crypto"
+	"github.com/ethereum/go-ethereum/rlp"
 )
 
 type mockCaller struct {
@@ -41,7 +47,6 @@ func (mc *mockCaller) CallContract(ctx context.Context, call ethereum.CallMsg, b
 	mc.callContractBlockNumber = blockNumber
 	return nil, nil
 }
-
 func TestPassingBlockNumber(t *testing.T) {
 
 	mc := &mockCaller{}
@@ -78,3 +83,291 @@ func TestPassingBlockNumber(t *testing.T) {
 		t.Fatalf("CodeAt() was passed a block number when it should not have been")
 	}
 }
+
+const hexData = "0x000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158"
+
+func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
+	hash := crypto.Keccak256Hash([]byte("testName"))
+	mockLog := types.Log{
+		Address: common.HexToAddress("0x0"),
+		Topics: []common.Hash{
+			common.HexToHash("0x0"),
+			hash,
+		},
+		Data:        hexutil.MustDecode(hexData),
+		BlockNumber: uint64(26),
+		TxHash:      common.HexToHash("0x0"),
+		TxIndex:     111,
+		BlockHash:   common.BytesToHash([]byte{1, 2, 3, 4, 5}),
+		Index:       7,
+		Removed:     false,
+	}
+
+	abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"string"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
+	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
+	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
+
+	receivedMap := make(map[string]interface{})
+	expectedReceivedMap := map[string]interface{}{
+		"name":   hash,
+		"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
+		"amount": big.NewInt(1),
+		"memo":   []byte{88},
+	}
+	if err := bc.UnpackLogIntoMap(receivedMap, "received", mockLog); err != nil {
+		t.Error(err)
+	}
+
+	if len(receivedMap) != 4 {
+		t.Fatal("unpacked map expected to have length 4")
+	}
+	if receivedMap["name"] != expectedReceivedMap["name"] {
+		t.Error("unpacked map does not match expected map")
+	}
+	if receivedMap["sender"] != expectedReceivedMap["sender"] {
+		t.Error("unpacked map does not match expected map")
+	}
+	if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
+		t.Error("unpacked map does not match expected map")
+	}
+	if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
+		t.Error("unpacked map does not match expected map")
+	}
+}
+
+func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
+	sliceBytes, err := rlp.EncodeToBytes([]string{"name1", "name2", "name3", "name4"})
+	if err != nil {
+		t.Fatal(err)
+	}
+	hash := crypto.Keccak256Hash(sliceBytes)
+	mockLog := types.Log{
+		Address: common.HexToAddress("0x0"),
+		Topics: []common.Hash{
+			common.HexToHash("0x0"),
+			hash,
+		},
+		Data:        hexutil.MustDecode(hexData),
+		BlockNumber: uint64(26),
+		TxHash:      common.HexToHash("0x0"),
+		TxIndex:     111,
+		BlockHash:   common.BytesToHash([]byte{1, 2, 3, 4, 5}),
+		Index:       7,
+		Removed:     false,
+	}
+
+	abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"names","type":"string[]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
+	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
+	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
+
+	receivedMap := make(map[string]interface{})
+	expectedReceivedMap := map[string]interface{}{
+		"names":  hash,
+		"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
+		"amount": big.NewInt(1),
+		"memo":   []byte{88},
+	}
+	if err := bc.UnpackLogIntoMap(receivedMap, "received", mockLog); err != nil {
+		t.Error(err)
+	}
+
+	if len(receivedMap) != 4 {
+		t.Fatal("unpacked map expected to have length 4")
+	}
+	if receivedMap["names"] != expectedReceivedMap["names"] {
+		t.Error("unpacked map does not match expected map")
+	}
+	if receivedMap["sender"] != expectedReceivedMap["sender"] {
+		t.Error("unpacked map does not match expected map")
+	}
+	if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
+		t.Error("unpacked map does not match expected map")
+	}
+	if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
+		t.Error("unpacked map does not match expected map")
+	}
+}
+
+func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
+	arrBytes, err := rlp.EncodeToBytes([2]common.Address{common.HexToAddress("0x0"), common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")})
+	if err != nil {
+		t.Fatal(err)
+	}
+	hash := crypto.Keccak256Hash(arrBytes)
+	mockLog := types.Log{
+		Address: common.HexToAddress("0x0"),
+		Topics: []common.Hash{
+			common.HexToHash("0x0"),
+			hash,
+		},
+		Data:        hexutil.MustDecode(hexData),
+		BlockNumber: uint64(26),
+		TxHash:      common.HexToHash("0x0"),
+		TxIndex:     111,
+		BlockHash:   common.BytesToHash([]byte{1, 2, 3, 4, 5}),
+		Index:       7,
+		Removed:     false,
+	}
+
+	abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"addresses","type":"address[2]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
+	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
+	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
+
+	receivedMap := make(map[string]interface{})
+	expectedReceivedMap := map[string]interface{}{
+		"addresses": hash,
+		"sender":    common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
+		"amount":    big.NewInt(1),
+		"memo":      []byte{88},
+	}
+	if err := bc.UnpackLogIntoMap(receivedMap, "received", mockLog); err != nil {
+		t.Error(err)
+	}
+
+	if len(receivedMap) != 4 {
+		t.Fatal("unpacked map expected to have length 4")
+	}
+	if receivedMap["addresses"] != expectedReceivedMap["addresses"] {
+		t.Error("unpacked map does not match expected map")
+	}
+	if receivedMap["sender"] != expectedReceivedMap["sender"] {
+		t.Error("unpacked map does not match expected map")
+	}
+	if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
+		t.Error("unpacked map does not match expected map")
+	}
+	if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
+		t.Error("unpacked map does not match expected map")
+	}
+}
+
+func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
+	mockAddress := common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")
+	addrBytes := mockAddress.Bytes()
+	hash := crypto.Keccak256Hash([]byte("mockFunction(address,uint)"))
+	functionSelector := hash[:4]
+	functionTyBytes := append(addrBytes, functionSelector...)
+	var functionTy [24]byte
+	copy(functionTy[:], functionTyBytes[0:24])
+	mockLog := types.Log{
+		Address: common.HexToAddress("0x0"),
+		Topics: []common.Hash{
+			common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
+			common.BytesToHash(functionTyBytes),
+		},
+		Data:        hexutil.MustDecode(hexData),
+		BlockNumber: uint64(26),
+		TxHash:      common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"),
+		TxIndex:     111,
+		BlockHash:   common.BytesToHash([]byte{1, 2, 3, 4, 5}),
+		Index:       7,
+		Removed:     false,
+	}
+
+	abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"function","type":"function"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
+	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
+	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
+
+	receivedMap := make(map[string]interface{})
+	expectedReceivedMap := map[string]interface{}{
+		"function": functionTy,
+		"sender":   common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
+		"amount":   big.NewInt(1),
+		"memo":     []byte{88},
+	}
+	if err := bc.UnpackLogIntoMap(receivedMap, "received", mockLog); err != nil {
+		t.Error(err)
+	}
+
+	if len(receivedMap) != 4 {
+		t.Fatal("unpacked map expected to have length 4")
+	}
+	if receivedMap["function"] != expectedReceivedMap["function"] {
+		t.Error("unpacked map does not match expected map")
+	}
+	if receivedMap["sender"] != expectedReceivedMap["sender"] {
+		t.Error("unpacked map does not match expected map")
+	}
+	if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
+		t.Error("unpacked map does not match expected map")
+	}
+	if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
+		t.Error("unpacked map does not match expected map")
+	}
+}
+
+func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
+	byts := []byte{1, 2, 3, 4, 5}
+	hash := crypto.Keccak256Hash(byts)
+	mockLog := types.Log{
+		Address: common.HexToAddress("0x0"),
+		Topics: []common.Hash{
+			common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
+			hash,
+		},
+		Data:        hexutil.MustDecode(hexData),
+		BlockNumber: uint64(26),
+		TxHash:      common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"),
+		TxIndex:     111,
+		BlockHash:   common.BytesToHash([]byte{1, 2, 3, 4, 5}),
+		Index:       7,
+		Removed:     false,
+	}
+
+	abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"content","type":"bytes"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
+	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
+	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
+
+	receivedMap := make(map[string]interface{})
+	expectedReceivedMap := map[string]interface{}{
+		"content": hash,
+		"sender":  common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
+		"amount":  big.NewInt(1),
+		"memo":    []byte{88},
+	}
+	if err := bc.UnpackLogIntoMap(receivedMap, "received", mockLog); err != nil {
+		t.Error(err)
+	}
+
+	if len(receivedMap) != 4 {
+		t.Fatal("unpacked map expected to have length 4")
+	}
+	if receivedMap["content"] != expectedReceivedMap["content"] {
+		t.Error("unpacked map does not match expected map")
+	}
+	if receivedMap["sender"] != expectedReceivedMap["sender"] {
+		t.Error("unpacked map does not match expected map")
+	}
+	if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
+		t.Error("unpacked map does not match expected map")
+	}
+	if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
+		t.Error("unpacked map does not match expected map")
+	}
+}
+
+func TestUnpackIntoMapNamingConflict(t *testing.T) {
+	hash := crypto.Keccak256Hash([]byte("testName"))
+	mockLog := types.Log{
+		Address: common.HexToAddress("0x0"),
+		Topics: []common.Hash{
+			common.HexToHash("0x0"),
+			hash,
+		},
+		Data:        hexutil.MustDecode(hexData),
+		BlockNumber: uint64(26),
+		TxHash:      common.HexToHash("0x0"),
+		TxIndex:     111,
+		BlockHash:   common.BytesToHash([]byte{1, 2, 3, 4, 5}),
+		Index:       7,
+		Removed:     false,
+	}
+
+	abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"string"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"received","type":"event"}]`
+	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
+	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
+	receivedMap := make(map[string]interface{})
+	if err := bc.UnpackLogIntoMap(receivedMap, "received", mockLog); err == nil {
+		t.Error("naming conflict between two events; error expected")
+	}
+}
diff --git a/accounts/abi/bind/topics.go b/accounts/abi/bind/topics.go
index af9c272cdb51699f776eb4686e67039c58fa4dc7..c7657b4a41a2591cc83a59a952b867f1bdc1428f 100644
--- a/accounts/abi/bind/topics.go
+++ b/accounts/abi/bind/topics.go
@@ -17,6 +17,7 @@
 package bind
 
 import (
+	"encoding/binary"
 	"errors"
 	"fmt"
 	"math/big"
@@ -191,3 +192,50 @@ func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) er
 	}
 	return nil
 }
+
+// parseTopicsIntoMap converts the indexed topic field-value pairs into map key-value pairs
+func parseTopicsIntoMap(out map[string]interface{}, fields abi.Arguments, topics []common.Hash) error {
+	// Sanity check that the fields and topics match up
+	if len(fields) != len(topics) {
+		return errors.New("topic/field count mismatch")
+	}
+	// Iterate over all the fields and reconstruct them from topics
+	for _, arg := range fields {
+		if !arg.Indexed {
+			return errors.New("non-indexed field in topic reconstruction")
+		}
+
+		switch arg.Type.T {
+		case abi.BoolTy:
+			out[arg.Name] = topics[0][common.HashLength-1] == 1
+		case abi.IntTy, abi.UintTy:
+			num := new(big.Int).SetBytes(topics[0][:])
+			out[arg.Name] = num
+		case abi.AddressTy:
+			var addr common.Address
+			copy(addr[:], topics[0][common.HashLength-common.AddressLength:])
+			out[arg.Name] = addr
+		case abi.HashTy:
+			out[arg.Name] = topics[0]
+		case abi.FixedBytesTy:
+			out[arg.Name] = topics[0][:]
+		case abi.StringTy, abi.BytesTy, abi.SliceTy, abi.ArrayTy:
+			// Array types (including strings and bytes) have their keccak256 hashes stored in the topic- not a hash
+			// whose bytes can be decoded to the actual value- so the best we can do is retrieve that hash
+			out[arg.Name] = topics[0]
+		case abi.FunctionTy:
+			if garbage := binary.BigEndian.Uint64(topics[0][0:8]); garbage != 0 {
+				return fmt.Errorf("bind: got improperly encoded function type, got %v", topics[0].Bytes())
+			}
+			var tmp [24]byte
+			copy(tmp[:], topics[0][8:32])
+			out[arg.Name] = tmp
+		default: // Not handling tuples
+			return fmt.Errorf("unsupported indexed type: %v", arg.Type)
+		}
+
+		topics = topics[1:]
+	}
+
+	return nil
+}
diff --git a/accounts/abi/unpack.go b/accounts/abi/unpack.go
index 8406b09c801d6be65b6bd264efb55687f00ed0d4..b2e61d06c41647bb018dd3749a8177e4ff683df9 100644
--- a/accounts/abi/unpack.go
+++ b/accounts/abi/unpack.go
@@ -269,7 +269,7 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
 	totalSize.Add(totalSize, bigOffsetEnd)
 	totalSize.Add(totalSize, lengthBig)
 	if totalSize.BitLen() > 63 {
-		return 0, 0, fmt.Errorf("abi length larger than int64: %v", totalSize)
+		return 0, 0, fmt.Errorf("abi: length larger than int64: %v", totalSize)
 	}
 
 	if totalSize.Cmp(outputLength) > 0 {