diff --git a/account/address.go b/account/address.go
index 2340afd48a4fd4c4b52f8beceddc29dd5e1481b6..70353baccef9a398392e0a7be6d596e611523064 100644
--- a/account/address.go
+++ b/account/address.go
@@ -1,6 +1,7 @@
 package account
 
 import (
+	"bytes"
 	"encoding/json"
 	"fmt"
 
@@ -11,6 +12,19 @@ import (
 
 type Address binary.Word160
 
+type Addresses []Address
+
+func (as Addresses) Len() int {
+	return len(as)
+}
+
+func (as Addresses) Less(i, j int) bool {
+	return bytes.Compare(as[i][:], as[j][:]) < 0
+}
+func (as Addresses) Swap(i, j int) {
+	as[i], as[j] = as[j], as[i]
+}
+
 const AddressHexLength = 2 * binary.Word160Length
 
 var ZeroAddress = Address{}
diff --git a/account/address_test.go b/account/address_test.go
index 3ea47daa8672bfbd3528bc749732bc1f17c10cd7..5a1572b34d1d763435b2d3961d597ab9a56dd1e5 100644
--- a/account/address_test.go
+++ b/account/address_test.go
@@ -1,9 +1,9 @@
 package account
 
 import (
-	"testing"
-
 	"encoding/json"
+	"sort"
+	"testing"
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -70,3 +70,19 @@ func TestAddress_Length(t *testing.T) {
 	err = addrOut.UnmarshalText(([]byte)("49EA30FCAE731BDE36742F85901549F515EA1A1020"))
 	assert.Error(t, err, "address too long")
 }
+
+func TestAddress_Sort(t *testing.T) {
+	addresses := Addresses{
+		{2, 3, 4},
+		{3, 1, 2},
+		{2, 1, 2},
+	}
+	sorted := make(Addresses, len(addresses))
+	copy(sorted, addresses)
+	sort.Stable(sorted)
+	assert.Equal(t, Addresses{
+		{2, 1, 2},
+		{2, 3, 4},
+		{3, 1, 2},
+	}, sorted)
+}
diff --git a/account/state_cache.go b/account/state_cache.go
new file mode 100644
index 0000000000000000000000000000000000000000..723126a2a1e66b2e31513f8f64f1e2234174f751
--- /dev/null
+++ b/account/state_cache.go
@@ -0,0 +1,191 @@
+// Copyright 2017 Monax Industries Limited
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package account
+
+import (
+	"fmt"
+	"sort"
+
+	"github.com/hyperledger/burrow/binary"
+)
+
+type StateCache struct {
+	backend  StateReader
+	accounts map[Address]*accountInfo
+}
+
+type accountInfo struct {
+	account Account
+	storage map[binary.Word256]binary.Word256
+	removed bool
+	updated bool
+}
+
+var _ StateWriter = &StateCache{}
+
+func NewStateCache(backend StateReader) *StateCache {
+	return &StateCache{
+		backend:  backend,
+		accounts: make(map[Address]*accountInfo),
+	}
+}
+
+func (cache *StateCache) GetAccount(address Address) (Account, error) {
+	accInfo, err := cache.get(address)
+	if err != nil {
+		return nil, err
+	}
+	if accInfo.removed {
+		return nil, nil
+	}
+
+	if accInfo.account == nil {
+		// fill cache
+		account, err := cache.backend.GetAccount(address)
+		if err != nil {
+			return nil, err
+		}
+		accInfo.account = account
+	}
+	return accInfo.account, nil
+}
+
+func (cache *StateCache) UpdateAccount(account Account) error {
+	accInfo, err := cache.get(account.Address())
+	if err != nil {
+		return err
+	}
+	if accInfo.removed {
+		return fmt.Errorf("UpdateAccount on a removed account %s", account.Address())
+	}
+	accInfo.account = account
+	accInfo.updated = true
+	return nil
+}
+
+func (cache *StateCache) RemoveAccount(address Address) error {
+	accInfo, err := cache.get(address)
+	if err != nil {
+		return err
+	}
+	if accInfo.removed {
+		fmt.Errorf("RemoveAccount on a removed account %s", address)
+	} else {
+		accInfo.removed = true
+	}
+	return nil
+}
+
+func (cache *StateCache) GetStorage(address Address, key binary.Word256) (binary.Word256, error) {
+	accInfo, err := cache.get(address)
+	if err != nil {
+		return binary.Zero256, err
+	}
+	// Check cache
+	value, ok := accInfo.storage[key]
+	if ok {
+		return value, nil
+	} else {
+		// Load from backend
+		value, err := cache.backend.GetStorage(address, key)
+		if err != nil {
+			return binary.Zero256, err
+		}
+		accInfo.storage[key] = value
+		return value, nil
+	}
+}
+
+// NOTE: Set value to zero to remove.
+func (cache *StateCache) SetStorage(address Address, key binary.Word256, value binary.Word256) error {
+	accInfo, err := cache.get(address)
+	if err != nil {
+		return err
+	}
+	if accInfo.removed {
+		return fmt.Errorf("SetStorage on a removed account %s", address)
+	}
+	accInfo.storage[key] = value
+	accInfo.updated = true
+	return nil
+}
+
+// Syncs changes to the backend in deterministic order. Sends storage updates before updating
+// the account they belong so that storage values can be taken account of in the update.
+func (cache *StateCache) Sync(state StateWriter) error {
+	var addresses Addresses
+	for address := range cache.accounts {
+		addresses = append(addresses, address)
+	}
+
+	sort.Stable(addresses)
+	for _, address := range addresses {
+		accInfo := cache.accounts[address]
+		if accInfo.removed {
+			err := state.RemoveAccount(address)
+			if err != nil {
+				return err
+			}
+		} else if accInfo.updated {
+			var keys binary.Words256
+			for key := range accInfo.storage {
+				keys = append(keys, key)
+			}
+			// First update keys
+			sort.Stable(keys)
+			for _, key := range keys {
+				value := accInfo.storage[key]
+				err := state.SetStorage(address, key, value)
+				if err != nil {
+					return err
+				}
+			}
+			// Update account - this gives backend the opportunity to update StorageRoot after calculating the new
+			// value from any storage value updates
+			err := state.UpdateAccount(accInfo.account)
+			if err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+// Resets the cache to empty initialising the backing map to the same size as the previous iteration.
+func (cache *StateCache) Reset(backend StateReader) {
+	cache.backend = backend
+	cache.accounts = make(map[Address]*accountInfo, len(cache.accounts))
+}
+
+func (cache *StateCache) Backend() StateReader {
+	return cache.backend
+}
+
+// Get the cache accountInfo item creating it if necessary
+func (cache *StateCache) get(address Address) (*accountInfo, error) {
+	accInfo := cache.accounts[address]
+	if accInfo == nil {
+		account, err := cache.backend.GetAccount(address)
+		if err != nil {
+			return nil, err
+		}
+		accInfo = &accountInfo{
+			account: account,
+			storage: make(map[binary.Word256]binary.Word256),
+		}
+		cache.accounts[address] = accInfo
+	}
+	return accInfo, nil
+}
diff --git a/account/state_cache_test.go b/account/state_cache_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..cc40d7e94834e28c401dec199f0a11974d9bcd9b
--- /dev/null
+++ b/account/state_cache_test.go
@@ -0,0 +1,62 @@
+package account
+
+import (
+	"testing"
+
+	"fmt"
+
+	"github.com/hyperledger/burrow/binary"
+)
+
+type testStateReader struct {
+	Accounts map[Address]Account
+	Storage  map[Address]map[binary.Word256]binary.Word256
+}
+
+func accountAndStorage(account Account, keyvals ...binary.Word256) *testStateReader {
+	return &testStateReader{}
+
+}
+
+func (tsr *testStateReader) GetAccount(address Address) (Account, error) {
+	account, ok := tsr.Accounts[address]
+	if !ok {
+		return nil, fmt.Errorf("could not find account %s", address)
+	}
+	return account, nil
+}
+
+func (tsr *testStateReader) GetStorage(address Address, key binary.Word256) (binary.Word256, error) {
+	storage, ok := tsr.Storage[address]
+	if !ok {
+		return binary.Zero256, fmt.Errorf("could not find storage for account %s", address)
+	}
+	value, ok := storage[key]
+	if !ok {
+		return binary.Zero256, fmt.Errorf("could not find key %x for account %s", key, address)
+	}
+	return value, nil
+}
+
+var _ StateReader = &testStateReader{}
+
+func TestStateCache_GetAccount(t *testing.T) {
+}
+
+func TestStateCache_UpdateAccount(t *testing.T) {
+}
+
+func TestStateCache_RemoveAccount(t *testing.T) {
+}
+
+func TestStateCache_GetStorage(t *testing.T) {
+}
+
+func TestStateCache_SetStorage(t *testing.T) {
+}
+
+func TestStateCache_Sync(t *testing.T) {
+}
+
+func TestStateCache_get(t *testing.T) {
+}
diff --git a/binary/word256.go b/binary/word256.go
index 02a90581f9c297f49c9fe08369755efa82694b3f..7bdbb4ec0e63921e44ff8b3191b2485aa3a8cb6f 100644
--- a/binary/word256.go
+++ b/binary/word256.go
@@ -114,6 +114,20 @@ func Int64FromWord256(word Word256) int64 {
 
 //-------------------------------------
 
+type Words256 []Word256
+
+func (ws Words256) Len() int {
+	return len(ws)
+}
+
+func (ws Words256) Less(i, j int) bool {
+	return ws[i].Compare(ws[j]) < 0
+}
+
+func (ws Words256) Swap(i, j int) {
+	ws[i], ws[j] = ws[j], ws[i]
+}
+
 type Tuple256 struct {
 	First  Word256
 	Second Word256
diff --git a/execution/execution.go b/execution/execution.go
index 0689e6c9be77a40901f6344f58be6e5db10005de..f62560d7b1f122fd2bc908729c299e4a16e1e094 100644
--- a/execution/execution.go
+++ b/execution/execution.go
@@ -320,7 +320,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 				callee  acm.MutableAccount = nil // initialized below
 				code    []byte             = nil
 				ret     []byte             = nil
-				txCache                    = NewTxCache(exe.blockCache)
+				txCache                    = acm.NewStateCache(exe.blockCache)
 				params                     = evm.Params{
 					BlockHeight: exe.tip.LastBlockHeight(),
 					BlockHash:   binary.LeftPadWord256(exe.tip.LastBlockHash()),
diff --git a/execution/transactor.go b/execution/transactor.go
index e527fe5fd227342176a32a71e8ebc8a050130098..e4642112a3d5cc1cc8102200aee34477915f3d1b 100644
--- a/execution/transactor.go
+++ b/execution/transactor.go
@@ -98,7 +98,7 @@ func (trans *transactor) Call(fromAddress, toAddress acm.Address, data []byte) (
 		return nil, fmt.Errorf("account %s does not exist", toAddress)
 	}
 	caller := acm.ConcreteAccount{Address: fromAddress}.MutableAccount()
-	txCache := NewTxCache(trans.state)
+	txCache := acm.NewStateCache(trans.state)
 	params := vmParams(trans.blockchain)
 
 	vmach := evm.NewVM(txCache, evm.DefaultDynamicMemoryProvider, params, caller.Address(), nil,
@@ -120,7 +120,7 @@ func (trans *transactor) CallCode(fromAddress acm.Address, code, data []byte) (*
 	// This was being run against CheckTx cache, need to understand the reasoning
 	callee := acm.ConcreteAccount{Address: fromAddress}.MutableAccount()
 	caller := acm.ConcreteAccount{Address: fromAddress}.MutableAccount()
-	txCache := NewTxCache(trans.state)
+	txCache := acm.NewStateCache(trans.state)
 	params := vmParams(trans.blockchain)
 
 	vmach := evm.NewVM(txCache, evm.DefaultDynamicMemoryProvider, params, caller.Address(), nil,
diff --git a/execution/tx_cache.go b/execution/tx_cache.go
deleted file mode 100644
index 4dc56485f739954c5ab9f5c7e0f70c210c66a826..0000000000000000000000000000000000000000
--- a/execution/tx_cache.go
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2017 Monax Industries Limited
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package execution
-
-import (
-	"fmt"
-
-	acm "github.com/hyperledger/burrow/account"
-	"github.com/hyperledger/burrow/binary"
-)
-
-type TxCache struct {
-	backend  acm.StateReader
-	accounts map[acm.Address]vmAccountInfo
-	storages map[binary.Tuple256]binary.Word256
-}
-
-var _ acm.StateWriter = &TxCache{}
-
-func NewTxCache(backend acm.StateReader) *TxCache {
-	return &TxCache{
-		backend:  backend,
-		accounts: make(map[acm.Address]vmAccountInfo),
-		storages: make(map[binary.Tuple256]binary.Word256),
-	}
-}
-
-//-------------------------------------
-// TxCache.account
-
-func (cache *TxCache) GetAccount(addr acm.Address) (acm.Account, error) {
-	acc, removed := cache.accounts[addr].unpack()
-	if removed {
-		return nil, nil
-	} else if acc == nil {
-		return cache.backend.GetAccount(addr)
-	}
-	return acc, nil
-}
-
-func (cache *TxCache) UpdateAccount(acc acm.Account) error {
-	_, removed := cache.accounts[acc.Address()].unpack()
-	if removed {
-		return fmt.Errorf("UpdateAccount on a removed account %s", acc.Address())
-	}
-	cache.accounts[acc.Address()] = vmAccountInfo{acc, false}
-	return nil
-}
-
-func (cache *TxCache) RemoveAccount(addr acm.Address) error {
-	acc, removed := cache.accounts[addr].unpack()
-	if removed {
-		fmt.Errorf("RemoveAccount on a removed account %s", addr)
-	}
-	cache.accounts[addr] = vmAccountInfo{acc, true}
-	return nil
-}
-
-// TxCache.account
-//-------------------------------------
-// TxCache.storage
-
-func (cache *TxCache) GetStorage(addr acm.Address, key binary.Word256) (binary.Word256, error) {
-	// Check cache
-	value, ok := cache.storages[binary.Tuple256{First: addr.Word256(), Second: key}]
-	if ok {
-		return value, nil
-	}
-
-	// Load from backend
-	return cache.backend.GetStorage(addr, key)
-}
-
-// NOTE: Set value to zero to removed from the trie.
-func (cache *TxCache) SetStorage(addr acm.Address, key binary.Word256, value binary.Word256) error {
-	_, removed := cache.accounts[addr].unpack()
-	if removed {
-		fmt.Errorf("SetStorage on a removed account %s", addr)
-	}
-	cache.storages[binary.Tuple256{First: addr.Word256(), Second: key}] = value
-	return nil
-}
-
-// TxCache.storage
-//-------------------------------------
-
-// These updates do not have to be in deterministic order,
-// the backend is responsible for ordering updates.
-func (cache *TxCache) Sync(backend acm.StateWriter) {
-	// Remove or update storage
-	for addrKey, value := range cache.storages {
-		addrWord256, key := binary.Tuple256Split(addrKey)
-		backend.SetStorage(acm.AddressFromWord256(addrWord256), key, value)
-	}
-
-	// Remove or update accounts
-	for addr, accInfo := range cache.accounts {
-		acc, removed := accInfo.unpack()
-		if removed {
-			backend.RemoveAccount(addr)
-		} else {
-			backend.UpdateAccount(acc)
-		}
-	}
-}
-
-//-----------------------------------------------------------------------------
-
-type vmAccountInfo struct {
-	account acm.Account
-	removed bool
-}
-
-func (accInfo vmAccountInfo) unpack() (acm.Account, bool) {
-	return accInfo.account, accInfo.removed
-}