diff --git a/execution/errors/errors.go b/execution/errors/errors.go
index 8e3bd2cc144312712983a4ddb2418dea055daace..b3bc554166d19ec2900db0c186d47069a8d06edc 100644
--- a/execution/errors/errors.go
+++ b/execution/errors/errors.go
@@ -32,6 +32,12 @@ const (
 	ErrorCodeEventPublish
 	ErrorCodeInvalidString
 	ErrorCodeEventMapping
+	ErrorCodeInvalidAddress
+	ErrorCodeDuplicateAddress
+	ErrorCodeInsufficientFunds
+	ErrorCodeOverpayment
+	ErrorCodeZeroPayment
+	ErrorCodeInvalidSequence
 )
 
 func (c Code) ErrorCode() Code {
@@ -39,6 +45,10 @@ func (c Code) ErrorCode() Code {
 }
 
 func (c Code) Error() string {
+	return fmt.Sprintf("Error %d: %s", c, c.String())
+}
+
+func (c Code) String() string {
 	switch c {
 	case ErrorCodeUnknownAddress:
 		return "Unknown address"
@@ -82,6 +92,18 @@ func (c Code) Error() string {
 		return "Event mapping error"
 	case ErrorCodeGeneric:
 		return "Generic error"
+	case ErrorCodeInvalidAddress:
+		return "Invalid address"
+	case ErrorCodeDuplicateAddress:
+		return "Duplicate address"
+	case ErrorCodeInsufficientFunds:
+		return "Insufficient funds"
+	case ErrorCodeOverpayment:
+		return "Overpayment"
+	case ErrorCodeZeroPayment:
+		return "Zero payment error"
+	case ErrorCodeInvalidSequence:
+		return "Invalid sequence number"
 	default:
 		return "Unknown error"
 	}
diff --git a/execution/errors/errors_test.go b/execution/errors/errors_test.go
index 2a4a0a37bfda11113edfeb08417a37cf497c0e25..c80c057229958daa077b7b7e19bb6aa589b33c7b 100644
--- a/execution/errors/errors_test.go
+++ b/execution/errors/errors_test.go
@@ -2,6 +2,7 @@ package errors
 
 import (
 	"encoding/json"
+	"fmt"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -9,7 +10,7 @@ import (
 )
 
 func TestErrorCode_MarshalJSON(t *testing.T) {
-	ec := NewCodedError(ErrorCodeDataStackOverflow, "arrg")
+	ec := NewCodedError(ErrorCodeDataStackOverflow, "arrgh")
 	bs, err := json.Marshal(ec)
 	require.NoError(t, err)
 
@@ -19,3 +20,8 @@ func TestErrorCode_MarshalJSON(t *testing.T) {
 
 	assert.Equal(t, ec, ecOut)
 }
+
+func TestCode_String(t *testing.T) {
+	err := ErrorCodeCodeOutOfBounds
+	fmt.Println(err.Error())
+}
diff --git a/execution/executors/call_context.go b/execution/executors/call_context.go
index 78fa25e4a7218770693655e267da7a433d428bfd..0f343819c67bdc7e4167fcdb5a6c0a936f0b8472 100644
--- a/execution/executors/call_context.go
+++ b/execution/executors/call_context.go
@@ -58,7 +58,7 @@ func (ctx *CallContext) Precheck() (*acm.MutableAccount, acm.Account, error) {
 	if inAcc == nil {
 		ctx.Logger.InfoMsg("Cannot find input account",
 			"tx_input", ctx.tx.Input)
-		return nil, nil, payload.ErrTxInvalidAddress
+		return nil, nil, errors.ErrorCodeInvalidAddress
 	}
 
 	err = validateInput(inAcc, ctx.tx.Input)
@@ -70,7 +70,7 @@ func (ctx *CallContext) Precheck() (*acm.MutableAccount, acm.Account, error) {
 	if ctx.tx.Input.Amount < ctx.tx.Fee {
 		ctx.Logger.InfoMsg("Sender did not send enough to cover the fee",
 			"tx_input", ctx.tx.Input)
-		return nil, nil, payload.ErrTxInsufficientFunds
+		return nil, nil, errors.ErrorCodeInsufficientFunds
 	}
 
 	ctx.Logger.TraceMsg("Incrementing sequence number for CallTx",
@@ -191,7 +191,7 @@ func (ctx *CallContext) Deliver(inAcc, outAcc acm.Account, value uint64) error {
 					"caller_address", inAcc.Address(),
 					"callee_address", ctx.tx.Address)
 			}
-			ctx.CallEvents(payload.ErrTxInvalidAddress)
+			ctx.CallEvents(errors.ErrorCodeInvalidAddress)
 			return nil
 		}
 		callee = acm.AsMutableAccount(outAcc)
diff --git a/execution/executors/name_context.go b/execution/executors/name_context.go
index ce8396704b35f75b58d536f017282ecaab9589d6..3be220f9f4d5d23ce0eab129b324495c77a6568e 100644
--- a/execution/executors/name_context.go
+++ b/execution/executors/name_context.go
@@ -42,7 +42,7 @@ func (ctx *NameContext) Execute(txe *exec.TxExecution) error {
 	if inAcc == nil {
 		ctx.Logger.InfoMsg("Cannot find input account",
 			"tx_input", ctx.tx.Input)
-		return payload.ErrTxInvalidAddress
+		return errors.ErrorCodeInvalidAddress
 	}
 	// check permission
 	if !hasNamePermission(ctx.StateWriter, inAcc, ctx.Logger) {
@@ -57,7 +57,7 @@ func (ctx *NameContext) Execute(txe *exec.TxExecution) error {
 	if ctx.tx.Input.Amount < ctx.tx.Fee {
 		ctx.Logger.InfoMsg("Sender did not send enough to cover the fee",
 			"tx_input", ctx.tx.Input)
-		return payload.ErrTxInsufficientFunds
+		return errors.ErrorCodeInsufficientFunds
 	}
 
 	// validate the input strings
diff --git a/execution/executors/permissions_context.go b/execution/executors/permissions_context.go
index 39292a3b0f8cd20c74cb1b0d78ad4c54b0f02f88..11730e1376886a0dfebaf22b39bd00adc45e8491 100644
--- a/execution/executors/permissions_context.go
+++ b/execution/executors/permissions_context.go
@@ -7,6 +7,7 @@ import (
 	"github.com/hyperledger/burrow/acm/state"
 	"github.com/hyperledger/burrow/blockchain"
 	"github.com/hyperledger/burrow/crypto"
+	"github.com/hyperledger/burrow/execution/errors"
 	"github.com/hyperledger/burrow/execution/exec"
 	"github.com/hyperledger/burrow/logging"
 	"github.com/hyperledger/burrow/logging/structure"
@@ -35,7 +36,7 @@ func (ctx *PermissionsContext) Execute(txe *exec.TxExecution) error {
 	if inAcc == nil {
 		ctx.Logger.InfoMsg("Cannot find input account",
 			"tx_input", ctx.tx.Input)
-		return payload.ErrTxInvalidAddress
+		return errors.ErrorCodeInvalidAddress
 	}
 
 	err = ctx.tx.PermArgs.EnsureValid()
diff --git a/execution/executors/send_context.go b/execution/executors/send_context.go
index 5cad86303d48049084e716abb33c803233a3c5f3..813a433bf6b093550331ab101a14c0a8b0f04db9 100644
--- a/execution/executors/send_context.go
+++ b/execution/executors/send_context.go
@@ -5,6 +5,7 @@ import (
 
 	"github.com/hyperledger/burrow/acm/state"
 	"github.com/hyperledger/burrow/blockchain"
+	"github.com/hyperledger/burrow/execution/errors"
 	"github.com/hyperledger/burrow/execution/exec"
 	"github.com/hyperledger/burrow/logging"
 	"github.com/hyperledger/burrow/txs/payload"
@@ -49,7 +50,13 @@ func (ctx *SendContext) Execute(txe *exec.TxExecution) error {
 		return err
 	}
 	if outTotal > inTotal {
-		return payload.ErrTxInsufficientFunds
+		return errors.ErrorCodeInsufficientFunds
+	}
+	if outTotal < inTotal {
+		return errors.ErrorCodeOverpayment
+	}
+	if outTotal == 0 {
+		return errors.ErrorCodeZeroPayment
 	}
 
 	// Good! Adjust accounts
diff --git a/execution/executors/shared.go b/execution/executors/shared.go
index 58904bb98efddc27cd0df9e2a4e3ef7cbc6e94da..33ad90a0de8bfe602cdf8d60c5d169c4d958da80 100644
--- a/execution/executors/shared.go
+++ b/execution/executors/shared.go
@@ -6,6 +6,7 @@ import (
 	"github.com/hyperledger/burrow/acm"
 	"github.com/hyperledger/burrow/acm/state"
 	"github.com/hyperledger/burrow/crypto"
+	"github.com/hyperledger/burrow/execution/errors"
 	"github.com/hyperledger/burrow/logging"
 	"github.com/hyperledger/burrow/logging/structure"
 	"github.com/hyperledger/burrow/permission"
@@ -23,14 +24,14 @@ func getInputs(accountGetter state.AccountGetter,
 	for _, in := range ins {
 		// Account shouldn't be duplicated
 		if _, ok := accounts[in.Address]; ok {
-			return nil, payload.ErrTxDuplicateAddress
+			return nil, errors.ErrorCodeDuplicateAddress
 		}
 		acc, err := state.GetMutableAccount(accountGetter, in.Address)
 		if err != nil {
 			return nil, err
 		}
 		if acc == nil {
-			return nil, payload.ErrTxInvalidAddress
+			return nil, errors.ErrorCodeInvalidAddress
 		}
 		accounts[in.Address] = acc
 	}
@@ -48,7 +49,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, payload.ErrTxDuplicateAddress
+			return nil, errors.ErrorCodeDuplicateAddress
 		}
 		acc, err := state.GetMutableAccount(accountGetter, out.Address)
 		if err != nil {
@@ -92,20 +93,16 @@ func validateInputs(accs map[crypto.Address]*acm.MutableAccount, ins []*payload.
 }
 
 func validateInput(acc *acm.MutableAccount, in *payload.TxInput) error {
-	// Check TxInput basic
-	if err := in.ValidateBasic(); err != nil {
-		return err
-	}
 	// Check sequences
 	if acc.Sequence()+1 != uint64(in.Sequence) {
 		return payload.ErrTxInvalidSequence{
-			Got:      in.Sequence,
-			Expected: acc.Sequence() + uint64(1),
+			Input:   in,
+			Account: acc,
 		}
 	}
 	// Check amount
 	if acc.Balance() < uint64(in.Amount) {
-		return payload.ErrTxInsufficientFunds
+		return errors.ErrorCodeInsufficientFunds
 	}
 	return nil
 }
@@ -113,10 +110,6 @@ func validateInput(acc *acm.MutableAccount, in *payload.TxInput) error {
 func validateOutputs(outs []*payload.TxOutput) (uint64, error) {
 	total := uint64(0)
 	for _, out := range outs {
-		// Check TxOutput basic
-		if err := out.ValidateBasic(); err != nil {
-			return 0, err
-		}
 		// Good. Add amount to total
 		total += out.Amount
 	}
diff --git a/txs/payload/errors.go b/txs/payload/errors.go
index affd564371667d29d81218fdf8316182c5ff6a91..0c056c4f317157e52ff24268f632b1007b5c6e88 100644
--- a/txs/payload/errors.go
+++ b/txs/payload/errors.go
@@ -1,25 +1,22 @@
 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")
+	"github.com/hyperledger/burrow/acm"
+	"github.com/hyperledger/burrow/execution/errors"
 )
 
 type ErrTxInvalidSequence struct {
-	Got      uint64
-	Expected uint64
+	Input   *TxInput
+	Account acm.Account
 }
 
 func (e ErrTxInvalidSequence) Error() string {
-	return fmt.Sprintf("Error invalid sequence. Got %d, expected %d", e.Got, e.Expected)
+	return fmt.Sprintf("Error invalid sequence in input %v: input has sequence %d, but account has expected "+
+		"%d, so expected input to have sequence %d", e.Input, e.Input.Sequence, e.Account.Sequence(), e.Account.Sequence()+1)
+}
+
+func (e ErrTxInvalidSequence) ErrorCode() errors.Code {
+	return errors.ErrorCodeInvalidSequence
 }
diff --git a/txs/payload/tx_input.go b/txs/payload/tx_input.go
index 0a69fa2c401b79f76df1c659b835a66c1b094cef..ff02d7cf9e313130a94e15385c1a584c4bad720a 100644
--- a/txs/payload/tx_input.go
+++ b/txs/payload/tx_input.go
@@ -2,20 +2,8 @@ package payload
 
 import (
 	"fmt"
-
-	"github.com/hyperledger/burrow/crypto"
 )
 
-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, Amount: %v, Sequence:%v}", txIn.Address, txIn.Amount, txIn.Sequence)
 }
diff --git a/txs/payload/tx_output.go b/txs/payload/tx_output.go
index dcd6d73ce1604d1392e200a0d99aff62de51a31a..f32a29f7ae1af7b1c4a4c07e3bbf6c0f58bc3178 100644
--- a/txs/payload/tx_output.go
+++ b/txs/payload/tx_output.go
@@ -4,16 +4,6 @@ import (
 	"fmt"
 )
 
-func (txOut *TxOutput) ValidateBasic() error {
-	if len(txOut.Address) != 20 {
-		return ErrTxInvalidAddress
-	}
-	if txOut.Amount == 0 {
-		return ErrTxInvalidAmount
-	}
-	return nil
-}
-
 func (txOut *TxOutput) String() string {
 	return fmt.Sprintf("TxOutput{%s, Amount: %v}", txOut.Address, txOut.Amount)
 }