diff --git a/account/state/state_cache.go b/account/state/state_cache.go
index 75dd70b1cf33be132d2685990a2ade2d302fb466..869842431bc3213eda869d65103ddf384d13738f 100644
--- a/account/state/state_cache.go
+++ b/account/state/state_cache.go
@@ -193,7 +193,7 @@ func (cache *stateCache) Sync(state Writer) error {
 		addresses = append(addresses, address)
 	}
 
-	sort.Stable(addresses)
+	sort.Sort(addresses)
 	for _, address := range addresses {
 		accInfo := cache.accounts[address]
 		accInfo.RLock()
@@ -208,7 +208,7 @@ func (cache *stateCache) Sync(state Writer) error {
 				keys = append(keys, key)
 			}
 			// First update keys
-			sort.Stable(keys)
+			sort.Sort(keys)
 			for _, key := range keys {
 				value := accInfo.storage[key]
 				err := state.SetStorage(address, key, value)
diff --git a/account/validator.go b/account/validator.go
index 017b1eb2a180036f22665aaac2b8ff5fd6750ba6..121b76fd234a4b27303e47bbef4f1b1f83b80f13 100644
--- a/account/validator.go
+++ b/account/validator.go
@@ -10,9 +10,6 @@ type Validator interface {
 	Addressable
 	// The validator's voting power
 	Power() uint64
-	// Alter the validator's voting power by amount that can be negative or positive.
-	// A power of 0 effectively unbonds the validator
-	WithNewPower(uint64) Validator
 }
 
 // Neither abci_types or tm_types has quite the representation we want
diff --git a/account/validator_test.go b/account/validator_test.go
index ff660300316d650d7e3ab64ff89ae4b88effecd4..0bc76357b7691133223a361dd82e5f7dde55f285 100644
--- a/account/validator_test.go
+++ b/account/validator_test.go
@@ -1,14 +1 @@
 package account
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestAlterPower(t *testing.T) {
-	val := AsValidator(NewConcreteAccountFromSecret("seeeeecret").Account())
-	valInc := val.WithNewPower(2442132)
-	assert.Equal(t, uint64(0), val.Power())
-	assert.Equal(t, uint64(2442132), valInc.Power())
-}
diff --git a/binary/integer.go b/binary/integer.go
index cd57bf3a1b8ccf9536a31318b6c52d0f8a8cc624..5824a4a9a9efbdb0a596765dbbf004ede7ef02d4 100644
--- a/binary/integer.go
+++ b/binary/integer.go
@@ -81,8 +81,6 @@ func IsUint64SumOverflow(a, b uint64) bool {
 	return math.MaxUint64-a < b
 }
 
-//
-
 // Converts a possibly negative big int x into a positive big int encoding a twos complement representation of x
 // truncated to 32 bytes
 func U256(x *big.Int) *big.Int {
diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go
index 74cbe338568a2bcf7ab16b3ece5065aa18f47129..ec125ea7b4192f524c2ce39261df74ab65822409 100644
--- a/blockchain/blockchain.go
+++ b/blockchain/blockchain.go
@@ -18,81 +18,53 @@ import (
 	"bytes"
 	"encoding/json"
 	"fmt"
-	"sync"
 	"time"
 
-	acm "github.com/hyperledger/burrow/account"
+	"sync"
+
+	"github.com/hyperledger/burrow/crypto"
 	"github.com/hyperledger/burrow/genesis"
 	"github.com/hyperledger/burrow/logging"
 	dbm "github.com/tendermint/tmlibs/db"
 )
 
+// Blocks to average validator power over
+const DefaultValidatorsWindowSize = 10
+
 var stateKey = []byte("BlockchainState")
 
-// Immutable Root of blockchain
-type Root interface {
-	// GenesisHash precomputed from GenesisDoc
+type BlockchainInfo interface {
 	GenesisHash() []byte
 	GenesisDoc() genesis.GenesisDoc
-}
-
-// Immutable pointer to the current tip of the blockchain
-type Tip interface {
-	// ChainID precomputed from GenesisDoc
 	ChainID() string
-	// All Last* references are to the block last committed
 	LastBlockHeight() uint64
 	LastBlockTime() time.Time
 	LastBlockHash() []byte
-	// Note this is the hash of the application state after the most recently committed block's transactions executed
-	// and so lastBlock.Header.AppHash will be one block older than our AppHashAfterLastBlock (i.e. Tendermint closes
-	// the AppHash we return from ABCI Commit into the _next_ block)
 	AppHashAfterLastBlock() []byte
 }
 
-// Burrow's portion of the Blockchain state
-type Blockchain interface {
-	// Read locker
-	sync.Locker
-	Root
-	Tip
-	// Returns an immutable copy of the tip
-	Tip() Tip
-	// Returns a copy of the current validator set
-	Validators() []acm.Validator
-}
-
-type MutableBlockchain interface {
-	Blockchain
-	CommitBlock(blockTime time.Time, blockHash, appHash []byte) error
-}
-
-type root struct {
+type Root struct {
 	genesisHash []byte
 	genesisDoc  genesis.GenesisDoc
 }
 
-type tip struct {
+type Tip struct {
 	chainID               string
 	lastBlockHeight       uint64
 	lastBlockTime         time.Time
 	lastBlockHash         []byte
 	appHashAfterLastBlock []byte
+	validators            Validators
+	validatorsWindow      ValidatorsWindow
 }
 
-type blockchain struct {
+type Blockchain struct {
+	*Root
+	*Tip
 	sync.RWMutex
 	db dbm.DB
-	*root
-	*tip
-	validators []acm.Validator
 }
 
-var _ Root = &blockchain{}
-var _ Tip = &blockchain{}
-var _ Blockchain = &blockchain{}
-var _ MutableBlockchain = &blockchain{}
-
 type PersistedState struct {
 	AppHashAfterLastBlock []byte
 	LastBlockHeight       uint64
@@ -100,23 +72,23 @@ type PersistedState struct {
 }
 
 func LoadOrNewBlockchain(db dbm.DB, genesisDoc *genesis.GenesisDoc,
-	logger *logging.Logger) (*blockchain, error) {
+	logger *logging.Logger) (*Blockchain, error) {
 
 	logger = logger.WithScope("LoadOrNewBlockchain")
 	logger.InfoMsg("Trying to load blockchain state from database",
 		"database_key", stateKey)
-	blockchain, err := loadBlockchain(db)
+	bc, err := loadBlockchain(db)
 	if err != nil {
 		return nil, fmt.Errorf("error loading blockchain state from database: %v", err)
 	}
-	if blockchain != nil {
-		dbHash := blockchain.genesisDoc.Hash()
+	if bc != nil {
+		dbHash := bc.genesisDoc.Hash()
 		argHash := genesisDoc.Hash()
 		if !bytes.Equal(dbHash, argHash) {
 			return nil, fmt.Errorf("GenesisDoc passed to LoadOrNewBlockchain has hash: 0x%X, which does not "+
 				"match the one found in database: 0x%X", argHash, dbHash)
 		}
-		return blockchain, nil
+		return bc, nil
 	}
 
 	logger.InfoMsg("No existing blockchain state found in database, making new blockchain")
@@ -124,24 +96,19 @@ func LoadOrNewBlockchain(db dbm.DB, genesisDoc *genesis.GenesisDoc,
 }
 
 // Pointer to blockchain state initialised from genesis
-func newBlockchain(db dbm.DB, genesisDoc *genesis.GenesisDoc) *blockchain {
-	var validators []acm.Validator
-	for _, gv := range genesisDoc.Validators {
-		validators = append(validators, acm.ConcreteValidator{
-			PublicKey: gv.PublicKey,
-			Power:     uint64(gv.Amount),
-		}.Validator())
+func newBlockchain(db dbm.DB, genesisDoc *genesis.GenesisDoc) *Blockchain {
+	bc := &Blockchain{
+		db:   db,
+		Root: NewRoot(genesisDoc),
+		Tip:  NewTip(genesisDoc.ChainID(), NewRoot(genesisDoc).genesisDoc.GenesisTime, NewRoot(genesisDoc).genesisHash),
 	}
-	root := NewRoot(genesisDoc)
-	return &blockchain{
-		db:         db,
-		root:       root,
-		tip:        NewTip(genesisDoc.ChainID(), root.genesisDoc.GenesisTime, root.genesisHash),
-		validators: validators,
+	for _, gv := range genesisDoc.Validators {
+		bc.validators.AlterPower(gv.PublicKey, gv.Amount)
 	}
+	return bc
 }
 
-func loadBlockchain(db dbm.DB) (*blockchain, error) {
+func loadBlockchain(db dbm.DB) (*Blockchain, error) {
 	buf := db.Get(stateKey)
 	if len(buf) == 0 {
 		return nil, nil
@@ -150,29 +117,31 @@ func loadBlockchain(db dbm.DB) (*blockchain, error) {
 	if err != nil {
 		return nil, err
 	}
-	blockchain := newBlockchain(db, &persistedState.GenesisDoc)
-	blockchain.lastBlockHeight = persistedState.LastBlockHeight
-	blockchain.appHashAfterLastBlock = persistedState.AppHashAfterLastBlock
-	return blockchain, nil
+	bc := newBlockchain(db, &persistedState.GenesisDoc)
+	bc.lastBlockHeight = persistedState.LastBlockHeight
+	bc.appHashAfterLastBlock = persistedState.AppHashAfterLastBlock
+	return bc, nil
 }
 
-func NewRoot(genesisDoc *genesis.GenesisDoc) *root {
-	return &root{
+func NewRoot(genesisDoc *genesis.GenesisDoc) *Root {
+	return &Root{
 		genesisHash: genesisDoc.Hash(),
 		genesisDoc:  *genesisDoc,
 	}
 }
 
 // Create genesis Tip
-func NewTip(chainID string, genesisTime time.Time, genesisHash []byte) *tip {
-	return &tip{
+func NewTip(chainID string, genesisTime time.Time, genesisHash []byte) *Tip {
+	return &Tip{
 		chainID:               chainID,
 		lastBlockTime:         genesisTime,
 		appHashAfterLastBlock: genesisHash,
+		validators:            NewValidators(),
+		validatorsWindow:      NewValidatorsWindow(DefaultValidatorsWindowSize),
 	}
 }
 
-func (bc *blockchain) CommitBlock(blockTime time.Time, blockHash, appHash []byte) error {
+func (bc *Blockchain) CommitBlock(blockTime time.Time, blockHash, appHash []byte) error {
 	bc.Lock()
 	defer bc.Unlock()
 	bc.lastBlockHeight += 1
@@ -182,7 +151,7 @@ func (bc *blockchain) CommitBlock(blockTime time.Time, blockHash, appHash []byte
 	return bc.save()
 }
 
-func (bc *blockchain) save() error {
+func (bc *Blockchain) save() error {
 	if bc.db != nil {
 		encodedState, err := bc.Encode()
 		if err != nil {
@@ -193,28 +162,7 @@ func (bc *blockchain) save() error {
 	return nil
 }
 
-func (bc *blockchain) Root() Root {
-	return bc.root
-}
-
-func (bc *blockchain) Tip() Tip {
-	bc.RLock()
-	defer bc.RUnlock()
-	t := *bc.tip
-	return &t
-}
-
-func (bc *blockchain) Validators() []acm.Validator {
-	bc.RLock()
-	defer bc.RUnlock()
-	vs := make([]acm.Validator, len(bc.validators))
-	for i, v := range bc.validators {
-		vs[i] = v
-	}
-	return vs
-}
-
-func (bc *blockchain) Encode() ([]byte, error) {
+func (bc *Blockchain) Encode() ([]byte, error) {
 	persistedState := &PersistedState{
 		GenesisDoc:            bc.genesisDoc,
 		AppHashAfterLastBlock: bc.appHashAfterLastBlock,
@@ -236,30 +184,38 @@ func Decode(encodedState []byte) (*PersistedState, error) {
 	return persistedState, nil
 }
 
-func (r *root) GenesisHash() []byte {
+func (r *Root) GenesisHash() []byte {
 	return r.genesisHash
 }
 
-func (r *root) GenesisDoc() genesis.GenesisDoc {
+func (r *Root) GenesisDoc() genesis.GenesisDoc {
 	return r.genesisDoc
 }
 
-func (t *tip) ChainID() string {
+func (t *Tip) ChainID() string {
 	return t.chainID
 }
 
-func (t *tip) LastBlockHeight() uint64 {
+func (t *Tip) LastBlockHeight() uint64 {
 	return t.lastBlockHeight
 }
 
-func (t *tip) LastBlockTime() time.Time {
+func (t *Tip) LastBlockTime() time.Time {
 	return t.lastBlockTime
 }
 
-func (t *tip) LastBlockHash() []byte {
+func (t *Tip) LastBlockHash() []byte {
 	return t.lastBlockHash
 }
 
-func (t *tip) AppHashAfterLastBlock() []byte {
+func (t *Tip) AppHashAfterLastBlock() []byte {
 	return t.appHashAfterLastBlock
 }
+
+func (t *Tip) IterateValidators(iter func(publicKey crypto.PublicKey, power uint64) (stop bool)) (stopped bool) {
+	return t.validators.Iterate(iter)
+}
+
+func (t *Tip) NumValidators() int {
+	return t.validators.Length()
+}
diff --git a/blockchain/validators.go b/blockchain/validators.go
new file mode 100644
index 0000000000000000000000000000000000000000..0ba398af0cd0d2103664d84158223865134978bd
--- /dev/null
+++ b/blockchain/validators.go
@@ -0,0 +1,122 @@
+package blockchain
+
+import (
+	"fmt"
+
+	"sort"
+
+	"bytes"
+	"encoding/binary"
+
+	burrowBinary "github.com/hyperledger/burrow/binary"
+	"github.com/hyperledger/burrow/crypto"
+)
+
+// A Validator multiset
+type Validators struct {
+	power      map[crypto.Address]uint64
+	publicKey  map[crypto.Address]crypto.PublicKey
+	totalPower uint64
+}
+
+func NewValidators() Validators {
+	return Validators{
+		power:     make(map[crypto.Address]uint64),
+		publicKey: make(map[crypto.Address]crypto.PublicKey),
+	}
+}
+
+// Add the power of a validator
+func (vs *Validators) AlterPower(publicKey crypto.PublicKey, power uint64) error {
+	address := publicKey.Address()
+	// Remove existing power (possibly 0) from total
+	vs.totalPower -= vs.power[address]
+	if burrowBinary.IsUint64SumOverflow(vs.totalPower, power) {
+		// Undo removing existing power
+		vs.totalPower += vs.power[address]
+		return fmt.Errorf("could not increase total validator power by %v from %v since that would overflow "+
+			"uint64", power, vs.totalPower)
+	}
+	vs.publicKey[address] = publicKey
+	vs.power[address] = power
+	// Note we are adjusting by the difference in power (+/-) since we subtracted the previous amount above
+	vs.totalPower += power
+	return nil
+}
+
+func (vs *Validators) AddPower(publicKey crypto.PublicKey, power uint64) error {
+	currentPower := vs.power[publicKey.Address()]
+	if burrowBinary.IsUint64SumOverflow(currentPower, power) {
+		return fmt.Errorf("could add power %v to validator %v with power %v because that would overflow uint64",
+			power, publicKey.Address(), currentPower)
+	}
+	return vs.AlterPower(publicKey, vs.power[publicKey.Address()]+power)
+}
+
+func (vs *Validators) SubtractPower(publicKey crypto.PublicKey, power uint64) error {
+	currentPower := vs.power[publicKey.Address()]
+	if currentPower < power {
+		return fmt.Errorf("could subtract power %v from validator %v with power %v because that would "+
+			"underflow uint64", power, publicKey.Address(), currentPower)
+	}
+	return vs.AlterPower(publicKey, vs.power[publicKey.Address()]-power)
+}
+
+// Iterates over validators sorted by address
+func (vs *Validators) Iterate(iter func(publicKey crypto.PublicKey, power uint64) (stop bool)) (stopped bool) {
+	addresses := make(crypto.Addresses, 0, len(vs.power))
+	for address := range vs.power {
+		addresses = append(addresses, address)
+	}
+	sort.Sort(addresses)
+	for _, address := range addresses {
+		if iter(vs.publicKey[address], vs.power[address]) {
+			return true
+		}
+	}
+	return false
+}
+
+func (vs *Validators) Length() int {
+	return len(vs.power)
+}
+
+func (vs *Validators) TotalPower() uint64 {
+	return vs.totalPower
+}
+
+// Uses the fixed width public key encoding to
+func (vs *Validators) Encode() []byte {
+	buffer := new(bytes.Buffer)
+	// varint buffer
+	buf := make([]byte, 8)
+	vs.Iterate(func(publicKey crypto.PublicKey, power uint64) (stop bool) {
+		buffer.Write(publicKey.Encode())
+		buffer.Write(buf[:binary.PutUvarint(buf, power)])
+		return
+	})
+	return buffer.Bytes()
+}
+
+// Decodes validators encoded with Encode - expects the exact encoded size with no trailing bytes
+func DecodeValidators(encoded []byte, validators *Validators) error {
+	publicKey := new(crypto.PublicKey)
+	i := 0
+	for i < len(encoded) {
+		n, err := crypto.DecodePublicKeyFixedWidth(encoded[i:], publicKey)
+		if err != nil {
+			return err
+		}
+		i += n
+		power, n := binary.Uvarint(encoded[i:])
+		if n <= 0 {
+			return fmt.Errorf("error decoding uint64 from validators binary encoding")
+		}
+		i += n
+		err = validators.AlterPower(*publicKey, power)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/blockchain/validators_test.go b/blockchain/validators_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..35513d43af9c052f3c31a494ec6d5ea4ee7c149b
--- /dev/null
+++ b/blockchain/validators_test.go
@@ -0,0 +1,50 @@
+package blockchain
+
+import (
+	"testing"
+
+	"fmt"
+
+	"math/rand"
+
+	acm "github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/crypto"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestValidators_AlterPower(t *testing.T) {
+	vs := NewValidators()
+	pow1 := uint64(2312312321)
+	assert.NoError(t, vs.AlterPower(pubKey(1), pow1))
+	assert.Equal(t, pow1, vs.TotalPower())
+}
+
+func TestValidators_Encode(t *testing.T) {
+	vs := NewValidators()
+	rnd := rand.New(rand.NewSource(43534543))
+	for i := 0; i < 100; i++ {
+		power := uint64(rnd.Intn(10))
+		require.NoError(t, vs.AlterPower(pubKey(rnd.Int63()), power))
+	}
+	encoded := vs.Encode()
+	vsOut := NewValidators()
+	require.NoError(t, DecodeValidators(encoded, &vsOut))
+	// Check decoded matches encoded
+	var publicKeyPower []interface{}
+	vs.Iterate(func(publicKey crypto.PublicKey, power uint64) (stop bool) {
+		publicKeyPower = append(publicKeyPower, publicKey, power)
+		return
+	})
+	vsOut.Iterate(func(publicKey crypto.PublicKey, power uint64) (stop bool) {
+		assert.Equal(t, publicKeyPower[0], publicKey)
+		assert.Equal(t, publicKeyPower[1], power)
+		publicKeyPower = publicKeyPower[2:]
+		return
+	})
+	assert.Len(t, publicKeyPower, 0, "should exhaust all validators in decoded multiset")
+}
+
+func pubKey(secret interface{}) crypto.PublicKey {
+	return acm.NewConcreteAccountFromSecret(fmt.Sprintf("%v", secret)).PublicKey
+}
diff --git a/blockchain/validators_window.go b/blockchain/validators_window.go
new file mode 100644
index 0000000000000000000000000000000000000000..11ebf209b36e441b3826785725abdd7975e485fa
--- /dev/null
+++ b/blockchain/validators_window.go
@@ -0,0 +1,64 @@
+package blockchain
+
+import (
+	"github.com/hyperledger/burrow/crypto"
+)
+
+type ValidatorsWindow struct {
+	Buckets []Validators
+	Total   Validators
+	head    int
+}
+
+// Provides a sliding window over the last size buckets of validator power changes
+func NewValidatorsWindow(size int) ValidatorsWindow {
+	if size < 1 {
+		size = 1
+	}
+	vw := ValidatorsWindow{
+		Buckets: make([]Validators, size),
+		Total:   NewValidators(),
+	}
+	vw.Buckets[vw.head] = NewValidators()
+	return vw
+}
+
+// Updates the current head bucket (accumulator)
+func (vw *ValidatorsWindow) AlterPower(publicKey crypto.PublicKey, power uint64) error {
+	return vw.Buckets[vw.head].AlterPower(publicKey, power)
+}
+
+func (vw *ValidatorsWindow) CommitInto(validatorsToUpdate *Validators) error {
+	var err error
+	if vw.Buckets[vw.head].Iterate(func(publicKey crypto.PublicKey, power uint64) (stop bool) {
+		// Update the sink validators
+		err = validatorsToUpdate.AlterPower(publicKey, power)
+		if err != nil {
+			return true
+		}
+		// Add to total power
+		err = vw.Total.AddPower(publicKey, power)
+		if err != nil {
+			return true
+		}
+		return false
+	}) {
+		// If iteration stopped there was an error
+		return err
+	}
+	// move the ring buffer on
+	vw.head = (vw.head + 1) % len(vw.Buckets)
+	// Subtract the tail bucket (if any) from the total
+	if vw.Buckets[vw.head].Iterate(func(publicKey crypto.PublicKey, power uint64) (stop bool) {
+		err = vw.Total.SubtractPower(publicKey, power)
+		if err != nil {
+			return false
+		}
+		return true
+	}) {
+		return err
+	}
+	// Clear new head bucket (and possibly previous tail)
+	vw.Buckets[vw.head] = NewValidators()
+	return nil
+}
diff --git a/client/methods/call.go b/client/methods/call.go
index 38271439ae27955983272440f640df28c6eeebde..16655803f15bf4e7c1eaf8f8fcb936f756853cbc 100644
--- a/client/methods/call.go
+++ b/client/methods/call.go
@@ -20,6 +20,7 @@ import (
 	"github.com/hyperledger/burrow/client"
 	"github.com/hyperledger/burrow/client/rpc"
 	"github.com/hyperledger/burrow/keys"
+	"github.com/hyperledger/burrow/txs"
 )
 
 func Call(do *client.Do) error {
@@ -48,8 +49,8 @@ func Call(do *client.Do) error {
 	// TODO: [ben] we carry over the sign bool, but always set it to true,
 	// as we move away from and deprecate the api that allows sending unsigned
 	// transactions and relying on (our) receiving node to sign it.
-	txResult, err := rpc.SignAndBroadcast(chainID, burrowNodeClient, burrowKeyClient,
-		callTransaction, true, do.BroadcastFlag, do.WaitFlag)
+	txResult, err := rpc.SignAndBroadcast(burrowNodeClient, burrowKeyClient,
+		txs.Enclose(chainID, callTransaction), true, do.BroadcastFlag, do.WaitFlag)
 
 	if err != nil {
 		return fmt.Errorf("Failed on signing (and broadcasting) transaction: %s", err)
diff --git a/client/methods/send.go b/client/methods/send.go
index e04771e185b358a22c0e8fcb53b99adb47504bc9..6ce809721629d4c7e87b6f62dec578f51d6609c7 100644
--- a/client/methods/send.go
+++ b/client/methods/send.go
@@ -20,6 +20,7 @@ import (
 	"github.com/hyperledger/burrow/client"
 	"github.com/hyperledger/burrow/client/rpc"
 	"github.com/hyperledger/burrow/keys"
+	"github.com/hyperledger/burrow/txs"
 )
 
 func Send(do *client.Do) error {
@@ -47,8 +48,8 @@ func Send(do *client.Do) error {
 	// TODO: [ben] we carry over the sign bool, but always set it to true,
 	// as we move away from and deprecate the api that allows sending unsigned
 	// transactions and relying on (our) receiving node to sign it.
-	txResult, err := rpc.SignAndBroadcast(chainID, burrowNodeClient, burrowKeyClient,
-		sendTransaction, true, do.BroadcastFlag, do.WaitFlag)
+	txResult, err := rpc.SignAndBroadcast(burrowNodeClient, burrowKeyClient,
+		txs.Enclose(chainID, sendTransaction), true, do.BroadcastFlag, do.WaitFlag)
 	if err != nil {
 		return fmt.Errorf("Failed on signing (and broadcasting) transaction: %s", err)
 	}
diff --git a/client/mock/client_mock.go b/client/mock/client_mock.go
index 1a689bb39227d4118850b4ccae94b4cc37b20aa7..2231661225556c619474ea9f2e607f51d6784d1b 100644
--- a/client/mock/client_mock.go
+++ b/client/mock/client_mock.go
@@ -35,7 +35,7 @@ func NewMockNodeClient() *MockNodeClient {
 	}
 }
 
-func (mock *MockNodeClient) Broadcast(transaction txs.Tx) (*txs.Receipt, error) {
+func (mock *MockNodeClient) Broadcast(txEnv *txs.Envelope) (*txs.Receipt, error) {
 	// make zero transaction receipt
 	txReceipt := &txs.Receipt{
 		TxHash:          make([]byte, 20),
diff --git a/client/node_client.go b/client/node_client.go
index c7508fe16b38edff113a426a4daa35ea74f51558..b48c1f891adcb0c2ca60557069615dabedc19b84 100644
--- a/client/node_client.go
+++ b/client/node_client.go
@@ -27,7 +27,7 @@ import (
 )
 
 type NodeClient interface {
-	Broadcast(transaction txs.Tx) (*txs.Receipt, error)
+	Broadcast(transaction *txs.Envelope) (*txs.Receipt, error)
 	DeriveWebsocketClient() (nodeWsClient NodeWebsocketClient, err error)
 
 	Status() (ChainId []byte, ValidatorPublicKey []byte, LatestBlockHash []byte,
@@ -47,8 +47,7 @@ type NodeClient interface {
 type NodeWebsocketClient interface {
 	Subscribe(eventId string) error
 	Unsubscribe(eventId string) error
-
-	WaitForConfirmation(tx txs.Tx, chainId string, inputAddr crypto.Address) (chan Confirmation, error)
+	WaitForConfirmation(tx *txs.Envelope, inputAddr crypto.Address) (chan Confirmation, error)
 	Close()
 }
 
@@ -74,9 +73,9 @@ func NewBurrowNodeClient(rpcString string, logger *logging.Logger) *burrowNodeCl
 //------------------------------------------------------------------------------------
 // broadcast to blockchain node
 
-func (burrowNodeClient *burrowNodeClient) Broadcast(tx txs.Tx) (*txs.Receipt, error) {
+func (burrowNodeClient *burrowNodeClient) Broadcast(txEnv *txs.Envelope) (*txs.Receipt, error) {
 	client := rpcClient.NewURIClient(burrowNodeClient.broadcastRPC)
-	receipt, err := tmClient.BroadcastTx(client, tx)
+	receipt, err := tmClient.BroadcastTx(client, txEnv)
 	if err != nil {
 		return nil, err
 	}
diff --git a/client/rpc/client.go b/client/rpc/client.go
index b2afaec4a55ba75d1ff2bf2ec94f323459ca040a..e940ee3e15e1a26d442309d04088618ca110c90b 100644
--- a/client/rpc/client.go
+++ b/client/rpc/client.go
@@ -21,6 +21,7 @@ import (
 
 	"github.com/hyperledger/burrow/crypto"
 	ptypes "github.com/hyperledger/burrow/permission"
+	"github.com/hyperledger/burrow/txs/payload"
 
 	"github.com/hyperledger/burrow/client"
 	"github.com/hyperledger/burrow/keys"
@@ -32,7 +33,7 @@ import (
 // core functions with string args.
 // validates strings and forms transaction
 
-func Send(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr, toAddr, amtS, sequenceS string) (*txs.SendTx, error) {
+func Send(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr, toAddr, amtS, sequenceS string) (*payload.SendTx, error) {
 	pub, amt, sequence, err := checkCommon(nodeClient, keyClient, pubkey, addr, amtS, sequenceS)
 	if err != nil {
 		return nil, err
@@ -47,14 +48,14 @@ func Send(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr,
 		return nil, err
 	}
 
-	tx := txs.NewSendTx()
+	tx := payload.NewSendTx()
 	tx.AddInputWithSequence(pub, amt, sequence)
 	tx.AddOutput(toAddress, amt)
 
 	return tx, nil
 }
 
-func Call(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr, toAddr, amtS, sequenceS, gasS, feeS, data string) (*txs.CallTx, error) {
+func Call(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr, toAddr, amtS, sequenceS, gasS, feeS, data string) (*payload.CallTx, error) {
 	pub, amt, sequence, err := checkCommon(nodeClient, keyClient, pubkey, addr, amtS, sequenceS)
 	if err != nil {
 		return nil, err
@@ -85,11 +86,11 @@ func Call(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr,
 		return nil, fmt.Errorf("data is bad hex: %v", err)
 	}
 
-	tx := txs.NewCallTxWithSequence(pub, toAddress, dataBytes, amt, gas, fee, sequence)
+	tx := payload.NewCallTxWithSequence(pub, toAddress, dataBytes, amt, gas, fee, sequence)
 	return tx, nil
 }
 
-func Name(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr, amtS, sequenceS, feeS, name, data string) (*txs.NameTx, error) {
+func Name(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr, amtS, sequenceS, feeS, name, data string) (*payload.NameTx, error) {
 	pub, amt, sequence, err := checkCommon(nodeClient, keyClient, pubkey, addr, amtS, sequenceS)
 	if err != nil {
 		return nil, err
@@ -100,12 +101,12 @@ func Name(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr,
 		return nil, fmt.Errorf("fee is misformatted: %v", err)
 	}
 
-	tx := txs.NewNameTxWithSequence(pub, name, data, amt, fee, sequence)
+	tx := payload.NewNameTxWithSequence(pub, name, data, amt, fee, sequence)
 	return tx, nil
 }
 
 func Permissions(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addrS, sequenceS string,
-	action, target, permissionFlag, role, value string) (*txs.PermissionsTx, error) {
+	action, target, permissionFlag, role, value string) (*payload.PermissionsTx, error) {
 
 	pub, _, sequence, err := checkCommon(nodeClient, keyClient, pubkey, addrS, "0", sequenceS)
 	if err != nil {
@@ -154,11 +155,11 @@ func Permissions(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey,
 		return nil, err
 	}
 
-	tx := txs.NewPermissionsTxWithSequence(pub, permArgs, sequence)
+	tx := payload.NewPermissionsTxWithSequence(pub, permArgs, sequence)
 	return tx, nil
 }
 
-func Bond(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, unbondAddr, amtS, sequenceS string) (*txs.BondTx, error) {
+func Bond(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, unbondAddr, amtS, sequenceS string) (*payload.BondTx, error) {
 	return nil, fmt.Errorf("Bond Transaction formation to be implemented on 0.12.0")
 	// pub, amt, sequence, err := checkCommon(nodeAddr, signAddr, pubkey, "", amtS, sequenceS)
 	// if err != nil {
@@ -189,7 +190,7 @@ func Bond(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, unbond
 	// return tx, nil
 }
 
-func Unbond(addrS, heightS string) (*txs.UnbondTx, error) {
+func Unbond(addrS, heightS string) (*payload.UnbondTx, error) {
 	return nil, fmt.Errorf("Unbond Transaction formation to be implemented on 0.12.0")
 	// if addrS == "" {
 	// 	return nil, fmt.Errorf("Validator address must be given with --addr flag")
@@ -211,28 +212,6 @@ func Unbond(addrS, heightS string) (*txs.UnbondTx, error) {
 	// }, nil
 }
 
-func Rebond(addrS, heightS string) (*txs.RebondTx, error) {
-	return nil, fmt.Errorf("Rebond Transaction formation to be implemented on 0.12.0")
-	// 	if addrS == "" {
-	// 		return nil, fmt.Errorf("Validator address must be given with --addr flag")
-	// 	}
-
-	// 	addrBytes, err := hex.DecodeString(addrS)
-	// 	if err != nil {
-	// 		return nil, fmt.Errorf("addr is bad hex: %v", err)
-	// 	}
-
-	// 	height, err := strconv.ParseInt(heightS, 10, 32)
-	// 	if err != nil {
-	// 		return nil, fmt.Errorf("height is misformatted: %v", err)
-	// 	}
-
-	// 	return &types.RebondTx{
-	// 		Address: addrBytes,
-	// 		Height:  int(height),
-	// 	}, nil
-}
-
 type TxResult struct {
 	BlockHash []byte // all txs get in a block
 	Hash      []byte // all txs get a hash
@@ -247,12 +226,12 @@ type TxResult struct {
 }
 
 // Preserve
-func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient keys.KeyClient, tx txs.Tx, sign,
+func SignAndBroadcast(nodeClient client.NodeClient, keyClient keys.KeyClient, txEnv *txs.Envelope, sign,
 	broadcast, wait bool) (txResult *TxResult, err error) {
 
 	var inputAddr crypto.Address
 	if sign {
-		inputAddr, tx, err = signTx(keyClient, chainID, tx)
+		inputAddr, txEnv, err = signTx(keyClient, txEnv.Tx)
 		if err != nil {
 			return nil, err
 		}
@@ -266,7 +245,7 @@ func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient ke
 				return nil, err
 			}
 			var confirmationChannel chan client.Confirmation
-			confirmationChannel, err = wsClient.WaitForConfirmation(tx, chainID, inputAddr)
+			confirmationChannel, err = wsClient.WaitForConfirmation(txEnv, inputAddr)
 			if err != nil {
 				return nil, err
 			}
@@ -300,7 +279,7 @@ func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient ke
 		}
 
 		var receipt *txs.Receipt
-		receipt, err = nodeClient.Broadcast(tx)
+		receipt, err = nodeClient.Broadcast(txEnv)
 		if err != nil {
 			return nil, err
 		}
@@ -310,7 +289,7 @@ func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient ke
 		// NOTE: [ben] is this consistent with the Ethereum protocol?  It should seem
 		// reasonable to get this returned from the chain directly.  Alternatively,
 		// the benefit is that the we don't need to trust the chain node
-		if tx_, ok := tx.(*txs.CallTx); ok {
+		if tx_, ok := txEnv.Tx.Payload.(*payload.CallTx); ok {
 			if tx_.Address == nil {
 				address := crypto.NewContractAddress(tx_.Input.Address, tx_.Input.Sequence)
 				txResult.Address = &address
diff --git a/client/rpc/client_util.go b/client/rpc/client_util.go
index b904a4f0901bc2b53f319804a6834a13df85e118..d59453a7c2cea259c863962cc69ca311f61e719c 100644
--- a/client/rpc/client_util.go
+++ b/client/rpc/client_util.go
@@ -30,49 +30,18 @@ import (
 
 // tx has either one input or we default to the first one (ie for send/bond)
 // TODO: better support for multisig and bonding
-func signTx(keyClient keys.KeyClient, chainID string, tx_ txs.Tx) (crypto.Address, txs.Tx, error) {
-	signBytes := crypto.SignBytes(chainID, tx_)
-	var err error
-	switch tx := tx_.(type) {
-	case *txs.SendTx:
-		signAddress := tx.Inputs[0].Address
-		tx.Inputs[0].Signature, err = keyClient.Sign(signAddress, signBytes)
-		return signAddress, tx, err
-
-	case *txs.NameTx:
-		signAddress := tx.Input.Address
-		tx.Input.Signature, err = keyClient.Sign(signAddress, signBytes)
-		return signAddress, tx, err
-
-	case *txs.CallTx:
-		signAddress := tx.Input.Address
-		tx.Input.Signature, err = keyClient.Sign(signAddress, signBytes)
-		return signAddress, tx, err
-
-	case *txs.PermissionsTx:
-		signAddress := tx.Input.Address
-		tx.Input.Signature, err = keyClient.Sign(signAddress, signBytes)
-		return signAddress, tx, err
-
-	case *txs.BondTx:
-		signAddress := tx.Inputs[0].Address
-		tx.Signature, err = keyClient.Sign(signAddress, signBytes)
-		tx.Inputs[0].Signature = tx.Signature
-		return signAddress, tx, err
-
-	case *txs.UnbondTx:
-		signAddress := tx.Address
-		tx.Signature, err = keyClient.Sign(signAddress, signBytes)
-		return signAddress, tx, err
-
-	case *txs.RebondTx:
-		signAddress := tx.Address
-		tx.Signature, err = keyClient.Sign(signAddress, signBytes)
-		return signAddress, tx, err
-
-	default:
-		return crypto.ZeroAddress, nil, fmt.Errorf("unknown transaction type for signTx: %#v", tx_)
+func signTx(keyClient keys.KeyClient, tx *txs.Tx) (crypto.Address, *txs.Envelope, error) {
+	txEnv := tx.Enclose()
+	inputs := tx.GetInputs()
+	signer, err := keys.AddressableSigner(keyClient, inputs[0].Address)
+	if err != nil {
+		return crypto.ZeroAddress, nil, err
+	}
+	err = txEnv.Sign(signer)
+	if err != nil {
+		return crypto.ZeroAddress, nil, err
 	}
+	return signer.Address(), txEnv, nil
 }
 
 func checkCommon(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr, amtS,
diff --git a/client/websocket_client.go b/client/websocket_client.go
index 90e737b275f3a1ced1f731d60db4a28217f6b77b..81be41982a8cc735f934bcbec2b702dfbbc7a4cb 100644
--- a/client/websocket_client.go
+++ b/client/websocket_client.go
@@ -68,7 +68,7 @@ func (burrowNodeWebsocketClient *burrowNodeWebsocketClient) Unsubscribe(subscrip
 
 // Returns a channel that will receive a confirmation with a result or the exception that
 // has been confirmed; or an error is returned and the confirmation channel is nil.
-func (burrowNodeWebsocketClient *burrowNodeWebsocketClient) WaitForConfirmation(tx txs.Tx, chainId string,
+func (burrowNodeWebsocketClient *burrowNodeWebsocketClient) WaitForConfirmation(txEnv *txs.Envelope,
 	inputAddr crypto.Address) (chan Confirmation, error) {
 
 	// Setup the confirmation channel to be returned
@@ -163,10 +163,10 @@ func (burrowNodeWebsocketClient *burrowNodeWebsocketClient) WaitForConfirmation(
 						return
 					}
 
-					if !bytes.Equal(eventDataTx.Tx.Hash(chainId), tx.Hash(chainId)) {
+					if !bytes.Equal(eventDataTx.Tx.Hash(), txEnv.Tx.Hash()) {
 						burrowNodeWebsocketClient.logger.TraceMsg("Received different event",
 							// TODO: consider re-implementing TxID again, or other more clear debug
-							"received transaction event", eventDataTx.Tx.Hash(chainId))
+							"received transaction event", eventDataTx.Tx.Hash())
 						continue
 					}
 
diff --git a/cmd/burrow/commands/dump.go b/cmd/burrow/commands/dump.go
index 07ddb156f5e98993c1b4786fee018bf2e9fb5e08..1e4ef041254bd5a09095ca017a5c1a8fc7c7f898 100644
--- a/cmd/burrow/commands/dump.go
+++ b/cmd/burrow/commands/dump.go
@@ -60,8 +60,8 @@ func Dump(output Output) func(cmd *cli.Cmd) {
 
 				_, err = explorer.Blocks(start, end,
 					func(block *forensics.Block) (stop bool) {
-						stopped, err := block.Transactions(func(tx txs.Tx) (stop bool) {
-							bs, err := json.Marshal(tx)
+						stopped, err := block.Transactions(func(txEnv *txs.Envelope) (stop bool) {
+							bs, err := json.Marshal(txEnv)
 							if err != nil {
 								output.Fatalf("Could not deserialise transaction: %v", err)
 							}
diff --git a/config/config.go b/config/config.go
index a120986d4a80324656acc00738d3c900187d6247..747dc208285911e028c4f9da10ca2cf6e6801444 100644
--- a/config/config.go
+++ b/config/config.go
@@ -67,11 +67,15 @@ func (conf *BurrowConfig) Kernel(ctx context.Context) (*core.Kernel, error) {
 		keyClient = keys.NewLocalKeyClient(keyStore, logger)
 	}
 
-	val, err := keys.Addressable(keyClient, *conf.ValidatorAddress)
+	val, err := keys.AddressableSigner(keyClient, *conf.ValidatorAddress)
 	if err != nil {
 		return nil, fmt.Errorf("could not get validator addressable from keys client: %v", err)
 	}
-	privValidator := validator.NewPrivValidatorMemory(val, keys.Signer(keyClient, val.Address()))
+	signer, err := keys.AddressableSigner(keyClient, val.Address())
+	if err != nil {
+		return nil, err
+	}
+	privValidator := validator.NewPrivValidatorMemory(val, signer)
 
 	var exeOptions []execution.ExecutionOption
 	if conf.Execution != nil {
diff --git a/consensus/tendermint/abci/app.go b/consensus/tendermint/abci/app.go
index 6ad441166a96d5dcaa9b572927fbace51314cbab..77ca81f230458460e0843934258308e06ecb0ad7 100644
--- a/consensus/tendermint/abci/app.go
+++ b/consensus/tendermint/abci/app.go
@@ -5,37 +5,39 @@ import (
 	"sync"
 	"time"
 
+	"encoding/json"
+
 	bcm "github.com/hyperledger/burrow/blockchain"
 	"github.com/hyperledger/burrow/consensus/tendermint/codes"
+	"github.com/hyperledger/burrow/crypto"
 	"github.com/hyperledger/burrow/execution"
 	"github.com/hyperledger/burrow/logging"
 	"github.com/hyperledger/burrow/logging/structure"
 	"github.com/hyperledger/burrow/project"
 	"github.com/hyperledger/burrow/txs"
 	"github.com/pkg/errors"
-	abci_types "github.com/tendermint/abci/types"
-	"github.com/tendermint/go-wire"
+	abciTypes "github.com/tendermint/abci/types"
 )
 
 const responseInfoName = "Burrow"
 
 type App struct {
 	// State
-	blockchain    bcm.MutableBlockchain
+	blockchain    *bcm.Blockchain
 	checker       execution.BatchExecutor
 	committer     execution.BatchCommitter
 	mempoolLocker sync.Locker
 	// We need to cache these from BeginBlock for when we need actually need it in Commit
-	block *abci_types.RequestBeginBlock
+	block *abciTypes.RequestBeginBlock
 	// Utility
 	txDecoder txs.Decoder
 	// Logging
 	logger *logging.Logger
 }
 
-var _ abci_types.Application = &App{}
+var _ abciTypes.Application = &App{}
 
-func NewApp(blockchain bcm.MutableBlockchain,
+func NewApp(blockchain *bcm.Blockchain,
 	checker execution.BatchExecutor,
 	committer execution.BatchCommitter,
 	logger *logging.Logger) *App {
@@ -43,7 +45,7 @@ func NewApp(blockchain bcm.MutableBlockchain,
 		blockchain: blockchain,
 		checker:    checker,
 		committer:  committer,
-		txDecoder:  txs.NewGoWireCodec(),
+		txDecoder:  txs.NewJSONCodec(),
 		logger:     logger.WithScope("abci.NewApp").With(structure.ComponentKey, "ABCI_App"),
 	}
 }
@@ -55,9 +57,9 @@ func (app *App) SetMempoolLocker(mempoolLocker sync.Locker) {
 	app.mempoolLocker = mempoolLocker
 }
 
-func (app *App) Info(info abci_types.RequestInfo) abci_types.ResponseInfo {
-	tip := app.blockchain.Tip()
-	return abci_types.ResponseInfo{
+func (app *App) Info(info abciTypes.RequestInfo) abciTypes.ResponseInfo {
+	tip := app.blockchain.Tip
+	return abciTypes.ResponseInfo{
 		Data:             responseInfoName,
 		Version:          project.History.CurrentVersion().String(),
 		LastBlockHeight:  int64(tip.LastBlockHeight()),
@@ -65,89 +67,95 @@ func (app *App) Info(info abci_types.RequestInfo) abci_types.ResponseInfo {
 	}
 }
 
-func (app *App) SetOption(option abci_types.RequestSetOption) (respSetOption abci_types.ResponseSetOption) {
+func (app *App) SetOption(option abciTypes.RequestSetOption) (respSetOption abciTypes.ResponseSetOption) {
 	respSetOption.Log = "SetOption not supported"
 	respSetOption.Code = codes.UnsupportedRequestCode
 	return
 }
 
-func (app *App) Query(reqQuery abci_types.RequestQuery) (respQuery abci_types.ResponseQuery) {
+func (app *App) Query(reqQuery abciTypes.RequestQuery) (respQuery abciTypes.ResponseQuery) {
 	respQuery.Log = "Query not supported"
 	respQuery.Code = codes.UnsupportedRequestCode
 	return
 }
 
-func (app *App) CheckTx(txBytes []byte) abci_types.ResponseCheckTx {
-	tx, err := app.txDecoder.DecodeTx(txBytes)
+func (app *App) CheckTx(txBytes []byte) abciTypes.ResponseCheckTx {
+	txEnv, err := app.txDecoder.DecodeTx(txBytes)
 	if err != nil {
 		app.logger.TraceMsg("CheckTx decoding error",
 			"tag", "CheckTx",
 			structure.ErrorKey, err)
-		return abci_types.ResponseCheckTx{
+		return abciTypes.ResponseCheckTx{
 			Code: codes.EncodingErrorCode,
 			Log:  fmt.Sprintf("Encoding error: %s", err),
 		}
 	}
-	receipt := txs.GenerateReceipt(app.blockchain.ChainID(), tx)
+	receipt := txEnv.Tx.GenerateReceipt()
 
-	err = app.checker.Execute(tx)
+	err = app.checker.Execute(txEnv)
 	if err != nil {
 		app.logger.TraceMsg("CheckTx execution error",
 			structure.ErrorKey, err,
 			"tag", "CheckTx",
 			"tx_hash", receipt.TxHash,
 			"creates_contract", receipt.CreatesContract)
-		return abci_types.ResponseCheckTx{
+		return abciTypes.ResponseCheckTx{
 			Code: codes.EncodingErrorCode,
-			Log:  fmt.Sprintf("CheckTx could not execute transaction: %s, error: %v", tx, err),
+			Log:  fmt.Sprintf("CheckTx could not execute transaction: %s, error: %v", txEnv, err),
 		}
 	}
 
-	receiptBytes := wire.BinaryBytes(receipt)
+	receiptBytes, err := json.Marshal(receipt)
+	if err != nil {
+		return abciTypes.ResponseCheckTx{
+			Code: codes.TxExecutionErrorCode,
+			Log:  fmt.Sprintf("CheckTx could not serialise receipt: %s", err),
+		}
+	}
 	app.logger.TraceMsg("CheckTx success",
 		"tag", "CheckTx",
 		"tx_hash", receipt.TxHash,
 		"creates_contract", receipt.CreatesContract)
-	return abci_types.ResponseCheckTx{
+	return abciTypes.ResponseCheckTx{
 		Code: codes.TxExecutionSuccessCode,
 		Log:  "CheckTx success - receipt in data",
 		Data: receiptBytes,
 	}
 }
 
-func (app *App) InitChain(chain abci_types.RequestInitChain) (respInitChain abci_types.ResponseInitChain) {
+func (app *App) InitChain(chain abciTypes.RequestInitChain) (respInitChain abciTypes.ResponseInitChain) {
 	// Could verify agreement on initial validator set here
 	return
 }
 
-func (app *App) BeginBlock(block abci_types.RequestBeginBlock) (respBeginBlock abci_types.ResponseBeginBlock) {
+func (app *App) BeginBlock(block abciTypes.RequestBeginBlock) (respBeginBlock abciTypes.ResponseBeginBlock) {
 	app.block = &block
 	return
 }
 
-func (app *App) DeliverTx(txBytes []byte) abci_types.ResponseDeliverTx {
-	tx, err := app.txDecoder.DecodeTx(txBytes)
+func (app *App) DeliverTx(txBytes []byte) abciTypes.ResponseDeliverTx {
+	txEnv, err := app.txDecoder.DecodeTx(txBytes)
 	if err != nil {
 		app.logger.TraceMsg("DeliverTx decoding error",
 			"tag", "DeliverTx",
 			structure.ErrorKey, err)
-		return abci_types.ResponseDeliverTx{
+		return abciTypes.ResponseDeliverTx{
 			Code: codes.EncodingErrorCode,
 			Log:  fmt.Sprintf("Encoding error: %s", err),
 		}
 	}
 
-	receipt := txs.GenerateReceipt(app.blockchain.ChainID(), tx)
-	err = app.committer.Execute(tx)
+	receipt := txEnv.Tx.GenerateReceipt()
+	err = app.committer.Execute(txEnv)
 	if err != nil {
 		app.logger.TraceMsg("DeliverTx execution error",
 			structure.ErrorKey, err,
 			"tag", "DeliverTx",
 			"tx_hash", receipt.TxHash,
 			"creates_contract", receipt.CreatesContract)
-		return abci_types.ResponseDeliverTx{
+		return abciTypes.ResponseDeliverTx{
 			Code: codes.TxExecutionErrorCode,
-			Log:  fmt.Sprintf("DeliverTx could not execute transaction: %s, error: %s", tx, err),
+			Log:  fmt.Sprintf("DeliverTx could not execute transaction: %s, error: %s", txEnv, err),
 		}
 	}
 
@@ -155,21 +163,37 @@ func (app *App) DeliverTx(txBytes []byte) abci_types.ResponseDeliverTx {
 		"tag", "DeliverTx",
 		"tx_hash", receipt.TxHash,
 		"creates_contract", receipt.CreatesContract)
-	receiptBytes := wire.BinaryBytes(receipt)
-	return abci_types.ResponseDeliverTx{
+	receiptBytes, err := json.Marshal(receipt)
+	if err != nil {
+		return abciTypes.ResponseDeliverTx{
+			Code: codes.TxExecutionErrorCode,
+			Log:  fmt.Sprintf("DeliverTx could not serialise receipt: %s", err),
+		}
+	}
+	return abciTypes.ResponseDeliverTx{
 		Code: codes.TxExecutionSuccessCode,
 		Log:  "DeliverTx success - receipt in data",
 		Data: receiptBytes,
 	}
 }
 
-func (app *App) EndBlock(reqEndBlock abci_types.RequestEndBlock) (respEndBlock abci_types.ResponseEndBlock) {
+func (app *App) EndBlock(reqEndBlock abciTypes.RequestEndBlock) abciTypes.ResponseEndBlock {
 	// Validator mutation goes here
-	return
+	var validatorUpdates abciTypes.Validators
+	app.blockchain.IterateValidators(func(publicKey crypto.PublicKey, power uint64) (stop bool) {
+		validatorUpdates = append(validatorUpdates, abciTypes.Validator{
+			Address: publicKey.Address().Bytes(),
+			PubKey:  publicKey.ABCIPubKey(),
+			Power:   int64(power),
+		})
+		return
+	})
+	return abciTypes.ResponseEndBlock{
+		ValidatorUpdates: validatorUpdates,
+	}
 }
 
-func (app *App) Commit() abci_types.ResponseCommit {
-	tip := app.blockchain.Tip()
+func (app *App) Commit() abciTypes.ResponseCommit {
 	app.logger.InfoMsg("Committing block",
 		"tag", "Commit",
 		structure.ScopeKey, "Commit()",
@@ -177,8 +201,8 @@ func (app *App) Commit() abci_types.ResponseCommit {
 		"hash", app.block.Hash,
 		"txs", app.block.Header.NumTxs,
 		"block_time", app.block.Header.Time, // [CSK] this sends a fairly non-sensical number; should be human readable
-		"last_block_time", tip.LastBlockTime(),
-		"last_block_hash", tip.LastBlockHash())
+		"last_block_time", app.blockchain.Tip.LastBlockTime(),
+		"last_block_hash", app.blockchain.Tip.LastBlockHash())
 
 	// Lock the checker while we reset it and possibly while recheckTxs replays transactions
 	app.checker.Lock()
@@ -233,7 +257,7 @@ func (app *App) Commit() abci_types.ResponseCommit {
 			"but Tendermint reports a block height of %v, and the two should agree",
 			app.blockchain.LastBlockHeight(), app.block.Header.Height))
 	}
-	return abci_types.ResponseCommit{
+	return abciTypes.ResponseCommit{
 		Data: appHash,
 	}
 }
diff --git a/consensus/tendermint/query/node_view.go b/consensus/tendermint/query/node_view.go
index 915f4001b8ca64dc7c6399a0e0ecd343079f0706..32184486086fbe1d595afe981b94e583f8938ef2 100644
--- a/consensus/tendermint/query/node_view.go
+++ b/consensus/tendermint/query/node_view.go
@@ -53,14 +53,14 @@ func (nv *NodeView) BlockStore() types.BlockStoreRPC {
 }
 
 // Pass -1 to get all available transactions
-func (nv *NodeView) MempoolTransactions(maxTxs int) ([]txs.Tx, error) {
-	var transactions []txs.Tx
+func (nv *NodeView) MempoolTransactions(maxTxs int) ([]*txs.Envelope, error) {
+	var transactions []*txs.Envelope
 	for _, txBytes := range nv.tmNode.MempoolReactor().Mempool.Reap(maxTxs) {
-		tx, err := nv.txDecoder.DecodeTx(txBytes)
+		txEnv, err := nv.txDecoder.DecodeTx(txBytes)
 		if err != nil {
 			return nil, err
 		}
-		transactions = append(transactions, tx)
+		transactions = append(transactions, txEnv)
 	}
 	return transactions, nil
 }
diff --git a/consensus/tendermint/tendermint.go b/consensus/tendermint/tendermint.go
index c1139345a2fcc5b16a8bf2d2f28894b8ab548efa..3bf235b9777eb6665aed6782a8abec914be95279 100644
--- a/consensus/tendermint/tendermint.go
+++ b/consensus/tendermint/tendermint.go
@@ -54,7 +54,7 @@ func NewNode(
 	conf *config.Config,
 	privValidator tm_types.PrivValidator,
 	genesisDoc *tm_types.GenesisDoc,
-	blockchain bcm.MutableBlockchain,
+	blockchain *bcm.Blockchain,
 	checker execution.BatchExecutor,
 	committer execution.BatchCommitter,
 	logger *logging.Logger) (*Node, error) {
@@ -86,11 +86,11 @@ func NewNode(
 	return nde, nil
 }
 
-func BroadcastTxAsyncFunc(validator *Node, txEncoder txs.Encoder) func(tx txs.Tx,
+func BroadcastTxAsyncFunc(validator *Node, txEncoder txs.Encoder) func(env *txs.Envelope,
 	callback func(res *abci_types.Response)) error {
 
-	return func(tx txs.Tx, callback func(res *abci_types.Response)) error {
-		txBytes, err := txEncoder.EncodeTx(tx)
+	return func(env *txs.Envelope, callback func(res *abci_types.Response)) error {
+		txBytes, err := txEncoder.EncodeTx(env)
 		if err != nil {
 			return fmt.Errorf("error encoding transaction: %v", err)
 		}
diff --git a/core/kernel.go b/core/kernel.go
index 555b78885dfe3e53833891bb41a4be734a72f3eb..eceaec854897465455f7fcc15b709ae21221085d 100644
--- a/core/kernel.go
+++ b/core/kernel.go
@@ -94,17 +94,17 @@ func NewKernel(ctx context.Context, keyClient keys.KeyClient, privValidator tm_t
 	}
 
 	tmGenesisDoc := tendermint.DeriveGenesisDoc(genesisDoc)
-	checker := execution.NewBatchChecker(state, tmGenesisDoc.ChainID, blockchain, logger)
+	checker := execution.NewBatchChecker(state, tmGenesisDoc.ChainID, blockchain.Tip, logger)
 
 	emitter := event.NewEmitter(logger)
-	committer := execution.NewBatchCommitter(state, tmGenesisDoc.ChainID, blockchain, emitter, logger, exeOptions...)
+	committer := execution.NewBatchCommitter(state, tmGenesisDoc.ChainID, blockchain.Tip, emitter, logger, exeOptions...)
 	tmNode, err := tendermint.NewNode(tmConf, privValidator, tmGenesisDoc, blockchain, checker, committer, tmLogger)
 
 	if err != nil {
 		return nil, err
 	}
-	txCodec := txs.NewGoWireCodec()
-	transactor := execution.NewTransactor(blockchain, emitter, tendermint.BroadcastTxAsyncFunc(tmNode, txCodec),
+	txCodec := txs.NewJSONCodec()
+	transactor := execution.NewTransactor(blockchain.Tip, emitter, tendermint.BroadcastTxAsyncFunc(tmNode, txCodec),
 		logger)
 
 	nameReg := state
diff --git a/crypto/crypto.go b/crypto/crypto.go
index da0e85dd0af80b0c12d026c551fd0b6ae8ed0389..a1d588e9094571333c531b9d53bb0490112b3cbf 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -1,18 +1,7 @@
 package crypto
 
 import (
-	"bytes"
-	crand "crypto/rand"
-	"crypto/sha256"
-	"encoding/json"
 	"fmt"
-	"io"
-
-	"github.com/btcsuite/btcd/btcec"
-	tm_crypto "github.com/tendermint/go-crypto"
-	"github.com/tmthrgd/go-hex"
-	"golang.org/x/crypto/ed25519"
-	"golang.org/x/crypto/ripemd160"
 )
 
 type CurveType int8
@@ -33,6 +22,11 @@ func (k CurveType) String() string {
 	}
 }
 
+// Get this CurveType's 8 bit identifier as a byte
+func (k CurveType) Byte() byte {
+	return byte(k)
+}
+
 func CurveTypeFromString(s string) (CurveType, error) {
 	switch s {
 	case "secp256k1":
@@ -45,17 +39,6 @@ func CurveTypeFromString(s string) (CurveType, error) {
 	}
 }
 
-func (p PublicKey) AddressHashType() string {
-	switch p.CurveType {
-	case CurveTypeEd25519:
-		return "go-crypto-0.5.0"
-	case CurveTypeSecp256k1:
-		return "btc"
-	default:
-		return ""
-	}
-}
-
 type ErrInvalidCurve string
 
 func (err ErrInvalidCurve) Error() string {
@@ -69,293 +52,8 @@ type Signer interface {
 	Sign(msg []byte) (Signature, error)
 }
 
-// PublicKey
-type PublicKey struct {
-	CurveType CurveType
-	PublicKey []byte
-}
-
-type PrivateKey struct {
-	CurveType  CurveType
-	PublicKey  []byte
-	PrivateKey []byte
-}
-
-type PublicKeyJSON struct {
-	CurveType string
-	PublicKey string
-}
-
-func (p PublicKey) MarshalJSON() ([]byte, error) {
-	jStruct := PublicKeyJSON{
-		CurveType: p.CurveType.String(),
-		PublicKey: hex.EncodeUpperToString(p.PublicKey),
-	}
-	txt, err := json.Marshal(jStruct)
-	return txt, err
-}
-
-func (p PublicKey) MarshalText() ([]byte, error) {
-	return p.MarshalJSON()
-}
-
-func (p *PublicKey) UnmarshalJSON(text []byte) error {
-	var jStruct PublicKeyJSON
-	err := json.Unmarshal(text, &jStruct)
-	if err != nil {
-		return err
-	}
-	CurveType, err := CurveTypeFromString(jStruct.CurveType)
-	if err != nil {
-		return err
-	}
-	bs, err := hex.DecodeString(jStruct.PublicKey)
-	if err != nil {
-		return err
-	}
-	p.CurveType = CurveType
-	p.PublicKey = bs
-	return nil
-}
-
-func (p *PublicKey) UnmarshalText(text []byte) error {
-	return p.UnmarshalJSON(text)
-}
-
-func (p PublicKey) IsValid() bool {
-	switch p.CurveType {
-	case CurveTypeEd25519:
-		return len(p.PublicKey) == ed25519.PublicKeySize
-	case CurveTypeSecp256k1:
-		return len(p.PublicKey) == btcec.PubKeyBytesLenCompressed
-	default:
-		return false
-	}
-}
-func (p PublicKey) Verify(msg []byte, signature Signature) bool {
-	switch p.CurveType {
-	case CurveTypeEd25519:
-		return ed25519.Verify(p.PublicKey, msg, signature.Signature[:])
-	case CurveTypeSecp256k1:
-		pub, err := btcec.ParsePubKey(p.PublicKey, btcec.S256())
-		if err != nil {
-			return false
-		}
-		sig, err := btcec.ParseDERSignature(signature.Signature, btcec.S256())
-		if err != nil {
-			return false
-		}
-		return sig.Verify(msg, pub)
-	default:
-		panic(fmt.Sprintf("invalid curve type"))
-	}
-}
-
-func (p PublicKey) Address() Address {
-	switch p.CurveType {
-	case CurveTypeEd25519:
-		// FIMXE: tendermint go-crypto-0.5.0 uses weird scheme, this is fixed in 0.6.0
-		tmPubKey := new(tm_crypto.PubKeyEd25519)
-		copy(tmPubKey[:], p.PublicKey)
-		addr, _ := AddressFromBytes(tmPubKey.Address())
-		return addr
-	case CurveTypeSecp256k1:
-		sha := sha256.New()
-		sha.Write(p.PublicKey[:])
-
-		hash := ripemd160.New()
-		hash.Write(sha.Sum(nil))
-		addr, _ := AddressFromBytes(hash.Sum(nil))
-		return addr
-	default:
-		panic(fmt.Sprintf("unknown CurveType %d", p.CurveType))
-	}
-}
-
-func (p PublicKey) RawBytes() []byte {
-	return p.PublicKey[:]
-}
-
-func (p PublicKey) String() string {
-	return hex.EncodeUpperToString(p.PublicKey)
-}
-
 // Signable is an interface for all signable things.
 // It typically removes signatures before serializing.
 type Signable interface {
-	WriteSignBytes(chainID string, w io.Writer, n *int, err *error)
-}
-
-// SignBytes is a convenience method for getting the bytes to sign of a Signable.
-func SignBytes(chainID string, o Signable) []byte {
-	buf, n, err := new(bytes.Buffer), new(int), new(error)
-	o.WriteSignBytes(chainID, buf, n, err)
-	if *err != nil {
-		panic(fmt.Sprintf("could not write sign bytes for a signable: %s", *err))
-	}
-	return buf.Bytes()
-}
-
-// Currently this is a stub that reads the raw bytes returned by key_client and returns
-// an ed25519 public key.
-func PublicKeyFromBytes(bs []byte, curveType CurveType) (PublicKey, error) {
-	switch curveType {
-	case CurveTypeEd25519:
-		if len(bs) != ed25519.PublicKeySize {
-			return PublicKey{}, fmt.Errorf("bytes passed have length %v but ed25519 public keys have %v bytes",
-				len(bs), ed25519.PublicKeySize)
-		}
-	case CurveTypeSecp256k1:
-		if len(bs) != btcec.PubKeyBytesLenCompressed {
-			return PublicKey{}, fmt.Errorf("bytes passed have length %v but secp256k1 public keys have %v bytes",
-				len(bs), btcec.PubKeyBytesLenCompressed)
-		}
-	default:
-		return PublicKey{}, ErrInvalidCurve(curveType)
-	}
-
-	return PublicKey{PublicKey: bs, CurveType: curveType}, nil
-}
-
-func (p PrivateKey) RawBytes() []byte {
-	return p.PrivateKey
-}
-
-func (p PrivateKey) Sign(msg []byte) (Signature, error) {
-	switch p.CurveType {
-	case CurveTypeEd25519:
-		if len(p.PrivateKey) != ed25519.PrivateKeySize {
-			return Signature{}, fmt.Errorf("bytes passed have length %v but ed25519 private keys have %v bytes",
-				len(p.PrivateKey), ed25519.PrivateKeySize)
-		}
-		privKey := ed25519.PrivateKey(p.PrivateKey)
-		return Signature{ed25519.Sign(privKey, msg)}, nil
-	case CurveTypeSecp256k1:
-		if len(p.PrivateKey) != btcec.PrivKeyBytesLen {
-			return Signature{}, fmt.Errorf("bytes passed have length %v but secp256k1 private keys have %v bytes",
-				len(p.PrivateKey), btcec.PrivKeyBytesLen)
-		}
-		privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), p.PrivateKey)
-
-		sig, err := privKey.Sign(msg)
-		if err != nil {
-			return Signature{}, err
-		}
-		return Signature{Signature: sig.Serialize()}, nil
-	default:
-		return Signature{}, ErrInvalidCurve(p.CurveType)
-	}
-}
-
-func (p PrivateKey) GetPublicKey() PublicKey {
-	return PublicKey{CurveType: p.CurveType, PublicKey: p.PublicKey}
-}
-
-func PrivateKeyFromRawBytes(privKeyBytes []byte, curveType CurveType) (PrivateKey, error) {
-	switch curveType {
-	case CurveTypeEd25519:
-		if len(privKeyBytes) != ed25519.PrivateKeySize {
-			return PrivateKey{}, fmt.Errorf("bytes passed have length %v but ed25519 private keys have %v bytes",
-				len(privKeyBytes), ed25519.PrivateKeySize)
-		}
-		return PrivateKey{PrivateKey: privKeyBytes, PublicKey: privKeyBytes[32:], CurveType: CurveTypeEd25519}, nil
-	case CurveTypeSecp256k1:
-		if len(privKeyBytes) != btcec.PrivKeyBytesLen {
-			return PrivateKey{}, fmt.Errorf("bytes passed have length %v but secp256k1 private keys have %v bytes",
-				len(privKeyBytes), btcec.PrivKeyBytesLen)
-		}
-		privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes)
-		return PrivateKey{PrivateKey: privKey.Serialize(), PublicKey: pubKey.SerializeCompressed(), CurveType: CurveTypeSecp256k1}, nil
-	default:
-		return PrivateKey{}, ErrInvalidCurve(curveType)
-	}
-}
-
-func GeneratePrivateKey(random io.Reader, curveType CurveType) (PrivateKey, error) {
-	if random == nil {
-		random = crand.Reader
-	}
-	switch curveType {
-	case CurveTypeEd25519:
-		_, priv, err := ed25519.GenerateKey(random)
-		if err != nil {
-			return PrivateKey{}, err
-		}
-		return PrivateKeyFromRawBytes(priv, CurveTypeEd25519)
-	case CurveTypeSecp256k1:
-		privKeyBytes := make([]byte, 32)
-		_, err := random.Read(privKeyBytes)
-		if err != nil {
-			return PrivateKey{}, err
-		}
-		return PrivateKeyFromRawBytes(privKeyBytes, CurveTypeSecp256k1)
-	default:
-		return PrivateKey{}, ErrInvalidCurve(curveType)
-	}
-}
-
-func PrivateKeyFromSecret(secret string, curveType CurveType) PrivateKey {
-	hasher := sha256.New()
-	hasher.Write(([]byte)(secret))
-	// No error from a buffer
-	privateKey, _ := GeneratePrivateKey(bytes.NewBuffer(hasher.Sum(nil)), curveType)
-	return privateKey
-}
-
-// Ensures the last 32 bytes of the ed25519 private key is the public key derived from the first 32 private bytes
-func EnsureEd25519PrivateKeyCorrect(candidatePrivateKey ed25519.PrivateKey) error {
-	if len(candidatePrivateKey) != ed25519.PrivateKeySize {
-		return fmt.Errorf("ed25519 key has size %v but %v bytes passed as key", ed25519.PrivateKeySize,
-			len(candidatePrivateKey))
-	}
-	_, derivedPrivateKey, err := ed25519.GenerateKey(bytes.NewBuffer(candidatePrivateKey))
-	if err != nil {
-		return err
-	}
-	if !bytes.Equal(derivedPrivateKey, candidatePrivateKey) {
-		return fmt.Errorf("ed25519 key generated from prefix of %X should equal %X, but is %X",
-			candidatePrivateKey, candidatePrivateKey, derivedPrivateKey)
-	}
-	return nil
-}
-
-func ChainSign(signer Signer, chainID string, o Signable) (Signature, error) {
-	sig, err := signer.Sign(SignBytes(chainID, o))
-	if err != nil {
-		return Signature{}, err
-	}
-	return sig, nil
-}
-
-// Signature
-
-type Signature struct {
-	Signature []byte
-}
-
-// Currently this is a stub that reads the raw bytes returned by key_client and returns
-// an ed25519 signature.
-func SignatureFromBytes(bs []byte, curveType CurveType) (Signature, error) {
-	switch curveType {
-	case CurveTypeEd25519:
-		signatureEd25519 := Signature{}
-		if len(bs) != ed25519.SignatureSize {
-			return Signature{}, fmt.Errorf("bytes passed have length %v by ed25519 signatures have %v bytes",
-				len(bs), ed25519.SignatureSize)
-		}
-		copy(signatureEd25519.Signature[:], bs)
-		return Signature{
-			Signature: bs,
-		}, nil
-	case CurveTypeSecp256k1:
-		return Signature{
-			Signature: bs,
-		}, nil
-	default:
-		return Signature{}, nil
-	}
-}
-
-func (sig Signature) RawBytes() []byte {
-	return sig.Signature
+	SignBytes(chainID string) ([]byte, error)
 }
diff --git a/crypto/private_key.go b/crypto/private_key.go
new file mode 100644
index 0000000000000000000000000000000000000000..f1bad18969e89de0d4882a293e6f2b0117a19709
--- /dev/null
+++ b/crypto/private_key.go
@@ -0,0 +1,141 @@
+package crypto
+
+import (
+	"bytes"
+	cryptoRand "crypto/rand"
+	"crypto/sha256"
+	"fmt"
+	"io"
+
+	"github.com/btcsuite/btcd/btcec"
+	"golang.org/x/crypto/ed25519"
+)
+
+type PrivateKey struct {
+	CurveType  CurveType
+	PublicKey  []byte
+	PrivateKey []byte
+}
+
+// Currently this is a stub that reads the raw bytes returned by key_client and returns
+// an ed25519 public key.
+func PublicKeyFromBytes(bs []byte, curveType CurveType) (PublicKey, error) {
+	switch curveType {
+	case CurveTypeEd25519:
+		if len(bs) != ed25519.PublicKeySize {
+			return PublicKey{}, fmt.Errorf("bytes passed have length %v but ed25519 public keys have %v bytes",
+				len(bs), ed25519.PublicKeySize)
+		}
+	case CurveTypeSecp256k1:
+		if len(bs) != btcec.PubKeyBytesLenCompressed {
+			return PublicKey{}, fmt.Errorf("bytes passed have length %v but secp256k1 public keys have %v bytes",
+				len(bs), btcec.PubKeyBytesLenCompressed)
+		}
+	default:
+		return PublicKey{}, ErrInvalidCurve(curveType)
+	}
+
+	return PublicKey{PublicKey: bs, CurveType: curveType}, nil
+}
+
+func (p PrivateKey) RawBytes() []byte {
+	return p.PrivateKey
+}
+
+func (p PrivateKey) Sign(msg []byte) (Signature, error) {
+	switch p.CurveType {
+	case CurveTypeEd25519:
+		if len(p.PrivateKey) != ed25519.PrivateKeySize {
+			return Signature{}, fmt.Errorf("bytes passed have length %v but ed25519 private keys have %v bytes",
+				len(p.PrivateKey), ed25519.PrivateKeySize)
+		}
+		privKey := ed25519.PrivateKey(p.PrivateKey)
+		return Signature{ed25519.Sign(privKey, msg)}, nil
+	case CurveTypeSecp256k1:
+		if len(p.PrivateKey) != btcec.PrivKeyBytesLen {
+			return Signature{}, fmt.Errorf("bytes passed have length %v but secp256k1 private keys have %v bytes",
+				len(p.PrivateKey), btcec.PrivKeyBytesLen)
+		}
+		privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), p.PrivateKey)
+
+		sig, err := privKey.Sign(msg)
+		if err != nil {
+			return Signature{}, err
+		}
+		return Signature{Signature: sig.Serialize()}, nil
+	default:
+		return Signature{}, ErrInvalidCurve(p.CurveType)
+	}
+}
+
+func (p PrivateKey) GetPublicKey() PublicKey {
+	return PublicKey{CurveType: p.CurveType, PublicKey: p.PublicKey}
+}
+
+func PrivateKeyFromRawBytes(privKeyBytes []byte, curveType CurveType) (PrivateKey, error) {
+	switch curveType {
+	case CurveTypeEd25519:
+		if len(privKeyBytes) != ed25519.PrivateKeySize {
+			return PrivateKey{}, fmt.Errorf("bytes passed have length %v but ed25519 private keys have %v bytes",
+				len(privKeyBytes), ed25519.PrivateKeySize)
+		}
+		return PrivateKey{PrivateKey: privKeyBytes, PublicKey: privKeyBytes[32:], CurveType: CurveTypeEd25519}, nil
+	case CurveTypeSecp256k1:
+		if len(privKeyBytes) != btcec.PrivKeyBytesLen {
+			return PrivateKey{}, fmt.Errorf("bytes passed have length %v but secp256k1 private keys have %v bytes",
+				len(privKeyBytes), btcec.PrivKeyBytesLen)
+		}
+		privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes)
+		return PrivateKey{PrivateKey: privKey.Serialize(), PublicKey: pubKey.SerializeCompressed(), CurveType: CurveTypeSecp256k1}, nil
+	default:
+		return PrivateKey{}, ErrInvalidCurve(curveType)
+	}
+}
+
+func GeneratePrivateKey(random io.Reader, curveType CurveType) (PrivateKey, error) {
+	if random == nil {
+		random = cryptoRand.Reader
+	}
+	switch curveType {
+	case CurveTypeEd25519:
+		_, priv, err := ed25519.GenerateKey(random)
+		if err != nil {
+			return PrivateKey{}, err
+		}
+		return PrivateKeyFromRawBytes(priv, CurveTypeEd25519)
+	case CurveTypeSecp256k1:
+		privKeyBytes := make([]byte, 32)
+		_, err := random.Read(privKeyBytes)
+		if err != nil {
+			return PrivateKey{}, err
+		}
+		return PrivateKeyFromRawBytes(privKeyBytes, CurveTypeSecp256k1)
+	default:
+		return PrivateKey{}, ErrInvalidCurve(curveType)
+	}
+}
+
+func PrivateKeyFromSecret(secret string, curveType CurveType) PrivateKey {
+	hasher := sha256.New()
+	hasher.Write(([]byte)(secret))
+	// No error from a buffer
+	privateKey, _ := GeneratePrivateKey(bytes.NewBuffer(hasher.Sum(nil)), curveType)
+	return privateKey
+}
+
+// Ensures the last 32 bytes of the ed25519 private key is the public key derived from the first 32 private bytes
+func EnsureEd25519PrivateKeyCorrect(candidatePrivateKey ed25519.PrivateKey) error {
+	if len(candidatePrivateKey) != ed25519.PrivateKeySize {
+		return fmt.Errorf("ed25519 key has size %v but %v bytes passed as key", ed25519.PrivateKeySize,
+			len(candidatePrivateKey))
+	}
+	_, derivedPrivateKey, err := ed25519.GenerateKey(bytes.NewBuffer(candidatePrivateKey))
+	if err != nil {
+		return err
+	}
+	if !bytes.Equal(derivedPrivateKey, candidatePrivateKey) {
+		return fmt.Errorf("ed25519 key generated from prefix of %X should equal %X, but is %X",
+			candidatePrivateKey, candidatePrivateKey, derivedPrivateKey)
+	}
+	return nil
+}
diff --git a/crypto/public_key.go b/crypto/public_key.go
new file mode 100644
index 0000000000000000000000000000000000000000..b3281c61fceaae826678264145a6ec7aa830e7ea
--- /dev/null
+++ b/crypto/public_key.go
@@ -0,0 +1,198 @@
+package crypto
+
+import (
+	"crypto/sha256"
+	"encoding/json"
+	"fmt"
+
+	"github.com/btcsuite/btcd/btcec"
+	abci "github.com/tendermint/abci/types"
+	tmCrypto "github.com/tendermint/go-crypto"
+	"github.com/tmthrgd/go-hex"
+	"golang.org/x/crypto/ed25519"
+	"golang.org/x/crypto/ripemd160"
+)
+
+// PublicKey
+type PublicKey struct {
+	CurveType CurveType
+	PublicKey []byte
+	// memoised address
+	address *Address
+}
+
+type PublicKeyJSON struct {
+	CurveType string
+	PublicKey string
+}
+
+// Returns the length in bytes of the public key
+func PublicKeyLength(curveType CurveType) int {
+	switch curveType {
+	case CurveTypeEd25519:
+		return ed25519.PublicKeySize
+	case CurveTypeSecp256k1:
+		return btcec.PubKeyBytesLenCompressed
+	default:
+		// Other functions rely on this
+		return 0
+	}
+}
+
+func (p PublicKey) MarshalJSON() ([]byte, error) {
+	jStruct := PublicKeyJSON{
+		CurveType: p.CurveType.String(),
+		PublicKey: hex.EncodeUpperToString(p.PublicKey),
+	}
+	txt, err := json.Marshal(jStruct)
+	return txt, err
+}
+
+func (p PublicKey) MarshalText() ([]byte, error) {
+	return p.MarshalJSON()
+}
+
+func (p *PublicKey) UnmarshalJSON(text []byte) error {
+	var jStruct PublicKeyJSON
+	err := json.Unmarshal(text, &jStruct)
+	if err != nil {
+		return err
+	}
+	CurveType, err := CurveTypeFromString(jStruct.CurveType)
+	if err != nil {
+		return err
+	}
+	bs, err := hex.DecodeString(jStruct.PublicKey)
+	if err != nil {
+		return err
+	}
+	p.CurveType = CurveType
+	p.PublicKey = bs
+	return nil
+}
+
+func (p *PublicKey) UnmarshalText(text []byte) error {
+	return p.UnmarshalJSON(text)
+}
+
+func (p PublicKey) IsValid() bool {
+	publicKeyLength := PublicKeyLength(p.CurveType)
+	return publicKeyLength != 0 && publicKeyLength == len(p.PublicKey)
+}
+
+func (p PublicKey) Verify(msg []byte, signature Signature) bool {
+	switch p.CurveType {
+	case CurveTypeEd25519:
+		return ed25519.Verify(p.PublicKey, msg, signature.Signature[:])
+	case CurveTypeSecp256k1:
+		pub, err := btcec.ParsePubKey(p.PublicKey, btcec.S256())
+		if err != nil {
+			return false
+		}
+		sig, err := btcec.ParseDERSignature(signature.Signature, btcec.S256())
+		if err != nil {
+			return false
+		}
+		return sig.Verify(msg, pub)
+	default:
+		panic(fmt.Sprintf("invalid curve type"))
+	}
+}
+
+func (p PublicKey) Address() Address {
+	if p.address == nil {
+		address := p.computeAddress()
+		p.address = &address
+	}
+	return *p.address
+}
+
+func (p PublicKey) computeAddress() Address {
+	switch p.CurveType {
+	case CurveTypeEd25519:
+		// FIMXE: tendermint go-crypto-0.5.0 uses weird scheme, this is fixed in 0.6.0
+		tmPubKey := new(tmCrypto.PubKeyEd25519)
+		copy(tmPubKey[:], p.PublicKey)
+		addr, _ := AddressFromBytes(tmPubKey.Address())
+		return addr
+	case CurveTypeSecp256k1:
+		sha := sha256.New()
+		sha.Write(p.PublicKey[:])
+
+		hash := ripemd160.New()
+		hash.Write(sha.Sum(nil))
+		addr, _ := AddressFromBytes(hash.Sum(nil))
+		return addr
+	default:
+		panic(fmt.Sprintf("unknown CurveType %d", p.CurveType))
+	}
+}
+
+func (p PublicKey) AddressHashType() string {
+	switch p.CurveType {
+	case CurveTypeEd25519:
+		return "go-crypto-0.5.0"
+	case CurveTypeSecp256k1:
+		return "btc"
+	default:
+		return ""
+	}
+}
+
+func (p PublicKey) RawBytes() []byte {
+	return p.PublicKey[:]
+}
+
+// Return the ABCI PubKey. See Tendermint protobuf.go for the go-crypto conversion this is based on
+func (p PublicKey) ABCIPubKey() abci.PubKey {
+	switch p.CurveType {
+	case CurveTypeEd25519:
+		return abci.PubKey{
+			Type: "ed25519",
+			Data: p.RawBytes(),
+		}
+	case CurveTypeSecp256k1:
+		return abci.PubKey{
+			Type: "secp256k1",
+			Data: p.RawBytes(),
+		}
+	default:
+		return abci.PubKey{}
+	}
+}
+
+func (p PublicKey) String() string {
+	return hex.EncodeUpperToString(p.PublicKey)
+}
+
+// Produces a binary encoding of the CurveType byte plus
+// the public key for padded to a fixed width on the right
+func (p PublicKey) Encode() []byte {
+	encoded := make([]byte, PublicKeyLength(p.CurveType)+1)
+	encoded[0] = p.CurveType.Byte()
+	copy(encoded[1:], p.PublicKey)
+	return encoded
+}
+
+// Decodes an encoded public key returning the number of bytes consumed
+func DecodePublicKeyFixedWidth(buf []byte, publicKey *PublicKey) (int, error) {
+	if len(buf) < 1 {
+		return 0, fmt.Errorf("encoded bytes buffer must not be empty")
+	}
+	curveType := CurveType(buf[0])
+	publicKeyEnd := PublicKeyLength(curveType) + 1
+	if publicKeyEnd <= 0 {
+		return 0, fmt.Errorf("CurveType with identifier %v is unknown", curveType.Byte())
+	}
+	if len(buf) < publicKeyEnd {
+		return 0, fmt.Errorf("encoded bytes buffer has length %v but public key encoding for %v needs %v bytes",
+			len(buf), curveType, publicKeyEnd)
+	}
+
+	publicKey.CurveType = curveType
+	publicKey.PublicKey = buf[1:publicKeyEnd]
+	if !publicKey.IsValid() {
+		return publicKeyEnd, fmt.Errorf("decoded public key %v is not valid", publicKey)
+	}
+	return publicKeyEnd, nil
+}
diff --git a/crypto/signature.go b/crypto/signature.go
new file mode 100644
index 0000000000000000000000000000000000000000..51758dd712357e99cd2195978a54c870e88f1193
--- /dev/null
+++ b/crypto/signature.go
@@ -0,0 +1,47 @@
+package crypto
+
+import (
+	"fmt"
+
+	"golang.org/x/crypto/ed25519"
+)
+
+type Signature struct {
+	Signature []byte
+}
+
+//
+//func (sig *Signature) MarshalJSON() ([]byte, error) {
+//
+//}
+//
+//func (sig *Signature) UnmarshalJSON(data []byte) error {
+//
+//}
+
+// Currently this is a stub that reads the raw bytes returned by key_client and returns
+// an ed25519 signature.
+func SignatureFromBytes(bs []byte, curveType CurveType) (Signature, error) {
+	switch curveType {
+	case CurveTypeEd25519:
+		signatureEd25519 := Signature{}
+		if len(bs) != ed25519.SignatureSize {
+			return Signature{}, fmt.Errorf("bytes passed have length %v by ed25519 signatures have %v bytes",
+				len(bs), ed25519.SignatureSize)
+		}
+		copy(signatureEd25519.Signature[:], bs)
+		return Signature{
+			Signature: bs,
+		}, nil
+	case CurveTypeSecp256k1:
+		return Signature{
+			Signature: bs,
+		}, nil
+	default:
+		return Signature{}, nil
+	}
+}
+
+func (sig Signature) RawBytes() []byte {
+	return sig.Signature
+}
diff --git a/execution/accounts.go b/execution/accounts.go
index ec9d663c002dd3aaf928a62a480d060a77c11a57..1a9fe3aa53cb1f53217ffc1a65ecf1032cd85007 100644
--- a/execution/accounts.go
+++ b/execution/accounts.go
@@ -59,14 +59,17 @@ func (accs *Accounts) SigningAccount(address crypto.Address, signer crypto.Signe
 	}, nil
 }
 
-func (accs *Accounts) SequentialSigningAccount(address crypto.Address) *SequentialSigningAccount {
-	signer := keys.Signer(accs.keyClient, address)
+func (accs *Accounts) SequentialSigningAccount(address crypto.Address) (*SequentialSigningAccount, error) {
+	signer, err := keys.AddressableSigner(accs.keyClient, address)
+	if err != nil {
+		return nil, err
+	}
 	return &SequentialSigningAccount{
 		accountLocker: accs.Mutex(address.Bytes()),
 		getter: func() (*SigningAccount, error) {
 			return accs.SigningAccount(address, signer)
 		},
-	}
+	}, nil
 }
 
 func (accs *Accounts) SequentialSigningAccountFromPrivateKey(privateKeyBytes []byte) (*SequentialSigningAccount, error) {
diff --git a/execution/events/events.go b/execution/events/events.go
index b9aa92df754ccdb8acff76523a183aa63d9ad694..fd28bfe896d42f7e8910f5cf208028c7c0f658a3 100644
--- a/execution/events/events.go
+++ b/execution/events/events.go
@@ -2,13 +2,13 @@ package events
 
 import (
 	"context"
-	"encoding/json"
 	"fmt"
 	"reflect"
 
 	"github.com/hyperledger/burrow/crypto"
 	"github.com/hyperledger/burrow/event"
 	"github.com/hyperledger/burrow/txs"
+	"github.com/hyperledger/burrow/txs/payload"
 	"github.com/tmthrgd/go-hex"
 )
 
@@ -22,7 +22,7 @@ func EventStringRebond() string                           { return "Rebond" }
 
 // All txs fire EventDataTx, but only CallTx might have Return or Exception
 type EventDataTx struct {
-	Tx        txs.Tx
+	Tx        *txs.Tx
 	Return    []byte
 	Exception string
 }
@@ -30,49 +30,22 @@ type EventDataTx struct {
 // For re-use
 var sendTxQuery = event.NewQueryBuilder().
 	AndEquals(event.MessageTypeKey, reflect.TypeOf(&EventDataTx{}).String()).
-	AndEquals(event.TxTypeKey, reflect.TypeOf(&txs.SendTx{}).String())
+	AndEquals(event.TxTypeKey, payload.TypeSend.String())
 
 var callTxQuery = event.NewQueryBuilder().
 	AndEquals(event.MessageTypeKey, reflect.TypeOf(&EventDataTx{}).String()).
-	AndEquals(event.TxTypeKey, reflect.TypeOf(&txs.CallTx{}).String())
-
-type eventDataTx struct {
-	Tx        txs.Wrapper
-	Return    []byte
-	Exception string
-}
-
-func (edTx EventDataTx) MarshalJSON() ([]byte, error) {
-	model := eventDataTx{
-		Tx:        txs.Wrap(edTx.Tx),
-		Exception: edTx.Exception,
-		Return:    edTx.Return,
-	}
-	return json.Marshal(model)
-}
-
-func (edTx *EventDataTx) UnmarshalJSON(data []byte) error {
-	model := new(eventDataTx)
-	err := json.Unmarshal(data, model)
-	if err != nil {
-		return err
-	}
-	edTx.Tx = model.Tx.Unwrap()
-	edTx.Return = model.Return
-	edTx.Exception = model.Exception
-	return nil
-}
+	AndEquals(event.TxTypeKey, payload.TypeCall.String())
 
 // Publish/Subscribe
 func SubscribeAccountOutputSendTx(ctx context.Context, subscribable event.Subscribable, subscriber string,
-	address crypto.Address, txHash []byte, ch chan<- *txs.SendTx) error {
+	address crypto.Address, txHash []byte, ch chan<- *payload.SendTx) error {
 
 	query := sendTxQuery.And(event.QueryForEventID(EventStringAccountOutput(address))).
 		AndEquals(event.TxHashKey, hex.EncodeUpperToString(txHash))
 
 	return event.SubscribeCallback(ctx, subscribable, subscriber, query, func(message interface{}) bool {
 		if edt, ok := message.(*EventDataTx); ok {
-			if sendTx, ok := edt.Tx.(*txs.SendTx); ok {
+			if sendTx, ok := edt.Tx.Payload.(*payload.SendTx); ok {
 				ch <- sendTx
 			}
 		}
@@ -80,8 +53,8 @@ func SubscribeAccountOutputSendTx(ctx context.Context, subscribable event.Subscr
 	})
 }
 
-func PublishAccountOutput(publisher event.Publisher, address crypto.Address, txHash []byte,
-	tx txs.Tx, ret []byte, exception string) error {
+func PublishAccountOutput(publisher event.Publisher, address crypto.Address, tx *txs.Tx, ret []byte,
+	exception string) error {
 
 	return event.PublishWithEventID(publisher, EventStringAccountOutput(address),
 		&EventDataTx{
@@ -91,13 +64,13 @@ func PublishAccountOutput(publisher event.Publisher, address crypto.Address, txH
 		},
 		map[string]interface{}{
 			"address":       address,
-			event.TxTypeKey: reflect.TypeOf(tx).String(),
-			event.TxHashKey: hex.EncodeUpperToString(txHash),
+			event.TxTypeKey: tx.Type().String(),
+			event.TxHashKey: hex.EncodeUpperToString(tx.Hash()),
 		})
 }
 
-func PublishAccountInput(publisher event.Publisher, address crypto.Address, txHash []byte,
-	tx txs.Tx, ret []byte, exception string) error {
+func PublishAccountInput(publisher event.Publisher, address crypto.Address, tx *txs.Tx, ret []byte,
+	exception string) error {
 
 	return event.PublishWithEventID(publisher, EventStringAccountInput(address),
 		&EventDataTx{
@@ -107,25 +80,33 @@ func PublishAccountInput(publisher event.Publisher, address crypto.Address, txHa
 		},
 		map[string]interface{}{
 			"address":       address,
-			event.TxTypeKey: reflect.TypeOf(tx).String(),
-			event.TxHashKey: hex.EncodeUpperToString(txHash),
+			event.TxTypeKey: tx.Type().String(),
+			event.TxHashKey: hex.EncodeUpperToString(tx.Hash()),
 		})
 }
 
-func PublishNameReg(publisher event.Publisher, txHash []byte, tx *txs.NameTx) error {
-	return event.PublishWithEventID(publisher, EventStringNameReg(tx.Name), &EventDataTx{Tx: tx},
+func PublishNameReg(publisher event.Publisher, tx *txs.Tx) error {
+	nameTx, ok := tx.Payload.(*payload.NameTx)
+	if !ok {
+		return fmt.Errorf("Tx payload must be NameTx to PublishNameReg")
+	}
+	return event.PublishWithEventID(publisher, EventStringNameReg(nameTx.Name), &EventDataTx{Tx: tx},
 		map[string]interface{}{
-			"name":          tx.Name,
-			event.TxTypeKey: reflect.TypeOf(tx).String(),
-			event.TxHashKey: hex.EncodeUpperToString(txHash),
+			"name":          nameTx.Name,
+			event.TxTypeKey: tx.Type().String(),
+			event.TxHashKey: hex.EncodeUpperToString(tx.Hash()),
 		})
 }
 
-func PublishPermissions(publisher event.Publisher, name string, txHash []byte, tx *txs.PermissionsTx) error {
+func PublishPermissions(publisher event.Publisher, name string, tx *txs.Tx) error {
+	_, ok := tx.Payload.(*payload.PermissionsTx)
+	if !ok {
+		return fmt.Errorf("Tx payload must be PermissionsTx to PublishPermissions")
+	}
 	return event.PublishWithEventID(publisher, EventStringPermissions(name), &EventDataTx{Tx: tx},
 		map[string]interface{}{
 			"name":          name,
-			event.TxTypeKey: reflect.TypeOf(tx).String(),
-			event.TxHashKey: hex.EncodeUpperToString(txHash),
+			event.TxTypeKey: tx.Type().String(),
+			event.TxHashKey: hex.EncodeUpperToString(tx.Hash()),
 		})
 }
diff --git a/execution/execution.go b/execution/execution.go
index 3e7068f2408195bd4036747ec6d147c807c0a43b..74eab81fe51fae147219e0fbcd3bc4935a478cc7 100644
--- a/execution/execution.go
+++ b/execution/execution.go
@@ -27,11 +27,13 @@ import (
 	"github.com/hyperledger/burrow/event"
 	"github.com/hyperledger/burrow/execution/events"
 	"github.com/hyperledger/burrow/execution/evm"
+	"github.com/hyperledger/burrow/execution/names"
 	"github.com/hyperledger/burrow/logging"
 	"github.com/hyperledger/burrow/logging/structure"
 	"github.com/hyperledger/burrow/permission"
 	ptypes "github.com/hyperledger/burrow/permission/types"
 	"github.com/hyperledger/burrow/txs"
+	"github.com/hyperledger/burrow/txs/payload"
 )
 
 // TODO: make configurable
@@ -42,7 +44,7 @@ type BatchExecutor interface {
 	sync.Locker
 	state.Reader
 	// Execute transaction against block cache (i.e. block buffer)
-	Execute(tx txs.Tx) error
+	Execute(txEnv *txs.Envelope) error
 	// Reset executor to underlying State
 	Reset() error
 }
@@ -58,7 +60,7 @@ type BatchCommitter interface {
 type executor struct {
 	sync.RWMutex
 	chainID      string
-	tip          bcm.Tip
+	tip          *bcm.Tip
 	runCall      bool
 	state        *State
 	stateCache   state.Cache
@@ -72,21 +74,21 @@ type executor struct {
 var _ BatchExecutor = (*executor)(nil)
 
 // Wraps a cache of what is variously known as the 'check cache' and 'mempool'
-func NewBatchChecker(backend *State, chainID string, tip bcm.Tip, logger *logging.Logger,
+func NewBatchChecker(backend *State, chainID string, tip *bcm.Tip, logger *logging.Logger,
 	options ...ExecutionOption) BatchExecutor {
 
 	return newExecutor("CheckCache", false, backend, chainID, tip, event.NewNoOpPublisher(),
 		logger.WithScope("NewBatchExecutor"), options...)
 }
 
-func NewBatchCommitter(backend *State, chainID string, tip bcm.Tip, publisher event.Publisher, logger *logging.Logger,
+func NewBatchCommitter(backend *State, chainID string, tip *bcm.Tip, publisher event.Publisher, logger *logging.Logger,
 	options ...ExecutionOption) BatchCommitter {
 
 	return newExecutor("CommitCache", true, backend, chainID, tip, publisher,
 		logger.WithScope("NewBatchCommitter"), options...)
 }
 
-func newExecutor(name string, runCall bool, backend *State, chainID string, tip bcm.Tip, publisher event.Publisher,
+func newExecutor(name string, runCall bool, backend *State, chainID string, tip *bcm.Tip, publisher event.Publisher,
 	logger *logging.Logger, options ...ExecutionOption) *executor {
 
 	exe := &executor{
@@ -161,25 +163,30 @@ func (exe *executor) Reset() error {
 
 // If the tx is invalid, an error will be returned.
 // Unlike ExecBlock(), state will not be altered.
-func (exe *executor) Execute(tx txs.Tx) (err error) {
+func (exe *executor) Execute(txEnv *txs.Envelope) (err error) {
 	defer func() {
 		if r := recover(); r != nil {
-			err = fmt.Errorf("recovered from panic in executor.Execute(%s): %v\n%s", tx.String(), r,
+			err = fmt.Errorf("recovered from panic in executor.Execute(%s): %v\n%s", txEnv.String(), r,
 				debug.Stack())
 		}
 	}()
 
-	txHash := tx.Hash(exe.chainID)
 	logger := exe.logger.WithScope("executor.Execute(tx txs.Tx)").With(
 		"run_call", exe.runCall,
-		"tx_hash", txHash)
-	logger.TraceMsg("Executing transaction", "tx", tx.String())
+		"tx_hash", txEnv.Tx.Hash())
+	logger.TraceMsg("Executing transaction", "tx", txEnv.String())
 	// TODO: do something with fees
 	fees := uint64(0)
 
+	// Verify transaction signature against inputs
+	err = txEnv.Verify(exe.stateCache)
+	if err != nil {
+		return err
+	}
+
 	// Exec tx
-	switch tx := tx.(type) {
-	case *txs.SendTx:
+	switch tx := txEnv.Tx.Payload.(type) {
+	case *payload.SendTx:
 		accounts, err := getInputs(exe.stateCache, tx.Inputs)
 		if err != nil {
 			return err
@@ -197,8 +204,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 			return err
 		}
 
-		signBytes := crypto.SignBytes(exe.chainID, tx)
-		inTotal, err := validateInputs(accounts, signBytes, tx.Inputs)
+		inTotal, err := validateInputs(accounts, tx.Inputs)
 		if err != nil {
 			return err
 		}
@@ -207,7 +213,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 			return err
 		}
 		if outTotal > inTotal {
-			return txs.ErrTxInsufficientFunds
+			return payload.ErrTxInsufficientFunds
 		}
 		fee := inTotal - outTotal
 		fees += fee
@@ -229,16 +235,16 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 
 		if exe.eventCache != nil {
 			for _, i := range tx.Inputs {
-				events.PublishAccountInput(exe.eventCache, i.Address, txHash, tx, nil, "")
+				events.PublishAccountInput(exe.eventCache, i.Address, txEnv.Tx, nil, "")
 			}
 
 			for _, o := range tx.Outputs {
-				events.PublishAccountOutput(exe.eventCache, o.Address, txHash, tx, nil, "")
+				events.PublishAccountOutput(exe.eventCache, o.Address, txEnv.Tx, nil, "")
 			}
 		}
 		return nil
 
-	case *txs.CallTx:
+	case *payload.CallTx:
 		var inAcc acm.MutableAccount
 		var outAcc acm.Account
 
@@ -250,7 +256,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 		if inAcc == nil {
 			logger.InfoMsg("Cannot find input account",
 				"tx_input", tx.Input)
-			return txs.ErrTxInvalidAddress
+			return payload.ErrTxInvalidAddress
 		}
 
 		// Calling a nil destination is defined as requesting contract creation
@@ -265,14 +271,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 			}
 		}
 
-		// pubKey should be present in either "inAcc" or "tx.Input"
-		if err := checkInputPubKey(inAcc, tx.Input); err != nil {
-			logger.InfoMsg("Cannot find public key for input account",
-				"tx_input", tx.Input)
-			return err
-		}
-		signBytes := crypto.SignBytes(exe.chainID, tx)
-		err = validateInput(inAcc, signBytes, tx.Input)
+		err = validateInput(inAcc, tx.Input)
 		if err != nil {
 			logger.InfoMsg("validateInput failed",
 				"tx_input", tx.Input, structure.ErrorKey, err)
@@ -281,7 +280,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 		if tx.Input.Amount < tx.Fee {
 			logger.InfoMsg("Sender did not send enough to cover the fee",
 				"tx_input", tx.Input)
-			return txs.ErrTxInsufficientFunds
+			return payload.ErrTxInsufficientFunds
 		}
 
 		if !createContract {
@@ -357,7 +356,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 						"caller_address", inAcc.Address(),
 						"callee_address", tx.Address)
 				}
-				err = txs.ErrTxInvalidAddress
+				err = payload.ErrTxInvalidAddress
 				goto CALL_COMPLETE
 			}
 
@@ -367,7 +366,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 				callee = evm.DeriveNewAccount(caller, state.GlobalAccountPermissions(exe.state),
 					logger.With(
 						"tx", tx.String(),
-						"tx_hash", txHash,
+						"tx_hash", txEnv.Tx.Hash(),
 						"run_call", exe.runCall,
 					))
 				code = tx.Data
@@ -389,7 +388,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 				// Write caller/callee to txCache.
 				txCache.UpdateAccount(caller)
 				txCache.UpdateAccount(callee)
-				vmach := evm.NewVM(params, caller.Address(), tx.Hash(exe.chainID), logger, exe.vmOptions...)
+				vmach := evm.NewVM(params, caller.Address(), txEnv.Tx.Hash(), logger, exe.vmOptions...)
 				vmach.SetPublisher(exe.eventCache)
 				// NOTE: Call() transfers the value from caller to callee iff call succeeds.
 				ret, err = vmach.Call(txCache, caller, callee, code, tx.Data, value, &gas)
@@ -423,10 +422,9 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 				if err != nil {
 					exception = err.Error()
 				}
-				txHash := tx.Hash(exe.chainID)
-				events.PublishAccountInput(exe.eventCache, tx.Input.Address, txHash, tx, ret, exception)
+				events.PublishAccountInput(exe.eventCache, tx.Input.Address, txEnv.Tx, ret, exception)
 				if tx.Address != nil {
-					events.PublishAccountOutput(exe.eventCache, *tx.Address, txHash, tx, ret, exception)
+					events.PublishAccountOutput(exe.eventCache, *tx.Address, txEnv.Tx, ret, exception)
 				}
 			}
 		} else {
@@ -452,7 +450,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 
 		return nil
 
-	case *txs.NameTx:
+	case *payload.NameTx:
 		// Validate input
 		inAcc, err := state.GetMutableAccount(exe.stateCache, tx.Input.Address)
 		if err != nil {
@@ -461,20 +459,13 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 		if inAcc == nil {
 			logger.InfoMsg("Cannot find input account",
 				"tx_input", tx.Input)
-			return txs.ErrTxInvalidAddress
+			return payload.ErrTxInvalidAddress
 		}
 		// check permission
 		if !hasNamePermission(exe.stateCache, inAcc, logger) {
 			return fmt.Errorf("account %s does not have Name permission", tx.Input.Address)
 		}
-		// pubKey should be present in either "inAcc" or "tx.Input"
-		if err := checkInputPubKey(inAcc, tx.Input); err != nil {
-			logger.InfoMsg("Cannot find public key for input account",
-				"tx_input", tx.Input)
-			return err
-		}
-		signBytes := crypto.SignBytes(exe.chainID, tx)
-		err = validateInput(inAcc, signBytes, tx.Input)
+		err = validateInput(inAcc, tx.Input)
 		if err != nil {
 			logger.InfoMsg("validateInput failed",
 				"tx_input", tx.Input, structure.ErrorKey, err)
@@ -483,7 +474,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 		if tx.Input.Amount < tx.Fee {
 			logger.InfoMsg("Sender did not send enough to cover the fee",
 				"tx_input", tx.Input)
-			return txs.ErrTxInsufficientFunds
+			return payload.ErrTxInsufficientFunds
 		}
 
 		// validate the input strings
@@ -494,7 +485,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 		value := tx.Input.Amount - tx.Fee
 
 		// let's say cost of a name for one block is len(data) + 32
-		costPerBlock := txs.NameCostPerBlock(txs.NameBaseCost(tx.Name, tx.Data))
+		costPerBlock := names.NameCostPerBlock(names.NameBaseCost(tx.Name, tx.Data))
 		expiresIn := value / uint64(costPerBlock)
 		lastBlockHeight := exe.tip.LastBlockHeight()
 
@@ -538,8 +529,8 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 				// update the entry by bumping the expiry
 				// and changing the data
 				if expired {
-					if expiresIn < txs.MinNameRegistrationPeriod {
-						return fmt.Errorf("Names must be registered for at least %d blocks", txs.MinNameRegistrationPeriod)
+					if expiresIn < names.MinNameRegistrationPeriod {
+						return fmt.Errorf("Names must be registered for at least %d blocks", names.MinNameRegistrationPeriod)
 					}
 					entry.Expires = lastBlockHeight + expiresIn
 					entry.Owner = tx.Input.Address
@@ -550,11 +541,11 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 				} else {
 					// since the size of the data may have changed
 					// we use the total amount of "credit"
-					oldCredit := (entry.Expires - lastBlockHeight) * txs.NameBaseCost(entry.Name, entry.Data)
+					oldCredit := (entry.Expires - lastBlockHeight) * names.NameBaseCost(entry.Name, entry.Data)
 					credit := oldCredit + value
 					expiresIn = uint64(credit / costPerBlock)
-					if expiresIn < txs.MinNameRegistrationPeriod {
-						return fmt.Errorf("names must be registered for at least %d blocks", txs.MinNameRegistrationPeriod)
+					if expiresIn < names.MinNameRegistrationPeriod {
+						return fmt.Errorf("names must be registered for at least %d blocks", names.MinNameRegistrationPeriod)
 					}
 					entry.Expires = lastBlockHeight + expiresIn
 					logger.TraceMsg("Updated NameReg entry",
@@ -571,8 +562,8 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 				}
 			}
 		} else {
-			if expiresIn < txs.MinNameRegistrationPeriod {
-				return fmt.Errorf("Names must be registered for at least %d blocks", txs.MinNameRegistrationPeriod)
+			if expiresIn < names.MinNameRegistrationPeriod {
+				return fmt.Errorf("Names must be registered for at least %d blocks", names.MinNameRegistrationPeriod)
 			}
 			// entry does not exist, so create it
 			entry = &NameRegEntry{
@@ -608,9 +599,8 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 		// TODO: maybe we want to take funds on error and allow txs in that don't do anythingi?
 
 		if exe.eventCache != nil {
-			txHash := tx.Hash(exe.chainID)
-			events.PublishAccountInput(exe.eventCache, tx.Input.Address, txHash, tx, nil, "")
-			events.PublishNameReg(exe.eventCache, txHash, tx)
+			events.PublishAccountInput(exe.eventCache, tx.Input.Address, txEnv.Tx, nil, "")
+			events.PublishNameReg(exe.eventCache, txEnv.Tx)
 		}
 
 		return nil
@@ -618,7 +608,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 		// Consensus related Txs inactivated for now
 		// TODO!
 		/*
-			case *txs.BondTx:
+			case *payload.BondTx:
 						valInfo := exe.blockCache.State().GetValidatorInfo(tx.PublicKey().Address())
 						if valInfo != nil {
 							// TODO: In the future, check that the validator wasn't destroyed,
@@ -657,14 +647,14 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 							return err
 						}
 						if !tx.PublicKey().VerifyBytes(signBytes, tx.Signature) {
-							return txs.ErrTxInvalidSignature
+							return payload.ErrTxInvalidSignature
 						}
 						outTotal, err := validateOutputs(tx.UnbondTo)
 						if err != nil {
 							return err
 						}
 						if outTotal > inTotal {
-							return txs.ErrTxInsufficientFunds
+							return payload.ErrTxInsufficientFunds
 						}
 						fee := inTotal - outTotal
 						fees += fee
@@ -699,17 +689,17 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 						}
 						return nil
 
-					case *txs.UnbondTx:
+					case *payload.UnbondTx:
 						// The validator must be active
 						_, val := _s.BondedValidators.GetByAddress(tx.Address)
 						if val == nil {
-							return txs.ErrTxInvalidAddress
+							return payload.ErrTxInvalidAddress
 						}
 
 						// Verify the signature
 						signBytes := acm.SignBytes(exe.chainID, tx)
 						if !val.PublicKey().VerifyBytes(signBytes, tx.Signature) {
-							return txs.ErrTxInvalidSignature
+							return payload.ErrTxInvalidSignature
 						}
 
 						// tx.Height must be greater than val.LastCommitHeight
@@ -728,13 +718,13 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 						// The validator must be inactive
 						_, val := _s.UnbondingValidators.GetByAddress(tx.Address)
 						if val == nil {
-							return txs.ErrTxInvalidAddress
+							return payload.ErrTxInvalidAddress
 						}
 
 						// Verify the signature
 						signBytes := acm.SignBytes(exe.chainID, tx)
 						if !val.PublicKey().VerifyBytes(signBytes, tx.Signature) {
-							return txs.ErrTxInvalidSignature
+							return payload.ErrTxInvalidSignature
 						}
 
 						// tx.Height must be in a suitable range
@@ -754,7 +744,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 
 		*/
 
-	case *txs.PermissionsTx:
+	case *payload.PermissionsTx:
 		// Validate input
 		inAcc, err := state.GetMutableAccount(exe.stateCache, tx.Input.Address)
 		if err != nil {
@@ -763,7 +753,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 		if inAcc == nil {
 			logger.InfoMsg("Cannot find input account",
 				"tx_input", tx.Input)
-			return txs.ErrTxInvalidAddress
+			return payload.ErrTxInvalidAddress
 		}
 
 		err = tx.PermArgs.EnsureValid()
@@ -778,14 +768,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 				permission.PermFlagToString(permFlag), permFlag)
 		}
 
-		// pubKey should be present in either "inAcc" or "tx.Input"
-		if err := checkInputPubKey(inAcc, tx.Input); err != nil {
-			logger.InfoMsg("Cannot find public key for input account",
-				"tx_input", tx.Input)
-			return err
-		}
-		signBytes := crypto.SignBytes(exe.chainID, tx)
-		err = validateInput(inAcc, signBytes, tx.Input)
+		err = validateInput(inAcc, tx.Input)
 		if err != nil {
 			logger.InfoMsg("validateInput failed",
 				"tx_input", tx.Input,
@@ -864,9 +847,8 @@ func (exe *executor) Execute(tx txs.Tx) (err error) {
 		}
 
 		if exe.eventCache != nil {
-			txHash := tx.Hash(exe.chainID)
-			events.PublishAccountInput(exe.eventCache, tx.Input.Address, txHash, tx, nil, "")
-			events.PublishPermissions(exe.eventCache, permission.PermFlagToString(permFlag), txHash, tx)
+			events.PublishAccountInput(exe.eventCache, tx.Input.Address, txEnv.Tx, nil, "")
+			events.PublishPermissions(exe.eventCache, permission.PermFlagToString(permFlag), txEnv.Tx)
 		}
 
 		return nil
@@ -1026,24 +1008,20 @@ func execBlock(s *State, block *txs.Block, blockPartsHeader txs.PartSetHeader) e
 // or it must be specified in the TxInput.  If redeclared,
 // the TxInput is modified and input.PublicKey() set to nil.
 func getInputs(accountGetter state.AccountGetter,
-	ins []*txs.TxInput) (map[crypto.Address]acm.MutableAccount, error) {
+	ins []*payload.TxInput) (map[crypto.Address]acm.MutableAccount, error) {
 
 	accounts := map[crypto.Address]acm.MutableAccount{}
 	for _, in := range ins {
 		// Account shouldn't be duplicated
 		if _, ok := accounts[in.Address]; ok {
-			return nil, txs.ErrTxDuplicateAddress
+			return nil, payload.ErrTxDuplicateAddress
 		}
 		acc, err := state.GetMutableAccount(accountGetter, in.Address)
 		if err != nil {
 			return nil, err
 		}
 		if acc == nil {
-			return nil, txs.ErrTxInvalidAddress
-		}
-		// PublicKey should be present in either "account" or "in"
-		if err := checkInputPubKey(acc, in); err != nil {
-			return nil, err
+			return nil, payload.ErrTxInvalidAddress
 		}
 		accounts[in.Address] = acc
 	}
@@ -1051,7 +1029,7 @@ func getInputs(accountGetter state.AccountGetter,
 }
 
 func getOrMakeOutputs(accountGetter state.AccountGetter, accs map[crypto.Address]acm.MutableAccount,
-	outs []*txs.TxOutput, logger *logging.Logger) (map[crypto.Address]acm.MutableAccount, error) {
+	outs []*payload.TxOutput, logger *logging.Logger) (map[crypto.Address]acm.MutableAccount, error) {
 	if accs == nil {
 		accs = make(map[crypto.Address]acm.MutableAccount)
 	}
@@ -1061,7 +1039,7 @@ func getOrMakeOutputs(accountGetter state.AccountGetter, accs map[crypto.Address
 	for _, out := range outs {
 		// Account shouldn't be duplicated
 		if _, ok := accs[out.Address]; ok {
-			return nil, txs.ErrTxDuplicateAddress
+			return nil, payload.ErrTxDuplicateAddress
 		}
 		acc, err := state.GetMutableAccount(accountGetter, out.Address)
 		if err != nil {
@@ -1087,37 +1065,14 @@ func getOrMakeOutputs(accountGetter state.AccountGetter, accs map[crypto.Address
 	return accs, nil
 }
 
-// Since all ethereum accounts implicitly exist we sometimes lazily create an Account object to represent them
-// only when needed. Sometimes we need to create an unknown Account knowing only its address (which is expected to
-// be a deterministic hash of its associated public key) and not its public key. When we eventually receive a
-// transaction acting on behalf of that account we will be given a public key that we can check matches the address.
-// If it does then we will associate the public key with the stub account already registered in the system once and
-// for all time.
-func checkInputPubKey(acc acm.MutableAccount, in *txs.TxInput) error {
-	if !acc.PublicKey().IsValid() {
-		if !in.PublicKey.IsValid() {
-			return txs.ErrTxUnknownPubKey
-		}
-		addressFromPubKey := in.PublicKey.Address()
-		addressFromAccount := acc.Address()
-		if addressFromPubKey != addressFromAccount {
-			return txs.ErrTxInvalidPubKey
-		}
-		acc.SetPublicKey(in.PublicKey)
-	} else {
-		in.PublicKey = crypto.PublicKey{}
-	}
-	return nil
-}
-
-func validateInputs(accs map[crypto.Address]acm.MutableAccount, signBytes []byte, ins []*txs.TxInput) (uint64, error) {
+func validateInputs(accs map[crypto.Address]acm.MutableAccount, ins []*payload.TxInput) (uint64, error) {
 	total := uint64(0)
 	for _, in := range ins {
 		acc := accs[in.Address]
 		if acc == nil {
 			return 0, fmt.Errorf("validateInputs() expects account in accounts, but account %s not found", in.Address)
 		}
-		err := validateInput(acc, signBytes, in)
+		err := validateInput(acc, in)
 		if err != nil {
 			return 0, err
 		}
@@ -1127,30 +1082,26 @@ func validateInputs(accs map[crypto.Address]acm.MutableAccount, signBytes []byte
 	return total, nil
 }
 
-func validateInput(acc acm.MutableAccount, signBytes []byte, in *txs.TxInput) error {
+func validateInput(acc acm.MutableAccount, in *payload.TxInput) error {
 	// Check TxInput basic
 	if err := in.ValidateBasic(); err != nil {
 		return err
 	}
-	// Check signatures
-	if !acc.PublicKey().Verify(signBytes, in.Signature) {
-		return txs.ErrTxInvalidSignature
-	}
 	// Check sequences
 	if acc.Sequence()+1 != uint64(in.Sequence) {
-		return txs.ErrTxInvalidSequence{
+		return payload.ErrTxInvalidSequence{
 			Got:      in.Sequence,
 			Expected: acc.Sequence() + uint64(1),
 		}
 	}
 	// Check amount
 	if acc.Balance() < uint64(in.Amount) {
-		return txs.ErrTxInsufficientFunds
+		return payload.ErrTxInsufficientFunds
 	}
 	return nil
 }
 
-func validateOutputs(outs []*txs.TxOutput) (uint64, error) {
+func validateOutputs(outs []*payload.TxOutput) (uint64, error) {
 	total := uint64(0)
 	for _, out := range outs {
 		// Check TxOutput basic
@@ -1163,7 +1114,7 @@ func validateOutputs(outs []*txs.TxOutput) (uint64, error) {
 	return total, nil
 }
 
-func adjustByInputs(accs map[crypto.Address]acm.MutableAccount, ins []*txs.TxInput, logger *logging.Logger) error {
+func adjustByInputs(accs map[crypto.Address]acm.MutableAccount, ins []*payload.TxInput, logger *logging.Logger) error {
 	for _, in := range ins {
 		acc := accs[in.Address]
 		if acc == nil {
@@ -1188,7 +1139,7 @@ func adjustByInputs(accs map[crypto.Address]acm.MutableAccount, ins []*txs.TxInp
 	return nil
 }
 
-func adjustByOutputs(accs map[crypto.Address]acm.MutableAccount, outs []*txs.TxOutput) error {
+func adjustByOutputs(accs map[crypto.Address]acm.MutableAccount, outs []*payload.TxOutput) error {
 	for _, out := range outs {
 		acc := accs[out.Address]
 		if acc == nil {
diff --git a/execution/execution_test.go b/execution/execution_test.go
index 41c1d1cb78ce7cf822fe5b907d0d58da419c69b9..e846840971b8f741ffa2bf54a8f425161728fbd7 100644
--- a/execution/execution_test.go
+++ b/execution/execution_test.go
@@ -22,6 +22,8 @@ import (
 	"testing"
 	"time"
 
+	"runtime/debug"
+
 	acm "github.com/hyperledger/burrow/account"
 	"github.com/hyperledger/burrow/account/state"
 	. "github.com/hyperledger/burrow/binary"
@@ -34,12 +36,14 @@ import (
 	"github.com/hyperledger/burrow/execution/evm/asm/bc"
 	evm_events "github.com/hyperledger/burrow/execution/evm/events"
 	"github.com/hyperledger/burrow/execution/evm/sha3"
+	"github.com/hyperledger/burrow/execution/names"
 	"github.com/hyperledger/burrow/genesis"
 	"github.com/hyperledger/burrow/logging"
 	"github.com/hyperledger/burrow/permission"
 	"github.com/hyperledger/burrow/permission/snatives"
 	ptypes "github.com/hyperledger/burrow/permission/types"
 	"github.com/hyperledger/burrow/txs"
+	"github.com/hyperledger/burrow/txs/payload"
 	"github.com/stretchr/testify/require"
 	dbm "github.com/tendermint/tmlibs/db"
 	"github.com/tmthrgd/go-hex"
@@ -128,7 +132,7 @@ func makeUsers(n int) []acm.AddressableSigner {
 	}
 	return users
 }
-func newBlockchain(genesisDoc *genesis.GenesisDoc) bcm.MutableBlockchain {
+func newBlockchain(genesisDoc *genesis.GenesisDoc) *bcm.Blockchain {
 	testDB := dbm.NewDB("test", dbBackend, ".")
 	bc, _ := bcm.LoadOrNewBlockchain(testDB, testGenesisDoc, logger)
 
@@ -137,7 +141,7 @@ func newBlockchain(genesisDoc *genesis.GenesisDoc) bcm.MutableBlockchain {
 
 func makeExecutor(state *State) *executor {
 	return newExecutor("makeExecutorCache", true, state, testChainID,
-		newBlockchain(testGenesisDoc), event.NewEmitter(logger), logger)
+		newBlockchain(testGenesisDoc).Tip, event.NewEmitter(logger), logger)
 }
 
 func newBaseGenDoc(globalPerm, accountPerm ptypes.AccountPermissions) genesis.GenesisDoc {
@@ -194,59 +198,40 @@ func TestSendFails(t *testing.T) {
 	// send txs
 
 	// simple send tx should fail
-	tx := txs.NewSendTx()
+	tx := payload.NewSendTx()
 	if err := tx.AddInput(batchCommitter.stateCache, users[0].PublicKey(), 5); err != nil {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[1].Address(), 5)
-	require.NoError(t, tx.Sign(testChainID, users[0]))
-	if err := batchCommitter.Execute(tx); err == nil {
-		t.Fatal("Expected error")
-	} else {
-		fmt.Println(err)
-	}
+	signAndExecute(t, true, batchCommitter, testChainID, tx, users[0])
 
 	// simple send tx with call perm should fail
-	tx = txs.NewSendTx()
+	tx = payload.NewSendTx()
 	if err := tx.AddInput(batchCommitter.stateCache, users[2].PublicKey(), 5); err != nil {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[4].Address(), 5)
-	require.NoError(t, tx.Sign(testChainID, users[2]))
-	if err := batchCommitter.Execute(tx); err == nil {
-		t.Fatal("Expected error")
-	} else {
-		fmt.Println(err)
-	}
+
+	signAndExecute(t, true, batchCommitter, testChainID, tx, users[2])
 
 	// simple send tx with create perm should fail
-	tx = txs.NewSendTx()
+	tx = payload.NewSendTx()
 	if err := tx.AddInput(batchCommitter.stateCache, users[3].PublicKey(), 5); err != nil {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[4].Address(), 5)
-	require.NoError(t, tx.Sign(testChainID, users[3]))
-	if err := batchCommitter.Execute(tx); err == nil {
-		t.Fatal("Expected error")
-	} else {
-		fmt.Println(err)
-	}
+	signAndExecute(t, true, batchCommitter, testChainID, tx, users[3])
 
 	// simple send tx to unknown account without create_account perm should fail
 	acc := getAccount(batchCommitter.stateCache, users[3].Address())
 	acc.MutablePermissions().Base.Set(permission.Send, true)
 	batchCommitter.stateCache.UpdateAccount(acc)
-	tx = txs.NewSendTx()
+	tx = payload.NewSendTx()
 	if err := tx.AddInput(batchCommitter.stateCache, users[3].PublicKey(), 5); err != nil {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[6].Address(), 5)
-	require.NoError(t, tx.Sign(testChainID, users[3]))
-	if err := batchCommitter.Execute(tx); err == nil {
-		t.Fatal("Expected error")
-	} else {
-		fmt.Println(err)
-	}
+	signAndExecute(t, true, batchCommitter, testChainID, tx, users[3])
 }
 
 func TestName(t *testing.T) {
@@ -263,26 +248,18 @@ func TestName(t *testing.T) {
 	// name txs
 
 	// simple name tx without perm should fail
-	tx, err := txs.NewNameTx(st, users[0].PublicKey(), "somename", "somedata", 10000, 100)
+	tx, err := payload.NewNameTx(st, users[0].PublicKey(), "somename", "somedata", 10000, 100)
 	if err != nil {
 		t.Fatal(err)
 	}
-	tx.Sign(testChainID, users[0])
-	if err := batchCommitter.Execute(tx); err == nil {
-		t.Fatal("Expected error")
-	} else {
-		fmt.Println(err)
-	}
+	signAndExecute(t, true, batchCommitter, testChainID, tx, users[0])
 
 	// simple name tx with perm should pass
-	tx, err = txs.NewNameTx(st, users[1].PublicKey(), "somename", "somedata", 10000, 100)
+	tx, err = payload.NewNameTx(st, users[1].PublicKey(), "somename", "somedata", 10000, 100)
 	if err != nil {
 		t.Fatal(err)
 	}
-	tx.Sign(testChainID, users[1])
-	if err := batchCommitter.Execute(tx); err != nil {
-		t.Fatal(err)
-	}
+	signAndExecute(t, false, batchCommitter, testChainID, tx, users[1])
 }
 
 func TestCallFails(t *testing.T) {
@@ -301,61 +278,31 @@ func TestCallFails(t *testing.T) {
 
 	address4 := users[4].Address()
 	// simple call tx should fail
-	tx, _ := txs.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &address4, nil, 100, 100, 100)
-	tx.Sign(testChainID, users[0])
-	if err := batchCommitter.Execute(tx); err == nil {
-		t.Fatal("Expected error")
-	} else {
-		fmt.Println(err)
-	}
+	tx, _ := payload.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &address4, nil, 100, 100, 100)
+	signAndExecute(t, true, batchCommitter, testChainID, tx, users[0])
 
 	// simple call tx with send permission should fail
-	tx, _ = txs.NewCallTx(batchCommitter.stateCache, users[1].PublicKey(), &address4, nil, 100, 100, 100)
-	tx.Sign(testChainID, users[1])
-	if err := batchCommitter.Execute(tx); err == nil {
-		t.Fatal("Expected error")
-	} else {
-		fmt.Println(err)
-	}
+	tx, _ = payload.NewCallTx(batchCommitter.stateCache, users[1].PublicKey(), &address4, nil, 100, 100, 100)
+	signAndExecute(t, true, batchCommitter, testChainID, tx, users[1])
 
 	// simple call tx with create permission should fail
-	tx, _ = txs.NewCallTx(batchCommitter.stateCache, users[3].PublicKey(), &address4, nil, 100, 100, 100)
-	tx.Sign(testChainID, users[3])
-	if err := batchCommitter.Execute(tx); err == nil {
-		t.Fatal("Expected error")
-	} else {
-		fmt.Println(err)
-	}
+	tx, _ = payload.NewCallTx(batchCommitter.stateCache, users[3].PublicKey(), &address4, nil, 100, 100, 100)
+	signAndExecute(t, true, batchCommitter, testChainID, tx, users[3])
 
 	//-------------------
 	// create txs
 
 	// simple call create tx should fail
-	tx, _ = txs.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), nil, nil, 100, 100, 100)
-	tx.Sign(testChainID, users[0])
-	if err := batchCommitter.Execute(tx); err == nil {
-		t.Fatal("Expected error")
-	} else {
-		fmt.Println(err)
-	}
+	tx, _ = payload.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), nil, nil, 100, 100, 100)
+	signAndExecute(t, true, batchCommitter, testChainID, tx, users[0])
 
 	// simple call create tx with send perm should fail
-	tx, _ = txs.NewCallTx(batchCommitter.stateCache, users[1].PublicKey(), nil, nil, 100, 100, 100)
-	tx.Sign(testChainID, users[1])
-	if err := batchCommitter.Execute(tx); err == nil {
-		t.Fatal("Expected error")
-	} else {
-		fmt.Println(err)
-	}
+	tx, _ = payload.NewCallTx(batchCommitter.stateCache, users[1].PublicKey(), nil, nil, 100, 100, 100)
+	signAndExecute(t, true, batchCommitter, testChainID, tx, users[1])
 
 	// simple call create tx with call perm should fail
-	tx, _ = txs.NewCallTx(batchCommitter.stateCache, users[2].PublicKey(), nil, nil, 100, 100, 100)
-	tx.Sign(testChainID, users[2])
-	if err := batchCommitter.Execute(tx); err == nil {
-		t.Fatal("Expected error")
-	} else {
-		fmt.Println(err)
-	}
+	tx, _ = payload.NewCallTx(batchCommitter.stateCache, users[2].PublicKey(), nil, nil, 100, 100, 100)
+	signAndExecute(t, true, batchCommitter, testChainID, tx, users[2])
 }
 
 func TestSendPermission(t *testing.T) {
@@ -368,31 +315,19 @@ func TestSendPermission(t *testing.T) {
 	batchCommitter := makeExecutor(st)
 
 	// A single input, having the permission, should succeed
-	tx := txs.NewSendTx()
+	tx := payload.NewSendTx()
 	if err := tx.AddInput(batchCommitter.stateCache, users[0].PublicKey(), 5); err != nil {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[1].Address(), 5)
-	require.NoError(t, tx.Sign(testChainID, users[0]))
-	if err := batchCommitter.Execute(tx); err != nil {
-		t.Fatal("Transaction failed", err)
-	}
+	signAndExecute(t, false, batchCommitter, testChainID, tx, users[0])
 
 	// Two inputs, one with permission, one without, should fail
-	tx = txs.NewSendTx()
-	if err := tx.AddInput(batchCommitter.stateCache, users[0].PublicKey(), 5); err != nil {
-		t.Fatal(err)
-	}
-	if err := tx.AddInput(batchCommitter.stateCache, users[1].PublicKey(), 5); err != nil {
-		t.Fatal(err)
-	}
-	tx.AddOutput(users[2].Address(), 10)
-	require.NoError(t, tx.Sign(testChainID, users[:2]...))
-	if err := batchCommitter.Execute(tx); err == nil {
-		t.Fatal("Expected error")
-	} else {
-		fmt.Println(err)
-	}
+	tx = payload.NewSendTx()
+	require.NoError(t, tx.AddInput(batchCommitter.stateCache, users[0].PublicKey(), 5))
+	require.NoError(t, tx.AddInput(batchCommitter.stateCache, users[1].PublicKey(), 5))
+	require.NoError(t, tx.AddOutput(users[2].Address(), 10))
+	signAndExecute(t, true, batchCommitter, testChainID, tx, users[:2]...)
 }
 
 func TestCallPermission(t *testing.T) {
@@ -421,11 +356,8 @@ func TestCallPermission(t *testing.T) {
 	st.UpdateAccount(simpleAcc)
 
 	// A single input, having the permission, should succeed
-	tx, _ := txs.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &simpleContractAddr, nil, 100, 100, 100)
-	tx.Sign(testChainID, users[0])
-	if err := batchCommitter.Execute(tx); err != nil {
-		t.Fatal("Transaction failed", err)
-	}
+	tx, _ := payload.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &simpleContractAddr, nil, 100, 100, 100)
+	signAndExecute(t, false, batchCommitter, testChainID, tx, users[0])
 
 	//----------------------------------------------------------
 	// call to contract that calls simple contract - without perm
@@ -445,11 +377,12 @@ func TestCallPermission(t *testing.T) {
 	batchCommitter.stateCache.UpdateAccount(caller1Acc)
 
 	// A single input, having the permission, but the contract doesn't have permission
-	tx, _ = txs.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &caller1ContractAddr, nil, 100, 10000, 100)
-	tx.Sign(testChainID, users[0])
+	tx, _ = payload.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &caller1ContractAddr, nil, 100, 10000, 100)
+	txEnv := txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(users[0]))
 
 	// we need to subscribe to the Call event to detect the exception
-	_, exception := execTxWaitEvent(t, batchCommitter, tx, evm_events.EventStringAccountCall(caller1ContractAddr)) //
+	_, exception := execTxWaitEvent(t, batchCommitter, txEnv, evm_events.EventStringAccountCall(caller1ContractAddr)) //
 	if exception == "" {
 		t.Fatal("Expected exception")
 	}
@@ -461,11 +394,12 @@ func TestCallPermission(t *testing.T) {
 	// A single input, having the permission, and the contract has permission
 	caller1Acc.MutablePermissions().Base.Set(permission.Call, true)
 	batchCommitter.stateCache.UpdateAccount(caller1Acc)
-	tx, _ = txs.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &caller1ContractAddr, nil, 100, 10000, 100)
-	tx.Sign(testChainID, users[0])
+	tx, _ = payload.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &caller1ContractAddr, nil, 100, 10000, 100)
+	txEnv = txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(users[0]))
 
 	// we need to subscribe to the Call event to detect the exception
-	_, exception = execTxWaitEvent(t, batchCommitter, tx, evm_events.EventStringAccountCall(caller1ContractAddr)) //
+	_, exception = execTxWaitEvent(t, batchCommitter, txEnv, evm_events.EventStringAccountCall(caller1ContractAddr)) //
 	if exception != "" {
 		t.Fatal("Unexpected exception:", exception)
 	}
@@ -491,11 +425,11 @@ func TestCallPermission(t *testing.T) {
 	batchCommitter.stateCache.UpdateAccount(caller1Acc)
 	batchCommitter.stateCache.UpdateAccount(caller2Acc)
 
-	tx, _ = txs.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &caller2ContractAddr, nil, 100, 10000, 100)
-	tx.Sign(testChainID, users[0])
-
+	tx, _ = payload.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &caller2ContractAddr, nil, 100, 10000, 100)
+	txEnv = txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(users[0]))
 	// we need to subscribe to the Call event to detect the exception
-	_, exception = execTxWaitEvent(t, batchCommitter, tx, evm_events.EventStringAccountCall(caller1ContractAddr)) //
+	_, exception = execTxWaitEvent(t, batchCommitter, txEnv, evm_events.EventStringAccountCall(caller1ContractAddr)) //
 	if exception == "" {
 		t.Fatal("Expected exception")
 	}
@@ -509,11 +443,12 @@ func TestCallPermission(t *testing.T) {
 	caller1Acc.MutablePermissions().Base.Set(permission.Call, true)
 	batchCommitter.stateCache.UpdateAccount(caller1Acc)
 
-	tx, _ = txs.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &caller2ContractAddr, nil, 100, 10000, 100)
-	tx.Sign(testChainID, users[0])
+	tx, _ = payload.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &caller2ContractAddr, nil, 100, 10000, 100)
+	txEnv = txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(users[0]))
 
 	// we need to subscribe to the Call event to detect the exception
-	_, exception = execTxWaitEvent(t, batchCommitter, tx, evm_events.EventStringAccountCall(caller1ContractAddr)) //
+	_, exception = execTxWaitEvent(t, batchCommitter, txEnv, evm_events.EventStringAccountCall(caller1ContractAddr)) //
 	if exception != "" {
 		t.Fatal("Unexpected exception", exception)
 	}
@@ -537,11 +472,9 @@ func TestCreatePermission(t *testing.T) {
 	createCode := wrapContractForCreate(contractCode)
 
 	// A single input, having the permission, should succeed
-	tx, _ := txs.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), nil, createCode, 100, 100, 100)
-	tx.Sign(testChainID, users[0])
-	if err := batchCommitter.Execute(tx); err != nil {
-		t.Fatal("Transaction failed", err)
-	}
+	tx, _ := payload.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), nil, createCode, 100, 100, 100)
+	signAndExecute(t, false, batchCommitter, testChainID, tx, users[0])
+
 	// ensure the contract is there
 	contractAddr := crypto.NewContractAddress(tx.Input.Address, tx.Input.Sequence)
 	contractAcc := getAccount(batchCommitter.stateCache, contractAddr)
@@ -562,11 +495,9 @@ func TestCreatePermission(t *testing.T) {
 	createFactoryCode := wrapContractForCreate(factoryCode)
 
 	// A single input, having the permission, should succeed
-	tx, _ = txs.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), nil, createFactoryCode, 100, 100, 100)
-	tx.Sign(testChainID, users[0])
-	if err := batchCommitter.Execute(tx); err != nil {
-		t.Fatal("Transaction failed", err)
-	}
+	tx, _ = payload.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), nil, createFactoryCode, 100, 100, 100)
+	signAndExecute(t, false, batchCommitter, testChainID, tx, users[0])
+
 	// ensure the contract is there
 	contractAddr = crypto.NewContractAddress(tx.Input.Address, tx.Input.Sequence)
 	contractAcc = getAccount(batchCommitter.stateCache, contractAddr)
@@ -582,10 +513,11 @@ func TestCreatePermission(t *testing.T) {
 	fmt.Println("\n###### CALL THE FACTORY (FAIL)")
 
 	// A single input, having the permission, should succeed
-	tx, _ = txs.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &contractAddr, createCode, 100, 100, 100)
-	tx.Sign(testChainID, users[0])
+	tx, _ = payload.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &contractAddr, createCode, 100, 100, 100)
+	txEnv := txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(users[0]))
 	// we need to subscribe to the Call event to detect the exception
-	_, exception := execTxWaitEvent(t, batchCommitter, tx, evm_events.EventStringAccountCall(contractAddr)) //
+	_, exception := execTxWaitEvent(t, batchCommitter, txEnv, evm_events.EventStringAccountCall(contractAddr)) //
 	if exception == "" {
 		t.Fatal("expected exception")
 	}
@@ -598,10 +530,11 @@ func TestCreatePermission(t *testing.T) {
 	batchCommitter.stateCache.UpdateAccount(contractAcc)
 
 	// A single input, having the permission, should succeed
-	tx, _ = txs.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &contractAddr, createCode, 100, 100, 100)
-	tx.Sign(testChainID, users[0])
+	tx, _ = payload.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &contractAddr, createCode, 100, 100, 100)
+	txEnv = txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(users[0]))
 	// we need to subscribe to the Call event to detect the exception
-	_, exception = execTxWaitEvent(t, batchCommitter, tx, evm_events.EventStringAccountCall(contractAddr)) //
+	_, exception = execTxWaitEvent(t, batchCommitter, txEnv, evm_events.EventStringAccountCall(contractAddr)) //
 	if exception != "" {
 		t.Fatal("unexpected exception", exception)
 	}
@@ -624,10 +557,11 @@ func TestCreatePermission(t *testing.T) {
 	batchCommitter.stateCache.UpdateAccount(contractAcc)
 
 	// this should call the 0 address but not create ...
-	tx, _ = txs.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &contractAddr, createCode, 100, 10000, 100)
-	tx.Sign(testChainID, users[0])
+	tx, _ = payload.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &contractAddr, createCode, 100, 10000, 100)
+	txEnv = txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(users[0]))
 	// we need to subscribe to the Call event to detect the exception
-	_, exception = execTxWaitEvent(t, batchCommitter, tx, evm_events.EventStringAccountCall(crypto.Address{})) //
+	_, exception = execTxWaitEvent(t, batchCommitter, txEnv, evm_events.EventStringAccountCall(crypto.Address{})) //
 	if exception != "" {
 		t.Fatal("unexpected exception", exception)
 	}
@@ -652,18 +586,15 @@ func TestCreateAccountPermission(t *testing.T) {
 	// SendTx to unknown account
 
 	// A single input, having the permission, should succeed
-	tx := txs.NewSendTx()
+	tx := payload.NewSendTx()
 	if err := tx.AddInput(batchCommitter.stateCache, users[0].PublicKey(), 5); err != nil {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[6].Address(), 5)
-	require.NoError(t, tx.Sign(testChainID, users[0]))
-	if err := batchCommitter.Execute(tx); err != nil {
-		t.Fatal("Transaction failed", err)
-	}
+	signAndExecute(t, false, batchCommitter, testChainID, tx, users[0])
 
 	// Two inputs, both with send, one with create, one without, should fail
-	tx = txs.NewSendTx()
+	tx = payload.NewSendTx()
 	if err := tx.AddInput(batchCommitter.stateCache, users[0].PublicKey(), 5); err != nil {
 		t.Fatal(err)
 	}
@@ -671,15 +602,10 @@ func TestCreateAccountPermission(t *testing.T) {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[7].Address(), 10)
-	require.NoError(t, tx.Sign(testChainID, users[:2]...))
-	if err := batchCommitter.Execute(tx); err == nil {
-		t.Fatal("Expected error")
-	} else {
-		fmt.Println(err)
-	}
+	signAndExecute(t, true, batchCommitter, testChainID, tx, users[:2]...)
 
 	// Two inputs, both with send, one with create, one without, two ouputs (one known, one unknown) should fail
-	tx = txs.NewSendTx()
+	tx = payload.NewSendTx()
 	if err := tx.AddInput(batchCommitter.stateCache, users[0].PublicKey(), 5); err != nil {
 		t.Fatal(err)
 	}
@@ -688,18 +614,13 @@ func TestCreateAccountPermission(t *testing.T) {
 	}
 	tx.AddOutput(users[7].Address(), 4)
 	tx.AddOutput(users[4].Address(), 6)
-	require.NoError(t, tx.Sign(testChainID, users[:2]...))
-	if err := batchCommitter.Execute(tx); err == nil {
-		t.Fatal("Expected error")
-	} else {
-		fmt.Println(err)
-	}
+	signAndExecute(t, true, batchCommitter, testChainID, tx, users[:2]...)
 
 	// Two inputs, both with send, both with create, should pass
 	acc := getAccount(batchCommitter.stateCache, users[1].Address())
 	acc.MutablePermissions().Base.Set(permission.CreateAccount, true)
 	batchCommitter.stateCache.UpdateAccount(acc)
-	tx = txs.NewSendTx()
+	tx = payload.NewSendTx()
 	if err := tx.AddInput(batchCommitter.stateCache, users[0].PublicKey(), 5); err != nil {
 		t.Fatal(err)
 	}
@@ -707,13 +628,10 @@ func TestCreateAccountPermission(t *testing.T) {
 		t.Fatal(err)
 	}
 	tx.AddOutput(users[7].Address(), 10)
-	require.NoError(t, tx.Sign(testChainID, users[:2]...))
-	if err := batchCommitter.Execute(tx); err != nil {
-		t.Fatal("Unexpected error", err)
-	}
+	signAndExecute(t, false, batchCommitter, testChainID, tx, users[:2]...)
 
 	// Two inputs, both with send, both with create, two outputs (one known, one unknown) should pass
-	tx = txs.NewSendTx()
+	tx = payload.NewSendTx()
 	if err := tx.AddInput(batchCommitter.stateCache, users[0].PublicKey(), 5); err != nil {
 		t.Fatal(err)
 	}
@@ -722,10 +640,7 @@ func TestCreateAccountPermission(t *testing.T) {
 	}
 	tx.AddOutput(users[7].Address(), 7)
 	tx.AddOutput(users[4].Address(), 3)
-	require.NoError(t, tx.Sign(testChainID, users[:2]...))
-	if err := batchCommitter.Execute(tx); err != nil {
-		t.Fatal("Unexpected error", err)
-	}
+	signAndExecute(t, false, batchCommitter, testChainID, tx, users[:2]...)
 
 	//----------------------------------------------------------
 	// CALL to unknown account
@@ -749,11 +664,12 @@ func TestCreateAccountPermission(t *testing.T) {
 	batchCommitter.stateCache.UpdateAccount(caller1Acc)
 
 	// A single input, having the permission, but the contract doesn't have permission
-	txCall, _ := txs.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &caller1ContractAddr, nil, 100, 10000, 100)
-	txCall.Sign(testChainID, users[0])
+	txCall, _ := payload.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &caller1ContractAddr, nil, 100, 10000, 100)
+	txCallEnv := txs.Enclose(testChainID, txCall)
+	txCallEnv.Sign(users[0])
 
 	// we need to subscribe to the Call event to detect the exception
-	_, exception := execTxWaitEvent(t, batchCommitter, txCall, evm_events.EventStringAccountCall(caller1ContractAddr)) //
+	_, exception := execTxWaitEvent(t, batchCommitter, txCallEnv, evm_events.EventStringAccountCall(caller1ContractAddr)) //
 	if exception == "" {
 		t.Fatal("Expected exception")
 	}
@@ -764,11 +680,12 @@ func TestCreateAccountPermission(t *testing.T) {
 	caller1Acc.MutablePermissions().Base.Set(permission.Call, true)
 	batchCommitter.stateCache.UpdateAccount(caller1Acc)
 	// A single input, having the permission, but the contract doesn't have permission
-	txCall, _ = txs.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &caller1ContractAddr, nil, 100, 10000, 100)
-	txCall.Sign(testChainID, users[0])
+	txCall, _ = payload.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &caller1ContractAddr, nil, 100, 10000, 100)
+	txCallEnv = txs.Enclose(testChainID, txCall)
+	txCallEnv.Sign(users[0])
 
 	// we need to subscribe to the Call event to detect the exception
-	_, exception = execTxWaitEvent(t, batchCommitter, txCall, evm_events.EventStringAccountCall(caller1ContractAddr)) //
+	_, exception = execTxWaitEvent(t, batchCommitter, txCallEnv, evm_events.EventStringAccountCall(caller1ContractAddr)) //
 	if exception != "" {
 		t.Fatal("Unexpected exception", exception)
 	}
@@ -1001,12 +918,13 @@ func TestTxSequence(t *testing.T) {
 	// The tx should only pass when i == 1.
 	for i := uint64(0); i < 3; i++ {
 		sequence := acc0.Sequence() + i
-		tx := txs.NewSendTx()
+		tx := payload.NewSendTx()
 		tx.AddInputWithSequence(acc0PubKey, 1, sequence)
 		tx.AddOutput(acc1.Address(), 1)
-		require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
+		txEnv := txs.Enclose(testChainID, tx)
+		require.NoError(t, txEnv.Sign(privAccounts[0]))
 		stateCopy := state.Copy(dbm.NewMemDB())
-		err := execTxWithState(stateCopy, tx)
+		err := execTxWithState(stateCopy, txEnv)
 		if i == 1 {
 			// Sequence is good.
 			if err != nil {
@@ -1038,23 +956,24 @@ func TestNameTxs(t *testing.T) {
 	require.NoError(t, err)
 	state.Save()
 
-	txs.MinNameRegistrationPeriod = 5
+	names.MinNameRegistrationPeriod = 5
 	blockchain := newBlockchain(testGenesisDoc)
 	startingBlock := blockchain.LastBlockHeight()
 
 	// try some bad names. these should all fail
-	names := []string{"", "\n", "123#$%", "\x00", string([]byte{20, 40, 60, 80}),
+	nameStrings := []string{"", "\n", "123#$%", "\x00", string([]byte{20, 40, 60, 80}),
 		"baffledbythespectacleinallofthisyouseeehesaidwithouteyessurprised", "no spaces please"}
 	data := "something about all this just doesn't feel right."
 	fee := uint64(1000)
 	numDesiredBlocks := uint64(5)
-	for _, name := range names {
-		amt := fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier*
-			txs.NameBaseCost(name, data)
-		tx, _ := txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee)
-		tx.Sign(testChainID, testPrivAccounts[0])
-
-		if err := execTxWithState(state, tx); err == nil {
+	for _, name := range nameStrings {
+		amt := fee + numDesiredBlocks*names.NameByteCostMultiplier*names.NameBlockCostMultiplier*
+			names.NameBaseCost(name, data)
+		tx, _ := payload.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee)
+		txEnv := txs.Enclose(testChainID, tx)
+		txEnv.Sign(testPrivAccounts[0])
+
+		if err := execTxWithState(state, txEnv); err == nil {
 			t.Fatalf("Expected invalid name error from %s", name)
 		}
 	}
@@ -1063,12 +982,13 @@ func TestNameTxs(t *testing.T) {
 	name := "hold_it_chum"
 	datas := []string{"cold&warm", "!@#$%^&*()", "<<<>>>>", "because why would you ever need a ~ or a & or even a % in a json file? make your case and we'll talk"}
 	for _, data := range datas {
-		amt := fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier*
-			txs.NameBaseCost(name, data)
-		tx, _ := txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee)
-		tx.Sign(testChainID, testPrivAccounts[0])
+		amt := fee + numDesiredBlocks*names.NameByteCostMultiplier*names.NameBlockCostMultiplier*
+			names.NameBaseCost(name, data)
+		tx, _ := payload.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee)
+		txEnv := txs.Enclose(testChainID, tx)
+		txEnv.Sign(testPrivAccounts[0])
 
-		if err := execTxWithState(state, tx); err == nil {
+		if err := execTxWithState(state, txEnv); err == nil {
 			t.Fatalf("Expected invalid data error from %s", data)
 		}
 	}
@@ -1095,10 +1015,11 @@ func TestNameTxs(t *testing.T) {
 	// try a good one, check data, owner, expiry
 	name = "@looking_good/karaoke_bar.broadband"
 	data = "on this side of neptune there are 1234567890 people: first is OMNIVORE+-3. Or is it. Ok this is pretty restrictive. No exclamations :(. Faces tho :')"
-	amt := fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier*txs.NameBaseCost(name, data)
-	tx, _ := txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee)
-	tx.Sign(testChainID, testPrivAccounts[0])
-	if err := execTxWithState(state, tx); err != nil {
+	amt := fee + numDesiredBlocks*names.NameByteCostMultiplier*names.NameBlockCostMultiplier*names.NameBaseCost(name, data)
+	tx, _ := payload.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee)
+	txEnv := txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(testPrivAccounts[0]))
+	if err := execTxWithState(state, txEnv); err != nil {
 		t.Fatal(err)
 	}
 	entry, err := state.GetNameRegEntry(name)
@@ -1106,17 +1027,19 @@ func TestNameTxs(t *testing.T) {
 	validateEntry(t, entry, name, data, testPrivAccounts[0].Address(), startingBlock+numDesiredBlocks)
 
 	// fail to update it as non-owner, in same block
-	tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee)
-	tx.Sign(testChainID, testPrivAccounts[1])
-	if err := execTxWithState(state, tx); err == nil {
+	tx, _ = payload.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee)
+	txEnv = txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(testPrivAccounts[1]))
+	if err := execTxWithState(state, txEnv); err == nil {
 		t.Fatal("Expected error")
 	}
 
 	// update it as owner, just to increase expiry, in same block
 	// NOTE: we have to resend the data or it will clear it (is this what we want?)
-	tx, _ = txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee)
-	tx.Sign(testChainID, testPrivAccounts[0])
-	if err := execTxWithStateNewBlock(state, blockchain, tx); err != nil {
+	tx, _ = payload.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee)
+	txEnv = txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(testPrivAccounts[0]))
+	if err := execTxWithStateNewBlock(state, blockchain, txEnv); err != nil {
 		t.Fatal(err)
 	}
 	entry, err = state.GetNameRegEntry(name)
@@ -1124,9 +1047,10 @@ func TestNameTxs(t *testing.T) {
 	validateEntry(t, entry, name, data, testPrivAccounts[0].Address(), startingBlock+numDesiredBlocks*2)
 
 	// update it as owner, just to increase expiry, in next block
-	tx, _ = txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee)
-	tx.Sign(testChainID, testPrivAccounts[0])
-	if err := execTxWithStateNewBlock(state, blockchain, tx); err != nil {
+	tx, _ = payload.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee)
+	txEnv = txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(testPrivAccounts[0]))
+	if err := execTxWithStateNewBlock(state, blockchain, txEnv); err != nil {
 		t.Fatal(err)
 	}
 	entry, err = state.GetNameRegEntry(name)
@@ -1135,20 +1059,22 @@ func TestNameTxs(t *testing.T) {
 
 	// fail to update it as non-owner
 	// Fast forward
-	for blockchain.Tip().LastBlockHeight() < entry.Expires-1 {
+	for blockchain.Tip.LastBlockHeight() < entry.Expires-1 {
 		commitNewBlock(state, blockchain)
 	}
-	tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee)
-	tx.Sign(testChainID, testPrivAccounts[1])
-	if err := execTxWithStateAndBlockchain(state, blockchain, tx); err == nil {
+	tx, _ = payload.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee)
+	txEnv = txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(testPrivAccounts[1]))
+	if err := execTxWithStateAndBlockchain(state, blockchain.Tip, txEnv); err == nil {
 		t.Fatal("Expected error")
 	}
 	commitNewBlock(state, blockchain)
 
 	// once expires, non-owner succeeds
-	tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee)
-	tx.Sign(testChainID, testPrivAccounts[1])
-	if err := execTxWithStateAndBlockchain(state, blockchain, tx); err != nil {
+	tx, _ = payload.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee)
+	txEnv = txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(testPrivAccounts[1]))
+	if err := execTxWithStateAndBlockchain(state, blockchain.Tip, txEnv); err != nil {
 		t.Fatal(err)
 	}
 	entry, err = state.GetNameRegEntry(name)
@@ -1159,10 +1085,11 @@ func TestNameTxs(t *testing.T) {
 	data = "In the beginning there was no thing, not even the beginning. It hadn't been here, no there, nor for that matter anywhere, not especially because it had not to even exist, let alone to not. Nothing especially odd about that."
 	oldCredit := amt - fee
 	numDesiredBlocks = 10
-	amt = fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier*txs.NameBaseCost(name, data) - oldCredit
-	tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee)
-	tx.Sign(testChainID, testPrivAccounts[1])
-	if err := execTxWithStateAndBlockchain(state, blockchain, tx); err != nil {
+	amt = fee + numDesiredBlocks*names.NameByteCostMultiplier*names.NameBlockCostMultiplier*names.NameBaseCost(name, data) - oldCredit
+	tx, _ = payload.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee)
+	txEnv = txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(testPrivAccounts[1]))
+	if err := execTxWithStateAndBlockchain(state, blockchain.Tip, txEnv); err != nil {
 		t.Fatal(err)
 	}
 	entry, err = state.GetNameRegEntry(name)
@@ -1172,9 +1099,10 @@ func TestNameTxs(t *testing.T) {
 	// test removal
 	amt = fee
 	data = ""
-	tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee)
-	tx.Sign(testChainID, testPrivAccounts[1])
-	if err := execTxWithStateNewBlock(state, blockchain, tx); err != nil {
+	tx, _ = payload.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee)
+	txEnv = txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(testPrivAccounts[1]))
+	if err := execTxWithStateNewBlock(state, blockchain, txEnv); err != nil {
 		t.Fatal(err)
 	}
 	entry, err = state.GetNameRegEntry(name)
@@ -1187,25 +1115,27 @@ func TestNameTxs(t *testing.T) {
 	// test removal by key1 after expiry
 	name = "looking_good/karaoke_bar"
 	data = "some data"
-	amt = fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier*txs.NameBaseCost(name, data)
-	tx, _ = txs.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee)
-	tx.Sign(testChainID, testPrivAccounts[0])
-	if err := execTxWithStateAndBlockchain(state, blockchain, tx); err != nil {
+	amt = fee + numDesiredBlocks*names.NameByteCostMultiplier*names.NameBlockCostMultiplier*names.NameBaseCost(name, data)
+	tx, _ = payload.NewNameTx(state, testPrivAccounts[0].PublicKey(), name, data, amt, fee)
+	txEnv = txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(testPrivAccounts[0]))
+	if err := execTxWithStateAndBlockchain(state, blockchain.Tip, txEnv); err != nil {
 		t.Fatal(err)
 	}
 	entry, err = state.GetNameRegEntry(name)
 	require.NoError(t, err)
 	validateEntry(t, entry, name, data, testPrivAccounts[0].Address(), blockchain.LastBlockHeight()+numDesiredBlocks)
 	// Fast forward
-	for blockchain.Tip().LastBlockHeight() < entry.Expires {
+	for blockchain.Tip.LastBlockHeight() < entry.Expires {
 		commitNewBlock(state, blockchain)
 	}
 
 	amt = fee
 	data = ""
-	tx, _ = txs.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee)
-	tx.Sign(testChainID, testPrivAccounts[1])
-	if err := execTxWithStateNewBlock(state, blockchain, tx); err != nil {
+	tx, _ = payload.NewNameTx(state, testPrivAccounts[1].PublicKey(), name, data, amt, fee)
+	txEnv = txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(testPrivAccounts[1]))
+	if err := execTxWithStateNewBlock(state, blockchain, txEnv); err != nil {
 		t.Fatal(err)
 	}
 	entry, err = state.GetNameRegEntry(name)
@@ -1245,7 +1175,6 @@ func TestCreates(t *testing.T) {
 
 	//val0 := state.GetValidatorInfo(privValidators[0].Address())
 	acc0 := getAccount(state, privAccounts[0].Address())
-	acc0PubKey := privAccounts[0].PublicKey()
 	acc1 := getAccount(state, privAccounts[1].Address())
 	acc2 := getAccount(state, privAccounts[2].Address())
 
@@ -1260,20 +1189,20 @@ func TestCreates(t *testing.T) {
 	createData = append(createData, acc2.Address().Word256().Bytes()...)
 
 	// call the pre-factory, triggering the factory to run a create
-	tx := &txs.CallTx{
-		Input: &txs.TxInput{
-			Address:   acc0.Address(),
-			Amount:    1,
-			Sequence:  acc0.Sequence() + 1,
-			PublicKey: acc0PubKey,
+	tx := &payload.CallTx{
+		Input: &payload.TxInput{
+			Address:  acc0.Address(),
+			Amount:   1,
+			Sequence: acc0.Sequence() + 1,
 		},
 		Address:  addressPtr(acc1),
 		GasLimit: 10000,
 		Data:     createData,
 	}
 
-	require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
-	err := execTxWithState(state, tx)
+	txEnv := txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(privAccounts[0]))
+	err := execTxWithState(state, txEnv)
 	if err != nil {
 		t.Errorf("Got error in executing call transaction, %v", err)
 	}
@@ -1284,20 +1213,20 @@ func TestCreates(t *testing.T) {
 
 	acc0 = getAccount(state, acc0.Address())
 	// call the pre-factory, triggering the factory to run a create
-	tx = &txs.CallTx{
-		Input: &txs.TxInput{
-			Address:   acc0.Address(),
-			Amount:    1,
-			Sequence:  acc0.Sequence() + 1,
-			PublicKey: acc0PubKey,
+	tx = &payload.CallTx{
+		Input: &payload.TxInput{
+			Address:  acc0.Address(),
+			Amount:   1,
+			Sequence: acc0.Sequence() + 1,
 		},
 		Address:  addressPtr(acc1),
 		GasLimit: 100000,
 		Data:     createData,
 	}
 
-	require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
-	err = execTxWithState(state, tx)
+	txEnv = txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(privAccounts[0]))
+	err = execTxWithState(state, txEnv)
 	if err != nil {
 		t.Errorf("Got error in executing call transaction, %v", err)
 	}
@@ -1326,7 +1255,6 @@ func TestContractSend(t *testing.T) {
 
 	//val0 := state.GetValidatorInfo(privValidators[0].Address())
 	acc0 := getAccount(state, privAccounts[0].Address())
-	acc0PubKey := privAccounts[0].PublicKey()
 	acc1 := getAccount(state, privAccounts[1].Address())
 	acc2 := getAccount(state, privAccounts[2].Address())
 
@@ -1339,20 +1267,20 @@ func TestContractSend(t *testing.T) {
 	acc2Balance := acc2.Balance()
 
 	// call the contract, triggering the send
-	tx := &txs.CallTx{
-		Input: &txs.TxInput{
-			Address:   acc0.Address(),
-			Amount:    sendAmt,
-			Sequence:  acc0.Sequence() + 1,
-			PublicKey: acc0PubKey,
+	tx := &payload.CallTx{
+		Input: &payload.TxInput{
+			Address:  acc0.Address(),
+			Amount:   sendAmt,
+			Sequence: acc0.Sequence() + 1,
 		},
 		Address:  addressPtr(acc1),
 		GasLimit: 1000,
 		Data:     sendData,
 	}
 
-	require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
-	err := execTxWithState(state, tx)
+	txEnv := txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(privAccounts[0]))
+	err := execTxWithState(state, txEnv)
 	if err != nil {
 		t.Errorf("Got error in executing call transaction, %v", err)
 	}
@@ -1369,23 +1297,21 @@ func TestMerklePanic(t *testing.T) {
 
 	//val0 := state.GetValidatorInfo(privValidators[0].Address())
 	acc0 := getAccount(state, privAccounts[0].Address())
-	acc0PubKey := privAccounts[0].PublicKey()
 	acc1 := getAccount(state, privAccounts[1].Address())
 
 	state.Save()
 	// SendTx.
 	{
 		stateSendTx := state.Copy(dbm.NewMemDB())
-		tx := &txs.SendTx{
-			Inputs: []*txs.TxInput{
+		tx := &payload.SendTx{
+			Inputs: []*payload.TxInput{
 				{
-					Address:   acc0.Address(),
-					Amount:    1,
-					Sequence:  acc0.Sequence() + 1,
-					PublicKey: acc0PubKey,
+					Address:  acc0.Address(),
+					Amount:   1,
+					Sequence: acc0.Sequence() + 1,
 				},
 			},
-			Outputs: []*txs.TxOutput{
+			Outputs: []*payload.TxOutput{
 				{
 					Address: acc1.Address(),
 					Amount:  1,
@@ -1393,8 +1319,9 @@ func TestMerklePanic(t *testing.T) {
 			},
 		}
 
-		require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
-		err := execTxWithState(stateSendTx, tx)
+		txEnv := txs.Enclose(testChainID, tx)
+		require.NoError(t, txEnv.Sign(privAccounts[0]))
+		err := execTxWithState(stateSendTx, txEnv)
 		if err != nil {
 			t.Errorf("Got error in executing send transaction, %v", err)
 		}
@@ -1408,19 +1335,19 @@ func TestMerklePanic(t *testing.T) {
 		newAcc1 := getAccount(stateCallTx, acc1.Address())
 		newAcc1.SetCode([]byte{0x60})
 		stateCallTx.UpdateAccount(newAcc1)
-		tx := &txs.CallTx{
-			Input: &txs.TxInput{
-				Address:   acc0.Address(),
-				Amount:    1,
-				Sequence:  acc0.Sequence() + 1,
-				PublicKey: acc0PubKey,
+		tx := &payload.CallTx{
+			Input: &payload.TxInput{
+				Address:  acc0.Address(),
+				Amount:   1,
+				Sequence: acc0.Sequence() + 1,
 			},
 			Address:  addressPtr(acc1),
 			GasLimit: 10,
 		}
 
-		require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
-		err := execTxWithState(stateCallTx, tx)
+		txEnv := txs.Enclose(testChainID, tx)
+		require.NoError(t, txEnv.Sign(privAccounts[0]))
+		err := execTxWithState(stateCallTx, txEnv)
 		if err != nil {
 			t.Errorf("Got error in executing call transaction, %v", err)
 		}
@@ -1437,22 +1364,20 @@ func TestTxs(t *testing.T) {
 
 	//val0 := state.GetValidatorInfo(privValidators[0].Address())
 	acc0 := getAccount(state, privAccounts[0].Address())
-	acc0PubKey := privAccounts[0].PublicKey()
 	acc1 := getAccount(state, privAccounts[1].Address())
 
 	// SendTx.
 	{
 		stateSendTx := state.Copy(dbm.NewMemDB())
-		tx := &txs.SendTx{
-			Inputs: []*txs.TxInput{
+		tx := &payload.SendTx{
+			Inputs: []*payload.TxInput{
 				{
-					Address:   acc0.Address(),
-					Amount:    1,
-					Sequence:  acc0.Sequence() + 1,
-					PublicKey: acc0PubKey,
+					Address:  acc0.Address(),
+					Amount:   1,
+					Sequence: acc0.Sequence() + 1,
 				},
 			},
-			Outputs: []*txs.TxOutput{
+			Outputs: []*payload.TxOutput{
 				{
 					Address: acc1.Address(),
 					Amount:  1,
@@ -1460,8 +1385,9 @@ func TestTxs(t *testing.T) {
 			},
 		}
 
-		require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
-		err := execTxWithState(stateSendTx, tx)
+		txEnv := txs.Enclose(testChainID, tx)
+		require.NoError(t, txEnv.Sign(privAccounts[0]))
+		err := execTxWithState(stateSendTx, txEnv)
 		if err != nil {
 			t.Errorf("Got error in executing send transaction, %v", err)
 		}
@@ -1483,19 +1409,19 @@ func TestTxs(t *testing.T) {
 		newAcc1 := getAccount(stateCallTx, acc1.Address())
 		newAcc1.SetCode([]byte{0x60})
 		stateCallTx.UpdateAccount(newAcc1)
-		tx := &txs.CallTx{
-			Input: &txs.TxInput{
-				Address:   acc0.Address(),
-				Amount:    1,
-				Sequence:  acc0.Sequence() + 1,
-				PublicKey: acc0PubKey,
+		tx := &payload.CallTx{
+			Input: &payload.TxInput{
+				Address:  acc0.Address(),
+				Amount:   1,
+				Sequence: acc0.Sequence() + 1,
 			},
 			Address:  addressPtr(acc1),
 			GasLimit: 10,
 		}
 
-		require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
-		err := execTxWithState(stateCallTx, tx)
+		txEnv := txs.Enclose(testChainID, tx)
+		require.NoError(t, txEnv.Sign(privAccounts[0]))
+		err := execTxWithState(stateCallTx, txEnv)
 		if err != nil {
 			t.Errorf("Got error in executing call transaction, %v", err)
 		}
@@ -1534,20 +1460,20 @@ proof-of-work chain as proof of what happened while they were gone `
 		entryAmount := uint64(10000)
 
 		stateNameTx := state
-		tx := &txs.NameTx{
-			Input: &txs.TxInput{
-				Address:   acc0.Address(),
-				Amount:    entryAmount,
-				Sequence:  acc0.Sequence() + 1,
-				PublicKey: acc0PubKey,
+		tx := &payload.NameTx{
+			Input: &payload.TxInput{
+				Address:  acc0.Address(),
+				Amount:   entryAmount,
+				Sequence: acc0.Sequence() + 1,
 			},
 			Name: entryName,
 			Data: entryData,
 		}
 
-		require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
+		txEnv := txs.Enclose(testChainID, tx)
+		require.NoError(t, txEnv.Sign(privAccounts[0]))
 
-		err := execTxWithState(stateNameTx, tx)
+		err := execTxWithState(stateNameTx, txEnv)
 		if err != nil {
 			t.Errorf("Got error in executing call transaction, %v", err)
 		}
@@ -1568,9 +1494,10 @@ proof-of-work chain as proof of what happened while they were gone `
 		// test a bad string
 		tx.Data = string([]byte{0, 1, 2, 3, 127, 128, 129, 200, 251})
 		tx.Input.Sequence += 1
-		require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
-		err = execTxWithState(stateNameTx, tx)
-		if _, ok := err.(txs.ErrTxInvalidString); !ok {
+		txEnv = txs.Enclose(testChainID, tx)
+		require.NoError(t, txEnv.Sign(privAccounts[0]))
+		err = execTxWithState(stateNameTx, txEnv)
+		if _, ok := err.(payload.ErrTxInvalidString); !ok {
 			t.Errorf("Expected invalid string error. Got: %s", err.Error())
 		}
 	}
@@ -1579,18 +1506,18 @@ proof-of-work chain as proof of what happened while they were gone `
 	/*
 		{
 			state := state.Copy()
-			tx := &txs.BondTx{
+			tx := &payload.BondTx{
 				PublicKey: acc0PubKey.(acm.PublicKeyEd25519),
-				Inputs: []*txs.TxInput{
-					&txs.TxInput{
+				Inputs: []*payload.TxInput{
+					&payload.TxInput{
 						Address:  acc0.Address(),
 						Amount:   1,
 						Sequence: acc0.Sequence() + 1,
 						PublicKey:   acc0PubKey,
 					},
 				},
-				UnbondTo: []*txs.TxOutput{
-					&txs.TxOutput{
+				UnbondTo: []*payload.TxOutput{
+					&payload.TxOutput{
 						Address: acc0.Address(),
 						Amount:  1,
 					},
@@ -1630,7 +1557,6 @@ proof-of-work chain as proof of what happened while they were gone `
 }
 
 func TestSelfDestruct(t *testing.T) {
-
 	state, privAccounts := makeGenesisState(3, true, 1000, 1, true, 1000)
 
 	acc0 := getAccount(state, privAccounts[0].Address())
@@ -1649,22 +1575,16 @@ func TestSelfDestruct(t *testing.T) {
 	state.UpdateAccount(newAcc1)
 
 	// send call tx with no data, cause self-destruct
-	tx := txs.NewCallTxWithSequence(acc0PubKey, addressPtr(acc1), nil, sendingAmount, 1000, 0, acc0.Sequence()+1)
-	require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
+	tx := payload.NewCallTxWithSequence(acc0PubKey, addressPtr(acc1), nil, sendingAmount, 1000, 0, acc0.Sequence()+1)
 
 	// we use cache instead of execTxWithState so we can run the tx twice
-	exe := NewBatchCommitter(state, testChainID, newBlockchain(testGenesisDoc), event.NewNoOpPublisher(), logger)
-	if err := exe.Execute(tx); err != nil {
-		t.Errorf("Got error in executing call transaction, %v", err)
-	}
+	exe := NewBatchCommitter(state, testChainID, newBlockchain(testGenesisDoc).Tip, event.NewNoOpPublisher(), logger)
+	signAndExecute(t, false, exe, testChainID, tx, privAccounts[0])
 
 	// if we do it again, we won't get an error, but the self-destruct
 	// shouldn't happen twice and the caller should lose fee
 	tx.Input.Sequence += 1
-	require.NoError(t, tx.Sign(testChainID, privAccounts[0]))
-	if err := exe.Execute(tx); err != nil {
-		t.Errorf("Got error in executing call transaction, %v", err)
-	}
+	signAndExecute(t, false, exe, testChainID, tx, privAccounts[0])
 
 	// commit the block
 	exe.Commit()
@@ -1682,10 +1602,23 @@ func TestSelfDestruct(t *testing.T) {
 	}
 }
 
-func execTxWithStateAndBlockchain(state *State, tip bcm.Tip, tx txs.Tx) error {
+func signAndExecute(t *testing.T, shoudlFail bool, exe BatchExecutor, chainID string, tx payload.Payload,
+	signers ...acm.AddressableSigner) *txs.Envelope {
+
+	env := txs.Enclose(chainID, tx)
+	require.NoError(t, env.Sign(signers...), "Could not sign tx in call: %s", debug.Stack())
+	if shoudlFail {
+		require.Error(t, exe.Execute(env), "Tx should fail in call: %s", debug.Stack())
+	} else {
+		require.NoError(t, exe.Execute(env), "Could not execute tx in call: %s", debug.Stack())
+	}
+	return env
+}
+
+func execTxWithStateAndBlockchain(state *State, tip *bcm.Tip, txEnv *txs.Envelope) error {
 	exe := newExecutor("execTxWithStateAndBlockchainCache", true, state, testChainID, tip,
 		event.NewNoOpPublisher(), logger)
-	if err := exe.Execute(tx); err != nil {
+	if err := exe.Execute(txEnv); err != nil {
 		return err
 	} else {
 		exe.Commit()
@@ -1693,17 +1626,17 @@ func execTxWithStateAndBlockchain(state *State, tip bcm.Tip, tx txs.Tx) error {
 	}
 }
 
-func execTxWithState(state *State, tx txs.Tx) error {
-	return execTxWithStateAndBlockchain(state, newBlockchain(testGenesisDoc), tx)
+func execTxWithState(state *State, txEnv *txs.Envelope) error {
+	return execTxWithStateAndBlockchain(state, newBlockchain(testGenesisDoc).Tip, txEnv)
 }
 
-func commitNewBlock(state *State, blockchain bcm.MutableBlockchain) {
+func commitNewBlock(state *State, blockchain *bcm.Blockchain) {
 	blockchain.CommitBlock(blockchain.LastBlockTime().Add(time.Second), sha3.Sha3(blockchain.LastBlockHash()),
 		state.Hash())
 }
 
-func execTxWithStateNewBlock(state *State, blockchain bcm.MutableBlockchain, tx txs.Tx) error {
-	if err := execTxWithStateAndBlockchain(state, blockchain, tx); err != nil {
+func execTxWithStateNewBlock(state *State, blockchain *bcm.Blockchain, txEnv *txs.Envelope) error {
+	if err := execTxWithStateAndBlockchain(state, blockchain.Tip, txEnv); err != nil {
 		return err
 	}
 	commitNewBlock(state, blockchain)
@@ -1742,14 +1675,14 @@ var ExceptionTimeOut = "timed out waiting for event"
 
 // run ExecTx and wait for the Call event on given addr
 // returns the msg data and an error/exception
-func execTxWaitEvent(t *testing.T, batchCommitter *executor, tx txs.Tx, eventid string) (interface{}, string) {
+func execTxWaitEvent(t *testing.T, batchCommitter *executor, txEnv *txs.Envelope, eventid string) (interface{}, string) {
 	emitter := event.NewEmitter(logger)
 	ch := make(chan interface{})
 	emitter.Subscribe(context.Background(), "test", event.QueryForEventID(eventid), ch)
 	evc := event.NewEventCache(emitter)
 	batchCommitter.eventCache = evc
 	go func() {
-		if err := batchCommitter.Execute(tx); err != nil {
+		if err := batchCommitter.Execute(txEnv); err != nil {
 			ch <- err.Error()
 		}
 		evc.Flush()
@@ -1796,10 +1729,11 @@ func testSNativeCALL(t *testing.T, expectPass bool, batchCommitter *executor, do
 	dougAddress := doug.Address()
 
 	batchCommitter.stateCache.UpdateAccount(doug)
-	tx, _ := txs.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &dougAddress, data, 100, 10000, 100)
-	tx.Sign(testChainID, users[0])
-	fmt.Println("subscribing to", evm_events.EventStringAccountCall(snativeAddress))
-	ev, exception := execTxWaitEvent(t, batchCommitter, tx, evm_events.EventStringAccountCall(snativeAddress))
+	tx, _ := payload.NewCallTx(batchCommitter.stateCache, users[0].PublicKey(), &dougAddress, data, 100, 10000, 100)
+	txEnv := txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(users[0]))
+	t.Logf("subscribing to %v", evm_events.EventStringAccountCall(snativeAddress))
+	ev, exception := execTxWaitEvent(t, batchCommitter, txEnv, evm_events.EventStringAccountCall(snativeAddress))
 	if exception == ExceptionTimeOut {
 		t.Fatal("Timed out waiting for event")
 	}
@@ -1835,9 +1769,10 @@ func testSNativeTx(t *testing.T, expectPass bool, batchCommitter *executor, perm
 		acc.MutablePermissions().Base.Set(perm, true)
 		batchCommitter.stateCache.UpdateAccount(acc)
 	}
-	tx, _ := txs.NewPermissionsTx(batchCommitter.stateCache, users[0].PublicKey(), snativeArgs)
-	tx.Sign(testChainID, users[0])
-	err := batchCommitter.Execute(tx)
+	tx, _ := payload.NewPermissionsTx(batchCommitter.stateCache, users[0].PublicKey(), snativeArgs)
+	txEnv := txs.Enclose(testChainID, tx)
+	require.NoError(t, txEnv.Sign(users[0]))
+	err := batchCommitter.Execute(txEnv)
 	if expectPass {
 		if err != nil {
 			t.Fatal("Unexpected exception", err)
diff --git a/txs/names.go b/execution/names/names.go
similarity index 98%
rename from txs/names.go
rename to execution/names/names.go
index 3b0bceff26e0807dc549db8d198155af544a3e41..cfa656d696c3f920b8759a71746a17a6b2211ce6 100644
--- a/txs/names.go
+++ b/execution/names/names.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package txs
+package names
 
 var (
 	MinNameRegistrationPeriod uint64 = 5
diff --git a/execution/state.go b/execution/state.go
index 1521faad675e176da33f16e62525217afff2bf78..7ccc79196f5d61145ff22a9286e351ec05a9963b 100644
--- a/execution/state.go
+++ b/execution/state.go
@@ -25,10 +25,10 @@ import (
 	"github.com/hyperledger/burrow/account/state"
 	"github.com/hyperledger/burrow/binary"
 	"github.com/hyperledger/burrow/crypto"
+	"github.com/hyperledger/burrow/execution/names"
 	"github.com/hyperledger/burrow/genesis"
 	"github.com/hyperledger/burrow/logging"
 	ptypes "github.com/hyperledger/burrow/permission"
-	"github.com/hyperledger/burrow/txs"
 	"github.com/tendermint/go-wire"
 	"github.com/tendermint/iavl"
 	dbm "github.com/tendermint/tmlibs/db"
@@ -326,7 +326,7 @@ func NameRegEncode(o interface{}, w io.Writer, n *int, err *error) {
 }
 
 func NameRegDecode(r io.Reader, n *int, err *error) interface{} {
-	return wire.ReadBinary(&NameRegEntry{}, r, txs.MaxDataLength, n, err)
+	return wire.ReadBinary(&NameRegEntry{}, r, names.MaxDataLength, n, err)
 }
 
 func prefixedKey(prefix string, suffices ...[]byte) []byte {
diff --git a/execution/transactor.go b/execution/transactor.go
index 96f90c2d260a192bb81f9a1f88548ae48b2c16e7..bc0c9b9c69bc05bef889d5066da099d8002128f7 100644
--- a/execution/transactor.go
+++ b/execution/transactor.go
@@ -15,13 +15,13 @@
 package execution
 
 import (
+	"bytes"
 	"context"
+	"encoding/json"
 	"fmt"
 	"runtime/debug"
 	"time"
 
-	"bytes"
-
 	acm "github.com/hyperledger/burrow/account"
 	"github.com/hyperledger/burrow/account/state"
 	"github.com/hyperledger/burrow/binary"
@@ -35,8 +35,8 @@ import (
 	"github.com/hyperledger/burrow/logging"
 	"github.com/hyperledger/burrow/logging/structure"
 	"github.com/hyperledger/burrow/txs"
+	"github.com/hyperledger/burrow/txs/payload"
 	abci_types "github.com/tendermint/abci/types"
-	"github.com/tendermint/go-wire"
 )
 
 const BlockingTimeoutSeconds = 30
@@ -48,14 +48,14 @@ type Call struct {
 
 // Transactor is the controller/middleware for the v0 RPC
 type Transactor struct {
-	tip              blockchain.Tip
+	tip              *blockchain.Tip
 	eventEmitter     event.Emitter
-	broadcastTxAsync func(tx txs.Tx, callback func(res *abci_types.Response)) error
+	broadcastTxAsync func(tx *txs.Envelope, callback func(res *abci_types.Response)) error
 	logger           *logging.Logger
 }
 
-func NewTransactor(tip blockchain.Tip, eventEmitter event.Emitter,
-	broadcastTxAsync func(tx txs.Tx, callback func(res *abci_types.Response)) error,
+func NewTransactor(tip *blockchain.Tip, eventEmitter event.Emitter,
+	broadcastTxAsync func(tx *txs.Envelope, callback func(res *abci_types.Response)) error,
 	logger *logging.Logger) *Transactor {
 
 	return &Transactor{
@@ -124,18 +124,18 @@ func (trans *Transactor) CallCode(reader state.Reader, fromAddress crypto.Addres
 	return &Call{Return: ret, GasUsed: gasUsed}, nil
 }
 
-func (trans *Transactor) BroadcastTxAsync(tx txs.Tx, callback func(res *abci_types.Response)) error {
+func (trans *Transactor) BroadcastTxAsync(tx *txs.Envelope, callback func(res *abci_types.Response)) error {
 	return trans.broadcastTxAsync(tx, callback)
 }
 
 // Broadcast a transaction and waits for a response from the mempool. Transactions to BroadcastTx will block during
 // various mempool operations (managed by Tendermint) including mempool Reap, Commit, and recheckTx.
-func (trans *Transactor) BroadcastTx(tx txs.Tx) (*txs.Receipt, error) {
+func (trans *Transactor) BroadcastTx(txEnv *txs.Envelope) (*txs.Receipt, error) {
 	trans.logger.Trace.Log("method", "BroadcastTx",
-		"tx_hash", tx.Hash(trans.tip.ChainID()),
-		"tx", tx.String())
+		"tx_hash", txEnv.Tx.Hash(),
+		"tx", txEnv.String())
 	responseCh := make(chan *abci_types.Response, 1)
-	err := trans.BroadcastTxAsync(tx, func(res *abci_types.Response) {
+	err := trans.BroadcastTxAsync(txEnv, func(res *abci_types.Response) {
 		responseCh <- res
 	})
 
@@ -151,7 +151,7 @@ func (trans *Transactor) BroadcastTx(tx txs.Tx) (*txs.Receipt, error) {
 	switch checkTxResponse.Code {
 	case codes.TxExecutionSuccessCode:
 		receipt := new(txs.Receipt)
-		err := wire.ReadBinaryBytes(checkTxResponse.Data, receipt)
+		err := json.Unmarshal(checkTxResponse.Data, receipt)
 		if err != nil {
 			return nil, fmt.Errorf("could not deserialise transaction receipt: %s", err)
 		}
@@ -174,12 +174,12 @@ func (trans *Transactor) Transact(sequentialSigningAccount *SequentialSigningAcc
 	}
 	defer unlock()
 
-	callTx, _, err := trans.formulateCallTx(inputAccount, address, data, gasLimit, fee)
+	callTx, err := trans.formulateCallTx(inputAccount, address, data, gasLimit, fee)
 	if err != nil {
 		return nil, err
 	}
 	// Got ourselves a tx.
-	err = callTx.Sign(trans.tip.ChainID(), inputAccount)
+	err = callTx.Sign(inputAccount)
 	if err != nil {
 		return nil, err
 	}
@@ -195,11 +195,13 @@ func (trans *Transactor) TransactAndHold(sequentialSigningAccount *SequentialSig
 	}
 	defer unlock()
 
-	callTx, expectedReceipt, err := trans.formulateCallTx(inputAccount, address, data, gasLimit, fee)
+	callTxEnv, err := trans.formulateCallTx(inputAccount, address, data, gasLimit, fee)
 	if err != nil {
 		return nil, err
 	}
 
+	expectedReceipt := callTxEnv.Tx.GenerateReceipt()
+
 	subID, err := event.GenerateSubscriptionID()
 	if err != nil {
 		return nil, err
@@ -217,7 +219,7 @@ func (trans *Transactor) TransactAndHold(sequentialSigningAccount *SequentialSig
 	// Will clean up callback goroutine and subscription in pubsub
 	defer trans.eventEmitter.UnsubscribeAll(context.Background(), subID)
 
-	receipt, err := trans.BroadcastTx(callTx)
+	receipt, err := trans.BroadcastTx(callTxEnv)
 	if err != nil {
 		return nil, err
 	}
@@ -241,15 +243,14 @@ func (trans *Transactor) TransactAndHold(sequentialSigningAccount *SequentialSig
 	}
 }
 func (trans *Transactor) formulateCallTx(inputAccount *SigningAccount, address *crypto.Address, data []byte,
-	gasLimit, fee uint64) (*txs.CallTx, *txs.Receipt, error) {
+	gasLimit, fee uint64) (*txs.Envelope, error) {
 
-	txInput := &txs.TxInput{
-		Address:   inputAccount.Address(),
-		Amount:    fee,
-		Sequence:  inputAccount.Sequence() + 1,
-		PublicKey: inputAccount.PublicKey(),
+	txInput := &payload.TxInput{
+		Address:  inputAccount.Address(),
+		Amount:   fee,
+		Sequence: inputAccount.Sequence() + 1,
 	}
-	tx := &txs.CallTx{
+	tx := &payload.CallTx{
 		Input:    txInput,
 		Address:  address,
 		GasLimit: gasLimit,
@@ -257,13 +258,13 @@ func (trans *Transactor) formulateCallTx(inputAccount *SigningAccount, address *
 		Data:     data,
 	}
 
+	env := txs.Enclose(trans.tip.ChainID(), tx)
 	// Got ourselves a tx.
-	err := tx.Sign(trans.tip.ChainID(), inputAccount)
+	err := env.Sign(inputAccount)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
-	receipt := txs.GenerateReceipt(trans.tip.ChainID(), tx)
-	return tx, &receipt, nil
+	return env, nil
 }
 
 func (trans *Transactor) Send(sequentialSigningAccount *SequentialSigningAccount, toAddress crypto.Address,
@@ -275,12 +276,12 @@ func (trans *Transactor) Send(sequentialSigningAccount *SequentialSigningAccount
 	}
 	defer unlock()
 
-	sendTx, _, err := trans.formulateSendTx(inputAccount, toAddress, amount)
+	sendTxEnv, err := trans.formulateSendTx(inputAccount, toAddress, amount)
 	if err != nil {
 		return nil, err
 	}
 
-	return trans.BroadcastTx(sendTx)
+	return trans.BroadcastTx(sendTxEnv)
 }
 
 func (trans *Transactor) SendAndHold(sequentialSigningAccount *SequentialSigningAccount, toAddress crypto.Address,
@@ -292,17 +293,18 @@ func (trans *Transactor) SendAndHold(sequentialSigningAccount *SequentialSigning
 	}
 	defer unlock()
 
-	sendTx, expectedReceipt, err := trans.formulateSendTx(inputAccount, toAddress, amount)
+	sendTxEnv, err := trans.formulateSendTx(inputAccount, toAddress, amount)
 	if err != nil {
 		return nil, err
 	}
+	expectedReceipt := sendTxEnv.Tx.GenerateReceipt()
 
 	subID, err := event.GenerateSubscriptionID()
 	if err != nil {
 		return nil, err
 	}
 
-	wc := make(chan *txs.SendTx)
+	wc := make(chan *payload.SendTx)
 	err = exe_events.SubscribeAccountOutputSendTx(context.Background(), trans.eventEmitter, subID, toAddress,
 		expectedReceipt.TxHash, wc)
 	if err != nil {
@@ -310,7 +312,7 @@ func (trans *Transactor) SendAndHold(sequentialSigningAccount *SequentialSigning
 	}
 	defer trans.eventEmitter.UnsubscribeAll(context.Background(), subID)
 
-	receipt, err := trans.BroadcastTx(sendTx)
+	receipt, err := trans.BroadcastTx(sendTxEnv)
 	if err != nil {
 		return nil, err
 	}
@@ -336,26 +338,25 @@ func (trans *Transactor) SendAndHold(sequentialSigningAccount *SequentialSigning
 }
 
 func (trans *Transactor) formulateSendTx(inputAccount *SigningAccount, toAddress crypto.Address,
-	amount uint64) (*txs.SendTx, *txs.Receipt, error) {
+	amount uint64) (*txs.Envelope, error) {
 
-	sendTx := txs.NewSendTx()
-	txInput := &txs.TxInput{
-		Address:   inputAccount.Address(),
-		Amount:    amount,
-		Sequence:  inputAccount.Sequence() + 1,
-		PublicKey: inputAccount.PublicKey(),
+	sendTx := payload.NewSendTx()
+	txInput := &payload.TxInput{
+		Address:  inputAccount.Address(),
+		Amount:   amount,
+		Sequence: inputAccount.Sequence() + 1,
 	}
 	sendTx.Inputs = append(sendTx.Inputs, txInput)
-	txOutput := &txs.TxOutput{Address: toAddress, Amount: amount}
+	txOutput := &payload.TxOutput{Address: toAddress, Amount: amount}
 	sendTx.Outputs = append(sendTx.Outputs, txOutput)
 
-	err := sendTx.Sign(trans.tip.ChainID(), inputAccount)
+	env := txs.Enclose(trans.tip.ChainID(), sendTx)
+	err := env.Sign(inputAccount)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	receipt := txs.GenerateReceipt(trans.tip.ChainID(), sendTx)
-	return sendTx, &receipt, nil
+	return env, nil
 }
 
 func (trans *Transactor) TransactNameReg(sequentialSigningAccount *SequentialSigningAccount, name, data string, amount,
@@ -367,25 +368,26 @@ func (trans *Transactor) TransactNameReg(sequentialSigningAccount *SequentialSig
 	}
 	defer unlock()
 	// Formulate and sign
-	tx := txs.NewNameTxWithSequence(inputAccount.PublicKey(), name, data, amount, fee, inputAccount.Sequence()+1)
-	err = tx.Sign(trans.tip.ChainID(), inputAccount)
+	tx := payload.NewNameTxWithSequence(inputAccount.PublicKey(), name, data, amount, fee, inputAccount.Sequence()+1)
+	env := txs.Enclose(trans.tip.ChainID(), tx)
+	err = env.Sign(inputAccount)
 	if err != nil {
 		return nil, err
 	}
-	return trans.BroadcastTx(tx)
+	return trans.BroadcastTx(env)
 }
 
 // Sign a transaction
-func (trans *Transactor) SignTx(tx txs.Tx, signingAccounts []acm.AddressableSigner) (txs.Tx, error) {
+func (trans *Transactor) SignTx(txEnv *txs.Envelope, signingAccounts []acm.AddressableSigner) (*txs.Envelope, error) {
 	// more checks?
-	err := tx.Sign(trans.tip.ChainID(), signingAccounts...)
+	err := txEnv.Sign(signingAccounts...)
 	if err != nil {
 		return nil, err
 	}
-	return tx, nil
+	return txEnv, nil
 }
 
-func vmParams(tip blockchain.Tip) evm.Params {
+func vmParams(tip *blockchain.Tip) evm.Params {
 	return evm.Params{
 		BlockHeight: tip.LastBlockHeight(),
 		BlockHash:   binary.LeftPadWord256(tip.LastBlockHash()),
diff --git a/execution/transactor_test.go b/execution/transactor_test.go
index 5f0689a513feb04731cc9170b01ebf6a928b7c66..7a49f27f548e0cf06b0ec38b961e44820b14d2ad 100644
--- a/execution/transactor_test.go
+++ b/execution/transactor_test.go
@@ -21,12 +21,12 @@ type testTransactor struct {
 	*Transactor
 }
 
-func newTestTransactor(txProcessor func(tx txs.Tx) (*types.Response, error)) testTransactor {
+func newTestTransactor(txProcessor func(txEnv *txs.Envelope) (*types.Response, error)) testTransactor {
 	st := state.NewMemoryState()
 	emitter := event.NewEmitter(logger)
 	trans := NewTransactor(blockchain.NewTip(testChainID, time.Time{}, nil),
-		emitter, func(tx txs.Tx, callback func(res *types.Response)) error {
-			res, err := txProcessor(tx)
+		emitter, func(txEnv *txs.Envelope, callback func(res *types.Response)) error {
+			res, err := txProcessor(txEnv)
 			if err != nil {
 				return err
 			}
diff --git a/forensics/block.go b/forensics/block.go
index c6055790e3d90eb69a4fee43b31911a78355bb49..dbef115ba8a343ecf7aa5466f28940e051618e82 100644
--- a/forensics/block.go
+++ b/forensics/block.go
@@ -17,7 +17,7 @@ func NewBlock(txDecoder txs.Decoder, block *types.Block) *Block {
 	}
 }
 
-func (b *Block) Transactions(iter func(txs.Tx) (stop bool)) (stopped bool, err error) {
+func (b *Block) Transactions(iter func(*txs.Envelope) (stop bool)) (stopped bool, err error) {
 	for i := 0; i < len(b.Txs); i++ {
 		tx, err := b.txDecoder.DecodeTx(b.Txs[i])
 		if err != nil {
diff --git a/forensics/block_explorer.go b/forensics/block_explorer.go
index be8541135ba73253c07f9b830b1266ab570f3b22..a8d2c84c1d62b7779bd9f45cd37bfcb94a165122 100644
--- a/forensics/block_explorer.go
+++ b/forensics/block_explorer.go
@@ -16,7 +16,7 @@ type BlockExplorer struct {
 
 func NewBlockExplorer(dbBackendType db.DBBackendType, dbDir string) *BlockExplorer {
 	return &BlockExplorer{
-		txDecoder:  txs.NewGoWireCodec(),
+		txDecoder:  txs.NewJSONCodec(),
 		BlockStore: blockchain.NewBlockStore(tendermint.DBProvider("blockstore", dbBackendType, dbDir)),
 	}
 }
diff --git a/genesis/spec/genesis_spec.go b/genesis/spec/genesis_spec.go
index 48d8443a968d3385abb6278f9ce5e2e71dd96477..b1f6e92020727099ce5843b6035ab6ad75290689 100644
--- a/genesis/spec/genesis_spec.go
+++ b/genesis/spec/genesis_spec.go
@@ -7,7 +7,6 @@ import (
 	"fmt"
 	"time"
 
-	"github.com/hyperledger/burrow/crypto"
 	"github.com/hyperledger/burrow/genesis"
 	"github.com/hyperledger/burrow/keys"
 	"github.com/hyperledger/burrow/permission"
@@ -31,112 +30,6 @@ type GenesisSpec struct {
 	Accounts          []TemplateAccount `json:",omitempty" toml:",omitempty"`
 }
 
-type TemplateAccount struct {
-	// Template accounts sharing a name will be merged when merging genesis specs
-	Name string `json:",omitempty" toml:",omitempty"`
-	// Address  is convenient to have in file for reference, but otherwise ignored since derived from PublicKey
-	Address      *crypto.Address   `json:",omitempty" toml:",omitempty"`
-	PublicKey    *crypto.PublicKey `json:",omitempty" toml:",omitempty"`
-	Amount       *uint64           `json:",omitempty" toml:",omitempty"`
-	AmountBonded *uint64           `json:",omitempty" toml:",omitempty"`
-	Permissions  []string          `json:",omitempty" toml:",omitempty"`
-	Roles        []string          `json:",omitempty" toml:",omitempty"`
-}
-
-func (ta TemplateAccount) Validator(keyClient keys.KeyClient, index int) (*genesis.Validator, error) {
-	var err error
-	gv := new(genesis.Validator)
-	gv.PublicKey, gv.Address, err = ta.RealisePubKeyAndAddress(keyClient)
-	if err != nil {
-		return nil, err
-	}
-	if ta.AmountBonded == nil {
-		gv.Amount = DefaultAmountBonded
-	} else {
-		gv.Amount = *ta.AmountBonded
-	}
-	if ta.Name == "" {
-		gv.Name = accountNameFromIndex(index)
-	} else {
-		gv.Name = ta.Name
-	}
-
-	gv.UnbondTo = []genesis.BasicAccount{{
-		Address:   gv.Address,
-		PublicKey: gv.PublicKey,
-		Amount:    gv.Amount,
-	}}
-	return gv, nil
-}
-
-func (ta TemplateAccount) AccountPermissions() (ptypes.AccountPermissions, error) {
-	basePerms, err := permission.BasePermissionsFromStringList(ta.Permissions)
-	if err != nil {
-		return permission.ZeroAccountPermissions, nil
-	}
-	return ptypes.AccountPermissions{
-		Base:  basePerms,
-		Roles: ta.Roles,
-	}, nil
-}
-
-func (ta TemplateAccount) Account(keyClient keys.KeyClient, index int) (*genesis.Account, error) {
-	var err error
-	ga := new(genesis.Account)
-	ga.PublicKey, ga.Address, err = ta.RealisePubKeyAndAddress(keyClient)
-	if err != nil {
-		return nil, err
-	}
-	if ta.Amount == nil {
-		ga.Amount = DefaultAmount
-	} else {
-		ga.Amount = *ta.Amount
-	}
-	if ta.Name == "" {
-		ga.Name = accountNameFromIndex(index)
-	} else {
-		ga.Name = ta.Name
-	}
-	if ta.Permissions == nil {
-		ga.Permissions = permission.DefaultAccountPermissions.Clone()
-	} else {
-		ga.Permissions, err = ta.AccountPermissions()
-		if err != nil {
-			return nil, err
-		}
-	}
-	return ga, nil
-}
-
-// Adds a public key and address to the template. If PublicKey will try to fetch it by Address.
-// If both PublicKey and Address are not set will use the keyClient to generate a new keypair
-func (ta TemplateAccount) RealisePubKeyAndAddress(keyClient keys.KeyClient) (pubKey crypto.PublicKey, address crypto.Address, err error) {
-	if ta.PublicKey == nil {
-		if ta.Address == nil {
-			// If neither PublicKey or Address set then generate a new one
-			address, err = keyClient.Generate(ta.Name, crypto.CurveTypeEd25519)
-			if err != nil {
-				return
-			}
-		} else {
-			address = *ta.Address
-		}
-		// Get the (possibly existing) key
-		pubKey, err = keyClient.PublicKey(address)
-		if err != nil {
-			return
-		}
-	} else {
-		address = (*ta.PublicKey).Address()
-		if ta.Address != nil && *ta.Address != address {
-			err = fmt.Errorf("template address %s does not match public key derived address %s", ta.Address,
-				ta.PublicKey)
-		}
-		pubKey = *ta.PublicKey
-	}
-	return
-}
-
 func (gs *GenesisSpec) RealiseKeys(keyClient keys.KeyClient) error {
 	for _, templateAccount := range gs.Accounts {
 		_, _, err := templateAccount.RealisePubKeyAndAddress(keyClient)
diff --git a/genesis/spec/template_account.go b/genesis/spec/template_account.go
new file mode 100644
index 0000000000000000000000000000000000000000..51e43049837dd6915c363d8c40958ca21918f503
--- /dev/null
+++ b/genesis/spec/template_account.go
@@ -0,0 +1,117 @@
+package spec
+
+import (
+	"fmt"
+
+	"github.com/hyperledger/burrow/crypto"
+	"github.com/hyperledger/burrow/genesis"
+	"github.com/hyperledger/burrow/keys"
+	"github.com/hyperledger/burrow/permission"
+	ptypes "github.com/hyperledger/burrow/permission/types"
+)
+
+type TemplateAccount struct {
+	// Template accounts sharing a name will be merged when merging genesis specs
+	Name string `json:",omitempty" toml:",omitempty"`
+	// Address  is convenient to have in file for reference, but otherwise ignored since derived from PublicKey
+	Address      *crypto.Address   `json:",omitempty" toml:",omitempty"`
+	PublicKey    *crypto.PublicKey `json:",omitempty" toml:",omitempty"`
+	Amount       *uint64           `json:",omitempty" toml:",omitempty"`
+	AmountBonded *uint64           `json:",omitempty" toml:",omitempty"`
+	Permissions  []string          `json:",omitempty" toml:",omitempty"`
+	Roles        []string          `json:",omitempty" toml:",omitempty"`
+}
+
+func (ta TemplateAccount) Validator(keyClient keys.KeyClient, index int) (*genesis.Validator, error) {
+	var err error
+	gv := new(genesis.Validator)
+	gv.PublicKey, gv.Address, err = ta.RealisePubKeyAndAddress(keyClient)
+	if err != nil {
+		return nil, err
+	}
+	if ta.AmountBonded == nil {
+		gv.Amount = DefaultAmountBonded
+	} else {
+		gv.Amount = *ta.AmountBonded
+	}
+	if ta.Name == "" {
+		gv.Name = accountNameFromIndex(index)
+	} else {
+		gv.Name = ta.Name
+	}
+
+	gv.UnbondTo = []genesis.BasicAccount{{
+		Address:   gv.Address,
+		PublicKey: gv.PublicKey,
+		Amount:    gv.Amount,
+	}}
+	return gv, nil
+}
+
+func (ta TemplateAccount) AccountPermissions() (ptypes.AccountPermissions, error) {
+	basePerms, err := permission.BasePermissionsFromStringList(ta.Permissions)
+	if err != nil {
+		return permission.ZeroAccountPermissions, nil
+	}
+	return ptypes.AccountPermissions{
+		Base:  basePerms,
+		Roles: ta.Roles,
+	}, nil
+}
+
+func (ta TemplateAccount) Account(keyClient keys.KeyClient, index int) (*genesis.Account, error) {
+	var err error
+	ga := new(genesis.Account)
+	ga.PublicKey, ga.Address, err = ta.RealisePubKeyAndAddress(keyClient)
+	if err != nil {
+		return nil, err
+	}
+	if ta.Amount == nil {
+		ga.Amount = DefaultAmount
+	} else {
+		ga.Amount = *ta.Amount
+	}
+	if ta.Name == "" {
+		ga.Name = accountNameFromIndex(index)
+	} else {
+		ga.Name = ta.Name
+	}
+	if ta.Permissions == nil {
+		ga.Permissions = permission.DefaultAccountPermissions.Clone()
+	} else {
+		ga.Permissions, err = ta.AccountPermissions()
+		if err != nil {
+			return nil, err
+		}
+	}
+	return ga, nil
+}
+
+// Adds a public key and address to the template. If PublicKey will try to fetch it by Address.
+// If both PublicKey and Address are not set will use the keyClient to generate a new keypair
+func (ta TemplateAccount) RealisePubKeyAndAddress(keyClient keys.KeyClient) (pubKey crypto.PublicKey, address crypto.Address, err error) {
+	if ta.PublicKey == nil {
+		if ta.Address == nil {
+			// If neither PublicKey or Address set then generate a new one
+			address, err = keyClient.Generate(ta.Name, crypto.CurveTypeEd25519)
+			if err != nil {
+				return
+			}
+		} else {
+			address = *ta.Address
+		}
+		// Get the (possibly existing) key
+		pubKey, err = keyClient.PublicKey(address)
+		if err != nil {
+			return
+		}
+	} else {
+		address = (*ta.PublicKey).Address()
+		if ta.Address != nil && *ta.Address != address {
+			err = fmt.Errorf("template address %s does not match public key derived address %s", ta.Address,
+				ta.PublicKey)
+		}
+		pubKey = *ta.PublicKey
+	}
+	return
+}
diff --git a/governance/governance.go b/governance/governance.go
new file mode 100644
index 0000000000000000000000000000000000000000..b3e56f5b8307a645379c05d533e2eb64361700b7
--- /dev/null
+++ b/governance/governance.go
@@ -0,0 +1,17 @@
+// The governance package contains functionality for altering permissions, token distribution, consensus parameters,
+// validators, and network forks.
+package governance
+
+// TODO:
+// - Set validator power
+// - Set account amount(s)
+// - Set account permissions
+// - Set global permissions
+// - Set ConsensusParams
+// Future considerations:
+// - Handle network forks/termination/merging/replacement ?
+// - Provide transaction in stasis/sudo (voting?)
+// - Handle bonding by other means (e.g. pre-shared key permitting n bondings)
+// - Network administered proxies (i.e. instead of keys have password authentication for identities - allow calls to originate as if from address without key?)
+// Subject to:
+// - Less than 1/3 validator power change per block
diff --git a/keys/key_client.go b/keys/key_client.go
index 49983e41c987427e403b747b41a96cbe5234cb11..fb19e5b1afc42a122ca03e2ebd7b12f1860ac44f 100644
--- a/keys/key_client.go
+++ b/keys/key_client.go
@@ -19,7 +19,6 @@ import (
 	"fmt"
 	"time"
 
-	acm "github.com/hyperledger/burrow/account"
 	"github.com/hyperledger/burrow/crypto"
 	"github.com/hyperledger/burrow/keys/pbkeys"
 	"github.com/hyperledger/burrow/logging"
@@ -171,48 +170,38 @@ func NewLocalKeyClient(ks KeyStore, logger *logging.Logger) KeyClient {
 	return localKeyClient{ks: ks, logger: logger}
 }
 
-type signer struct {
+type Signer struct {
 	keyClient KeyClient
 	address   crypto.Address
+	publicKey crypto.PublicKey
 }
 
-func (ms *signer) Sign(messsage []byte) (crypto.Signature, error) {
-	signature, err := ms.keyClient.Sign(ms.address, messsage)
+// Creates a AddressableSigner that assumes the address holds an Ed25519 key
+func AddressableSigner(keyClient KeyClient, address crypto.Address) (*Signer, error) {
+	publicKey, err := keyClient.PublicKey(address)
 	if err != nil {
-		return crypto.Signature{}, err
+		return nil, err
 	}
-	return signature, nil
-}
-
-// Creates a Signer that assumes the address holds an Ed25519 key
-func Signer(keyClient KeyClient, address crypto.Address) crypto.Signer {
 	// TODO: we can do better than this and return a typed signature when we reform the keys service
-	return &signer{
+	return &Signer{
 		keyClient: keyClient,
 		address:   address,
-	}
-}
-
-type keyAddressable struct {
-	publicKey crypto.PublicKey
-	address   crypto.Address
+		publicKey: publicKey,
+	}, nil
 }
 
-func (ka *keyAddressable) Address() crypto.Address {
-	return ka.address
+func (ms *Signer) Address() crypto.Address {
+	return ms.address
 }
 
-func (ka *keyAddressable) PublicKey() crypto.PublicKey {
-	return ka.publicKey
+func (ms *Signer) PublicKey() crypto.PublicKey {
+	return ms.publicKey
 }
 
-func Addressable(keyClient KeyClient, address crypto.Address) (acm.Addressable, error) {
-	pubKey, err := keyClient.PublicKey(address)
+func (ms *Signer) Sign(messsage []byte) (crypto.Signature, error) {
+	signature, err := ms.keyClient.Sign(ms.address, messsage)
 	if err != nil {
-		return nil, err
+		return crypto.Signature{}, err
 	}
-	return &keyAddressable{
-		address:   address,
-		publicKey: pubKey,
-	}, nil
+	return signature, nil
 }
diff --git a/rpc/result.go b/rpc/result.go
index 63e05cf127b71705a87ff325c4efd19f6ff09f9f..8fd7a96fa883cb46028d34236b2519b7ecacd3d7 100644
--- a/rpc/result.go
+++ b/rpc/result.go
@@ -201,7 +201,7 @@ func (rbt ResultBroadcastTx) UnmarshalJSON(data []byte) (err error) {
 
 type ResultListUnconfirmedTxs struct {
 	NumTxs int
-	Txs    []txs.Wrapper
+	Txs    []*txs.Envelope
 }
 
 type ResultGetName struct {
@@ -213,7 +213,7 @@ type ResultGenesis struct {
 }
 
 type ResultSignTx struct {
-	Tx txs.Wrapper
+	Tx *txs.Envelope
 }
 
 type TendermintEvent struct {
diff --git a/rpc/result_test.go b/rpc/result_test.go
index 2aa750ba26cb4300a7a07d210d41c8074cc37d60..9a4b37f3bebd580da855d7b69750c0b3a507b54b 100644
--- a/rpc/result_test.go
+++ b/rpc/result_test.go
@@ -24,6 +24,7 @@ import (
 	"github.com/hyperledger/burrow/crypto"
 	"github.com/hyperledger/burrow/execution"
 	"github.com/hyperledger/burrow/txs"
+	"github.com/hyperledger/burrow/txs/payload"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"github.com/tendermint/go-wire"
@@ -44,24 +45,20 @@ func TestResultBroadcastTx(t *testing.T) {
 
 	js := string(wire.JSONBytes(res))
 	assert.Equal(t, `{"Receipt":{"TxHash":"666F6F","CreatesContract":true,"ContractAddress":"0002030000000000000000000000000000000000"}}`, js)
-
-	res2 := new(ResultBroadcastTx)
-	wire.ReadBinaryBytes(wire.BinaryBytes(res), res2)
-	assert.Equal(t, res, *res2)
 }
 
 func TestListUnconfirmedTxs(t *testing.T) {
 	res := &ResultListUnconfirmedTxs{
 		NumTxs: 3,
-		Txs: []txs.Wrapper{
-			txs.Wrap(&txs.CallTx{
+		Txs: []*txs.Envelope{
+			txs.Enclose("testChain", &payload.CallTx{
 				Address: &crypto.Address{1},
 			}),
 		},
 	}
 	bs, err := json.Marshal(res)
 	require.NoError(t, err)
-	assert.Equal(t, `{"NumTxs":3,"Txs":[{"type":"call_tx","data":{"Input":null,"Address":"0100000000000000000000000000000000000000","GasLimit":0,"Fee":0,"Data":null}}]}`,
+	assert.Equal(t, "{\"NumTxs\":3,\"Txs\":[{\"Signatories\":null,\"Tx\":{\"ChainID\":\"testChain\",\"Type\":\"CallTx\",\"Payload\":{\"Input\":null,\"Address\":\"0100000000000000000000000000000000000000\",\"GasLimit\":0,\"Fee\":0,\"Data\":null}}}]}",
 		string(bs))
 }
 
diff --git a/rpc/service.go b/rpc/service.go
index 3a40c9cbdf4ce1a7c5c1fb5f6057ea81d920fede..7348118ecb244fd990b0653fc01644945f4b1653 100644
--- a/rpc/service.go
+++ b/rpc/service.go
@@ -48,14 +48,14 @@ type Service struct {
 	nameReg         execution.NameRegIterable
 	mempoolAccounts *execution.Accounts
 	subscribable    event.Subscribable
-	blockchain      bcm.Blockchain
+	blockchain      *bcm.Blockchain
 	transactor      *execution.Transactor
 	nodeView        *query.NodeView
 	logger          *logging.Logger
 }
 
 func NewService(ctx context.Context, state state.Iterable, nameReg execution.NameRegIterable,
-	checker state.Reader, subscribable event.Subscribable, blockchain bcm.Blockchain, keyClient keys.KeyClient,
+	checker state.Reader, subscribable event.Subscribable, blockchain *bcm.Blockchain, keyClient keys.KeyClient,
 	transactor *execution.Transactor, nodeView *query.NodeView, logger *logging.Logger) *Service {
 
 	return &Service{
@@ -102,15 +102,19 @@ func (s *Service) State() state.Reader {
 	return s.state
 }
 
+func (s *Service) BlockchainInfo() bcm.BlockchainInfo {
+	return s.blockchain
+}
+
 func (s *Service) ListUnconfirmedTxs(maxTxs int) (*ResultListUnconfirmedTxs, error) {
 	// Get all transactions for now
 	transactions, err := s.nodeView.MempoolTransactions(maxTxs)
 	if err != nil {
 		return nil, err
 	}
-	wrappedTxs := make([]txs.Wrapper, len(transactions))
+	wrappedTxs := make([]*txs.Envelope, len(transactions))
 	for i, tx := range transactions {
-		wrappedTxs[i] = txs.Wrap(tx)
+		wrappedTxs[i] = tx
 	}
 	return &ResultListUnconfirmedTxs{
 		NumTxs: len(transactions),
@@ -151,7 +155,7 @@ func (s *Service) Unsubscribe(ctx context.Context, subscriptionID string) error
 }
 
 func (s *Service) Status() (*ResultStatus, error) {
-	tip := s.blockchain.Tip()
+	tip := s.blockchain.Tip
 	latestHeight := tip.LastBlockHeight()
 	var (
 		latestBlockMeta *tm_types.BlockMeta
@@ -178,7 +182,7 @@ func (s *Service) Status() (*ResultStatus, error) {
 	}, nil
 }
 
-func (s *Service) ChainId() (*ResultChainId, error) {
+func (s *Service) ChainIdentifiers() (*ResultChainId, error) {
 	return &ResultChainId{
 		ChainName:   s.blockchain.GenesisDoc().ChainName,
 		ChainId:     s.blockchain.ChainID(),
@@ -241,7 +245,7 @@ func (s *Service) ListAccounts(predicate func(acm.Account) bool) (*ResultListAcc
 	})
 
 	return &ResultListAccounts{
-		BlockHeight: s.blockchain.Tip().LastBlockHeight(),
+		BlockHeight: s.blockchain.Tip.LastBlockHeight(),
 		Accounts:    accounts,
 	}, nil
 }
@@ -335,7 +339,7 @@ func (s *Service) ListNames(predicate func(*execution.NameRegEntry) bool) (*Resu
 		return
 	})
 	return &ResultListNames{
-		BlockHeight: s.blockchain.Tip().LastBlockHeight(),
+		BlockHeight: s.blockchain.Tip.LastBlockHeight(),
 		Names:       names,
 	}, nil
 }
@@ -353,7 +357,7 @@ func (s *Service) GetBlock(height uint64) (*ResultGetBlock, error) {
 // Passing 0 for maxHeight sets the upper height of the range to the current
 // blockchain height.
 func (s *Service) ListBlocks(minHeight, maxHeight uint64) (*ResultListBlocks, error) {
-	latestHeight := s.blockchain.Tip().LastBlockHeight()
+	latestHeight := s.blockchain.Tip.LastBlockHeight()
 
 	if minHeight == 0 {
 		minHeight = 1
@@ -378,15 +382,17 @@ func (s *Service) ListBlocks(minHeight, maxHeight uint64) (*ResultListBlocks, er
 }
 
 func (s *Service) ListValidators() (*ResultListValidators, error) {
-	// TODO: when we reintroduce support for bonding and unbonding update this
-	// to reflect the mutable bonding state
-	validators := s.blockchain.Validators()
-	concreteValidators := make([]*acm.ConcreteValidator, len(validators))
-	for i, validator := range validators {
-		concreteValidators[i] = acm.AsConcreteValidator(validator)
-	}
+	concreteValidators := make([]*acm.ConcreteValidator, 0, s.blockchain.NumValidators())
+	s.blockchain.IterateValidators(func(publicKey crypto.PublicKey, power uint64) (stop bool) {
+		concreteValidators = append(concreteValidators, &acm.ConcreteValidator{
+			Address:   publicKey.Address(),
+			PublicKey: publicKey,
+			Power:     power,
+		})
+		return
+	})
 	return &ResultListValidators{
-		BlockHeight:         s.blockchain.Tip().LastBlockHeight(),
+		BlockHeight:         s.blockchain.Tip.LastBlockHeight(),
 		BondedValidators:    concreteValidators,
 		UnbondingValidators: nil,
 	}, nil
diff --git a/rpc/tm/client/client.go b/rpc/tm/client/client.go
index 773f34436402623306fe993425a245cb2f02c4af..8dbdc716799e55e8ffc35dcf537f86e91abc257c 100644
--- a/rpc/tm/client/client.go
+++ b/rpc/tm/client/client.go
@@ -30,9 +30,9 @@ type RPCClient interface {
 	Call(method string, params map[string]interface{}, result interface{}) (interface{}, error)
 }
 
-func BroadcastTx(client RPCClient, tx txs.Tx) (*txs.Receipt, error) {
+func BroadcastTx(client RPCClient, txEnv *txs.Envelope) (*txs.Receipt, error) {
 	res := new(txs.Receipt)
-	_, err := client.Call(tm.BroadcastTx, pmap("tx", txs.Wrap(tx)), res)
+	_, err := client.Call(tm.BroadcastTx, pmap("tx", txEnv), res)
 	if err != nil {
 		return nil, err
 	}
@@ -79,7 +79,7 @@ func GetAccount(client RPCClient, address crypto.Address) (acm.Account, error) {
 	return concreteAccount.Account(), nil
 }
 
-func SignTx(client RPCClient, tx txs.Tx, privAccounts []*acm.ConcretePrivateAccount) (txs.Tx, error) {
+func SignTx(client RPCClient, tx txs.Tx, privAccounts []*acm.ConcretePrivateAccount) (*txs.Envelope, error) {
 	res := new(rpc.ResultSignTx)
 	_, err := client.Call(tm.SignTx, pmap("tx", tx, "privAccounts", privAccounts), res)
 	if err != nil {
diff --git a/rpc/tm/integration/client_test.go b/rpc/tm/integration/client_test.go
index 87bb496acb1ef048f62e027b6c91a84b915f5570..260cb82926a763d7ef00466e8352bf1c608589dc 100644
--- a/rpc/tm/integration/client_test.go
+++ b/rpc/tm/integration/client_test.go
@@ -27,9 +27,11 @@ import (
 
 	"github.com/hyperledger/burrow/binary"
 	exe_events "github.com/hyperledger/burrow/execution/events"
+	"github.com/hyperledger/burrow/execution/names"
 	"github.com/hyperledger/burrow/rpc"
 	tm_client "github.com/hyperledger/burrow/rpc/tm/client"
 	"github.com/hyperledger/burrow/txs"
+	"github.com/hyperledger/burrow/txs/payload"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"github.com/tendermint/tendermint/consensus/types"
@@ -56,17 +58,15 @@ func TestBroadcastTx(t *testing.T) {
 		// Avoid duplicate Tx in mempool
 		amt := hashString(clientName) % 1000
 		toAddr := privateAccounts[1].Address()
-		tx := makeDefaultSendTxSigned(t, client, toAddr, amt)
-		receipt, err := broadcastTxAndWait(t, client, tx)
+		txEnv := makeDefaultSendTxSigned(t, client, toAddr, amt)
+		receipt, err := broadcastTxAndWait(t, client, txEnv)
 		require.NoError(t, err)
 		assert.False(t, receipt.CreatesContract, "This tx should not create a contract")
 		assert.NotEmpty(t, receipt.TxHash, "Failed to compute tx hash")
 
-		buf, n, errp := new(bytes.Buffer), new(int), new(error)
 		hasher := ripemd160.New()
-		tx.WriteSignBytes(genesisDoc.ChainID(), buf, n, errp)
-		assert.NoError(t, *errp)
-		txSignBytes := buf.Bytes()
+		txSignBytes, err := txEnv.Tx.SignBytes()
+		require.NoError(t, err)
 		hasher.Write(txSignBytes)
 		txHashExpected := hasher.Sum(nil)
 
@@ -182,7 +182,7 @@ func TestNameReg(t *testing.T) {
 	wsc := newWSClient()
 	defer stopWSClient(wsc)
 	testWithAllClients(t, func(t *testing.T, clientName string, client tm_client.RPCClient) {
-		txs.MinNameRegistrationPeriod = 1
+		names.MinNameRegistrationPeriod = 1
 
 		// register a new name, check if its there
 		// since entries ought to be unique and these run against different clients, we append the client
@@ -190,19 +190,19 @@ func TestNameReg(t *testing.T) {
 		const data = "if not now, when"
 		fee := uint64(1000)
 		numDesiredBlocks := uint64(2)
-		amt := fee + numDesiredBlocks*txs.NameByteCostMultiplier*txs.NameBlockCostMultiplier*txs.NameBaseCost(name, data)
+		amt := fee + numDesiredBlocks*names.NameByteCostMultiplier*names.NameBlockCostMultiplier*names.NameBaseCost(name, data)
 
-		tx := makeDefaultNameTx(t, client, name, data, amt, fee)
+		txEnv := makeDefaultNameTx(t, client, name, data, amt, fee)
 		// verify the name by both using the event and by checking get_name
 		subscribeAndWaitForNext(t, wsc, exe_events.EventStringNameReg(name),
 			func() {
-				broadcastTx(t, client, tx)
+				broadcastTx(t, client, txEnv)
 			},
 			func(eventID string, resultEvent *rpc.ResultEvent) (bool, error) {
 
 				eventDataTx := resultEvent.EventDataTx
 				assert.NotNil(t, eventDataTx, "could not convert %s to EventDataTx", resultEvent)
-				tx, ok := eventDataTx.Tx.(*txs.NameTx)
+				tx, ok := eventDataTx.Tx.Payload.(*payload.NameTx)
 				if !ok {
 					t.Fatalf("Could not convert %v to *NameTx", eventDataTx)
 				}
@@ -220,20 +220,21 @@ func TestNameReg(t *testing.T) {
 		const updatedData = "these are amongst the things I wish to bestow upon " +
 			"the youth of generations come: a safe supply of honey, and a better " +
 			"money. For what else shall they need"
-		amt = fee + numDesiredBlocks*txs.NameByteCostMultiplier*
-			txs.NameBlockCostMultiplier*txs.NameBaseCost(name, updatedData)
-		tx = makeDefaultNameTx(t, client, name, updatedData, amt, fee)
-		broadcastTxAndWait(t, client, tx)
+		amt = fee + numDesiredBlocks*names.NameByteCostMultiplier*
+			names.NameBlockCostMultiplier*names.NameBaseCost(name, updatedData)
+		txEnv = makeDefaultNameTx(t, client, name, updatedData, amt, fee)
+		broadcastTxAndWait(t, client, txEnv)
 		entry = getNameRegEntry(t, client, name)
 
 		assert.Equal(t, updatedData, entry.Data)
 
 		// try to update as non owner, should fail
-		tx = txs.NewNameTxWithSequence(privateAccounts[1].PublicKey(), name, "never mind", amt, fee,
+		tx := payload.NewNameTxWithSequence(privateAccounts[1].PublicKey(), name, "never mind", amt, fee,
 			getSequence(t, client, privateAccounts[1].Address())+1)
-		tx.Sign(genesisDoc.ChainID(), privateAccounts[1])
+		txEnv = txs.Enclose(genesisDoc.ChainID(), tx)
+		require.NoError(t, txEnv.Sign(privateAccounts[1]))
 
-		_, err := tm_client.BroadcastTx(client, tx)
+		_, err := tm_client.BroadcastTx(client, txEnv)
 		assert.Error(t, err, "Expected error when updating someone else's unexpired"+
 			" name registry entry")
 		if err != nil {
@@ -246,14 +247,15 @@ func TestNameReg(t *testing.T) {
 
 		//now the entry should be expired, so we can update as non owner
 		const data2 = "this is not my beautiful house"
-		tx = txs.NewNameTxWithSequence(privateAccounts[1].PublicKey(), name, data2, amt, fee,
+		tx = payload.NewNameTxWithSequence(privateAccounts[1].PublicKey(), name, data2, amt, fee,
 			getSequence(t, client, privateAccounts[1].Address())+1)
-		tx.Sign(genesisDoc.ChainID(), privateAccounts[1])
+		txEnv = txs.Enclose(genesisDoc.ChainID(), tx)
+		require.NoError(t, txEnv.Sign(privateAccounts[1]))
 
 		//_, err = tm_client.BroadcastTx(client, tx)
 		require.NoError(t, subscribeAndWaitForNext(t, wsc, exe_events.EventStringNameReg(name),
 			func() {
-				_, err = tm_client.BroadcastTx(client, tx)
+				_, err = tm_client.BroadcastTx(client, txEnv)
 				assert.NoError(t, err, "Should be able to update a previously expired name"+
 					" registry entry as a different address")
 			},
@@ -322,8 +324,8 @@ func TestListUnconfirmedTxs(t *testing.T) {
 		amt, gasLim, fee := uint64(1100), uint64(1000), uint64(1000)
 		code := []byte{0x60, 0x5, 0x60, 0x1, 0x55}
 		// Call with nil address will create a contract
-		tx := txs.Wrap(makeDefaultCallTx(t, client, nil, code, amt, gasLim, fee))
-		txChan := make(chan []txs.Wrapper)
+		txEnv := makeDefaultCallTx(t, client, nil, code, amt, gasLim, fee)
+		txChan := make(chan []*txs.Envelope)
 
 		// We want to catch the Tx in mempool before it gets reaped by tendermint
 		// consensus. We should be able to do this almost always if we broadcast our
@@ -346,14 +348,14 @@ func TestListUnconfirmedTxs(t *testing.T) {
 		}()
 
 		require.NoError(t, runThenWaitForBlock(t, wsc, nextBlockPredicateFn(), func() {
-			broadcastTx(t, client, tx)
+			broadcastTx(t, client, txEnv)
 			select {
 			case <-time.After(time.Second * timeoutSeconds * 10):
 				t.Fatal("Timeout out waiting for unconfirmed transactions to appear")
 			case transactions := <-txChan:
 				assert.Len(t, transactions, 1, "There should only be a single transaction in the "+
 					"mempool during this test (previous txs should have made it into a block)")
-				assert.Contains(t, transactions, tx, "Transaction should be returned by ListUnconfirmedTxs")
+				assert.Contains(t, transactions, txEnv, "Transaction should be returned by ListUnconfirmedTxs")
 			}
 		}))
 	})
diff --git a/rpc/tm/integration/shared.go b/rpc/tm/integration/shared.go
index e7b58d95de50d11d16efa2af153723e81c297268..9939c1e378ae8b652a46b92c0c352904c79c454f 100644
--- a/rpc/tm/integration/shared.go
+++ b/rpc/tm/integration/shared.go
@@ -31,6 +31,7 @@ import (
 	tmClient "github.com/hyperledger/burrow/rpc/tm/client"
 	rpcClient "github.com/hyperledger/burrow/rpc/tm/lib/client"
 	"github.com/hyperledger/burrow/txs"
+	"github.com/hyperledger/burrow/txs/payload"
 	"github.com/stretchr/testify/require"
 )
 
@@ -55,34 +56,35 @@ var (
 //-------------------------------------------------------------------------------
 // some default transaction functions
 
-func makeDefaultSendTx(t *testing.T, client tmClient.RPCClient, addr crypto.Address, amt uint64) *txs.SendTx {
+func makeDefaultSendTx(t *testing.T, client tmClient.RPCClient, addr crypto.Address, amt uint64) *payload.SendTx {
 	sequence := getSequence(t, client, privateAccounts[0].Address())
-	tx := txs.NewSendTx()
+	tx := payload.NewSendTx()
 	tx.AddInputWithSequence(privateAccounts[0].PublicKey(), amt, sequence+1)
 	tx.AddOutput(addr, amt)
 	return tx
 }
 
-func makeDefaultSendTxSigned(t *testing.T, client tmClient.RPCClient, addr crypto.Address, amt uint64) *txs.SendTx {
-	tx := makeDefaultSendTx(t, client, addr, amt)
-	require.NoError(t, tx.Sign(genesisDoc.ChainID(), privateAccounts[0]))
-	return tx
+func makeDefaultSendTxSigned(t *testing.T, client tmClient.RPCClient, addr crypto.Address, amt uint64) *txs.Envelope {
+	txEnv := txs.Enclose(genesisDoc.ChainID(), makeDefaultSendTx(t, client, addr, amt))
+	require.NoError(t, txEnv.Sign(privateAccounts[0]))
+	return txEnv
 }
 
 func makeDefaultCallTx(t *testing.T, client tmClient.RPCClient, addr *crypto.Address, code []byte, amt, gasLim,
-	fee uint64) *txs.CallTx {
+	fee uint64) *txs.Envelope {
 	sequence := getSequence(t, client, privateAccounts[0].Address())
-	tx := txs.NewCallTxWithSequence(privateAccounts[0].PublicKey(), addr, code, amt, gasLim, fee,
-		sequence+1)
-	require.NoError(t, tx.Sign(genesisDoc.ChainID(), privateAccounts[0]))
-	return tx
+	tx := payload.NewCallTxWithSequence(privateAccounts[0].PublicKey(), addr, code, amt, gasLim, fee, sequence+1)
+	txEnv := txs.Enclose(genesisDoc.ChainID(), tx)
+	require.NoError(t, txEnv.Sign(privateAccounts[0]))
+	return txEnv
 }
 
-func makeDefaultNameTx(t *testing.T, client tmClient.RPCClient, name, value string, amt, fee uint64) *txs.NameTx {
+func makeDefaultNameTx(t *testing.T, client tmClient.RPCClient, name, value string, amt, fee uint64) *txs.Envelope {
 	sequence := getSequence(t, client, privateAccounts[0].Address())
-	tx := txs.NewNameTxWithSequence(privateAccounts[0].PublicKey(), name, value, amt, fee, sequence+1)
-	require.NoError(t, tx.Sign(genesisDoc.ChainID(), privateAccounts[0]))
-	return tx
+	tx := payload.NewNameTxWithSequence(privateAccounts[0].PublicKey(), name, value, amt, fee, sequence+1)
+	txEnv := txs.Enclose(genesisDoc.ChainID(), tx)
+	require.NoError(t, txEnv.Sign(privateAccounts[0]))
+	return txEnv
 }
 
 //-------------------------------------------------------------------------------
@@ -111,7 +113,7 @@ func getAccount(t *testing.T, client tmClient.RPCClient, addr crypto.Address) ac
 
 // sign transaction
 func signTx(t *testing.T, client tmClient.RPCClient, tx txs.Tx,
-	privAcc *acm.ConcretePrivateAccount) txs.Tx {
+	privAcc *acm.ConcretePrivateAccount) *txs.Envelope {
 	signedTx, err := tmClient.SignTx(client, tx, []*acm.ConcretePrivateAccount{privAcc})
 	if err != nil {
 		t.Fatal(err)
@@ -120,8 +122,8 @@ func signTx(t *testing.T, client tmClient.RPCClient, tx txs.Tx,
 }
 
 // broadcast transaction
-func broadcastTx(t *testing.T, client tmClient.RPCClient, tx txs.Tx) *txs.Receipt {
-	rec, err := tmClient.BroadcastTx(client, tx)
+func broadcastTx(t *testing.T, client tmClient.RPCClient, txEnv *txs.Envelope) *txs.Receipt {
+	rec, err := tmClient.BroadcastTx(client, txEnv)
 	require.NoError(t, err)
 	return rec
 }
diff --git a/rpc/tm/integration/websocket_client_test.go b/rpc/tm/integration/websocket_client_test.go
index 9ff32410da0f6a27a433147c6044ba59fd4248bd..19ab475388e08153cc6049692e550519c25aadac 100644
--- a/rpc/tm/integration/websocket_client_test.go
+++ b/rpc/tm/integration/websocket_client_test.go
@@ -29,6 +29,7 @@ import (
 	"github.com/hyperledger/burrow/rpc"
 	tm_client "github.com/hyperledger/burrow/rpc/tm/client"
 	"github.com/hyperledger/burrow/txs"
+	"github.com/hyperledger/burrow/txs/payload"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	tm_types "github.com/tendermint/tendermint/types"
@@ -210,8 +211,8 @@ func TestWSCallNoWait(t *testing.T) {
 	code, _, returnVal := simpleContract()
 
 	sequence := getSequence(t, jsonRpcClient, privateAccounts[0].Address())
-	tx := makeDefaultCallTx(t, jsonRpcClient, nil, code, amt, gasLim, fee)
-	receipt := broadcastTx(t, jsonRpcClient, tx)
+	txEnv := makeDefaultCallTx(t, jsonRpcClient, nil, code, amt, gasLim, fee)
+	receipt := broadcastTx(t, jsonRpcClient, txEnv)
 	contractAddr := receipt.ContractAddress
 
 	// susbscribe to the new contract
@@ -222,10 +223,11 @@ func TestWSCallNoWait(t *testing.T) {
 
 	data := []byte{0x1}
 	waitForEvent(t, wsc, eid, func() {
-		tx = txs.NewCallTxWithSequence(privateAccounts[0].PublicKey(), &contractAddr, data, amt, gasLim, fee,
+		tx := payload.NewCallTxWithSequence(privateAccounts[0].PublicKey(), &contractAddr, data, amt, gasLim, fee,
 			sequence+3)
-		require.NoError(t, tx.Sign(genesisDoc.ChainID(), privateAccounts[0]))
-		broadcastTx(t, jsonRpcClient, tx)
+		txEnv = txs.Enclose(genesisDoc.ChainID(), tx)
+		require.NoError(t, txEnv.Sign(privateAccounts[0]))
+		broadcastTx(t, jsonRpcClient, txEnv)
 	}, unmarshalValidateTx(amt, returnVal))
 }
 
@@ -241,21 +243,21 @@ func TestWSCallCall(t *testing.T) {
 	TxHash := new([]byte)
 
 	// deploy the two contracts
-	tx := makeDefaultCallTx(t, jsonRpcClient, nil, code, amt, gasLim, fee)
-	receipt := txs.GenerateReceipt(genesisDoc.ChainID(), tx)
+	txEnv := makeDefaultCallTx(t, jsonRpcClient, nil, code, amt, gasLim, fee)
+	receipt := txEnv.Tx.GenerateReceipt()
 	contractAddr1 := receipt.ContractAddress
 	// subscribe to the new contracts
 	eid := evm_events.EventStringAccountCall(contractAddr1)
 	subId := subscribeAndGetSubscriptionId(t, wsc, eid)
 	defer unsubscribe(t, wsc, subId)
 
-	_, err := broadcastTxAndWait(t, jsonRpcClient, tx)
+	_, err := broadcastTxAndWait(t, jsonRpcClient, txEnv)
 	require.NoError(t, err)
 
 	// call contract2, which should call contract1, and wait for ev1
 	code, _, _ = simpleCallContract(contractAddr1)
-	tx = makeDefaultCallTx(t, jsonRpcClient, nil, code, amt, gasLim, fee)
-	receipt2, err := broadcastTxAndWait(t, jsonRpcClient, tx)
+	txEnv = makeDefaultCallTx(t, jsonRpcClient, nil, code, amt, gasLim, fee)
+	receipt2, err := broadcastTxAndWait(t, jsonRpcClient, txEnv)
 	require.NoError(t, err)
 	contractAddr2 := receipt2.ContractAddress
 
@@ -272,9 +274,9 @@ func TestWSCallCall(t *testing.T) {
 	waitForEvent(t, wsc, eid,
 		// Runner
 		func() {
-			tx := makeDefaultCallTx(t, jsonRpcClient, &contractAddr2, nil, amt, gasLim, fee)
-			broadcastTx(t, jsonRpcClient, tx)
-			*TxHash = tx.Hash(genesisDoc.ChainID())
+			txEnv := makeDefaultCallTx(t, jsonRpcClient, &contractAddr2, nil, amt, gasLim, fee)
+			broadcastTx(t, jsonRpcClient, txEnv)
+			*TxHash = txEnv.Tx.Hash()
 		},
 		// Event checker
 		unmarshalValidateCall(privateAccounts[0].Address(), returnVal, TxHash))
diff --git a/rpc/tm/integration/websocket_helpers.go b/rpc/tm/integration/websocket_helpers.go
index 4397010b8158ef5aa470026bd94478f06649e57e..0844f0e78211e96674917c12eefda230f98cf165 100644
--- a/rpc/tm/integration/websocket_helpers.go
+++ b/rpc/tm/integration/websocket_helpers.go
@@ -32,6 +32,7 @@ import (
 	rpcClient "github.com/hyperledger/burrow/rpc/tm/lib/client"
 	rpcTypes "github.com/hyperledger/burrow/rpc/tm/lib/types"
 	"github.com/hyperledger/burrow/txs"
+	"github.com/hyperledger/burrow/txs/payload"
 	"github.com/stretchr/testify/require"
 	tmTypes "github.com/tendermint/tendermint/types"
 )
@@ -97,10 +98,10 @@ func unsubscribe(t *testing.T, wsc *rpcClient.WSClient, subscriptionId string) {
 }
 
 // broadcast transaction and wait for new block
-func broadcastTxAndWait(t *testing.T, client tmClient.RPCClient, tx txs.Tx) (*txs.Receipt, error) {
+func broadcastTxAndWait(t *testing.T, client tmClient.RPCClient, txEnv *txs.Envelope) (*txs.Receipt, error) {
 	wsc := newWSClient()
 	defer stopWSClient(wsc)
-	inputs := tx.GetInputs()
+	inputs := txEnv.Tx.GetInputs()
 	if len(inputs) == 0 {
 		t.Fatalf("cannot broadcastAndWait fot Tx with no inputs")
 	}
@@ -111,7 +112,7 @@ func broadcastTxAndWait(t *testing.T, client tmClient.RPCClient, tx txs.Tx) (*tx
 
 	err = subscribeAndWaitForNext(t, wsc, events.EventStringAccountInput(address),
 		func() {
-			rec, err = tmClient.BroadcastTx(client, tx)
+			rec, err = tmClient.BroadcastTx(client, txEnv)
 		}, func(eventID string, resultEvent *rpc.ResultEvent) (bool, error) {
 			return true, nil
 		})
@@ -256,7 +257,7 @@ func unmarshalValidateSend(amt uint64, toAddr crypto.Address, resultEvent *rpc.R
 	if data.Exception != "" {
 		return fmt.Errorf(data.Exception)
 	}
-	tx := data.Tx.(*txs.SendTx)
+	tx := data.Tx.Payload.(*payload.SendTx)
 	if tx.Inputs[0].Address != privateAccounts[0].Address() {
 		return fmt.Errorf("senders do not match up! Got %s, expected %s", tx.Inputs[0].Address,
 			privateAccounts[0].Address())
@@ -280,7 +281,7 @@ func unmarshalValidateTx(amt uint64, returnCode []byte) resultEventChecker {
 		if data.Exception != "" {
 			return true, fmt.Errorf(data.Exception)
 		}
-		tx := data.Tx.(*txs.CallTx)
+		tx := data.Tx.Payload.(*payload.CallTx)
 		if tx.Input.Address != privateAccounts[0].Address() {
 			return true, fmt.Errorf("senders do not match up! Got %s, expected %s",
 				tx.Input.Address, privateAccounts[0].Address())
diff --git a/rpc/tm/methods.go b/rpc/tm/methods.go
index 987d4893ac5426d49a6f6c185692fd1fdb8a60b0..12807a640cde692828be707ce505fad760e14c18 100644
--- a/rpc/tm/methods.go
+++ b/rpc/tm/methods.go
@@ -63,8 +63,8 @@ func GetRoutes(service *rpc.Service, logger *logging.Logger) map[string]*server.
 	logger = logger.WithScope("GetRoutes")
 	return map[string]*server.RPCFunc{
 		// Transact
-		BroadcastTx: server.NewRPCFunc(func(tx txs.Wrapper) (*rpc.ResultBroadcastTx, error) {
-			receipt, err := service.Transactor().BroadcastTx(tx.Unwrap())
+		BroadcastTx: server.NewRPCFunc(func(txEnv *txs.Envelope) (*rpc.ResultBroadcastTx, error) {
+			receipt, err := service.Transactor().BroadcastTx(txEnv)
 			if err != nil {
 				return nil, err
 			}
@@ -73,9 +73,9 @@ func GetRoutes(service *rpc.Service, logger *logging.Logger) map[string]*server.
 			}, nil
 		}, "tx"),
 
-		SignTx: server.NewRPCFunc(func(tx txs.Tx, concretePrivateAccounts []*acm.ConcretePrivateAccount) (*rpc.ResultSignTx, error) {
-			tx, err := service.Transactor().SignTx(tx, acm.SigningAccounts(concretePrivateAccounts))
-			return &rpc.ResultSignTx{Tx: txs.Wrap(tx)}, err
+		SignTx: server.NewRPCFunc(func(txEnv *txs.Envelope, concretePrivateAccounts []*acm.ConcretePrivateAccount) (*rpc.ResultSignTx, error) {
+			txEnv, err := service.Transactor().SignTx(txEnv, acm.SigningAccounts(concretePrivateAccounts))
+			return &rpc.ResultSignTx{Tx: txEnv}, err
 
 		}, "tx,privAccounts"),
 
@@ -156,7 +156,7 @@ func GetRoutes(service *rpc.Service, logger *logging.Logger) map[string]*server.
 
 		// Blockchain
 		Genesis:    server.NewRPCFunc(service.Genesis, ""),
-		ChainID:    server.NewRPCFunc(service.ChainId, ""),
+		ChainID:    server.NewRPCFunc(service.ChainIdentifiers, ""),
 		ListBlocks: server.NewRPCFunc(service.ListBlocks, "minHeight,maxHeight"),
 		GetBlock:   server.NewRPCFunc(service.GetBlock, "height"),
 
diff --git a/rpc/v0/json_service_data_test.go b/rpc/v0/json_service_data_test.go
index bd809698da00a3f7d923c89336d44c5d5ed107bc..9d65bd052d5f02975723cf655d937445dd272467 100644
--- a/rpc/v0/json_service_data_test.go
+++ b/rpc/v0/json_service_data_test.go
@@ -19,8 +19,7 @@ import (
 	"testing"
 
 	"github.com/hyperledger/burrow/rpc"
-	"github.com/hyperledger/burrow/txs"
-
+	"github.com/hyperledger/burrow/txs/payload"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -58,7 +57,7 @@ var testBroadcastCallTxJsonRequest = []byte(`
 // (which was broken on v0.12)
 func TestCallTxJsonFormatCodec(t *testing.T) {
 	codec := NewTCodec()
-	param := new(txs.Tx)
+	param := new(payload.Payload)
 
 	// Create new request object and unmarshal.
 	request := &rpc.RPCRequest{}
@@ -66,6 +65,6 @@ func TestCallTxJsonFormatCodec(t *testing.T) {
 		"Provided JSON test data does not unmarshal to rpc.RPCRequest object.")
 	assert.NoError(t, codec.DecodeBytesPtr(param, request.Params),
 		"RPC codec failed to decode params as transaction type.")
-	_, ok := (*param).(*txs.CallTx)
+	_, ok := (*param).(*payload.CallTx)
 	assert.True(t, ok, "Type byte 0x02 should unmarshal into CallTx.")
 }
diff --git a/rpc/v0/methods.go b/rpc/v0/methods.go
index 3209b7f36952ac94608742f88236c1cbaa7f0487..26e788dd51ca51fdb117c015d19db8ace1d77f26 100644
--- a/rpc/v0/methods.go
+++ b/rpc/v0/methods.go
@@ -198,12 +198,12 @@ func GetMethods(codec rpc.Codec, service *rpc.Service, logger *logging.Logger) m
 		},
 		BROADCAST_TX: func(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) {
 			// Accept all transaction types as parameter for broadcast.
-			param := new(txs.Tx)
-			err := codec.DecodeBytesPtr(param, request.Params)
+			txEnv := new(txs.Envelope)
+			err := codec.DecodeBytesPtr(txEnv, request.Params)
 			if err != nil {
 				return nil, rpc.INVALID_PARAMS, err
 			}
-			receipt, err := service.Transactor().BroadcastTx(*param)
+			receipt, err := service.Transactor().BroadcastTx(txEnv)
 			if err != nil {
 				return nil, rpc.INTERNAL_ERROR, err
 			}
@@ -215,7 +215,8 @@ func GetMethods(codec rpc.Codec, service *rpc.Service, logger *logging.Logger) m
 			if err != nil {
 				return nil, rpc.INVALID_PARAMS, err
 			}
-			txRet, err := service.Transactor().SignTx(param.Tx, acm.SigningAccounts(param.PrivateAccounts))
+			txRet, err := service.Transactor().SignTx(txs.Enclose(service.BlockchainInfo().ChainID(), param.Tx),
+				acm.SigningAccounts(param.PrivateAccounts))
 			if err != nil {
 				return nil, rpc.INTERNAL_ERROR, err
 			}
@@ -428,7 +429,7 @@ func GetMethods(codec rpc.Codec, service *rpc.Service, logger *logging.Logger) m
 			return resultNetInfo, 0, nil
 		},
 		GET_CHAIN_ID: func(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) {
-			resultChainID, err := service.ChainId()
+			resultChainID, err := service.ChainIdentifiers()
 			if err != nil {
 				return nil, rpc.INTERNAL_ERROR, err
 			}
@@ -454,7 +455,7 @@ func signingAccount(accounts *execution.Accounts, inputAccount InputAccount) (*e
 		if err != nil {
 			return nil, err
 		}
-		return accounts.SequentialSigningAccount(address), nil
+		return accounts.SequentialSigningAccount(address)
 	}
 
 	return accounts.SequentialSigningAccountFromPrivateKey(inputAccount.PrivateKey)
diff --git a/rpc/v0/params.go b/rpc/v0/params.go
index a93f9a06ef0542956b3240637878f33f3fbb57b4..6827dd94d6afe0a5890f0e486e29122b0ee17e73 100644
--- a/rpc/v0/params.go
+++ b/rpc/v0/params.go
@@ -17,9 +17,19 @@ package v0
 import (
 	acm "github.com/hyperledger/burrow/account"
 	"github.com/hyperledger/burrow/rpc/filters"
-	"github.com/hyperledger/burrow/txs"
+	"github.com/hyperledger/burrow/txs/payload"
+	"github.com/tendermint/go-wire/data"
 )
 
+// Legacy for JS
+var _ = data.NewMapper(struct{ payload.Payload }{}).
+	RegisterImplementation(&payload.SendTx{}, "send_tx", byte(payload.TypeSend)).
+	RegisterImplementation(&payload.CallTx{}, "call_tx", byte(payload.TypeCall)).
+	RegisterImplementation(&payload.NameTx{}, "name_tx", byte(payload.TypeName)).
+	RegisterImplementation(&payload.BondTx{}, "bond_tx", byte(payload.TypeBond)).
+	RegisterImplementation(&payload.UnbondTx{}, "unbond_tx", byte(payload.TypeUnbond)).
+	RegisterImplementation(&payload.PermissionsTx{}, "permissions_tx", byte(payload.TypePermissions))
+
 type (
 	// Used to send an address. The address should be hex and properly formatted.
 	AddressParam struct {
@@ -86,7 +96,7 @@ type (
 
 	// Used when signing a tx. Uses placeholders just like TxParam
 	SignTxParam struct {
-		Tx              *txs.CallTx                   `json:"tx"`
+		Tx              *payload.CallTx               `json:"tx"`
 		PrivateAccounts []*acm.ConcretePrivateAccount `json:"privateAccounts"`
 	}
 
diff --git a/txs/bond_tx.go b/txs/bond_tx.go
deleted file mode 100644
index a7100ddf86f4094d5313f5288da10e5e63131d24..0000000000000000000000000000000000000000
--- a/txs/bond_tx.go
+++ /dev/null
@@ -1,112 +0,0 @@
-package txs
-
-import (
-	"fmt"
-	"io"
-
-	acm "github.com/hyperledger/burrow/account"
-	"github.com/hyperledger/burrow/account/state"
-	"github.com/hyperledger/burrow/crypto"
-	"github.com/tendermint/go-wire"
-)
-
-type BondTx struct {
-	PubKey    crypto.PublicKey
-	Signature crypto.Signature
-	Inputs    []*TxInput
-	UnbondTo  []*TxOutput
-	txHashMemoizer
-}
-
-var _ Tx = &BondTx{}
-
-func NewBondTx(pubkey crypto.PublicKey) (*BondTx, error) {
-	return &BondTx{
-		PubKey:   pubkey,
-		Inputs:   []*TxInput{},
-		UnbondTo: []*TxOutput{},
-	}, nil
-}
-
-func (tx *BondTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
-	wire.WriteTo([]byte(fmt.Sprintf(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
-	wire.WriteTo([]byte(fmt.Sprintf(`,"tx":[%v,{"inputs":[`, TxTypeBond)), w, n, err)
-	for i, in := range tx.Inputs {
-		in.WriteSignBytes(w, n, err)
-		if i != len(tx.Inputs)-1 {
-			wire.WriteTo([]byte(","), w, n, err)
-		}
-	}
-	wire.WriteTo([]byte(fmt.Sprintf(`],"pub_key":`)), w, n, err)
-	wire.WriteTo(wire.JSONBytes(tx.PubKey), w, n, err)
-	wire.WriteTo([]byte(`,"unbond_to":[`), w, n, err)
-	for i, out := range tx.UnbondTo {
-		out.WriteSignBytes(w, n, err)
-		if i != len(tx.UnbondTo)-1 {
-			wire.WriteTo([]byte(","), w, n, err)
-		}
-	}
-	wire.WriteTo([]byte(`]}]}`), w, n, err)
-}
-
-func (tx *BondTx) GetInputs() []TxInput {
-	return copyInputs(tx.Inputs)
-}
-
-func (tx *BondTx) String() string {
-	return fmt.Sprintf("BondTx{%v: %v -> %v}", tx.PubKey, tx.Inputs, tx.UnbondTo)
-}
-
-func (tx *BondTx) Hash(chainID string) []byte {
-	return tx.txHashMemoizer.hash(chainID, tx)
-}
-
-func (tx *BondTx) AddInput(st state.AccountGetter, pubkey crypto.PublicKey, amt uint64) error {
-	addr := pubkey.Address()
-	acc, err := st.GetAccount(addr)
-	if err != nil {
-		return err
-	}
-	if acc == nil {
-		return fmt.Errorf("Invalid address %s from pubkey %s", addr, pubkey)
-	}
-	return tx.AddInputWithSequence(pubkey, amt, acc.Sequence()+uint64(1))
-}
-
-func (tx *BondTx) AddInputWithSequence(pubkey crypto.PublicKey, amt uint64, sequence uint64) error {
-	tx.Inputs = append(tx.Inputs, &TxInput{
-		Address:   pubkey.Address(),
-		Amount:    amt,
-		Sequence:  sequence,
-		PublicKey: pubkey,
-	})
-	return nil
-}
-
-func (tx *BondTx) AddOutput(addr crypto.Address, amt uint64) error {
-	tx.UnbondTo = append(tx.UnbondTo, &TxOutput{
-		Address: addr,
-		Amount:  amt,
-	})
-	return nil
-}
-
-func (tx *BondTx) Sign(chainID string, signingAccounts ...acm.AddressableSigner) error {
-	if len(signingAccounts) != len(tx.Inputs)+1 {
-		return fmt.Errorf("BondTx expects %v SigningAccounts but got %v", len(tx.Inputs)+1,
-			len(signingAccounts))
-	}
-	var err error
-	tx.Signature, err = crypto.ChainSign(signingAccounts[0], chainID, tx)
-	if err != nil {
-		return fmt.Errorf("could not sign %v: %v", tx, err)
-	}
-	for i := 1; i <= len(signingAccounts); i++ {
-		tx.Inputs[i].PublicKey = signingAccounts[i].PublicKey()
-		tx.Inputs[i].Signature, err = crypto.ChainSign(signingAccounts[i], chainID, tx)
-		if err != nil {
-			return fmt.Errorf("could not sign tx %v input %v: %v", tx, tx.Inputs[i], err)
-		}
-	}
-	return nil
-}
diff --git a/txs/call_tx.go b/txs/call_tx.go
deleted file mode 100644
index ebfdb6f49f05444c23e56278f9e087af2b788b61..0000000000000000000000000000000000000000
--- a/txs/call_tx.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package txs
-
-import (
-	"fmt"
-	"io"
-
-	acm "github.com/hyperledger/burrow/account"
-	"github.com/hyperledger/burrow/account/state"
-	"github.com/hyperledger/burrow/crypto"
-	"github.com/tendermint/go-wire"
-)
-
-type CallTx struct {
-	Input *TxInput
-	// Pointer since CallTx defines unset 'to' address as inducing account creation
-	Address  *crypto.Address
-	GasLimit uint64
-	Fee      uint64
-	Data     []byte
-	txHashMemoizer
-}
-
-var _ Tx = &CallTx{}
-
-func NewCallTx(st state.AccountGetter, from crypto.PublicKey, to *crypto.Address, data []byte,
-	amt, gasLimit, fee uint64) (*CallTx, error) {
-
-	addr := from.Address()
-	acc, err := st.GetAccount(addr)
-	if err != nil {
-		return nil, err
-	}
-	if acc == nil {
-		return nil, fmt.Errorf("invalid address %s from pubkey %s", addr, from)
-	}
-
-	sequence := acc.Sequence() + 1
-	return NewCallTxWithSequence(from, to, data, amt, gasLimit, fee, sequence), nil
-}
-
-func NewCallTxWithSequence(from crypto.PublicKey, to *crypto.Address, data []byte,
-	amt, gasLimit, fee, sequence uint64) *CallTx {
-	input := &TxInput{
-		Address:   from.Address(),
-		Amount:    amt,
-		Sequence:  sequence,
-		PublicKey: from,
-	}
-
-	return &CallTx{
-		Input:    input,
-		Address:  to,
-		GasLimit: gasLimit,
-		Fee:      fee,
-		Data:     data,
-	}
-}
-
-func (tx *CallTx) Sign(chainID string, signingAccounts ...acm.AddressableSigner) error {
-	if len(signingAccounts) != 1 {
-		return fmt.Errorf("CallTx expects a single AddressableSigner for its single Input but %v were provieded",
-			len(signingAccounts))
-	}
-	var err error
-	tx.Input.PublicKey = signingAccounts[0].PublicKey()
-	tx.Input.Signature, err = crypto.ChainSign(signingAccounts[0], chainID, tx)
-	if err != nil {
-		return fmt.Errorf("could not sign %v: %v", tx, err)
-	}
-	return nil
-}
-
-func (tx *CallTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
-	wire.WriteTo([]byte(fmt.Sprintf(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
-	wire.WriteTo([]byte(fmt.Sprintf(`,"tx":[%v,{"address":"%s","data":"%X"`, TxTypeCall, tx.Address, tx.Data)), w, n, err)
-	wire.WriteTo([]byte(fmt.Sprintf(`,"fee":%v,"gas_limit":%v,"input":`, tx.Fee, tx.GasLimit)), w, n, err)
-	tx.Input.WriteSignBytes(w, n, err)
-	wire.WriteTo([]byte(`}]}`), w, n, err)
-}
-
-func (tx *CallTx) GetInputs() []TxInput {
-	return []TxInput{*tx.Input}
-}
-
-func (tx *CallTx) String() string {
-	return fmt.Sprintf("CallTx{%v -> %s: %X}", tx.Input, tx.Address, tx.Data)
-}
-
-func (tx *CallTx) Hash(chainID string) []byte {
-	return tx.txHashMemoizer.hash(chainID, tx)
-}
diff --git a/txs/envelope.go b/txs/envelope.go
new file mode 100644
index 0000000000000000000000000000000000000000..ca8b618b5116cc9e6eb9d089c4e1f5798afa6717
--- /dev/null
+++ b/txs/envelope.go
@@ -0,0 +1,132 @@
+package txs
+
+import (
+	"fmt"
+
+	acm "github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/account/state"
+	"github.com/hyperledger/burrow/crypto"
+	"github.com/hyperledger/burrow/txs/payload"
+)
+
+type Encoder interface {
+	EncodeTx(envelope *Envelope) ([]byte, error)
+}
+
+type Decoder interface {
+	DecodeTx(txBytes []byte) (*Envelope, error)
+}
+
+// An envelope contains both the signable Tx and the signatures for each input (in signatories)
+type Envelope struct {
+	Signatories []Signatory
+	Tx          *Tx
+}
+
+// Enclose a Payload in an Envelope so it is ready to be signed by first wrapping the Payload
+// as a Tx (including ChainID) and writing it to the Tx field of the Envelope
+func Enclose(chainID string, payload payload.Payload) *Envelope {
+	body := NewTx(payload)
+	body.ChainID = chainID
+	return body.Enclose()
+}
+
+func (txEnv *Envelope) String() string {
+	return fmt.Sprintf("TxEnvelope{Signatures: %v, Tx: %s}", len(txEnv.Signatories), txEnv.Tx)
+}
+
+// Signatory contains signature and one or both of Address and PublicKey to identify the signer
+type Signatory struct {
+	Address   *crypto.Address
+	PublicKey *crypto.PublicKey
+	crypto.Signature
+}
+
+// Attempts to 'realise' the PublicKey and Address of a Signatory possibly referring to state
+// in the case where the Signatory contains an Address by no PublicKey. Checks consistency in other
+// cases, possibly generating the Address from the PublicKey
+func (s *Signatory) RealisePublicKey(getter state.AccountGetter) error {
+	const errPrefix = "could not realise public key for signatory"
+	if s.PublicKey == nil {
+		if s.Address == nil {
+			return fmt.Errorf("%s: address not provided", errPrefix)
+		}
+		acc, err := getter.GetAccount(*s.Address)
+		if err != nil {
+			return fmt.Errorf("%s: could not get account %v: %v", errPrefix, *s.Address, err)
+		}
+		publicKey := acc.PublicKey()
+		s.PublicKey = &publicKey
+	}
+	if !s.PublicKey.IsValid() {
+		return fmt.Errorf("%s: public key %v is invalid", errPrefix, *s.PublicKey)
+	}
+	address := s.PublicKey.Address()
+	if s.Address == nil {
+		s.Address = &address
+	} else if address != *s.Address {
+		return fmt.Errorf("address %v provided with signatory does not match address generated from "+
+			"public key %v", *s.Address, address)
+	}
+	return nil
+}
+
+// Verifies the validity of the Signatories' Signatures in the Envelope. The Signatories must
+// appear in the same order as the inputs as returned by Tx.GetInputs().
+func (txEnv *Envelope) Verify(getter state.AccountGetter) error {
+	errPrefix := fmt.Sprintf("could not verify transaction %X", txEnv.Tx.Hash())
+	inputs := txEnv.Tx.GetInputs()
+	if len(inputs) != len(txEnv.Signatories) {
+		return fmt.Errorf("%s: number of inputs (= %v) should equal number of signatories (= %v)",
+			errPrefix, len(inputs), len(txEnv.Signatories))
+	}
+	signBytes, err := txEnv.Tx.SignBytes()
+	if err != nil {
+		return fmt.Errorf("%s: could not generate SignBytes: %v", errPrefix, err)
+	}
+	// Expect order to match (we could build lookup but we want Verify to be quicker than Sign which does order sigs)
+	for i, s := range txEnv.Signatories {
+		if inputs[i].Address != *s.Address {
+			return fmt.Errorf("signatory %v has address %v but input %v has address %v",
+				i, *s.Address, i, inputs[i].Address)
+		}
+		if !s.PublicKey.Verify(signBytes, s.Signature) {
+			return fmt.Errorf("invalid signature in signatory %v ", *s.Address)
+		}
+	}
+	return nil
+}
+
+// Sign the Tx Envelope by adding Signatories containing the signatures for each TxInput.
+// signing accounts for each input must be provided (in any order).
+func (txEnv *Envelope) Sign(signingAccounts ...acm.AddressableSigner) error {
+	// Clear any existing
+	txEnv.Signatories = nil
+	signBytes, err := txEnv.Tx.SignBytes()
+	if err != nil {
+		return err
+	}
+	signingAccountMap := make(map[crypto.Address]acm.AddressableSigner)
+	for _, sa := range signingAccounts {
+		signingAccountMap[sa.Address()] = sa
+	}
+	// Sign in order of inputs
+	for i, in := range txEnv.Tx.GetInputs() {
+		sa, ok := signingAccountMap[in.Address]
+		if !ok {
+			return fmt.Errorf("account to sign %v (position %v) not passed to Sign, passed: %v", in, i, signingAccounts)
+		}
+		sig, err := sa.Sign(signBytes)
+		if err != nil {
+			return err
+		}
+		address := sa.Address()
+		publicKey := sa.PublicKey()
+		txEnv.Signatories = append(txEnv.Signatories, Signatory{
+			Address:   &address,
+			PublicKey: &publicKey,
+			Signature: sig,
+		})
+	}
+	return nil
+}
diff --git a/txs/go_wire_codec.go b/txs/go_wire_codec.go
deleted file mode 100644
index 320c1f5e956a246fadf16937a3cdedb5142bec6c..0000000000000000000000000000000000000000
--- a/txs/go_wire_codec.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package txs
-
-import (
-	"bytes"
-	"sync"
-
-	"github.com/tendermint/go-wire"
-)
-
-type goWireCodec struct {
-	// Worth it? Possibly not, but we need to instantiate a codec though so...
-	bufferPool sync.Pool
-}
-
-func NewGoWireCodec() *goWireCodec {
-	return &goWireCodec{
-		bufferPool: sync.Pool{
-			New: func() interface{} {
-				return new(bytes.Buffer)
-			},
-		},
-	}
-}
-
-func (gwc *goWireCodec) EncodeTx(tx Tx) ([]byte, error) {
-	var n int
-	var err error
-	buf := gwc.bufferPool.Get().(*bytes.Buffer)
-	defer gwc.recycle(buf)
-	wire.WriteBinary(struct{ Tx }{tx}, buf, &n, &err)
-	if err != nil {
-		return nil, err
-	}
-	// Tendermint mempool exhibits odd concurrency issues when using a mutable buffer
-	return copyBuffer(buf)
-}
-
-// panic on err
-func (gwc *goWireCodec) DecodeTx(txBytes []byte) (Tx, error) {
-	var n int
-	var err error
-	tx := new(Tx)
-	buf := bytes.NewBuffer(txBytes)
-	wire.ReadBinaryPtr(tx, buf, len(txBytes), &n, &err)
-	if err != nil {
-		return nil, err
-	}
-	return *tx, nil
-}
-
-func (gwc *goWireCodec) recycle(buf *bytes.Buffer) {
-	buf.Reset()
-	gwc.bufferPool.Put(buf)
-}
-
-func copyBuffer(buf *bytes.Buffer) ([]byte, error) {
-	bs := make([]byte, buf.Len())
-	_, err := buf.Read(bs)
-	if err != nil {
-		return nil, err
-	}
-	return bs, nil
-}
diff --git a/txs/go_wire_codec_test.go b/txs/go_wire_codec_test.go
deleted file mode 100644
index 4b4dd579ef4d1926d84bdd638ea37b23541f5350..0000000000000000000000000000000000000000
--- a/txs/go_wire_codec_test.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package txs
-
-import (
-	"testing"
-
-	"github.com/hyperledger/burrow/crypto"
-	"github.com/stretchr/testify/assert"
-)
-
-func TestEncodeTxDecodeTx(t *testing.T) {
-	gwc := NewGoWireCodec()
-	inputAddress := crypto.Address{1, 2, 3, 4, 5}
-	outputAddress := crypto.Address{5, 4, 3, 2, 1}
-	amount := uint64(2)
-	sequence := uint64(3)
-	tx := &SendTx{
-		Inputs: []*TxInput{{
-			Address:   inputAddress,
-			Amount:    amount,
-			Sequence:  sequence,
-			PublicKey: crypto.PublicKey{PublicKey: []byte{0}},
-			Signature: crypto.Signature{Signature: []byte{0}},
-		}},
-		Outputs: []*TxOutput{{
-			Address: outputAddress,
-			Amount:  amount,
-		}},
-	}
-	txBytes, err := gwc.EncodeTx(tx)
-	if err != nil {
-		t.Fatal(err)
-	}
-	txOut, err := gwc.DecodeTx(txBytes)
-	assert.NoError(t, err, "DecodeTx error")
-	assert.Equal(t, tx, txOut)
-}
-
-func TestEncodeTxDecodeTx_CallTx(t *testing.T) {
-	gwc := NewGoWireCodec()
-	inputAddress := crypto.Address{1, 2, 3, 4, 5}
-	amount := uint64(2)
-	sequence := uint64(3)
-	tx := &CallTx{
-		Input: &TxInput{
-			Address:   inputAddress,
-			Amount:    amount,
-			Sequence:  sequence,
-			PublicKey: crypto.PublicKey{PublicKey: []byte{0}},
-			Signature: crypto.Signature{Signature: []byte{0}},
-		},
-		GasLimit: 233,
-		Fee:      2,
-		Address:  nil,
-		Data:     []byte("code"),
-	}
-	txBytes, err := gwc.EncodeTx(tx)
-	if err != nil {
-		t.Fatal(err)
-	}
-	txOut, err := gwc.DecodeTx(txBytes)
-	assert.NoError(t, err, "DecodeTx error")
-	assert.Equal(t, tx, txOut)
-}
diff --git a/txs/json_codec.go b/txs/json_codec.go
new file mode 100644
index 0000000000000000000000000000000000000000..ccfbfe8ceb2d0e6e1b96fa8e4963e13f8c0cb9bd
--- /dev/null
+++ b/txs/json_codec.go
@@ -0,0 +1,24 @@
+package txs
+
+import (
+	"encoding/json"
+)
+
+type jsonCodec struct{}
+
+func NewJSONCodec() *jsonCodec {
+	return &jsonCodec{}
+}
+
+func (gwc *jsonCodec) EncodeTx(env *Envelope) ([]byte, error) {
+	return json.Marshal(env)
+}
+
+func (gwc *jsonCodec) DecodeTx(txBytes []byte) (*Envelope, error) {
+	env := new(Envelope)
+	err := json.Unmarshal(txBytes, env)
+	if err != nil {
+		return nil, err
+	}
+	return env, nil
+}
diff --git a/txs/json_codec_test.go b/txs/json_codec_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2aa1d7e3540b04eff56c4af2d63454d72e578f1d
--- /dev/null
+++ b/txs/json_codec_test.go
@@ -0,0 +1,65 @@
+package txs
+
+import (
+	"testing"
+
+	"github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/crypto"
+	"github.com/hyperledger/burrow/txs/payload"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestEncodeTxDecodeTx(t *testing.T) {
+	codec := NewJSONCodec()
+	inputAddress := crypto.Address{1, 2, 3, 4, 5}
+	outputAddress := crypto.Address{5, 4, 3, 2, 1}
+	amount := uint64(2)
+	sequence := uint64(3)
+	tx := &payload.SendTx{
+		Inputs: []*payload.TxInput{{
+			Address:  inputAddress,
+			Amount:   amount,
+			Sequence: sequence,
+		}},
+		Outputs: []*payload.TxOutput{{
+			Address: outputAddress,
+			Amount:  amount,
+		}},
+	}
+	txEnv := Enclose(chainID, tx)
+	txBytes, err := codec.EncodeTx(txEnv)
+	if err != nil {
+		t.Fatal(err)
+	}
+	txEnvOut, err := codec.DecodeTx(txBytes)
+	assert.NoError(t, err, "DecodeTx error")
+	assert.Equal(t, txEnv, txEnvOut)
+}
+
+func TestEncodeTxDecodeTx_CallTx(t *testing.T) {
+	codec := NewJSONCodec()
+	inputAccount := account.GeneratePrivateAccountFromSecret("fooo")
+	amount := uint64(2)
+	sequence := uint64(3)
+	tx := &payload.CallTx{
+		Input: &payload.TxInput{
+			Address:  inputAccount.Address(),
+			Amount:   amount,
+			Sequence: sequence,
+		},
+		GasLimit: 233,
+		Fee:      2,
+		Address:  nil,
+		Data:     []byte("code"),
+	}
+	txEnv := Enclose(chainID, tx)
+	require.NoError(t, txEnv.Sign(inputAccount))
+	txBytes, err := codec.EncodeTx(txEnv)
+	if err != nil {
+		t.Fatal(err)
+	}
+	txEnvOut, err := codec.DecodeTx(txBytes)
+	assert.NoError(t, err, "DecodeTx error")
+	assert.Equal(t, txEnv, txEnvOut)
+}
diff --git a/txs/payload/bond_tx.go b/txs/payload/bond_tx.go
new file mode 100644
index 0000000000000000000000000000000000000000..7752b9411e23c338ae5996f1d3c19d2991f947f0
--- /dev/null
+++ b/txs/payload/bond_tx.go
@@ -0,0 +1,62 @@
+package payload
+
+import (
+	"fmt"
+
+	"github.com/hyperledger/burrow/account/state"
+	"github.com/hyperledger/burrow/crypto"
+)
+
+type BondTx struct {
+	// At least one should have bond permission (even if 0 amount transfer)
+	Inputs   []*TxInput
+	UnbondTo []*TxOutput
+}
+
+func NewBondTx(pubkey crypto.PublicKey) (*BondTx, error) {
+	return &BondTx{
+		Inputs:   []*TxInput{},
+		UnbondTo: []*TxOutput{},
+	}, nil
+}
+
+func (tx *BondTx) Type() Type {
+	return TypeBond
+}
+
+func (tx *BondTx) GetInputs() []*TxInput {
+	return tx.Inputs
+}
+
+func (tx *BondTx) String() string {
+	return fmt.Sprintf("BondTx{%v -> %v}", tx.Inputs, tx.UnbondTo)
+}
+
+func (tx *BondTx) AddInput(st state.AccountGetter, pubkey crypto.PublicKey, amt uint64) error {
+	addr := pubkey.Address()
+	acc, err := st.GetAccount(addr)
+	if err != nil {
+		return err
+	}
+	if acc == nil {
+		return fmt.Errorf("Invalid address %s from pubkey %s", addr, pubkey)
+	}
+	return tx.AddInputWithSequence(pubkey, amt, acc.Sequence()+uint64(1))
+}
+
+func (tx *BondTx) AddInputWithSequence(pubkey crypto.PublicKey, amt uint64, sequence uint64) error {
+	tx.Inputs = append(tx.Inputs, &TxInput{
+		Address:  pubkey.Address(),
+		Amount:   amt,
+		Sequence: sequence,
+	})
+	return nil
+}
+
+func (tx *BondTx) AddOutput(addr crypto.Address, amt uint64) error {
+	tx.UnbondTo = append(tx.UnbondTo, &TxOutput{
+		Address: addr,
+		Amount:  amt,
+	})
+	return nil
+}
diff --git a/txs/payload/call_tx.go b/txs/payload/call_tx.go
new file mode 100644
index 0000000000000000000000000000000000000000..b3c1270bf10da692fb23e3b9238980d67e3ff3c4
--- /dev/null
+++ b/txs/payload/call_tx.go
@@ -0,0 +1,61 @@
+package payload
+
+import (
+	"fmt"
+
+	"github.com/hyperledger/burrow/account/state"
+	"github.com/hyperledger/burrow/crypto"
+)
+
+type CallTx struct {
+	Input *TxInput
+	// Pointer since CallTx defines unset 'to' address as inducing account creation
+	Address  *crypto.Address
+	GasLimit uint64
+	Fee      uint64
+	Data     []byte
+}
+
+func NewCallTx(st state.AccountGetter, from crypto.PublicKey, to *crypto.Address, data []byte,
+	amt, gasLimit, fee uint64) (*CallTx, error) {
+
+	addr := from.Address()
+	acc, err := st.GetAccount(addr)
+	if err != nil {
+		return nil, err
+	}
+	if acc == nil {
+		return nil, fmt.Errorf("invalid address %s from pubkey %s", addr, from)
+	}
+
+	sequence := acc.Sequence() + 1
+	return NewCallTxWithSequence(from, to, data, amt, gasLimit, fee, sequence), nil
+}
+
+func NewCallTxWithSequence(from crypto.PublicKey, to *crypto.Address, data []byte,
+	amt, gasLimit, fee, sequence uint64) *CallTx {
+	input := &TxInput{
+		Address:  from.Address(),
+		Amount:   amt,
+		Sequence: sequence,
+	}
+
+	return &CallTx{
+		Input:    input,
+		Address:  to,
+		GasLimit: gasLimit,
+		Fee:      fee,
+		Data:     data,
+	}
+}
+
+func (tx *CallTx) Type() Type {
+	return TypeCall
+}
+func (tx *CallTx) GetInputs() []*TxInput {
+	return []*TxInput{tx.Input}
+}
+
+func (tx *CallTx) String() string {
+	return fmt.Sprintf("CallTx{%v -> %s: %X}", tx.Input, tx.Address, tx.Data)
+}
diff --git a/txs/payload/errors.go b/txs/payload/errors.go
new file mode 100644
index 0000000000000000000000000000000000000000..93a7f2feed1c4559812770b1ec99232a02eac10f
--- /dev/null
+++ b/txs/payload/errors.go
@@ -0,0 +1,33 @@
+package payload
+
+import (
+	"errors"
+	"fmt"
+)
+
+var (
+	ErrTxInvalidAddress    = errors.New("error invalid address")
+	ErrTxDuplicateAddress  = errors.New("error duplicate address")
+	ErrTxInvalidAmount     = errors.New("error invalid amount")
+	ErrTxInsufficientFunds = errors.New("error insufficient funds")
+	ErrTxUnknownPubKey     = errors.New("error unknown pubkey")
+	ErrTxInvalidPubKey     = errors.New("error invalid pubkey")
+	ErrTxInvalidSignature  = errors.New("error invalid signature")
+)
+
+type ErrTxInvalidString struct {
+	Msg string
+}
+
+func (e ErrTxInvalidString) Error() string {
+	return e.Msg
+}
+
+type ErrTxInvalidSequence struct {
+	Got      uint64
+	Expected uint64
+}
+
+func (e ErrTxInvalidSequence) Error() string {
+	return fmt.Sprintf("Error invalid sequence. Got %d, expected %d", e.Got, e.Expected)
+}
diff --git a/txs/payload/governance_tx.go b/txs/payload/governance_tx.go
new file mode 100644
index 0000000000000000000000000000000000000000..bf145470beb75eb2d29279be677de8dd91d05fa0
--- /dev/null
+++ b/txs/payload/governance_tx.go
@@ -0,0 +1,45 @@
+package payload
+
+import (
+	"fmt"
+
+	"github.com/hyperledger/burrow/account/state"
+	"github.com/hyperledger/burrow/crypto"
+	"github.com/hyperledger/burrow/genesis/spec"
+)
+
+type GovernanceTx struct {
+	Input          *TxInput
+	AccountUpdates []spec.TemplateAccount
+}
+
+func NewGovTx(st state.AccountGetter, from crypto.Address, accounts ...spec.TemplateAccount) (*GovernanceTx, error) {
+	acc, err := st.GetAccount(from)
+	if err != nil {
+		return nil, err
+	}
+	if acc == nil {
+		return nil, fmt.Errorf("could not get account %v", from)
+	}
+
+	sequence := acc.Sequence() + 1
+	return NewGovTxWithSequence(from, sequence, accounts), nil
+}
+
+func NewGovTxWithSequence(from crypto.Address, sequence uint64, accounts []spec.TemplateAccount) *GovernanceTx {
+	return &GovernanceTx{
+		Input: &TxInput{
+			Address:  from,
+			Sequence: sequence,
+		},
+		AccountUpdates: accounts,
+	}
+}
+
+func (tx *GovernanceTx) GetInputs() []*TxInput {
+	return []*TxInput{tx.Input}
+}
+
+func (tx *GovernanceTx) String() string {
+	return fmt.Sprintf("GovernanceTx{%v -> %v}", tx.Input, tx.AccountUpdates)
+}
diff --git a/txs/name_tx.go b/txs/payload/name_tx.go
similarity index 58%
rename from txs/name_tx.go
rename to txs/payload/name_tx.go
index d16ff0d1c026da67fa7052433d451595b05518e0..a1037fc5a9bef4b559dfd55d37b41fb781606506 100644
--- a/txs/name_tx.go
+++ b/txs/payload/name_tx.go
@@ -1,15 +1,12 @@
-package txs
+package payload
 
 import (
 	"fmt"
-	"io"
-
 	"regexp"
 
-	acm "github.com/hyperledger/burrow/account"
 	"github.com/hyperledger/burrow/account/state"
 	"github.com/hyperledger/burrow/crypto"
-	"github.com/tendermint/go-wire"
+	"github.com/hyperledger/burrow/execution/names"
 )
 
 // Name should be file system lik
@@ -22,11 +19,8 @@ type NameTx struct {
 	Name  string
 	Data  string
 	Fee   uint64
-	txHashMemoizer
 }
 
-var _ Tx = &NameTx{}
-
 func NewNameTx(st state.AccountGetter, from crypto.PublicKey, name, data string, amt, fee uint64) (*NameTx, error) {
 	addr := from.Address()
 	acc, err := st.GetAccount(addr)
@@ -43,10 +37,9 @@ func NewNameTx(st state.AccountGetter, from crypto.PublicKey, name, data string,
 
 func NewNameTxWithSequence(from crypto.PublicKey, name, data string, amt, fee, sequence uint64) *NameTx {
 	input := &TxInput{
-		Address:   from.Address(),
-		Amount:    amt,
-		Sequence:  sequence,
-		PublicKey: from,
+		Address:  from.Address(),
+		Amount:   amt,
+		Sequence: sequence,
 	}
 
 	return &NameTx{
@@ -57,42 +50,23 @@ func NewNameTxWithSequence(from crypto.PublicKey, name, data string, amt, fee, s
 	}
 }
 
-func (tx *NameTx) Sign(chainID string, signingAccounts ...acm.AddressableSigner) error {
-	if len(signingAccounts) != 1 {
-		return fmt.Errorf("NameTx expects a single AddressableSigner for its single Input but %v were provieded",
-			len(signingAccounts))
-	}
-	var err error
-	tx.Input.PublicKey = signingAccounts[0].PublicKey()
-	tx.Input.Signature, err = crypto.ChainSign(signingAccounts[0], chainID, tx)
-	if err != nil {
-		return fmt.Errorf("could not sign %v: %v", tx, err)
-	}
-	return nil
-}
-
-func (tx *NameTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
-	wire.WriteTo([]byte(fmt.Sprintf(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
-	wire.WriteTo([]byte(fmt.Sprintf(`,"tx":[%v,{"data":%s,"fee":%v`, TxTypeName, jsonEscape(tx.Data), tx.Fee)), w, n, err)
-	wire.WriteTo([]byte(`,"input":`), w, n, err)
-	tx.Input.WriteSignBytes(w, n, err)
-	wire.WriteTo([]byte(fmt.Sprintf(`,"name":%s`, jsonEscape(tx.Name))), w, n, err)
-	wire.WriteTo([]byte(`}]}`), w, n, err)
+func (tx *NameTx) Type() Type {
+	return TypeName
 }
 
-func (tx *NameTx) GetInputs() []TxInput {
-	return []TxInput{*tx.Input}
+func (tx *NameTx) GetInputs() []*TxInput {
+	return []*TxInput{tx.Input}
 }
 
 func (tx *NameTx) ValidateStrings() error {
 	if len(tx.Name) == 0 {
 		return ErrTxInvalidString{"Name must not be empty"}
 	}
-	if len(tx.Name) > MaxNameLength {
-		return ErrTxInvalidString{fmt.Sprintf("Name is too long. Max %d bytes", MaxNameLength)}
+	if len(tx.Name) > names.MaxNameLength {
+		return ErrTxInvalidString{fmt.Sprintf("Name is too long. Max %d bytes", names.MaxNameLength)}
 	}
-	if len(tx.Data) > MaxDataLength {
-		return ErrTxInvalidString{fmt.Sprintf("Data is too long. Max %d bytes", MaxDataLength)}
+	if len(tx.Data) > names.MaxDataLength {
+		return ErrTxInvalidString{fmt.Sprintf("Data is too long. Max %d bytes", names.MaxDataLength)}
 	}
 
 	if !validateNameRegEntryName(tx.Name) {
@@ -110,10 +84,6 @@ func (tx *NameTx) String() string {
 	return fmt.Sprintf("NameTx{%v -> %s: %s}", tx.Input, tx.Name, tx.Data)
 }
 
-func (tx *NameTx) Hash(chainID string) []byte {
-	return tx.txHashMemoizer.hash(chainID, tx)
-}
-
 // filter strings
 func validateNameRegEntryName(name string) bool {
 	return regexpAlphaNum.Match([]byte(name))
diff --git a/txs/payload/payload.go b/txs/payload/payload.go
new file mode 100644
index 0000000000000000000000000000000000000000..e1c2433d621ad5576bcf775ff72d99355813defb
--- /dev/null
+++ b/txs/payload/payload.go
@@ -0,0 +1,100 @@
+package payload
+
+/*
+Payload (Transaction) is an atomic operation on the ledger state.
+
+Account Txs:
+ - SendTx         Send coins to address
+ - CallTx         Send a msg to a contract that runs in the vm
+ - NameTx	  Store some value under a name in the global namereg
+
+Validation Txs:
+ - BondTx         New validator posts a bond
+ - UnbondTx       Validator leaves
+
+Admin Txs:
+ - PermissionsTx
+*/
+
+type Type int8
+
+// Types of Payload implementations
+const (
+	TypeUnknown = Type(0x00)
+	// Account transactions
+	TypeSend = Type(0x01)
+	TypeCall = Type(0x02)
+	TypeName = Type(0x03)
+
+	// Validation transactions
+	TypeBond   = Type(0x11)
+	TypeUnbond = Type(0x12)
+
+	// Admin transactions
+	TypePermissions = Type(0x21)
+	TypeGovernance  = Type(0x22)
+)
+
+var nameFromType = map[Type]string{
+	TypeUnknown:     "UnknownTx",
+	TypeSend:        "SendTx",
+	TypeCall:        "CallTx",
+	TypeName:        "NameTx",
+	TypeBond:        "BondTx",
+	TypeUnbond:      "UnbondTx",
+	TypePermissions: "PermissionsTx",
+	TypeGovernance:  "GovernanceTx",
+}
+
+var typeFromName = make(map[string]Type)
+
+func init() {
+	for t, n := range nameFromType {
+		typeFromName[n] = t
+	}
+}
+
+type Payload interface {
+	String() string
+	GetInputs() []*TxInput
+	Type() Type
+}
+
+func New(txType Type) Payload {
+	switch txType {
+	case TypeSend:
+		return &SendTx{}
+	case TypeCall:
+		return &CallTx{}
+	case TypeName:
+		return &NameTx{}
+	case TypeBond:
+		return &BondTx{}
+	case TypeUnbond:
+		return &UnbondTx{}
+	case TypePermissions:
+		return &PermissionsTx{}
+	}
+	return nil
+}
+
+func TxTypeFromString(name string) Type {
+	return typeFromName[name]
+}
+
+func (typ Type) String() string {
+	name, ok := nameFromType[typ]
+	if ok {
+		return name
+	}
+	return "UnknownTx"
+}
+
+func (typ Type) MarshalText() ([]byte, error) {
+	return []byte(typ.String()), nil
+}
+
+func (typ *Type) UnmarshalText(data []byte) error {
+	*typ = TxTypeFromString(string(data))
+	return nil
+}
diff --git a/txs/payload/permission_tx.go b/txs/payload/permission_tx.go
new file mode 100644
index 0000000000000000000000000000000000000000..9c219eecb4dff48e00e6d63634ae8d657232727f
--- /dev/null
+++ b/txs/payload/permission_tx.go
@@ -0,0 +1,53 @@
+package payload
+
+import (
+	"fmt"
+
+	"github.com/hyperledger/burrow/account/state"
+	"github.com/hyperledger/burrow/crypto"
+	"github.com/hyperledger/burrow/permission/snatives"
+)
+
+type PermissionsTx struct {
+	Input    *TxInput
+	PermArgs snatives.PermArgs
+}
+
+func NewPermissionsTx(st state.AccountGetter, from crypto.PublicKey, args snatives.PermArgs) (*PermissionsTx, error) {
+	addr := from.Address()
+	acc, err := st.GetAccount(addr)
+	if err != nil {
+		return nil, err
+	}
+	if acc == nil {
+		return nil, fmt.Errorf("Invalid address %s from pubkey %s", addr, from)
+	}
+
+	sequence := acc.Sequence() + 1
+	return NewPermissionsTxWithSequence(from, args, sequence), nil
+}
+
+func NewPermissionsTxWithSequence(from crypto.PublicKey, args snatives.PermArgs, sequence uint64) *PermissionsTx {
+	input := &TxInput{
+		Address:  from.Address(),
+		Amount:   1, // NOTE: amounts can't be 0 ...
+		Sequence: sequence,
+	}
+
+	return &PermissionsTx{
+		Input:    input,
+		PermArgs: args,
+	}
+}
+
+func (tx *PermissionsTx) Type() Type {
+	return TypePermissions
+}
+
+func (tx *PermissionsTx) GetInputs() []*TxInput {
+	return []*TxInput{tx.Input}
+}
+
+func (tx *PermissionsTx) String() string {
+	return fmt.Sprintf("PermissionsTx{%v -> %v}", tx.Input, tx.PermArgs)
+}
diff --git a/txs/payload/send_tx.go b/txs/payload/send_tx.go
new file mode 100644
index 0000000000000000000000000000000000000000..12dd7a54749d96359baf80d708130a53fa576296
--- /dev/null
+++ b/txs/payload/send_tx.go
@@ -0,0 +1,62 @@
+package payload
+
+import (
+	"fmt"
+
+	"github.com/hyperledger/burrow/account/state"
+	"github.com/hyperledger/burrow/crypto"
+)
+
+type SendTx struct {
+	Inputs  []*TxInput
+	Outputs []*TxOutput
+}
+
+func NewSendTx() *SendTx {
+	return &SendTx{
+		Inputs:  []*TxInput{},
+		Outputs: []*TxOutput{},
+	}
+}
+
+func (tx *SendTx) GetInputs() []*TxInput {
+	return tx.Inputs
+}
+
+func (tx *SendTx) Type() Type {
+	return TypeSend
+}
+
+func (tx *SendTx) String() string {
+	return fmt.Sprintf("SendTx{%v -> %v}", tx.Inputs, tx.Outputs)
+}
+
+func (tx *SendTx) AddInput(st state.AccountGetter, pubkey crypto.PublicKey, amt uint64) error {
+	addr := pubkey.Address()
+	acc, err := st.GetAccount(addr)
+	if err != nil {
+		return err
+	}
+	if acc == nil {
+		return fmt.Errorf("invalid address %s from pubkey %s", addr, pubkey)
+	}
+	return tx.AddInputWithSequence(pubkey, amt, acc.Sequence()+1)
+}
+
+func (tx *SendTx) AddInputWithSequence(pubkey crypto.PublicKey, amt uint64, sequence uint64) error {
+	addr := pubkey.Address()
+	tx.Inputs = append(tx.Inputs, &TxInput{
+		Address:  addr,
+		Amount:   amt,
+		Sequence: sequence,
+	})
+	return nil
+}
+
+func (tx *SendTx) AddOutput(addr crypto.Address, amt uint64) error {
+	tx.Outputs = append(tx.Outputs, &TxOutput{
+		Address: addr,
+		Amount:  amt,
+	})
+	return nil
+}
diff --git a/txs/payload/tx_input.go b/txs/payload/tx_input.go
new file mode 100644
index 0000000000000000000000000000000000000000..f435475f5af95cc54b69c37b1fcd6bd25b2d657c
--- /dev/null
+++ b/txs/payload/tx_input.go
@@ -0,0 +1,27 @@
+package payload
+
+import (
+	"fmt"
+
+	"github.com/hyperledger/burrow/crypto"
+)
+
+type TxInput struct {
+	Address  crypto.Address
+	Amount   uint64
+	Sequence uint64
+}
+
+func (txIn *TxInput) ValidateBasic() error {
+	if txIn.Address == crypto.ZeroAddress {
+		return ErrTxInvalidAddress
+	}
+	if txIn.Amount == 0 {
+		return ErrTxInvalidAmount
+	}
+	return nil
+}
+
+func (txIn *TxInput) String() string {
+	return fmt.Sprintf("TxInput{%s, Amt: %v, Seq:%v}", txIn.Address, txIn.Amount, txIn.Sequence)
+}
diff --git a/txs/tx_output.go b/txs/payload/tx_output.go
similarity index 64%
rename from txs/tx_output.go
rename to txs/payload/tx_output.go
index 7467d29b13d6f511099d02388cf521b2dc0f06bd..0dbd6c6856578e811d2766585ef728956f409978 100644
--- a/txs/tx_output.go
+++ b/txs/payload/tx_output.go
@@ -1,11 +1,9 @@
-package txs
+package payload
 
 import (
 	"fmt"
-	"io"
 
 	"github.com/hyperledger/burrow/crypto"
-	"github.com/tendermint/go-wire"
 )
 
 type TxOutput struct {
@@ -23,10 +21,6 @@ func (txOut *TxOutput) ValidateBasic() error {
 	return nil
 }
 
-func (txOut *TxOutput) WriteSignBytes(w io.Writer, n *int, err *error) {
-	wire.WriteTo([]byte(fmt.Sprintf(`{"address":"%s","amount":%v}`, txOut.Address, txOut.Amount)), w, n, err)
-}
-
 func (txOut *TxOutput) String() string {
 	return fmt.Sprintf("TxOutput{%s,%v}", txOut.Address, txOut.Amount)
 }
diff --git a/txs/payload/unbond_tx.go b/txs/payload/unbond_tx.go
new file mode 100644
index 0000000000000000000000000000000000000000..9975d590ceb481761fdf7c4efdbcfaa3a4dfdf56
--- /dev/null
+++ b/txs/payload/unbond_tx.go
@@ -0,0 +1,32 @@
+package payload
+
+import (
+	"fmt"
+
+	"github.com/hyperledger/burrow/crypto"
+)
+
+type UnbondTx struct {
+	Input   *TxInput
+	Address crypto.Address
+	Height  int
+}
+
+func NewUnbondTx(addr crypto.Address, height int) *UnbondTx {
+	return &UnbondTx{
+		Address: addr,
+		Height:  height,
+	}
+}
+
+func (tx *UnbondTx) Type() Type {
+	return TypeUnbond
+}
+
+func (tx *UnbondTx) GetInputs() []*TxInput {
+	return []*TxInput{tx.Input}
+}
+
+func (tx *UnbondTx) String() string {
+	return fmt.Sprintf("UnbondTx{%v -> %s,%v}", tx.Input, tx.Address, tx.Height)
+}
diff --git a/txs/permission_tx.go b/txs/permission_tx.go
deleted file mode 100644
index 2ec99eb81d921810594f1feeffaeab554eeb4109..0000000000000000000000000000000000000000
--- a/txs/permission_tx.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package txs
-
-import (
-	"fmt"
-	"io"
-
-	acm "github.com/hyperledger/burrow/account"
-	"github.com/hyperledger/burrow/account/state"
-	"github.com/hyperledger/burrow/crypto"
-	"github.com/hyperledger/burrow/permission/snatives"
-	"github.com/tendermint/go-wire"
-)
-
-type PermissionsTx struct {
-	Input    *TxInput
-	PermArgs snatives.PermArgs
-	txHashMemoizer
-}
-
-var _ Tx = &PermissionsTx{}
-
-func NewPermissionsTx(st state.AccountGetter, from crypto.PublicKey, args snatives.PermArgs) (*PermissionsTx, error) {
-	addr := from.Address()
-	acc, err := st.GetAccount(addr)
-	if err != nil {
-		return nil, err
-	}
-	if acc == nil {
-		return nil, fmt.Errorf("Invalid address %s from pubkey %s", addr, from)
-	}
-
-	sequence := acc.Sequence() + 1
-	return NewPermissionsTxWithSequence(from, args, sequence), nil
-}
-
-func NewPermissionsTxWithSequence(from crypto.PublicKey, args snatives.PermArgs, sequence uint64) *PermissionsTx {
-	input := &TxInput{
-		Address:   from.Address(),
-		Amount:    1, // NOTE: amounts can't be 0 ...
-		Sequence:  sequence,
-		PublicKey: from,
-	}
-
-	return &PermissionsTx{
-		Input:    input,
-		PermArgs: args,
-	}
-}
-
-func (tx *PermissionsTx) Sign(chainID string, signingAccounts ...acm.AddressableSigner) error {
-	if len(signingAccounts) != 1 {
-		return fmt.Errorf("PermissionsTx expects a single AddressableSigner for its single Input but %v were provieded",
-			len(signingAccounts))
-	}
-	var err error
-	tx.Input.PublicKey = signingAccounts[0].PublicKey()
-	tx.Input.Signature, err = crypto.ChainSign(signingAccounts[0], chainID, tx)
-	if err != nil {
-		return fmt.Errorf("could not sign %v: %v", tx, err)
-	}
-	return nil
-}
-
-func (tx *PermissionsTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
-	wire.WriteTo([]byte(fmt.Sprintf(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
-	wire.WriteTo([]byte(fmt.Sprintf(`,"tx":[%v,{"args":"`, TxTypePermissions)), w, n, err)
-	wire.WriteJSON(&tx.PermArgs, w, n, err)
-	wire.WriteTo([]byte(`","input":`), w, n, err)
-	tx.Input.WriteSignBytes(w, n, err)
-	wire.WriteTo([]byte(`}]}`), w, n, err)
-}
-
-func (tx *PermissionsTx) GetInputs() []TxInput {
-	return []TxInput{*tx.Input}
-}
-
-func (tx *PermissionsTx) String() string {
-	return fmt.Sprintf("PermissionsTx{%v -> %v}", tx.Input, tx.PermArgs)
-}
-
-func (tx *PermissionsTx) Hash(chainID string) []byte {
-	return tx.txHashMemoizer.hash(chainID, tx)
-}
diff --git a/txs/rebond_tx.go b/txs/rebond_tx.go
deleted file mode 100644
index 8816937d18f67fc7a8713947e0e78c22204e1bb8..0000000000000000000000000000000000000000
--- a/txs/rebond_tx.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package txs
-
-import (
-	"fmt"
-	"io"
-
-	acm "github.com/hyperledger/burrow/account"
-	"github.com/hyperledger/burrow/crypto"
-	"github.com/tendermint/go-wire"
-)
-
-type RebondTx struct {
-	Address   crypto.Address
-	Height    int
-	Signature crypto.Signature
-	txHashMemoizer
-}
-
-var _ Tx = &RebondTx{}
-
-func NewRebondTx(addr crypto.Address, height int) *RebondTx {
-	return &RebondTx{
-		Address: addr,
-		Height:  height,
-	}
-}
-
-func (tx *RebondTx) Sign(chainID string, signingAccounts ...acm.AddressableSigner) error {
-	if len(signingAccounts) != 1 {
-		return fmt.Errorf("RebondTx expects a single AddressableSigner for its signature but %v were provieded",
-			len(signingAccounts))
-	}
-	var err error
-	tx.Signature, err = crypto.ChainSign(signingAccounts[0], chainID, tx)
-	if err != nil {
-		return fmt.Errorf("could not sign %v: %v", tx, err)
-	}
-	return nil
-}
-
-func (tx *RebondTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
-	wire.WriteTo([]byte(fmt.Sprintf(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
-	wire.WriteTo([]byte(fmt.Sprintf(`,"tx":[%v,{"address":"%s","height":%v}]}`, TxTypeRebond, tx.Address, tx.Height)), w, n, err)
-}
-
-func (tx *RebondTx) GetInputs() []TxInput {
-	return nil
-}
-
-func (tx *RebondTx) String() string {
-	return fmt.Sprintf("RebondTx{%s,%v,%v}", tx.Address, tx.Height, tx.Signature)
-}
-
-func (tx *RebondTx) Hash(chainID string) []byte {
-	return tx.txHashMemoizer.hash(chainID, tx)
-}
diff --git a/txs/send_tx.go b/txs/send_tx.go
deleted file mode 100644
index 421d9f190e2d75fdca637d0b7a686409d7a78833..0000000000000000000000000000000000000000
--- a/txs/send_tx.go
+++ /dev/null
@@ -1,104 +0,0 @@
-package txs
-
-import (
-	"fmt"
-	"io"
-
-	acm "github.com/hyperledger/burrow/account"
-	"github.com/hyperledger/burrow/account/state"
-	"github.com/hyperledger/burrow/crypto"
-	"github.com/tendermint/go-wire"
-)
-
-type SendTx struct {
-	Inputs  []*TxInput
-	Outputs []*TxOutput
-	txHashMemoizer
-}
-
-var _ Tx = &SendTx{}
-
-func NewSendTx() *SendTx {
-	return &SendTx{
-		Inputs:  []*TxInput{},
-		Outputs: []*TxOutput{},
-	}
-}
-
-func (tx *SendTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
-	wire.WriteTo([]byte(fmt.Sprintf(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
-	wire.WriteTo([]byte(fmt.Sprintf(`,"tx":[%v,{"inputs":[`, TxTypeSend)), w, n, err)
-	for i, in := range tx.Inputs {
-		in.WriteSignBytes(w, n, err)
-		if i != len(tx.Inputs)-1 {
-			wire.WriteTo([]byte(","), w, n, err)
-		}
-	}
-	wire.WriteTo([]byte(`],"outputs":[`), w, n, err)
-	for i, out := range tx.Outputs {
-		out.WriteSignBytes(w, n, err)
-		if i != len(tx.Outputs)-1 {
-			wire.WriteTo([]byte(","), w, n, err)
-		}
-	}
-	wire.WriteTo([]byte(`]}]}`), w, n, err)
-}
-
-func (tx *SendTx) GetInputs() []TxInput {
-	return copyInputs(tx.Inputs)
-}
-
-func (tx *SendTx) String() string {
-	return fmt.Sprintf("SendTx{%v -> %v}", tx.Inputs, tx.Outputs)
-}
-
-func (tx *SendTx) Hash(chainID string) []byte {
-	return tx.txHashMemoizer.hash(chainID, tx)
-}
-
-func (tx *SendTx) AddInput(st state.AccountGetter, pubkey crypto.PublicKey, amt uint64) error {
-	addr := pubkey.Address()
-	acc, err := st.GetAccount(addr)
-	if err != nil {
-		return err
-	}
-	if acc == nil {
-		return fmt.Errorf("invalid address %s from pubkey %s", addr, pubkey)
-	}
-	return tx.AddInputWithSequence(pubkey, amt, acc.Sequence()+1)
-}
-
-func (tx *SendTx) AddInputWithSequence(pubkey crypto.PublicKey, amt uint64, sequence uint64) error {
-	addr := pubkey.Address()
-	tx.Inputs = append(tx.Inputs, &TxInput{
-		Address:   addr,
-		Amount:    amt,
-		Sequence:  sequence,
-		PublicKey: pubkey,
-	})
-	return nil
-}
-
-func (tx *SendTx) AddOutput(addr crypto.Address, amt uint64) error {
-	tx.Outputs = append(tx.Outputs, &TxOutput{
-		Address: addr,
-		Amount:  amt,
-	})
-	return nil
-}
-
-func (tx *SendTx) Sign(chainID string, signingAccounts ...acm.AddressableSigner) error {
-	if len(signingAccounts) != len(tx.Inputs) {
-		return fmt.Errorf("SendTx has %v Inputs but was provided with %v SigningAccounts", len(tx.Inputs),
-			len(signingAccounts))
-	}
-	var err error
-	for i, signingAccount := range signingAccounts {
-		tx.Inputs[i].PublicKey = signingAccount.PublicKey()
-		tx.Inputs[i].Signature, err = crypto.ChainSign(signingAccount, chainID, tx)
-		if err != nil {
-			return fmt.Errorf("could not sign tx %v input %v: %v", tx, tx.Inputs[i], err)
-		}
-	}
-	return nil
-}
diff --git a/txs/tx.go b/txs/tx.go
index e20292ef1f2f0e126b1a03ee8dc33013033453bd..ec4a667673ae67c1c3cdd35a80634e5599916e8e 100644
--- a/txs/tx.go
+++ b/txs/tx.go
@@ -16,156 +16,131 @@ package txs
 
 import (
 	"encoding/json"
-	"errors"
 	"fmt"
-	"io"
 
 	acm "github.com/hyperledger/burrow/account"
 	"github.com/hyperledger/burrow/crypto"
-	"github.com/tendermint/go-wire/data"
+	"github.com/hyperledger/burrow/txs/payload"
 	"golang.org/x/crypto/ripemd160"
 )
 
-var (
-	ErrTxInvalidAddress    = errors.New("error invalid address")
-	ErrTxDuplicateAddress  = errors.New("error duplicate address")
-	ErrTxInvalidAmount     = errors.New("error invalid amount")
-	ErrTxInsufficientFunds = errors.New("error insufficient funds")
-	ErrTxUnknownPubKey     = errors.New("error unknown pubkey")
-	ErrTxInvalidPubKey     = errors.New("error invalid pubkey")
-	ErrTxInvalidSignature  = errors.New("error invalid signature")
-)
-
-/*
-Tx (Transaction) is an atomic operation on the ledger state.
-
-Account Txs:
- - SendTx         Send coins to address
- - CallTx         Send a msg to a contract that runs in the vm
- - NameTx	  Store some value under a name in the global namereg
-
-Validation Txs:
- - BondTx         New validator posts a bond
- - UnbondTx       Validator leaves
-
-Admin Txs:
- - PermissionsTx
-*/
-
-// Types of Tx implementations
-const (
-	// Account transactions
-	TxTypeSend = byte(0x01)
-	TxTypeCall = byte(0x02)
-	TxTypeName = byte(0x03)
-
-	// Validation transactions
-	TxTypeBond   = byte(0x11)
-	TxTypeUnbond = byte(0x12)
-	TxTypeRebond = byte(0x13)
-
-	// Admin transactions
-	TxTypePermissions = byte(0x1f)
-)
-
-var mapper = data.NewMapper(Wrapper{}).
-	RegisterImplementation(&SendTx{}, "send_tx", TxTypeSend).
-	RegisterImplementation(&CallTx{}, "call_tx", TxTypeCall).
-	RegisterImplementation(&NameTx{}, "name_tx", TxTypeName).
-	RegisterImplementation(&BondTx{}, "bond_tx", TxTypeBond).
-	RegisterImplementation(&UnbondTx{}, "unbond_tx", TxTypeUnbond).
-	RegisterImplementation(&RebondTx{}, "rebond_tx", TxTypeRebond).
-	RegisterImplementation(&PermissionsTx{}, "permissions_tx", TxTypePermissions)
-
-	//-----------------------------------------------------------------------------
-
-// TODO: replace with sum-type struct like ResultEvent
-type Tx interface {
-	WriteSignBytes(chainID string, w io.Writer, n *int, err *error)
-	String() string
-	GetInputs() []TxInput
-	Hash(chainID string) []byte
-	Sign(chainID string, signingAccounts ...acm.AddressableSigner) error
+// Tx is the canonical object that we serialise to produce the SignBytes that we sign
+type Tx struct {
+	ChainID string
+	payload.Payload
+	txHash []byte
 }
 
-type Encoder interface {
-	EncodeTx(tx Tx) ([]byte, error)
-}
-
-type Decoder interface {
-	DecodeTx(txBytes []byte) (Tx, error)
+// Wrap the Payload in Tx required for signing and serialisation
+func NewTx(payload payload.Payload) *Tx {
+	switch t := payload.(type) {
+	case Tx:
+		return &t
+	case *Tx:
+		return t
+	}
+	return &Tx{
+		Payload: payload,
+	}
 }
 
-// BroadcastTx or Transact
-type Receipt struct {
-	TxHash          []byte
-	CreatesContract bool
-	ContractAddress crypto.Address
+// Enclose this Tx in an Envelope to be signed
+func (tx *Tx) Enclose() *Envelope {
+	return &Envelope{
+		Tx: tx,
+	}
 }
 
-type Wrapper struct {
-	Tx `json:"unwrap"`
+// Encloses in Envelope and signs envelope
+func (tx *Tx) Sign(signingAccounts ...acm.AddressableSigner) (*Envelope, error) {
+	env := tx.Enclose()
+	err := env.Sign(signingAccounts...)
+	if err != nil {
+		return nil, err
+	}
+	return env, nil
 }
 
-// Wrap the Tx in a struct that allows for go-wire JSON serialisation
-func Wrap(tx Tx) Wrapper {
-	if txWrapped, ok := tx.(Wrapper); ok {
-		return txWrapped
-	}
-	return Wrapper{
-		Tx: tx,
+// Generate SignBytes, panicking on any failure
+func (tx *Tx) MustSignBytes() []byte {
+	bs, err := tx.SignBytes()
+	if err != nil {
+		panic(err)
 	}
+	return bs
 }
 
-// A serialisation wrapper that is itself a Tx
-func (txw Wrapper) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
-	txw.Tx.WriteSignBytes(chainID, w, n, err)
+// Produces the canonical SignBytes (the Tx message that will be signed) for a Tx
+func (tx *Tx) SignBytes() ([]byte, error) {
+	bs, err := json.Marshal(tx)
+	if err != nil {
+		return nil, fmt.Errorf("could not generate canonical SignBytes for Payload %v: %v", tx.Payload, err)
+	}
+	return bs, nil
 }
 
-func (txw Wrapper) MarshalJSON() ([]byte, error) {
-	return mapper.ToJSON(txw.Tx)
+// Serialisation intermediate for switching on type
+type wrapper struct {
+	ChainID string
+	Type    payload.Type
+	Payload json.RawMessage
 }
 
-func (txw *Wrapper) UnmarshalJSON(data []byte) (err error) {
-	parsed, err := mapper.FromJSON(data)
-	if err == nil && parsed != nil {
-		txw.Tx = parsed.(Tx)
+func (tx *Tx) MarshalJSON() ([]byte, error) {
+	bs, err := json.Marshal(tx.Payload)
+	if err != nil {
+		return nil, err
 	}
-	return err
-}
-
-// Get the inner Tx that this Wrapper wraps
-func (txw *Wrapper) Unwrap() Tx {
-	return txw.Tx
+	return json.Marshal(wrapper{
+		ChainID: tx.ChainID,
+		Type:    tx.Type(),
+		Payload: bs,
+	})
 }
 
-// Avoid re-hashing the same in-memory Tx
-type txHashMemoizer struct {
-	txHashBytes []byte
-	chainID     string
+func (tx *Tx) UnmarshalJSON(data []byte) error {
+	w := new(wrapper)
+	err := json.Unmarshal(data, w)
+	if err != nil {
+		return err
+	}
+	tx.ChainID = w.ChainID
+	// Now we know the Type we can deserialise the Payload
+	tx.Payload = payload.New(w.Type)
+	return json.Unmarshal(w.Payload, tx.Payload)
 }
 
-func (thm *txHashMemoizer) hash(chainID string, tx Tx) []byte {
-	if thm.txHashBytes == nil || thm.chainID != chainID {
-		thm.chainID = chainID
-		thm.txHashBytes = TxHash(chainID, tx)
+// Generate a Hash for this transaction based on the SignBytes. The hash is memoized over the lifetime
+// of the Tx so repeated calls to Hash() are effectively free
+func (tx *Tx) Hash() []byte {
+	if tx.txHash == nil {
+		return tx.Rehash()
 	}
-	return thm.txHashBytes
+	return tx.txHash
 }
 
-func TxHash(chainID string, tx Tx) []byte {
-	signBytes := crypto.SignBytes(chainID, tx)
+// Regenerate the Tx hash if it has been mutated or as called by Hash() in first instance
+func (tx *Tx) Rehash() []byte {
 	hasher := ripemd160.New()
-	hasher.Write(signBytes)
-	// Calling Sum(nil) just gives us the digest with nothing prefixed
-	return hasher.Sum(nil)
+	hasher.Write(tx.MustSignBytes())
+	tx.txHash = hasher.Sum(nil)
+	return tx.txHash
+}
+
+// BroadcastTx or Transaction receipt
+type Receipt struct {
+	TxHash          []byte
+	CreatesContract bool
+	ContractAddress crypto.Address
 }
 
-func GenerateReceipt(chainId string, tx Tx) Receipt {
-	receipt := Receipt{
-		TxHash: tx.Hash(chainId),
+// Generate a transaction Receipt containing the Tx hash and other information if the Tx is call.
+// Returned by ABCI methods.
+func (tx *Tx) GenerateReceipt() *Receipt {
+	receipt := &Receipt{
+		TxHash: tx.Hash(),
 	}
-	if callTx, ok := tx.(*CallTx); ok {
+	if callTx, ok := tx.Payload.(*payload.CallTx); ok {
 		receipt.CreatesContract = callTx.Address == nil
 		if receipt.CreatesContract {
 			receipt.ContractAddress = crypto.NewContractAddress(callTx.Input.Address, callTx.Input.Sequence)
@@ -175,40 +150,3 @@ func GenerateReceipt(chainId string, tx Tx) Receipt {
 	}
 	return receipt
 }
-
-type ErrTxInvalidString struct {
-	Msg string
-}
-
-func (e ErrTxInvalidString) Error() string {
-	return e.Msg
-}
-
-type ErrTxInvalidSequence struct {
-	Got      uint64
-	Expected uint64
-}
-
-func (e ErrTxInvalidSequence) Error() string {
-	return fmt.Sprintf("Error invalid sequence. Got %d, expected %d", e.Got, e.Expected)
-}
-
-//--------------------------------------------------------------------------------
-
-func copyInputs(inputs []*TxInput) []TxInput {
-	inputsCopy := make([]TxInput, len(inputs))
-	for i, input := range inputs {
-		inputsCopy[i] = *input
-	}
-	return inputsCopy
-}
-
-// Contract: This function is deterministic and completely reversible.
-func jsonEscape(str string) string {
-	// TODO: escape without panic
-	escapedBytes, err := json.Marshal(str)
-	if err != nil {
-		panic(fmt.Errorf("error json-escaping string: %s", str))
-	}
-	return string(escapedBytes)
-}
diff --git a/txs/tx_input.go b/txs/tx_input.go
deleted file mode 100644
index b8d4d4a974ffb741bf2234699bdb6ad81a7aeffa..0000000000000000000000000000000000000000
--- a/txs/tx_input.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package txs
-
-import (
-	"fmt"
-	"io"
-
-	"github.com/hyperledger/burrow/crypto"
-	"github.com/tendermint/go-wire"
-)
-
-type TxInput struct {
-	Address   crypto.Address
-	Amount    uint64
-	Sequence  uint64
-	Signature crypto.Signature
-	PublicKey crypto.PublicKey
-}
-
-func (txIn *TxInput) ValidateBasic() error {
-	if len(txIn.Address) != 20 {
-		return ErrTxInvalidAddress
-	}
-	if txIn.Amount == 0 {
-		return ErrTxInvalidAmount
-	}
-	return nil
-}
-
-func (txIn *TxInput) WriteSignBytes(w io.Writer, n *int, err *error) {
-	wire.WriteTo([]byte(fmt.Sprintf(`{"address":"%s","amount":%v,"sequence":%v}`, txIn.Address, txIn.Amount, txIn.Sequence)), w, n, err)
-}
-
-func (txIn *TxInput) String() string {
-	return fmt.Sprintf("TxInput{%s,%v,%v,%v,%v}", txIn.Address, txIn.Amount, txIn.Sequence, txIn.Signature, txIn.PublicKey)
-}
diff --git a/txs/tx_test.go b/txs/tx_test.go
index 44f9892a73027b41bfc6be9a7c70b13ffbd31023..ada6bcd607b1964a943ae5fccb90f306afe69644 100644
--- a/txs/tx_test.go
+++ b/txs/tx_test.go
@@ -15,66 +15,63 @@
 package txs
 
 import (
-	"fmt"
-	"testing"
-
 	"encoding/json"
+	"runtime/debug"
+	"testing"
 
 	acm "github.com/hyperledger/burrow/account"
 	"github.com/hyperledger/burrow/crypto"
 	ptypes "github.com/hyperledger/burrow/permission"
 	"github.com/hyperledger/burrow/permission/snatives"
+	"github.com/hyperledger/burrow/txs/payload"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
 
 var chainID = "myChainID"
 
-func makeAddress(str string) (address crypto.Address) {
-	copy(address[:], ([]byte)(str))
-	return
+var privateAccounts = make(map[crypto.Address]acm.AddressableSigner)
+
+func makePrivateAccount(str string) acm.PrivateAccount {
+	acc := acm.GeneratePrivateAccountFromSecret(str)
+	privateAccounts[acc.Address()] = acc
+	return acc
 }
 
 func TestSendTxSignable(t *testing.T) {
-	sendTx := &SendTx{
-		Inputs: []*TxInput{
+	sendTx := &payload.SendTx{
+		Inputs: []*payload.TxInput{
 			{
-				Address:  makeAddress("input1"),
+				Address:  makePrivateAccount("input1").Address(),
 				Amount:   12345,
 				Sequence: 67890,
 			},
 			{
-				Address:  makeAddress("input2"),
+				Address:  makePrivateAccount("input2").Address(),
 				Amount:   111,
 				Sequence: 222,
 			},
 		},
-		Outputs: []*TxOutput{
+		Outputs: []*payload.TxOutput{
 			{
-				Address: makeAddress("output1"),
+				Address: makePrivateAccount("output1").Address(),
 				Amount:  333,
 			},
 			{
-				Address: makeAddress("output2"),
+				Address: makePrivateAccount("output2").Address(),
 				Amount:  444,
 			},
 		},
 	}
-	signBytes := crypto.SignBytes(chainID, sendTx)
-	signStr := string(signBytes)
-	expected := fmt.Sprintf(`{"chain_id":"%s","tx":[1,{"inputs":[{"address":"%s","amount":12345,"sequence":67890},{"address":"%s","amount":111,"sequence":222}],"outputs":[{"address":"%s","amount":333},{"address":"%s","amount":444}]}]}`,
-		chainID, sendTx.Inputs[0].Address.String(), sendTx.Inputs[1].Address.String(), sendTx.Outputs[0].Address.String(), sendTx.Outputs[1].Address.String())
-
-	if signStr != expected {
-		t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signStr)
-	}
+	testTxMarshalJSON(t, sendTx)
+	testTxSignVerify(t, sendTx)
 }
 
 func TestCallTxSignable(t *testing.T) {
-	toAddress := makeAddress("contract1")
-	callTx := &CallTx{
-		Input: &TxInput{
-			Address:  makeAddress("input1"),
+	toAddress := makePrivateAccount("contract1").Address()
+	callTx := &payload.CallTx{
+		Input: &payload.TxInput{
+			Address:  makePrivateAccount("input1").Address(),
 			Amount:   12345,
 			Sequence: 67890,
 		},
@@ -83,19 +80,14 @@ func TestCallTxSignable(t *testing.T) {
 		Fee:      222,
 		Data:     []byte("data1"),
 	}
-	signBytes := crypto.SignBytes(chainID, callTx)
-	signStr := string(signBytes)
-	expected := fmt.Sprintf(`{"chain_id":"%s","tx":[2,{"address":"%s","data":"6461746131","fee":222,"gas_limit":111,"input":{"address":"%s","amount":12345,"sequence":67890}}]}`,
-		chainID, callTx.Address.String(), callTx.Input.Address.String())
-	if signStr != expected {
-		t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr)
-	}
+	testTxMarshalJSON(t, callTx)
+	testTxSignVerify(t, callTx)
 }
 
 func TestNameTxSignable(t *testing.T) {
-	nameTx := &NameTx{
-		Input: &TxInput{
-			Address:  makeAddress("input1"),
+	nameTx := &payload.NameTx{
+		Input: &payload.TxInput{
+			Address:  makePrivateAccount("input1").Address(),
 			Amount:   12345,
 			Sequence: 250,
 		},
@@ -103,111 +95,70 @@ func TestNameTxSignable(t *testing.T) {
 		Data: "secretly.not.google.com",
 		Fee:  1000,
 	}
-	signBytes := crypto.SignBytes(chainID, nameTx)
-	signStr := string(signBytes)
-	expected := fmt.Sprintf(`{"chain_id":"%s","tx":[3,{"data":"secretly.not.google.com","fee":1000,"input":{"address":"%s","amount":12345,"sequence":250},"name":"google.com"}]}`,
-		chainID, nameTx.Input.Address.String())
-	if signStr != expected {
-		t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr)
-	}
+	testTxMarshalJSON(t, nameTx)
+	testTxSignVerify(t, nameTx)
 }
 
 func TestBondTxSignable(t *testing.T) {
-	privAccount := acm.GeneratePrivateAccountFromSecret("foooobars")
-	bondTx := &BondTx{
-		PubKey: privAccount.PublicKey(),
-		Inputs: []*TxInput{
+	bondTx := &payload.BondTx{
+		Inputs: []*payload.TxInput{
 			{
-				Address:  makeAddress("input1"),
+				Address:  makePrivateAccount("input1").Address(),
 				Amount:   12345,
 				Sequence: 67890,
 			},
 			{
-				Address:  makeAddress("input2"),
+				Address:  makePrivateAccount("input2").Address(),
 				Amount:   111,
 				Sequence: 222,
 			},
 		},
-		UnbondTo: []*TxOutput{
+		UnbondTo: []*payload.TxOutput{
 			{
-				Address: makeAddress("output1"),
+				Address: makePrivateAccount("output1").Address(),
 				Amount:  333,
 			},
 			{
-				Address: makeAddress("output2"),
+				Address: makePrivateAccount("output2").Address(),
 				Amount:  444,
 			},
 		},
 	}
-	expected := fmt.Sprintf(`{"chain_id":"%s",`+
-		`"tx":[17,{"inputs":[{"address":"%s",`+
-		`"amount":12345,"sequence":67890},{"address":"%s",`+
-		`"amount":111,"sequence":222}],"pub_key":{"CurveType":1,"PublicKey":"%X"},`+
-		`"unbond_to":[{"address":"%s",`+
-		`"amount":333},{"address":"%s",`+
-		`"amount":444}]}]}`,
-		chainID,
-		bondTx.Inputs[0].Address.String(),
-		bondTx.Inputs[1].Address.String(),
-		bondTx.PubKey.RawBytes(),
-		bondTx.UnbondTo[0].Address.String(),
-		bondTx.UnbondTo[1].Address.String())
-
-	assert.Equal(t, expected, string(crypto.SignBytes(chainID, bondTx)), "Unexpected sign string for BondTx")
+	testTxMarshalJSON(t, bondTx)
+	testTxSignVerify(t, bondTx)
 }
 
 func TestUnbondTxSignable(t *testing.T) {
-	unbondTx := &UnbondTx{
-		Address: makeAddress("address1"),
-		Height:  111,
-	}
-	signBytes := crypto.SignBytes(chainID, unbondTx)
-	signStr := string(signBytes)
-	expected := fmt.Sprintf(`{"chain_id":"%s","tx":[18,{"address":"%s","height":111}]}`,
-		chainID, unbondTx.Address.String())
-	if signStr != expected {
-		t.Errorf("Got unexpected sign string for UnbondTx")
-	}
-}
-
-func TestRebondTxSignable(t *testing.T) {
-	rebondTx := &RebondTx{
-		Address: makeAddress("address1"),
+	unbondTx := &payload.UnbondTx{
+		Input: &payload.TxInput{
+			Address: makePrivateAccount("fooo1").Address(),
+		},
+		Address: makePrivateAccount("address1").Address(),
 		Height:  111,
 	}
-	signBytes := crypto.SignBytes(chainID, rebondTx)
-	signStr := string(signBytes)
-	expected := fmt.Sprintf(`{"chain_id":"%s","tx":[19,{"address":"%s","height":111}]}`,
-		chainID, rebondTx.Address.String())
-	if signStr != expected {
-		t.Errorf("Got unexpected sign string for RebondTx")
-	}
+	testTxMarshalJSON(t, unbondTx)
+	testTxSignVerify(t, unbondTx)
 }
 
 func TestPermissionsTxSignable(t *testing.T) {
-	permsTx := &PermissionsTx{
-		Input: &TxInput{
-			Address:  makeAddress("input1"),
+	permsTx := &payload.PermissionsTx{
+		Input: &payload.TxInput{
+			Address:  makePrivateAccount("input1").Address(),
 			Amount:   12345,
 			Sequence: 250,
 		},
-		PermArgs: snatives.SetBaseArgs(makeAddress("address1"), 1, true),
+		PermArgs: snatives.SetBaseArgs(makePrivateAccount("address1").Address(), 1, true),
 	}
 
-	signBytes := crypto.SignBytes(chainID, permsTx)
-	signStr := string(signBytes)
-	expected := fmt.Sprintf(`{"chain_id":"%s","tx":[31,{"args":"{"PermFlag":%v,"Address":"%s","Permission":1,"Value":true}","input":{"address":"%s","amount":12345,"sequence":250}}]}`,
-		chainID, ptypes.SetBase, permsTx.PermArgs.Address.String(), permsTx.Input.Address.String())
-	if signStr != expected {
-		t.Errorf("Got unexpected sign string for PermsTx. Expected:\n%v\nGot:\n%v", expected, signStr)
-	}
+	testTxMarshalJSON(t, permsTx)
+	testTxSignVerify(t, permsTx)
 }
 
 func TestTxWrapper_MarshalJSON(t *testing.T) {
-	toAddress := makeAddress("contract1")
-	callTx := &CallTx{
-		Input: &TxInput{
-			Address:  makeAddress("input1"),
+	toAddress := makePrivateAccount("contract1").Address()
+	callTx := &payload.CallTx{
+		Input: &payload.TxInput{
+			Address:  makePrivateAccount("input1").Address(),
 			Amount:   12345,
 			Sequence: 67890,
 		},
@@ -217,21 +168,22 @@ func TestTxWrapper_MarshalJSON(t *testing.T) {
 		Data:     []byte("data1"),
 	}
 	testTxMarshalJSON(t, callTx)
+	testTxSignVerify(t, callTx)
 }
 
 func TestNewPermissionsTxWithSequence(t *testing.T) {
-	privateKey := crypto.PrivateKeyFromSecret("Shhh...", crypto.CurveTypeEd25519)
-
-	args := snatives.SetBaseArgs(privateKey.GetPublicKey().Address(), ptypes.HasRole, true)
-	permTx := NewPermissionsTxWithSequence(privateKey.GetPublicKey(), args, 1)
+	privateAccount := makePrivateAccount("shhhhh")
+	args := snatives.SetBaseArgs(privateAccount.PublicKey().Address(), ptypes.HasRole, true)
+	permTx := payload.NewPermissionsTxWithSequence(privateAccount.PublicKey(), args, 1)
 	testTxMarshalJSON(t, permTx)
+	testTxSignVerify(t, permTx)
 }
 
-func testTxMarshalJSON(t *testing.T, tx Tx) {
-	txw := &Wrapper{Tx: tx}
+func testTxMarshalJSON(t *testing.T, tx payload.Payload) {
+	txw := &Tx{Payload: tx}
 	bs, err := json.Marshal(txw)
 	require.NoError(t, err)
-	txwOut := new(Wrapper)
+	txwOut := new(Tx)
 	err = json.Unmarshal(bs, txwOut)
 	require.NoError(t, err)
 	bsOut, err := json.Marshal(txwOut)
@@ -239,13 +191,13 @@ func testTxMarshalJSON(t *testing.T, tx Tx) {
 	assert.Equal(t, string(bs), string(bsOut))
 }
 
-func TestTxHashMemoizer(t *testing.T) {
-	tx := &CallTx{
-		Input: &TxInput{
-			Sequence: 4,
-		},
+func testTxSignVerify(t *testing.T, tx payload.Payload) {
+	inputs := tx.GetInputs()
+	var signers []acm.AddressableSigner
+	for _, in := range inputs {
+		signers = append(signers, privateAccounts[in.Address])
 	}
-	hsh := tx.Hash("foo")
-	assert.Equal(t, hsh, tx.txHashMemoizer.txHashBytes)
-	assert.Equal(t, "foo", tx.txHashMemoizer.chainID)
+	txEnv := Enclose(chainID, tx)
+	require.NoError(t, txEnv.Sign(signers...), "Error signing tx: %s", debug.Stack())
+	require.NoError(t, txEnv.Verify(nil), "Error verifying tx: %s", debug.Stack())
 }
diff --git a/txs/unbond_tx.go b/txs/unbond_tx.go
deleted file mode 100644
index 850de4f5266c15d5fc4a318f45092587d013ff3a..0000000000000000000000000000000000000000
--- a/txs/unbond_tx.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package txs
-
-import (
-	"fmt"
-	"io"
-
-	acm "github.com/hyperledger/burrow/account"
-	"github.com/hyperledger/burrow/crypto"
-	"github.com/tendermint/go-wire"
-)
-
-type UnbondTx struct {
-	Address   crypto.Address
-	Height    int
-	Signature crypto.Signature
-	txHashMemoizer
-}
-
-var _ Tx = &UnbondTx{}
-
-func NewUnbondTx(addr crypto.Address, height int) *UnbondTx {
-	return &UnbondTx{
-		Address: addr,
-		Height:  height,
-	}
-}
-
-func (tx *UnbondTx) Sign(chainID string, signingAccounts ...acm.AddressableSigner) error {
-	if len(signingAccounts) != 1 {
-		return fmt.Errorf("UnbondTx expects a single AddressableSigner for its signature but %v were provided",
-			len(signingAccounts))
-	}
-	var err error
-	tx.Signature, err = crypto.ChainSign(signingAccounts[0], chainID, tx)
-	if err != nil {
-		return fmt.Errorf("could not sign %v: %v", tx, err)
-	}
-	return nil
-}
-
-func (tx *UnbondTx) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
-	wire.WriteTo([]byte(fmt.Sprintf(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err)
-	wire.WriteTo([]byte(fmt.Sprintf(`,"tx":[%v,{"address":"%s","height":%v}]}`, TxTypeUnbond, tx.Address, tx.Height)), w, n, err)
-}
-
-func (tx *UnbondTx) GetInputs() []TxInput {
-	return nil
-}
-
-func (tx *UnbondTx) String() string {
-	return fmt.Sprintf("UnbondTx{%s,%v,%v}", tx.Address, tx.Height, tx.Signature)
-}
-
-func (tx *UnbondTx) Hash(chainID string) []byte {
-	return tx.txHashMemoizer.hash(chainID, tx)
-}