Repository: coinbase/rosetta-sdk-go
Pull request #139 (2020-09-09 00:30:38)

[Specification Upgrade] v1.4.4 Support
v1.4.4 Release: https://github.com/coinbase/rosetta-specifications/releases/tag/v1.4.4

This PR updates `types` to handle deprecated string-based `Address` fields automatically using a custom marshaler, `storage` to only use `AccountIdentifier` for tracking account-related information, `constructor` to only use `AccountIdentifier` for interacting with accounts, and the `asserter` to correctly validate responses.

### Changes
- [x] update codegen
- [x] put automatic signing payload population in marshaler (like hex bytes conversion)
  - [x] don't even expose address in the processed struct
- [x] Modify `KeyStorage` to store `AccountIdentifier`
- [x] Add test for `ContainsAccountIdentifier`
- [x] Modify `BroadcastStorage` to store and load `AccountIdentifier`
- [x] Modify `Constructor` save address function to accept `AccountIdentifier`
- [x] Fix comments on new codegen files


Patch:
diff --git a/asserter/construction.go b/asserter/construction.go
index 90eb75a..ae8fffd 100644
--- a/asserter/construction.go
+++ b/asserter/construction.go
@@ -102,8 +102,8 @@ func ConstructionDeriveResponse(
 		return ErrConstructionDeriveResponseIsNil
 	}
 
-	if len(response.Address) == 0 {
-		return ErrConstructionDeriveResponseAddrEmpty
+	if err := AccountIdentifier(response.AccountIdentifier); err != nil {
+		return fmt.Errorf("%w: %s", ErrConstructionDeriveResponseAddrEmpty, err.Error())
 	}
 
 	return nil
@@ -133,20 +133,26 @@ func (a *Asserter) ConstructionParseResponse(
 		return fmt.Errorf("%w unable to parse operations", err)
 	}
 
-	if signed && len(response.Signers) == 0 {
+	if signed && len(response.AccountIdentifierSigners) == 0 {
 		return ErrConstructionParseResponseSignersEmptyOnSignedTx
 	}
 
-	if !signed && len(response.Signers) > 0 {
+	if !signed && len(response.AccountIdentifierSigners) > 0 {
 		return ErrConstructionParseResponseSignersNonEmptyOnUnsignedTx
 	}
 
-	for i, signer := range response.Signers {
-		if len(signer) == 0 {
+	for i, signer := range response.AccountIdentifierSigners {
+		if err := AccountIdentifier(signer); err != nil {
 			return fmt.Errorf("%w: at index %d", ErrConstructionParseResponseSignerEmpty, i)
 		}
 	}
 
+	if len(response.AccountIdentifierSigners) > 0 {
+		if err := AccountArray("signers", response.AccountIdentifierSigners); err != nil {
+			return fmt.Errorf("%w: %s", ErrConstructionParseResponseDuplicateSigner, err.Error())
+		}
+	}
+
 	return nil
 }
 
@@ -223,8 +229,8 @@ func SigningPayload(
 		return ErrSigningPayloadIsNil
 	}
 
-	if len(signingPayload.Address) == 0 {
-		return ErrSigningPayloadAddrEmpty
+	if err := AccountIdentifier(signingPayload.AccountIdentifier); err != nil {
+		return fmt.Errorf("%w: %s", ErrSigningPayloadAddrEmpty, err)
 	}
 
 	if len(signingPayload.Bytes) == 0 {
diff --git a/asserter/errors.go b/asserter/errors.go
index 7be181a..a0710ee 100644
--- a/asserter/errors.go
+++ b/asserter/errors.go
@@ -180,8 +180,9 @@ var (
 	ErrConstructionParseResponseSignersNonEmptyOnUnsignedTx = errors.New(
 		"signers should be empty for unsigned txs",
 	)
-	ErrConstructionParseResponseSignerEmpty = errors.New("signer cannot be empty string")
-	ErrConstructionPayloadsResponseIsNil    = errors.New(
+	ErrConstructionParseResponseSignerEmpty     = errors.New("signer cannot be empty string")
+	ErrConstructionParseResponseDuplicateSigner = errors.New("found duplicate signer")
+	ErrConstructionPayloadsResponseIsNil        = errors.New(
 		"construction payloads response cannot be nil",
 	)
 	ErrConstructionPayloadsResponseUnsignedTxEmpty = errors.New(
diff --git a/asserter/network.go b/asserter/network.go
index 6a4fd62..8564afe 100644
--- a/asserter/network.go
+++ b/asserter/network.go
@@ -15,8 +15,6 @@
 package asserter
 
 import (
-	"fmt"
-
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
@@ -78,29 +76,6 @@ func Version(version *types.Version) error {
 	return nil
 }
 
-// StringArray ensures all strings in an array
-// are non-empty strings and not duplicates.
-func StringArray(arrName string, arr []string) error {
-	if len(arr) == 0 {
-		return fmt.Errorf("no %s found", arrName)
-	}
-
-	parsed := make([]string, len(arr))
-	for i, s := range arr {
-		if s == "" {
-			return fmt.Errorf("%s has an empty string", arrName)
-		}
-
-		if containsString(parsed, s) {
-			return fmt.Errorf("%s contains a duplicate %s", arrName, s)
-		}
-
-		parsed[i] = s
-	}
-
-	return nil
-}
-
 // NetworkStatusResponse ensures any types.NetworkStatusResponse
 // is valid.
 func NetworkStatusResponse(response *types.NetworkStatusResponse) error {
diff --git a/asserter/utils.go b/asserter/utils.go
new file mode 100644
index 0000000..c367f16
--- /dev/null
+++ b/asserter/utils.go
@@ -0,0 +1,67 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package asserter
+
+import (
+	"fmt"
+
+	"github.com/coinbase/rosetta-sdk-go/types"
+)
+
+// StringArray ensures all strings in an array
+// are non-empty strings and not duplicates.
+func StringArray(arrName string, arr []string) error {
+	if len(arr) == 0 {
+		return fmt.Errorf("no %s found", arrName)
+	}
+
+	parsed := map[string]struct{}{}
+	for _, s := range arr {
+		if s == "" {
+			return fmt.Errorf("%s has an empty string", arrName)
+		}
+
+		if _, ok := parsed[s]; ok {
+			return fmt.Errorf("%s contains a duplicate %s", arrName, s)
+		}
+
+		parsed[s] = struct{}{}
+	}
+
+	return nil
+}
+
+// AccountArray ensures all *types.AccountIdentifier in an array
+// are valid and not duplicates.
+func AccountArray(arrName string, arr []*types.AccountIdentifier) error {
+	if len(arr) == 0 {
+		return fmt.Errorf("no %s found", arrName)
+	}
+
+	parsed := map[string]struct{}{}
+	for _, s := range arr {
+		if err := AccountIdentifier(s); err != nil {
+			return fmt.Errorf("%s has an invalid account identifier", arrName)
+		}
+
+		if _, ok := parsed[types.Hash(s)]; ok {
+			return fmt.Errorf("%s contains a duplicate %s", arrName, types.PrintStruct(s))
+		}
+
+		parsed[types.Hash(s)] = struct{}{}
+	}
+
+	return nil
+}
diff --git a/client/api_construction.go b/client/api_construction.go
index 4689ebb..ef38103 100644
--- a/client/api_construction.go
+++ b/client/api_construction.go
@@ -103,9 +103,8 @@ func (a *ConstructionAPIService) ConstructionCombine(
 	return &v, nil, nil
 }
 
-// ConstructionDerive Derive returns the network-specific address associated with a public key.
-// Blockchains that require an on-chain action to create an account should not implement this
-// method.
+// ConstructionDerive Derive returns the AccountIdentifier associated with a public key. Blockchains
+// that require an on-chain action to create an account should not implement this method.
 func (a *ConstructionAPIService) ConstructionDerive(
 	ctx _context.Context,
 	constructionDeriveRequest *types.ConstructionDeriveRequest,
@@ -390,12 +389,12 @@ func (a *ConstructionAPIService) ConstructionParse(
 
 // ConstructionPayloads Payloads is called with an array of operations and the response from
 // /construction/metadata. It returns an unsigned transaction blob and a collection of payloads that
-// must be signed by particular addresses using a certain SignatureType. The array of operations
-// provided in transaction construction often times can not specify all \effects\ of a transaction
-// (consider invoked transactions in Ethereum). However, they can deterministically specify the
-// \intent\ of the transaction, which is sufficient for construction. For this reason, parsing the
-// corresponding transaction in the Data API (when it lands on chain) will contain a superset of
-// whatever operations were provided during construction.
+// must be signed by particular AccountIdentifiers using a certain SignatureType. The array of
+// operations provided in transaction construction often times can not specify all \effects\ of a
+// transaction (consider invoked transactions in Ethereum). However, they can deterministically
+// specify the \intent\ of the transaction, which is sufficient for construction. For this reason,
+// parsing the corresponding transaction in the Data API (when it lands on chain) will contain a
+// superset of whatever operations were provided during construction.
 func (a *ConstructionAPIService) ConstructionPayloads(
 	ctx _context.Context,
 	constructionPayloadsRequest *types.ConstructionPayloadsRequest,
diff --git a/client/client.go b/client/client.go
index 9f938c7..2165e39 100644
--- a/client/client.go
+++ b/client/client.go
@@ -36,7 +36,7 @@ var (
 	jsonCheck = regexp.MustCompile(`(?i:(?:application|text)/(?:vnd\.[^;]+\+)?json)`)
 )
 
-// APIClient manages communication with the Rosetta API v1.4.3
+// APIClient manages communication with the Rosetta API v1.4.4
 // In most cases there should be only one, shared, APIClient.
 type APIClient struct {
 	cfg    *Configuration
diff --git a/codegen.sh b/codegen.sh
index 4111b98..7c3c54e 100755
--- a/codegen.sh
+++ b/codegen.sh
@@ -53,7 +53,7 @@ done
 rm -rf tmp;
 
 # Download spec file from releases
-ROSETTA_SPEC_VERSION=v1.4.3
+ROSETTA_SPEC_VERSION=v1.4.4
 curl -L https://github.com/coinbase/rosetta-specifications/releases/download/${ROSETTA_SPEC_VERSION}/api.json -o api.json;
 
 # Generate client + types code
@@ -150,6 +150,9 @@ sed "${SED_IFLAG[@]}" 's/\*\[\]/\[\]\*/g' client/* server/*;
 # Fix map pointers
 sed "${SED_IFLAG[@]}" 's/\*map/map/g' client/* server/*;
 
+# Fix string array pointers
+sed "${SED_IFLAG[@]}" 's/\[\]\*string/\[\]string/g' client/* server/*;
+
 # Move model files to types/
 mv client/model_*.go types/;
 for file in types/model_*.go; do
@@ -180,6 +183,14 @@ do
     \)/g' "${file}";
 done
 
+# Override certain types with complex marshaling
+OVERRIDDEN_TYPES=( signing_payload construction_derive_response construction_parse_response )
+for type in "${OVERRIDDEN_TYPES[@]}"
+do
+  echo "Overriding ${type}";
+  rm "types/${type}.go" && cp "templates/${type}.txt" "types/${type}.go";
+done
+
 # Format client generated code
 FORMAT_GEN="gofmt -w /local/types; gofmt -w /local/client; gofmt -w /local/server"
 GOLANG_VERSION=1.13
diff --git a/constructor/coordinator/coordinator.go b/constructor/coordinator/coordinator.go
index 4aa5bfa..351541f 100644
--- a/constructor/coordinator/coordinator.go
+++ b/constructor/coordinator/coordinator.go
@@ -205,7 +205,7 @@ func (c *Coordinator) createTransaction(
 
 	publicKeys := make([]*types.PublicKey, len(requiredPublicKeys))
 	for i, accountIdentifier := range requiredPublicKeys {
-		keyPair, err := c.helper.GetKey(ctx, dbTx, accountIdentifier.Address)
+		keyPair, err := c.helper.GetKey(ctx, dbTx, accountIdentifier)
 		if err != nil {
 			return nil, "", fmt.Errorf(
 				"%w: unable to find key for address %s",
diff --git a/constructor/coordinator/types.go b/constructor/coordinator/types.go
index fa36caa..bd1b5cf 100644
--- a/constructor/coordinator/types.go
+++ b/constructor/coordinator/types.go
@@ -49,12 +49,12 @@ type Helper interface {
 	// This is used to update jobs and enque them for broadcast atomically.
 	DatabaseTransaction(context.Context) storage.DatabaseTransaction
 
-	// StoreKey is called to persist an
-	// address + KeyPair.
+	// StoreKey is called to persist a
+	// *types.AccountIdentifier + KeyPair.
 	StoreKey(
 		context.Context,
 		storage.DatabaseTransaction,
-		string,
+		*types.AccountIdentifier,
 		*keys.KeyPair,
 	) error
 
@@ -63,21 +63,21 @@ type Helper interface {
 	GetKey(
 		context.Context,
 		storage.DatabaseTransaction,
-		string, // address
+		*types.AccountIdentifier,
 	) (*keys.KeyPair, error)
 
-	// AllAddresses returns a slice of all known addresses.
-	AllAddresses(
+	// AllAccounts returns a slice of all known *types.AccountIdentifier.
+	AllAccounts(
 		context.Context,
 		storage.DatabaseTransaction,
-	) ([]string, error)
+	) ([]*types.AccountIdentifier, error)
 
-	// LockedAccounts is a slice of all addresses currently sending or receiving
+	// LockedAccounts is a slice of all *types.AccountIdentifier currently sending or receiving
 	// funds.
-	LockedAddresses(
+	LockedAccounts(
 		context.Context,
 		storage.DatabaseTransaction,
-	) ([]string, error)
+	) ([]*types.AccountIdentifier, error)
 
 	// Balance returns the balance
 	// for a provided address.
@@ -112,13 +112,13 @@ type Helper interface {
 		int64, // confirmation depth
 	) error
 
-	// Derive returns a new address for a provided publicKey.
+	// Derive returns a new *types.AccountIdentifier for a provided publicKey.
 	Derive(
 		context.Context,
 		*types.NetworkIdentifier,
 		*types.PublicKey,
 		map[string]interface{},
-	) (string, map[string]interface{}, error)
+	) (*types.AccountIdentifier, map[string]interface{}, error)
 
 	// Preprocess calls the /construction/preprocess endpoint
 	// on an offline node.
@@ -155,7 +155,7 @@ type Helper interface {
 		*types.NetworkIdentifier,
 		bool, // signed
 		string, // transaction
-	) ([]*types.Operation, []string, map[string]interface{}, error)
+	) ([]*types.Operation, []*types.AccountIdentifier, map[string]interface{}, error)
 
 	// Combine calls the /construction/combine endpoint
 	// using the offline node.
diff --git a/constructor/job/types.go b/constructor/job/types.go
index dd713b6..5f33535 100644
--- a/constructor/job/types.go
+++ b/constructor/job/types.go
@@ -59,9 +59,9 @@ const (
 	// GenerateKey creates a new *keys.KeyPair.
 	GenerateKey ActionType = "generate_key"
 
-	// SaveAddress saves a generated *keys.KeyPair
-	// and address to key storage.
-	SaveAddress ActionType = "save_address"
+	// SaveAccount saves a generated *keys.KeyPair
+	// and *types.AccountIdentifier to key storage.
+	SaveAccount ActionType = "save_account"
 
 	// Derive calls `/construction/derive` with a *keys.PublicKey.
 	Derive ActionType = "derive"
@@ -115,10 +115,10 @@ type GenerateKeyInput struct {
 	CurveType types.CurveType `json:"curve_type"`
 }
 
-// SaveAddressInput is the input for SaveAddress.
-type SaveAddressInput struct {
-	Address string        `json:"address"`
-	KeyPair *keys.KeyPair `json:"keypair"`
+// SaveAccountInput is the input for SaveAccount.
+type SaveAccountInput struct {
+	AccountIdentifier *types.AccountIdentifier `json:"account_identifier"`
+	KeyPair           *keys.KeyPair            `json:"keypair"`
 }
 
 // RandomStringInput is the input to RandomString.
@@ -151,22 +151,28 @@ type MathInput struct {
 
 // FindBalanceInput is the input to FindBalance.
 type FindBalanceInput struct {
-	// Address can be optionally provided to ensure the balance returned
+	// AccountIdentifier can be optionally provided to ensure the balance returned
 	// is for a particular address (this is used when fetching the balance
 	// of the same account in multiple currencies, when requesting funds,
 	// or when constructing a multi-input UTXO transfer with the
 	// same address).
-	Address string `json:"address,omitempty"`
+	AccountIdentifier *types.AccountIdentifier `json:"account_identifier,omitempty"`
+
+	// SubAccountIdentifier can be used to find addresses with particular
+	// SubAccount balances. This is particularly useful for
+	// orchestrating staking transactions.
+	SubAccountIdentifier *types.SubAccountIdentifier `json:"sub_account_identifier,omitempty"`
 
 	// NotAddress can be populated to ensure a different
 	// address is found. This is useful when avoiding a
 	// self-transfer.
 	NotAddress []string `json:"not_address,omitempty"`
 
-	// SubAccount can be used to find addresses with particular
-	// SubAccount balances. This is particularly useful for
-	// orchestrating staking transactions.
-	SubAccount *types.SubAccountIdentifier `json:"sub_account,omitempty"`
+	// NotAccountIdentifier can be used to avoid entire
+	// *types.AccountIdentifiers and is used when only
+	// certain SubAccountIdentifiers of an Address are
+	// desired.
+	NotAccountIdentifier []*types.AccountIdentifier `json:"not_account_identifier,omitempty"`
 
 	// MinimumBalance is the minimum required balance that must be found.
 	MinimumBalance *types.Amount `json:"minimum_balance,omitempty"`
@@ -193,9 +199,9 @@ type FindBalanceInput struct {
 
 // FindBalanceOutput is returned by FindBalance.
 type FindBalanceOutput struct {
-	// Account is the account associated with the balance
+	// AccountIdentifier is the account associated with the balance
 	// (and coin).
-	Account *types.AccountIdentifier `json:"account"`
+	AccountIdentifier *types.AccountIdentifier `json:"account_identifier"`
 
 	// Balance found at a particular currency.
 	Balance *types.Amount `json:"balance"`
diff --git a/constructor/worker/types.go b/constructor/worker/types.go
index 6788a28..38d7f46 100644
--- a/constructor/worker/types.go
+++ b/constructor/worker/types.go
@@ -24,27 +24,27 @@ import (
 
 // Helper is used by the worker to process Jobs.
 type Helper interface {
-	// StoreKey is called to persist an
-	// address + KeyPair.
+	// StoreKey is called to persist a
+	// *types.AccountIdentifier + KeyPair.
 	StoreKey(
 		context.Context,
 		storage.DatabaseTransaction,
-		string,
+		*types.AccountIdentifier,
 		*keys.KeyPair,
 	) error
 
-	// AllAddresses returns a slice of all known addresses.
-	AllAddresses(
+	// AllAccounts returns a slice of all known *types.AccountIdentifier.
+	AllAccounts(
 		context.Context,
 		storage.DatabaseTransaction,
-	) ([]string, error)
+	) ([]*types.AccountIdentifier, error)
 
-	// LockedAccounts is a slice of all addresses currently sending or receiving
+	// LockedAccounts is a slice of all *types.AccountIdentifier currently sending or receiving
 	// funds.
-	LockedAddresses(
+	LockedAccounts(
 		context.Context,
 		storage.DatabaseTransaction,
-	) ([]string, error)
+	) ([]*types.AccountIdentifier, error)
 
 	// Balance returns the balance
 	// for a provided address and currency.
@@ -63,13 +63,13 @@ type Helper interface {
 		*types.Currency,
 	) ([]*types.Coin, error)
 
-	// Derive returns a new address for a provided publicKey.
+	// Derive returns a new *types.AccountIdentifier for a provided publicKey.
 	Derive(
 		context.Context,
 		*types.NetworkIdentifier,
 		*types.PublicKey,
 		map[string]interface{},
-	) (string, map[string]interface{}, error)
+	) (*types.AccountIdentifier, map[string]interface{}, error)
 }
 
 // Worker processes jobs.
diff --git a/constructor/worker/worker.go b/constructor/worker/worker.go
index f9d787a..22d3591 100644
--- a/constructor/worker/worker.go
+++ b/constructor/worker/worker.go
@@ -16,6 +16,7 @@ package worker
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"log"
 
@@ -52,8 +53,8 @@ func (w *Worker) invokeWorker(
 		return GenerateKeyWorker(input)
 	case job.Derive:
 		return w.DeriveWorker(ctx, input)
-	case job.SaveAddress:
-		return "", w.SaveAddressWorker(ctx, dbTx, input)
+	case job.SaveAccount:
+		return "", w.SaveAccountWorker(ctx, dbTx, input)
 	case job.PrintMessage:
 		PrintMessageWorker(input)
 		return "", nil
@@ -138,7 +139,7 @@ func (w *Worker) Process(
 	return j.CreateBroadcast()
 }
 
-// DeriveWorker attempts to derive an address given a
+// DeriveWorker attempts to derive an account given a
 // *types.ConstructionDeriveRequest input.
 func (w *Worker) DeriveWorker(
 	ctx context.Context,
@@ -154,7 +155,7 @@ func (w *Worker) DeriveWorker(
 		return "", fmt.Errorf("%w: %s", ErrInvalidInput, err.Error())
 	}
 
-	address, metadata, err := w.helper.Derive(
+	accountIdentifier, metadata, err := w.helper.Derive(
 		ctx,
 		input.NetworkIdentifier,
 		input.PublicKey,
@@ -164,9 +165,9 @@ func (w *Worker) DeriveWorker(
 		return "", fmt.Errorf("%w: %s", ErrActionFailed, err.Error())
 	}
 
-	return types.PrintStruct(types.ConstructionDeriveResponse{
-		Address:  address,
-		Metadata: metadata,
+	return types.PrintStruct(&types.ConstructionDeriveResponse{
+		AccountIdentifier: accountIdentifier,
+		Metadata:          metadata,
 	}), nil
 }
 
@@ -187,24 +188,24 @@ func GenerateKeyWorker(rawInput string) (string, error) {
 	return types.PrintStruct(kp), nil
 }
 
-// SaveAddressWorker saves an address and associated KeyPair
+// SaveAccountWorker saves a *types.AccountIdentifier and associated KeyPair
 // in KeyStorage.
-func (w *Worker) SaveAddressWorker(
+func (w *Worker) SaveAccountWorker(
 	ctx context.Context,
 	dbTx storage.DatabaseTransaction,
 	rawInput string,
 ) error {
-	var input job.SaveAddressInput
+	var input job.SaveAccountInput
 	err := job.UnmarshalInput([]byte(rawInput), &input)
 	if err != nil {
 		return fmt.Errorf("%w: %s", ErrInvalidInput, err.Error())
 	}
 
-	if len(input.Address) == 0 {
-		return fmt.Errorf("%w: %s", ErrInvalidInput, "empty address")
+	if err := asserter.AccountIdentifier(input.AccountIdentifier); err != nil {
+		return fmt.Errorf("%w: %s", ErrInvalidInput, err.Error())
 	}
 
-	if err := w.helper.StoreKey(ctx, dbTx, input.Address, input.KeyPair); err != nil {
+	if err := w.helper.StoreKey(ctx, dbTx, input.AccountIdentifier, input.KeyPair); err != nil {
 		return fmt.Errorf("%w: %s", ErrActionFailed, err.Error())
 	}
 
@@ -294,19 +295,19 @@ func balanceMessage(input *job.FindBalanceInput) string {
 		types.PrintStruct(input.MinimumBalance),
 	)
 
-	if len(input.Address) > 0 {
+	if input.AccountIdentifier != nil {
 		message = fmt.Sprintf(
-			"%s on address %s",
+			"%s on account %s",
 			message,
-			input.Address,
+			types.PrintStruct(input.AccountIdentifier),
 		)
 	}
 
-	if input.SubAccount != nil {
+	if input.SubAccountIdentifier != nil {
 		message = fmt.Sprintf(
 			"%s with sub_account %s",
 			message,
-			types.PrintStruct(input.SubAccount),
+			types.PrintStruct(input.SubAccountIdentifier),
 		)
 	}
 
@@ -318,6 +319,14 @@ func balanceMessage(input *job.FindBalanceInput) string {
 		)
 	}
 
+	if len(input.NotAccountIdentifier) > 0 {
+		message = fmt.Sprintf(
+			"%s != to accounts %s",
+			message,
+			types.PrintStruct(input.NotAccountIdentifier),
+		)
+	}
+
 	if len(input.NotCoins) > 0 {
 		message = fmt.Sprintf(
 			"%s != to coins %s",
@@ -364,10 +373,10 @@ func (w *Worker) checkAccountCoins(
 			continue
 		}
 
-		return types.PrintStruct(job.FindBalanceOutput{
-			Account: account,
-			Balance: coin.Amount,
-			Coin:    coin.CoinIdentifier,
+		return types.PrintStruct(&job.FindBalanceOutput{
+			AccountIdentifier: account,
+			Balance:           coin.Amount,
+			Coin:              coin.CoinIdentifier,
 		}), nil
 	}
 
@@ -400,59 +409,59 @@ func (w *Worker) checkAccountBalance(
 		return "", nil
 	}
 
-	return types.PrintStruct(job.FindBalanceOutput{
-		Account: account,
-		Balance: amount,
+	return types.PrintStruct(&job.FindBalanceOutput{
+		AccountIdentifier: account,
+		Balance:           amount,
 	}), nil
 }
 
-func (w *Worker) availableAddresses(
+func (w *Worker) availableAccounts(
 	ctx context.Context,
 	dbTx storage.DatabaseTransaction,
-) ([]string, []string, error) {
-	addresses, err := w.helper.AllAddresses(ctx, dbTx)
+) ([]*types.AccountIdentifier, []*types.AccountIdentifier, error) {
+	accounts, err := w.helper.AllAccounts(ctx, dbTx)
 	if err != nil {
 		return nil, nil, fmt.Errorf(
-			"%w: unable to get all addresses %s",
+			"%w: unable to get all accounts %s",
 			ErrActionFailed,
 			err.Error(),
 		)
 	}
 
-	// If there are no addresses, we should create one.
-	if len(addresses) == 0 {
+	// If there are no accounts, we should create one.
+	if len(accounts) == 0 {
 		return nil, nil, ErrCreateAccount
 	}
 
-	// We fetch all locked addresses to subtract them from AllAddresses.
-	// We consider an address "locked" if it is actively involved in a broadcast.
-	unlockedAddresses := []string{}
-	lockedAddresses, err := w.helper.LockedAddresses(ctx, dbTx)
+	// We fetch all locked accounts to subtract them from AllAccounts.
+	// We consider an account "locked" if it is actively involved in a broadcast.
+	unlockedAccounts := []*types.AccountIdentifier{}
+	lockedAccounts, err := w.helper.LockedAccounts(ctx, dbTx)
 	if err != nil {
-		return nil, nil, fmt.Errorf("%w: unable to get locked addresses %s", ErrActionFailed, err)
+		return nil, nil, fmt.Errorf("%w: unable to get locked accounts %s", ErrActionFailed, err)
 	}
 
 	// Convert to a map so can do fast lookups
 	lockedSet := map[string]struct{}{}
-	for _, address := range lockedAddresses {
-		lockedSet[address] = struct{}{}
+	for _, account := range lockedAccounts {
+		lockedSet[types.Hash(account)] = struct{}{}
 	}
 
-	for _, address := range addresses {
-		if _, exists := lockedSet[address]; !exists {
-			unlockedAddresses = append(unlockedAddresses, address)
+	for _, account := range accounts {
+		if _, exists := lockedSet[types.Hash(account)]; !exists {
+			unlockedAccounts = append(unlockedAccounts, account)
 		}
 	}
 
-	return addresses, unlockedAddresses, nil
+	return accounts, unlockedAccounts, nil
 }
 
-func shouldCreateRandomAccount(input *job.FindBalanceInput, addressCount int) bool {
+func shouldCreateRandomAccount(input *job.FindBalanceInput, accountCount int) bool {
 	if input.MinimumBalance.Value != "0" {
 		return false
 	}
 
-	if input.CreateLimit <= 0 || addressCount >= input.CreateLimit {
+	if input.CreateLimit <= 0 || accountCount >= input.CreateLimit {
 		return false
 	}
 
@@ -468,6 +477,73 @@ func shouldCreateRandomAccount(input *job.FindBalanceInput, addressCount int) bo
 	return true
 }
 
+// findBalanceWorkerInputValidation ensures the input to FindBalanceWorker
+// is valid.
+func findBalanceWorkerInputValidation(input *job.FindBalanceInput) error {
+	if err := asserter.Amount(input.MinimumBalance); err != nil {
+		return fmt.Errorf("%w: minimum balance invalid", err)
+	}
+
+	if input.AccountIdentifier != nil {
+		if err := asserter.AccountIdentifier(input.AccountIdentifier); err != nil {
+			return err
+		}
+
+		if input.SubAccountIdentifier != nil {
+			return errors.New("cannot populate both account and sub account")
+		}
+
+		if len(input.NotAccountIdentifier) > 0 {
+			return errors.New("cannot populate both account and not accounts")
+		}
+
+		if len(input.NotAddress) > 0 {
+			return errors.New("cannot populate both account and not address")
+		}
+	}
+
+	if len(input.NotAccountIdentifier) > 0 {
+		if err := asserter.AccountArray("not account identifier", input.NotAccountIdentifier); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func skipAccount(input job.FindBalanceInput, account *types.AccountIdentifier) bool {
+	// If we require an account and that account
+	// is not equal to the account we are considering,
+	// we should continue.
+	if input.AccountIdentifier != nil &&
+		types.Hash(account) != types.Hash(input.AccountIdentifier) {
+		return true
+	}
+
+	// If we specify not to use certain addresses and we are considering
+	// one of them, we should continue.
+	if utils.ContainsString(input.NotAddress, account.Address) {
+		return true
+	}
+
+	// If we specify that we do not use certain accounts
+	// and the account we are considering is one of them,
+	// we should continue.
+	if utils.ContainsAccountIdentifier(input.NotAccountIdentifier, account) {
+		return true
+	}
+
+	// If we require a particular SubAccountIdentifier, we skip
+	// if the account we are examining does not have it.
+	if input.SubAccountIdentifier != nil &&
+		(account.SubAccount == nil ||
+			types.Hash(account.SubAccount) != types.Hash(input.SubAccountIdentifier)) {
+		return true
+	}
+
+	return false
+}
+
 // FindBalanceWorker attempts to find an account (and coin) with some minimum
 // balance in a particular currency.
 func (w *Worker) FindBalanceWorker(
@@ -481,44 +557,30 @@ func (w *Worker) FindBalanceWorker(
 		return "", fmt.Errorf("%w: %s", ErrInvalidInput, err.Error())
 	}
 
-	if err := asserter.Amount(input.MinimumBalance); err != nil {
-		return "", fmt.Errorf("%w: minimum balance invalid %s", ErrInvalidInput, err.Error())
+	// Validate that input is properly formatted
+	if err := findBalanceWorkerInputValidation(&input); err != nil {
+		return "", fmt.Errorf("%w: %s", ErrInvalidInput, err.Error())
 	}
 
 	log.Println(balanceMessage(&input))
 
-	addresses, availableAddresses, err := w.availableAddresses(ctx, dbTx)
+	accounts, availableAccounts, err := w.availableAccounts(ctx, dbTx)
 	if err != nil {
-		return "", fmt.Errorf("%w: unable to get available addresses", err)
+		return "", fmt.Errorf("%w: unable to get available accounts", err)
 	}
 
 	// Randomly, we choose to generate a new account. If we didn't do this,
 	// we would never grow past 2 accounts for mocking transfers.
-	if shouldCreateRandomAccount(&input, len(addresses)) {
+	if shouldCreateRandomAccount(&input, len(accounts)) {
 		return "", ErrCreateAccount
 	}
 
-	// Consider each availableAddress as a potential account.
-	for _, address := range availableAddresses {
-		// If we require an address and that address
-		// is not equal to the address we are considering,
-		// we should continue.
-		if len(input.Address) != 0 && address != input.Address {
+	// Consider each available account as a potential account.
+	for _, account := range availableAccounts {
+		if skipAccount(input, account) {
 			continue
 		}
 
-		// If we specify that we do not use certain addresses
-		// and the address we are considering is one of them,
-		// we should continue.
-		if utils.ContainsString(input.NotAddress, address) {
-			continue
-		}
-
-		account := &types.AccountIdentifier{
-			Address:    address,
-			SubAccount: input.SubAccount,
-		}
-
 		var output string
 		var err error
 		if input.RequireCoin {
@@ -543,9 +605,9 @@ func (w *Worker) FindBalanceWorker(
 		return "", ErrUnsatisfiable
 	}
 
-	// If we should create an account and the number of addresses
+	// If we should create an account and the number of accounts
 	// we have is less than the limit, we return ErrCreateAccount.
-	if input.CreateLimit > 0 && len(addresses) < input.CreateLimit {
+	if input.CreateLimit > 0 && len(accounts) < input.CreateLimit {
 		return "", ErrCreateAccount
 	}
 
diff --git a/fetcher/construction.go b/fetcher/construction.go
index c586a12..5a7967e 100644
--- a/fetcher/construction.go
+++ b/fetcher/construction.go
@@ -76,9 +76,9 @@ func (f *Fetcher) ConstructionDerive(
 	network *types.NetworkIdentifier,
 	publicKey *types.PublicKey,
 	metadata map[string]interface{},
-) (string, map[string]interface{}, *Error) {
+) (*types.AccountIdentifier, map[string]interface{}, *Error) {
 	if err := f.connectionSemaphore.Acquire(ctx, semaphoreRequestWeight); err != nil {
-		return "", nil, &Error{
+		return nil, nil, &Error{
 			Err: fmt.Errorf("%w: %s", ErrCouldNotAcquireSemaphore, err.Error()),
 		}
 	}
@@ -96,17 +96,17 @@ func (f *Fetcher) ConstructionDerive(
 			Err:       fmt.Errorf("%w: /construction/derive %s", ErrRequestFailed, err.Error()),
 			ClientErr: clientErr,
 		}
-		return "", nil, fetcherErr
+		return nil, nil, fetcherErr
 	}
 
 	if err := asserter.ConstructionDeriveResponse(response); err != nil {
 		fetcherErr := &Error{
 			Err: fmt.Errorf("%w: /construction/derive %s", ErrAssertionFailed, err.Error()),
 		}
-		return "", nil, fetcherErr
+		return nil, nil, fetcherErr
 	}
 
-	return response.Address, response.Metadata, nil
+	return response.AccountIdentifier, response.Metadata, nil
 }
 
 // ConstructionHash returns the network-specific transaction hash for
@@ -197,7 +197,7 @@ func (f *Fetcher) ConstructionParse(
 	network *types.NetworkIdentifier,
 	signed bool,
 	transaction string,
-) ([]*types.Operation, []string, map[string]interface{}, *Error) {
+) ([]*types.Operation, []*types.AccountIdentifier, map[string]interface{}, *Error) {
 	if err := f.connectionSemaphore.Acquire(ctx, semaphoreRequestWeight); err != nil {
 		return nil, nil, nil, &Error{
 			Err: fmt.Errorf("%w: %s", ErrCouldNotAcquireSemaphore, err.Error()),
@@ -227,7 +227,7 @@ func (f *Fetcher) ConstructionParse(
 		return nil, nil, nil, fetcherErr
 	}
 
-	return response.Operations, response.Signers, response.Metadata, nil
+	return response.Operations, response.AccountIdentifierSigners, response.Metadata, nil
 }
 
 // ConstructionPayloads is called with an array of operations
diff --git a/mocks/constructor/coordinator/helper.go b/mocks/constructor/coordinator/helper.go
index 7bc431b..c3502c0 100644
--- a/mocks/constructor/coordinator/helper.go
+++ b/mocks/constructor/coordinator/helper.go
@@ -19,16 +19,16 @@ type Helper struct {
 	mock.Mock
 }
 
-// AllAddresses provides a mock function with given fields: _a0, _a1
-func (_m *Helper) AllAddresses(_a0 context.Context, _a1 storage.DatabaseTransaction) ([]string, error) {
+// AllAccounts provides a mock function with given fields: _a0, _a1
+func (_m *Helper) AllAccounts(_a0 context.Context, _a1 storage.DatabaseTransaction) ([]*types.AccountIdentifier, error) {
 	ret := _m.Called(_a0, _a1)
 
-	var r0 []string
-	if rf, ok := ret.Get(0).(func(context.Context, storage.DatabaseTransaction) []string); ok {
+	var r0 []*types.AccountIdentifier
+	if rf, ok := ret.Get(0).(func(context.Context, storage.DatabaseTransaction) []*types.AccountIdentifier); ok {
 		r0 = rf(_a0, _a1)
 	} else {
 		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]string)
+			r0 = ret.Get(0).([]*types.AccountIdentifier)
 		}
 	}
 
@@ -154,14 +154,16 @@ func (_m *Helper) DatabaseTransaction(_a0 context.Context) storage.DatabaseTrans
 }
 
 // Derive provides a mock function with given fields: _a0, _a1, _a2, _a3
-func (_m *Helper) Derive(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 *types.PublicKey, _a3 map[string]interface{}) (string, map[string]interface{}, error) {
+func (_m *Helper) Derive(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 *types.PublicKey, _a3 map[string]interface{}) (*types.AccountIdentifier, map[string]interface{}, error) {
 	ret := _m.Called(_a0, _a1, _a2, _a3)
 
-	var r0 string
-	if rf, ok := ret.Get(0).(func(context.Context, *types.NetworkIdentifier, *types.PublicKey, map[string]interface{}) string); ok {
+	var r0 *types.AccountIdentifier
+	if rf, ok := ret.Get(0).(func(context.Context, *types.NetworkIdentifier, *types.PublicKey, map[string]interface{}) *types.AccountIdentifier); ok {
 		r0 = rf(_a0, _a1, _a2, _a3)
 	} else {
-		r0 = ret.Get(0).(string)
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*types.AccountIdentifier)
+		}
 	}
 
 	var r1 map[string]interface{}
@@ -184,11 +186,11 @@ func (_m *Helper) Derive(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2
 }
 
 // GetKey provides a mock function with given fields: _a0, _a1, _a2
-func (_m *Helper) GetKey(_a0 context.Context, _a1 storage.DatabaseTransaction, _a2 string) (*keys.KeyPair, error) {
+func (_m *Helper) GetKey(_a0 context.Context, _a1 storage.DatabaseTransaction, _a2 *types.AccountIdentifier) (*keys.KeyPair, error) {
 	ret := _m.Called(_a0, _a1, _a2)
 
 	var r0 *keys.KeyPair
-	if rf, ok := ret.Get(0).(func(context.Context, storage.DatabaseTransaction, string) *keys.KeyPair); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, storage.DatabaseTransaction, *types.AccountIdentifier) *keys.KeyPair); ok {
 		r0 = rf(_a0, _a1, _a2)
 	} else {
 		if ret.Get(0) != nil {
@@ -197,7 +199,7 @@ func (_m *Helper) GetKey(_a0 context.Context, _a1 storage.DatabaseTransaction, _
 	}
 
 	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, storage.DatabaseTransaction, string) error); ok {
+	if rf, ok := ret.Get(1).(func(context.Context, storage.DatabaseTransaction, *types.AccountIdentifier) error); ok {
 		r1 = rf(_a0, _a1, _a2)
 	} else {
 		r1 = ret.Error(1)
@@ -243,16 +245,16 @@ func (_m *Helper) HeadBlockExists(_a0 context.Context) bool {
 	return r0
 }
 
-// LockedAddresses provides a mock function with given fields: _a0, _a1
-func (_m *Helper) LockedAddresses(_a0 context.Context, _a1 storage.DatabaseTransaction) ([]string, error) {
+// LockedAccounts provides a mock function with given fields: _a0, _a1
+func (_m *Helper) LockedAccounts(_a0 context.Context, _a1 storage.DatabaseTransaction) ([]*types.AccountIdentifier, error) {
 	ret := _m.Called(_a0, _a1)
 
-	var r0 []string
-	if rf, ok := ret.Get(0).(func(context.Context, storage.DatabaseTransaction) []string); ok {
+	var r0 []*types.AccountIdentifier
+	if rf, ok := ret.Get(0).(func(context.Context, storage.DatabaseTransaction) []*types.AccountIdentifier); ok {
 		r0 = rf(_a0, _a1)
 	} else {
 		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]string)
+			r0 = ret.Get(0).([]*types.AccountIdentifier)
 		}
 	}
 
@@ -290,7 +292,7 @@ func (_m *Helper) Metadata(_a0 context.Context, _a1 *types.NetworkIdentifier, _a
 }
 
 // Parse provides a mock function with given fields: _a0, _a1, _a2, _a3
-func (_m *Helper) Parse(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 bool, _a3 string) ([]*types.Operation, []string, map[string]interface{}, error) {
+func (_m *Helper) Parse(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 bool, _a3 string) ([]*types.Operation, []*types.AccountIdentifier, map[string]interface{}, error) {
 	ret := _m.Called(_a0, _a1, _a2, _a3)
 
 	var r0 []*types.Operation
@@ -302,12 +304,12 @@ func (_m *Helper) Parse(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 b
 		}
 	}
 
-	var r1 []string
-	if rf, ok := ret.Get(1).(func(context.Context, *types.NetworkIdentifier, bool, string) []string); ok {
+	var r1 []*types.AccountIdentifier
+	if rf, ok := ret.Get(1).(func(context.Context, *types.NetworkIdentifier, bool, string) []*types.AccountIdentifier); ok {
 		r1 = rf(_a0, _a1, _a2, _a3)
 	} else {
 		if ret.Get(1) != nil {
-			r1 = ret.Get(1).([]string)
+			r1 = ret.Get(1).([]*types.AccountIdentifier)
 		}
 	}
 
@@ -416,11 +418,11 @@ func (_m *Helper) Sign(_a0 context.Context, _a1 []*types.SigningPayload) ([]*typ
 }
 
 // StoreKey provides a mock function with given fields: _a0, _a1, _a2, _a3
-func (_m *Helper) StoreKey(_a0 context.Context, _a1 storage.DatabaseTransaction, _a2 string, _a3 *keys.KeyPair) error {
+func (_m *Helper) StoreKey(_a0 context.Context, _a1 storage.DatabaseTransaction, _a2 *types.AccountIdentifier, _a3 *keys.KeyPair) error {
 	ret := _m.Called(_a0, _a1, _a2, _a3)
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, storage.DatabaseTransaction, string, *keys.KeyPair) error); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, storage.DatabaseTransaction, *types.AccountIdentifier, *keys.KeyPair) error); ok {
 		r0 = rf(_a0, _a1, _a2, _a3)
 	} else {
 		r0 = ret.Error(0)
diff --git a/mocks/constructor/worker/helper.go b/mocks/constructor/worker/helper.go
index d156c74..a67b977 100644
--- a/mocks/constructor/worker/helper.go
+++ b/mocks/constructor/worker/helper.go
@@ -18,16 +18,16 @@ type Helper struct {
 	mock.Mock
 }
 
-// AllAddresses provides a mock function with given fields: _a0, _a1
-func (_m *Helper) AllAddresses(_a0 context.Context, _a1 storage.DatabaseTransaction) ([]string, error) {
+// AllAccounts provides a mock function with given fields: _a0, _a1
+func (_m *Helper) AllAccounts(_a0 context.Context, _a1 storage.DatabaseTransaction) ([]*types.AccountIdentifier, error) {
 	ret := _m.Called(_a0, _a1)
 
-	var r0 []string
-	if rf, ok := ret.Get(0).(func(context.Context, storage.DatabaseTransaction) []string); ok {
+	var r0 []*types.AccountIdentifier
+	if rf, ok := ret.Get(0).(func(context.Context, storage.DatabaseTransaction) []*types.AccountIdentifier); ok {
 		r0 = rf(_a0, _a1)
 	} else {
 		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]string)
+			r0 = ret.Get(0).([]*types.AccountIdentifier)
 		}
 	}
 
@@ -88,14 +88,16 @@ func (_m *Helper) Coins(_a0 context.Context, _a1 storage.DatabaseTransaction, _a
 }
 
 // Derive provides a mock function with given fields: _a0, _a1, _a2, _a3
-func (_m *Helper) Derive(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 *types.PublicKey, _a3 map[string]interface{}) (string, map[string]interface{}, error) {
+func (_m *Helper) Derive(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 *types.PublicKey, _a3 map[string]interface{}) (*types.AccountIdentifier, map[string]interface{}, error) {
 	ret := _m.Called(_a0, _a1, _a2, _a3)
 
-	var r0 string
-	if rf, ok := ret.Get(0).(func(context.Context, *types.NetworkIdentifier, *types.PublicKey, map[string]interface{}) string); ok {
+	var r0 *types.AccountIdentifier
+	if rf, ok := ret.Get(0).(func(context.Context, *types.NetworkIdentifier, *types.PublicKey, map[string]interface{}) *types.AccountIdentifier); ok {
 		r0 = rf(_a0, _a1, _a2, _a3)
 	} else {
-		r0 = ret.Get(0).(string)
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*types.AccountIdentifier)
+		}
 	}
 
 	var r1 map[string]interface{}
@@ -117,16 +119,16 @@ func (_m *Helper) Derive(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2
 	return r0, r1, r2
 }
 
-// LockedAddresses provides a mock function with given fields: _a0, _a1
-func (_m *Helper) LockedAddresses(_a0 context.Context, _a1 storage.DatabaseTransaction) ([]string, error) {
+// LockedAccounts provides a mock function with given fields: _a0, _a1
+func (_m *Helper) LockedAccounts(_a0 context.Context, _a1 storage.DatabaseTransaction) ([]*types.AccountIdentifier, error) {
 	ret := _m.Called(_a0, _a1)
 
-	var r0 []string
-	if rf, ok := ret.Get(0).(func(context.Context, storage.DatabaseTransaction) []string); ok {
+	var r0 []*types.AccountIdentifier
+	if rf, ok := ret.Get(0).(func(context.Context, storage.DatabaseTransaction) []*types.AccountIdentifier); ok {
 		r0 = rf(_a0, _a1)
 	} else {
 		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]string)
+			r0 = ret.Get(0).([]*types.AccountIdentifier)
 		}
 	}
 
@@ -141,11 +143,11 @@ func (_m *Helper) LockedAddresses(_a0 context.Context, _a1 storage.DatabaseTrans
 }
 
 // StoreKey provides a mock function with given fields: _a0, _a1, _a2, _a3
-func (_m *Helper) StoreKey(_a0 context.Context, _a1 storage.DatabaseTransaction, _a2 string, _a3 *keys.KeyPair) error {
+func (_m *Helper) StoreKey(_a0 context.Context, _a1 storage.DatabaseTransaction, _a2 *types.AccountIdentifier, _a3 *keys.KeyPair) error {
 	ret := _m.Called(_a0, _a1, _a2, _a3)
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, storage.DatabaseTransaction, string, *keys.KeyPair) error); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, storage.DatabaseTransaction, *types.AccountIdentifier, *keys.KeyPair) error); ok {
 		r0 = rf(_a0, _a1, _a2, _a3)
 	} else {
 		r0 = ret.Error(0)
diff --git a/parser/errors.go b/parser/errors.go
index 1d9023b..4a33546 100644
--- a/parser/errors.go
+++ b/parser/errors.go
@@ -35,16 +35,16 @@ var (
 		"intended type did not match observed type",
 	)
 	ErrExpectedOperationsExtraOperation = errors.New("found extra operation")
-	ErrExpectedSignerDuplicateSigner    = errors.New("found duplicate signer")
 	ErrExpectedSignerUnexpectedSigner   = errors.New("found unexpected signers")
+	ErrExpectedSignerMissing            = errors.New("missing expected signer")
 
 	IntentErrs = []error{
 		ErrExpectedOperationAccountMismatch,
 		ErrExpectedOperationAmountMismatch,
 		ErrExpectedOperationTypeMismatch,
 		ErrExpectedOperationsExtraOperation,
-		ErrExpectedSignerDuplicateSigner,
 		ErrExpectedSignerUnexpectedSigner,
+		ErrExpectedSignerMissing,
 	}
 
 	/////////////////////////////
diff --git a/parser/intent.go b/parser/intent.go
index 0b1d308..93c6bb3 100644
--- a/parser/intent.go
+++ b/parser/intent.go
@@ -18,7 +18,6 @@ import (
 	"errors"
 	"fmt"
 
-	"github.com/coinbase/rosetta-sdk-go/asserter"
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
@@ -143,40 +142,41 @@ func (p *Parser) ExpectedOperations(
 // ExpectedSigners returns an error if a slice of SigningPayload
 // has different signers than what was observed (typically populated
 // using the signers returned from parsing a transaction).
-func ExpectedSigners(intent []*types.SigningPayload, observed []string) error {
+func ExpectedSigners(intent []*types.SigningPayload, observed []*types.AccountIdentifier) error {
 	// De-duplicate required signers (ex: multiple UTXOs from same address)
 	intendedSigners := make(map[string]struct{})
 	for _, payload := range intent {
-		intendedSigners[payload.Address] = struct{}{}
+		intendedSigners[types.Hash(payload.AccountIdentifier)] = struct{}{}
 	}
 
-	if err := asserter.StringArray("observed signers", observed); err != nil {
-		return fmt.Errorf("%w: %s", ErrExpectedSignerDuplicateSigner, err.Error())
-	}
-
-	// Could exist here if len(intent) != len(observed) but
+	// Could exit here if len(intent) != len(observed) but
 	// more useful to print out a detailed error message.
 	seenSigners := make(map[string]struct{})
-	unmatched := []string{} // observed
+	unmatched := []*types.AccountIdentifier{} // observed
 	for _, signer := range observed {
-		_, exists := intendedSigners[signer]
+		_, exists := intendedSigners[types.Hash(signer)]
 		if !exists {
 			unmatched = append(unmatched, signer)
 		} else {
-			seenSigners[signer] = struct{}{}
+			seenSigners[types.Hash(signer)] = struct{}{}
 		}
 	}
 
-	for k := range intendedSigners {
-		if _, exists := seenSigners[k]; !exists {
+	// Check to see if there are any expected
+	// signers that we could not find.
+	for _, payload := range intent {
+		hash := types.Hash(payload.AccountIdentifier)
+		if _, exists := seenSigners[hash]; !exists {
 			return fmt.Errorf(
 				"%w: %s",
-				ErrExpectedSignerDuplicateSigner,
-				k,
+				ErrExpectedSignerMissing,
+				types.PrintStruct(payload.AccountIdentifier),
 			)
 		}
 	}
 
+	// Return an error if any observed signatures
+	// were not expected.
 	if len(unmatched) != 0 {
 		return fmt.Errorf(
 			"%w: %s",
diff --git a/server/api_construction.go b/server/api_construction.go
index 0f67966..0d5d662 100644
--- a/server/api_construction.go
+++ b/server/api_construction.go
@@ -127,7 +127,7 @@ func (c *ConstructionAPIController) ConstructionCombine(w http.ResponseWriter, r
 	EncodeJSONResponse(result, http.StatusOK, w)
 }
 
-// ConstructionDerive - Derive an Address from a PublicKey
+// ConstructionDerive - Derive an AccountIdentifier from a PublicKey
 func (c *ConstructionAPIController) ConstructionDerive(w http.ResponseWriter, r *http.Request) {
 	constructionDeriveRequest := &types.ConstructionDeriveRequest{}
 	if err := json.NewDecoder(r.Body).Decode(&constructionDeriveRequest); err != nil {
diff --git a/storage/broadcast_storage.go b/storage/broadcast_storage.go
index a0b5100..2b1618a 100644
--- a/storage/broadcast_storage.go
+++ b/storage/broadcast_storage.go
@@ -525,35 +525,36 @@ func (b *BroadcastStorage) BroadcastAll(ctx context.Context, onlyEligible bool)
 	return nil
 }
 
-// LockedAddresses returns all addresses currently active in transaction broadcasts.
+// LockedAccounts returns all *types.AccountIdentifier currently active in transaction broadcasts.
 // The caller SHOULD NOT broadcast a transaction from an account if it is
 // considered locked!
-func (b *BroadcastStorage) LockedAddresses(
+func (b *BroadcastStorage) LockedAccounts(
 	ctx context.Context,
 	dbTx DatabaseTransaction,
-) ([]string, error) {
+) ([]*types.AccountIdentifier, error) {
 	broadcasts, err := b.getAllBroadcasts(ctx, dbTx)
 	if err != nil {
 		return nil, fmt.Errorf("%w: %v", ErrBroadcastGetAllFailed, err)
 	}
 
-	addressMap := map[string]struct{}{}
+	// De-duplicate accounts present in broadcast storage.
+	accountMap := map[string]*types.AccountIdentifier{}
 	for _, broadcast := range broadcasts {
 		for _, op := range broadcast.Intent {
 			if op.Account == nil {
 				continue
 			}
 
-			addressMap[op.Account.Address] = struct{}{}
+			accountMap[types.Hash(op.Account)] = op.Account
 		}
 	}
 
-	addresses := []string{}
-	for k := range addressMap {
-		addresses = append(addresses, k)
+	accounts := []*types.AccountIdentifier{}
+	for _, v := range accountMap {
+		accounts = append(accounts, v)
 	}
 
-	return addresses, nil
+	return accounts, nil
 }
 
 // ClearBroadcasts deletes all in-progress broadcasts from BroadcastStorage. This
diff --git a/storage/key_storage.go b/storage/key_storage.go
index 26c8f2b..b569cee 100644
--- a/storage/key_storage.go
+++ b/storage/key_storage.go
@@ -31,10 +31,10 @@ import (
 
 // PrefundedAccount is used to load prefunded addresses into key storage.
 type PrefundedAccount struct {
-	PrivateKeyHex string          `json:"privkey"`
-	Address       string          `json:"address"`
-	CurveType     types.CurveType `json:"curve_type"`
-	Currency      *types.Currency `json:"currency"`
+	PrivateKeyHex string                   `json:"privkey"`
+	Account       *types.AccountIdentifier `json:"account"`
+	CurveType     types.CurveType          `json:"curve_type"`
+	Currency      *types.Currency          `json:"currency"`
 }
 
 func init() {
@@ -45,9 +45,9 @@ const (
 	keyNamespace = "key"
 )
 
-func getAddressKey(address string) []byte {
+func getAccountKey(account *types.AccountIdentifier) []byte {
 	return []byte(
-		fmt.Sprintf("%s/%s", keyNamespace, address),
+		fmt.Sprintf("%s/%s", keyNamespace, types.Hash(account)),
 	)
 }
 
@@ -70,8 +70,8 @@ func NewKeyStorage(
 // is public so that accounts can be loaded from
 // a configuration file.
 type Key struct {
-	Address string        `json:"address"`
-	KeyPair *keys.KeyPair `json:"keypair"`
+	Account *types.AccountIdentifier `json:"account"`
+	KeyPair *keys.KeyPair            `json:"keypair"`
 }
 
 func parseKey(buf []byte) (*Key, error) {
@@ -97,28 +97,32 @@ func serializeKey(k *Key) ([]byte, error) {
 // StoreTransactional stores a key in a database transaction.
 func (k *KeyStorage) StoreTransactional(
 	ctx context.Context,
-	address string,
+	account *types.AccountIdentifier,
 	keyPair *keys.KeyPair,
 	dbTx DatabaseTransaction,
 ) error {
-	exists, _, err := dbTx.Get(ctx, getAddressKey(address))
+	exists, _, err := dbTx.Get(ctx, getAccountKey(account))
 	if err != nil {
-		return fmt.Errorf("%w: %s. %v", ErrAddrCheckIfExistsFailed, address, err)
+		return fmt.Errorf("%w: %s %v", ErrAddrCheckIfExistsFailed, types.PrintStruct(account), err)
 	}
 
 	if exists {
-		return fmt.Errorf("%w: address %s already exists", ErrAddrExists, address)
+		return fmt.Errorf(
+			"%w: account %s already exists",
+			ErrAddrExists,
+			types.PrintStruct(account),
+		)
 	}
 
 	val, err := serializeKey(&Key{
-		Address: address,
+		Account: account,
 		KeyPair: keyPair,
 	})
 	if err != nil {
 		return fmt.Errorf("%w: %v", ErrSerializeKeyFailed, err)
 	}
 
-	err = dbTx.Set(ctx, getAddressKey(address), val, true)
+	err = dbTx.Set(ctx, getAccountKey(account), val, true)
 	if err != nil {
 		return fmt.Errorf("%w: %v", ErrStoreKeyFailed, err)
 	}
@@ -130,13 +134,13 @@ func (k *KeyStorage) StoreTransactional(
 // exists, an error is returned.
 func (k *KeyStorage) Store(
 	ctx context.Context,
-	address string,
+	account *types.AccountIdentifier,
 	keyPair *keys.KeyPair,
 ) error {
 	dbTx := k.db.NewDatabaseTransaction(ctx, true)
 	defer dbTx.Discard(ctx)
 
-	if err := k.StoreTransactional(ctx, address, keyPair, dbTx); err != nil {
+	if err := k.StoreTransactional(ctx, account, keyPair, dbTx); err != nil {
 		return fmt.Errorf("%w: unable to store key", err)
 	}
 
@@ -147,19 +151,20 @@ func (k *KeyStorage) Store(
 	return nil
 }
 
-// GetTransactional returns a *keys.KeyPair for an address in a DatabaseTransaction, if it exists.
+// GetTransactional returns a *keys.KeyPair for an AccountIdentifier in a
+// DatabaseTransaction, if it exists.
 func (k *KeyStorage) GetTransactional(
 	ctx context.Context,
 	dbTx DatabaseTransaction,
-	address string,
+	account *types.AccountIdentifier,
 ) (*keys.KeyPair, error) {
-	exists, rawKey, err := dbTx.Get(ctx, getAddressKey(address))
+	exists, rawKey, err := dbTx.Get(ctx, getAccountKey(account))
 	if err != nil {
-		return nil, fmt.Errorf("%w: %s. %v", ErrAddrGetFailed, address, err)
+		return nil, fmt.Errorf("%w: %s %v", ErrAddrGetFailed, types.PrintStruct(account), err)
 	}
 
 	if !exists {
-		return nil, fmt.Errorf("%w: %s", ErrAddrNotFound, address)
+		return nil, fmt.Errorf("%w: %s", ErrAddrNotFound, types.PrintStruct(account))
 	}
 
 	key, err := parseKey(rawKey)
@@ -170,43 +175,46 @@ func (k *KeyStorage) GetTransactional(
 	return key.KeyPair, nil
 }
 
-// Get returns a *keys.KeyPair for an address, if it exists.
-func (k *KeyStorage) Get(ctx context.Context, address string) (*keys.KeyPair, error) {
+// Get returns a *keys.KeyPair for an AccountIdentifier, if it exists.
+func (k *KeyStorage) Get(
+	ctx context.Context,
+	account *types.AccountIdentifier,
+) (*keys.KeyPair, error) {
 	transaction := k.db.NewDatabaseTransaction(ctx, false)
 	defer transaction.Discard(ctx)
 
-	return k.GetTransactional(ctx, transaction, address)
+	return k.GetTransactional(ctx, transaction, account)
 }
 
-// GetAllAddressesTransactional returns all addresses in key storage.
-func (k *KeyStorage) GetAllAddressesTransactional(
+// GetAllAccountsTransactional returns all AccountIdentifiers in key storage.
+func (k *KeyStorage) GetAllAccountsTransactional(
 	ctx context.Context,
 	dbTx DatabaseTransaction,
-) ([]string, error) {
+) ([]*types.AccountIdentifier, error) {
 	rawKeys, err := dbTx.Scan(ctx, []byte(keyNamespace), false)
 	if err != nil {
 		return nil, fmt.Errorf("%w: %v", ErrKeyScanFailed, err)
 	}
 
-	addresses := make([]string, len(rawKeys))
+	accounts := make([]*types.AccountIdentifier, len(rawKeys))
 	for i, rawKey := range rawKeys {
 		kp, err := parseKey(rawKey.Value)
 		if err != nil {
 			return nil, fmt.Errorf("%w: %v", ErrParseKeyPairFailed, err)
 		}
 
-		addresses[i] = kp.Address
+		accounts[i] = kp.Account
 	}
 
-	return addresses, nil
+	return accounts, nil
 }
 
-// GetAllAddresses returns all addresses in key storage.
-func (k *KeyStorage) GetAllAddresses(ctx context.Context) ([]string, error) {
+// GetAllAccounts returns all AccountIdentifiers in key storage.
+func (k *KeyStorage) GetAllAccounts(ctx context.Context) ([]*types.AccountIdentifier, error) {
 	dbTx := k.db.NewDatabaseTransaction(ctx, false)
 	defer dbTx.Discard(ctx)
 
-	return k.GetAllAddressesTransactional(ctx, dbTx)
+	return k.GetAllAccountsTransactional(ctx, dbTx)
 }
 
 // Sign attempts to sign a slice of *types.SigningPayload with the keys in KeyStorage.
@@ -216,9 +224,14 @@ func (k *KeyStorage) Sign(
 ) ([]*types.Signature, error) {
 	signatures := make([]*types.Signature, len(payloads))
 	for i, payload := range payloads {
-		keyPair, err := k.Get(ctx, payload.Address)
+		keyPair, err := k.Get(ctx, payload.AccountIdentifier)
 		if err != nil {
-			return nil, fmt.Errorf("%w for %s: %v", ErrKeyGetFailed, payload.Address, err)
+			return nil, fmt.Errorf(
+				"%w for %s: %v",
+				ErrKeyGetFailed,
+				types.PrintStruct(payload.AccountIdentifier),
+				err,
+			)
 		}
 
 		signer, err := keyPair.Signer()
@@ -241,18 +254,18 @@ func (k *KeyStorage) Sign(
 	return signatures, nil
 }
 
-// RandomAddress returns a random address from all addresses.
-func (k *KeyStorage) RandomAddress(ctx context.Context) (string, error) {
-	addresses, err := k.GetAllAddresses(ctx)
+// RandomAccount returns a random account from all accounts.
+func (k *KeyStorage) RandomAccount(ctx context.Context) (*types.AccountIdentifier, error) {
+	accounts, err := k.GetAllAccounts(ctx)
 	if err != nil {
-		return "", fmt.Errorf("%w: %v", ErrAddrsGetAllFailed, err)
+		return nil, fmt.Errorf("%w: %v", ErrAddrsGetAllFailed, err)
 	}
 
-	if len(addresses) == 0 {
-		return "", ErrNoAddrAvailable
+	if len(accounts) == 0 {
+		return nil, ErrNoAddrAvailable
 	}
 
-	return addresses[rand.Intn(len(addresses))], nil
+	return accounts[rand.Intn(len(accounts))], nil
 }
 
 // ImportAccounts loads a set of prefunded accounts into key storage.
@@ -265,7 +278,7 @@ func (k *KeyStorage) ImportAccounts(ctx context.Context, accounts []*PrefundedAc
 		}
 
 		// Skip if key already exists
-		err = k.Store(ctx, acc.Address, keyPair)
+		err = k.Store(ctx, acc.Account, keyPair)
 		if errors.Is(err, ErrAddrExists) {
 			continue
 		}
diff --git a/templates/construction_derive_response.txt b/templates/construction_derive_response.txt
new file mode 100644
index 0000000..05ff7d5
--- /dev/null
+++ b/templates/construction_derive_response.txt
@@ -0,0 +1,76 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+	"encoding/json"
+)
+
+// ConstructionDeriveResponse ConstructionDeriveResponse is returned by the `/construction/derive`
+// endpoint.
+type ConstructionDeriveResponse struct {
+	AccountIdentifier *AccountIdentifier     `json:"account_identifier,omitempty"`
+	Metadata          map[string]interface{} `json:"metadata,omitempty"`
+}
+
+// MarshalJSON overrides the default JSON marshaler
+// and adds the deprecated "address" field to the response.
+func (c *ConstructionDeriveResponse) MarshalJSON() ([]byte, error) {
+	type Alias ConstructionDeriveResponse
+	addressString := ""
+	if c.AccountIdentifier != nil {
+		addressString = c.AccountIdentifier.Address
+	}
+
+	j, err := json.Marshal(struct {
+		// [DEPRECATED by `account_identifier` in `v1.4.4`] Address is the network-specific
+		// address of the account that should sign the payload.
+		Address string `json:"address,omitempty"`
+		*Alias
+	}{
+		Address: addressString,
+		Alias:   (*Alias)(c),
+	})
+	if err != nil {
+		return nil, err
+	}
+	return j, nil
+}
+
+// UnmarshalJSON overrides the default JSON unmarshaler
+// and reads the deprecated "address" field from the response.
+func (c *ConstructionDeriveResponse) UnmarshalJSON(b []byte) error {
+	type Alias ConstructionDeriveResponse
+	r := struct {
+		// [DEPRECATED by `account_identifier` in `v1.4.4`] Address is the network-specific
+		// address of the account that should sign the payload.
+		Address string `json:"address,omitempty"`
+		*Alias
+	}{
+		Alias: (*Alias)(c),
+	}
+	err := json.Unmarshal(b, &r)
+	if err != nil {
+		return err
+	}
+
+	if c.AccountIdentifier == nil && len(r.Address) > 0 {
+		c.AccountIdentifier = &AccountIdentifier{
+			Address: r.Address,
+		}
+	}
+
+	return nil
+}
diff --git a/templates/construction_parse_response.txt b/templates/construction_parse_response.txt
new file mode 100644
index 0000000..77e8fec
--- /dev/null
+++ b/templates/construction_parse_response.txt
@@ -0,0 +1,85 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+	"encoding/json"
+)
+
+// ConstructionParseResponse ConstructionParseResponse contains an array of operations that occur in
+// a transaction blob. This should match the array of operations provided to
+// `/construction/preprocess` and `/construction/payloads`.
+type ConstructionParseResponse struct {
+	Operations               []*Operation           `json:"operations"`
+	AccountIdentifierSigners []*AccountIdentifier   `json:"account_identifier_signers,omitempty"`
+	Metadata                 map[string]interface{} `json:"metadata,omitempty"`
+}
+
+// MarshalJSON overrides the default JSON marshaler
+// and adds the deprecated "signers" field to the response.
+func (c *ConstructionParseResponse) MarshalJSON() ([]byte, error) {
+	type Alias ConstructionParseResponse
+
+	// Do not create a signers array if the AccountIdentifierSigners
+	// array is nil.
+	var signers []string
+	if c.AccountIdentifierSigners != nil {
+		signers = make([]string, len(c.AccountIdentifierSigners))
+		for i, signer := range c.AccountIdentifierSigners {
+			signers[i] = signer.Address
+		}
+	}
+
+	j, err := json.Marshal(struct {
+		// [DEPRECATED by `account_identifier_signers` in `v1.4.4`] All signers (addresses) of a
+		// particular transaction. If the transaction is unsigned, it should be empty.
+		Signers []string `json:"signers,omitempty"`
+		*Alias
+	}{
+		Signers: signers,
+		Alias:   (*Alias)(c),
+	})
+	if err != nil {
+		return nil, err
+	}
+	return j, nil
+}
+
+// UnmarshalJSON overrides the default JSON unmarshaler
+// and reads the deprecated "signers" field from the response.
+func (c *ConstructionParseResponse) UnmarshalJSON(b []byte) error {
+	type Alias ConstructionParseResponse
+	r := struct {
+		// [DEPRECATED by `account_identifier_signers` in `v1.4.4`] All signers (addresses) of a
+		// particular transaction. If the transaction is unsigned, it should be empty.
+		Signers []string `json:"signers,omitempty"`
+		*Alias
+	}{
+		Alias: (*Alias)(c),
+	}
+	err := json.Unmarshal(b, &r)
+	if err != nil {
+		return err
+	}
+
+	if len(c.AccountIdentifierSigners) == 0 && len(r.Signers) > 0 {
+		c.AccountIdentifierSigners = make([]*AccountIdentifier, len(r.Signers))
+		for i, signer := range r.Signers {
+			c.AccountIdentifierSigners[i] = &AccountIdentifier{Address: signer}
+		}
+	}
+
+	return nil
+}
diff --git a/templates/signing_payload.txt b/templates/signing_payload.txt
new file mode 100644
index 0000000..a8c6dda
--- /dev/null
+++ b/templates/signing_payload.txt
@@ -0,0 +1,90 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+	"encoding/hex"
+	"encoding/json"
+)
+
+// SigningPayload SigningPayload is signed by the client with the keypair associated with an
+// AccountIdentifier using the specified SignatureType. SignatureType can be optionally populated if
+// there is a restriction on the signature scheme that can be used to sign the payload.
+type SigningPayload struct {
+	AccountIdentifier *AccountIdentifier `json:"account_identifier,omitempty"`
+	Bytes             []byte             `json:"hex_bytes"`
+	SignatureType     SignatureType      `json:"signature_type,omitempty"`
+}
+
+// MarshalJSON overrides the default JSON marshaler
+// and encodes bytes as hex instead of base64. It also
+// writes the deprecated "address" field to the response.
+func (s *SigningPayload) MarshalJSON() ([]byte, error) {
+	type Alias SigningPayload
+	addressString := ""
+	if s.AccountIdentifier != nil {
+		addressString = s.AccountIdentifier.Address
+	}
+
+	j, err := json.Marshal(struct {
+    // [DEPRECATED by `account_identifier` in `v1.4.4`] Address is the network-specific
+    // address of the account that should sign the payload.
+		Address string `json:"address,omitempty"`
+		Bytes   string `json:"hex_bytes"`
+		*Alias
+	}{
+		Address: addressString,
+		Bytes:   hex.EncodeToString(s.Bytes),
+		Alias:   (*Alias)(s),
+	})
+	if err != nil {
+		return nil, err
+	}
+	return j, nil
+}
+
+// UnmarshalJSON overrides the default JSON unmarshaler
+// and decodes bytes from hex instead of base64. It also
+// reads the deprecated "address" field from the response.
+func (s *SigningPayload) UnmarshalJSON(b []byte) error {
+	type Alias SigningPayload
+	r := struct {
+    // [DEPRECATED by `account_identifier` in `v1.4.4`] Address is the network-specific
+    // address of the account that should sign the payload.
+    Address string `json:"address,omitempty"`
+		Bytes string `json:"hex_bytes"`
+		*Alias
+	}{
+		Alias: (*Alias)(s),
+	}
+	err := json.Unmarshal(b, &r)
+	if err != nil {
+		return err
+	}
+
+	bytes, err := hex.DecodeString(r.Bytes)
+	if err != nil {
+		return err
+	}
+	s.Bytes = bytes
+
+  if s.AccountIdentifier == nil && len(r.Address) > 0 {
+		s.AccountIdentifier = &AccountIdentifier{
+			Address: r.Address,
+		}
+	}
+
+	return nil
+}
diff --git a/types/construction_derive_response.go b/types/construction_derive_response.go
index 174a92d..05ff7d5 100644
--- a/types/construction_derive_response.go
+++ b/types/construction_derive_response.go
@@ -12,14 +12,65 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Generated by: OpenAPI Generator (https://openapi-generator.tech)
-
 package types
 
+import (
+	"encoding/json"
+)
+
 // ConstructionDeriveResponse ConstructionDeriveResponse is returned by the `/construction/derive`
 // endpoint.
 type ConstructionDeriveResponse struct {
-	// Address in network-specific format.
-	Address  string                 `json:"address"`
-	Metadata map[string]interface{} `json:"metadata,omitempty"`
+	AccountIdentifier *AccountIdentifier     `json:"account_identifier,omitempty"`
+	Metadata          map[string]interface{} `json:"metadata,omitempty"`
+}
+
+// MarshalJSON overrides the default JSON marshaler
+// and adds the deprecated "address" field to the response.
+func (c *ConstructionDeriveResponse) MarshalJSON() ([]byte, error) {
+	type Alias ConstructionDeriveResponse
+	addressString := ""
+	if c.AccountIdentifier != nil {
+		addressString = c.AccountIdentifier.Address
+	}
+
+	j, err := json.Marshal(struct {
+		// [DEPRECATED by `account_identifier` in `v1.4.4`] Address is the network-specific
+		// address of the account that should sign the payload.
+		Address string `json:"address,omitempty"`
+		*Alias
+	}{
+		Address: addressString,
+		Alias:   (*Alias)(c),
+	})
+	if err != nil {
+		return nil, err
+	}
+	return j, nil
+}
+
+// UnmarshalJSON overrides the default JSON unmarshaler
+// and reads the deprecated "address" field from the response.
+func (c *ConstructionDeriveResponse) UnmarshalJSON(b []byte) error {
+	type Alias ConstructionDeriveResponse
+	r := struct {
+		// [DEPRECATED by `account_identifier` in `v1.4.4`] Address is the network-specific
+		// address of the account that should sign the payload.
+		Address string `json:"address,omitempty"`
+		*Alias
+	}{
+		Alias: (*Alias)(c),
+	}
+	err := json.Unmarshal(b, &r)
+	if err != nil {
+		return err
+	}
+
+	if c.AccountIdentifier == nil && len(r.Address) > 0 {
+		c.AccountIdentifier = &AccountIdentifier{
+			Address: r.Address,
+		}
+	}
+
+	return nil
 }
diff --git a/types/construction_parse_response.go b/types/construction_parse_response.go
index a935f20..77e8fec 100644
--- a/types/construction_parse_response.go
+++ b/types/construction_parse_response.go
@@ -12,17 +12,74 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Generated by: OpenAPI Generator (https://openapi-generator.tech)
-
 package types
 
+import (
+	"encoding/json"
+)
+
 // ConstructionParseResponse ConstructionParseResponse contains an array of operations that occur in
 // a transaction blob. This should match the array of operations provided to
 // `/construction/preprocess` and `/construction/payloads`.
 type ConstructionParseResponse struct {
-	Operations []*Operation `json:"operations"`
-	// All signers (addresses) of a particular transaction. If the transaction is unsigned, it
-	// should be empty.
-	Signers  []string               `json:"signers"`
-	Metadata map[string]interface{} `json:"metadata,omitempty"`
+	Operations               []*Operation           `json:"operations"`
+	AccountIdentifierSigners []*AccountIdentifier   `json:"account_identifier_signers,omitempty"`
+	Metadata                 map[string]interface{} `json:"metadata,omitempty"`
+}
+
+// MarshalJSON overrides the default JSON marshaler
+// and adds the deprecated "signers" field to the response.
+func (c *ConstructionParseResponse) MarshalJSON() ([]byte, error) {
+	type Alias ConstructionParseResponse
+
+	// Do not create a signers array if the AccountIdentifierSigners
+	// array is nil.
+	var signers []string
+	if c.AccountIdentifierSigners != nil {
+		signers = make([]string, len(c.AccountIdentifierSigners))
+		for i, signer := range c.AccountIdentifierSigners {
+			signers[i] = signer.Address
+		}
+	}
+
+	j, err := json.Marshal(struct {
+		// [DEPRECATED by `account_identifier_signers` in `v1.4.4`] All signers (addresses) of a
+		// particular transaction. If the transaction is unsigned, it should be empty.
+		Signers []string `json:"signers,omitempty"`
+		*Alias
+	}{
+		Signers: signers,
+		Alias:   (*Alias)(c),
+	})
+	if err != nil {
+		return nil, err
+	}
+	return j, nil
+}
+
+// UnmarshalJSON overrides the default JSON unmarshaler
+// and reads the deprecated "signers" field from the response.
+func (c *ConstructionParseResponse) UnmarshalJSON(b []byte) error {
+	type Alias ConstructionParseResponse
+	r := struct {
+		// [DEPRECATED by `account_identifier_signers` in `v1.4.4`] All signers (addresses) of a
+		// particular transaction. If the transaction is unsigned, it should be empty.
+		Signers []string `json:"signers,omitempty"`
+		*Alias
+	}{
+		Alias: (*Alias)(c),
+	}
+	err := json.Unmarshal(b, &r)
+	if err != nil {
+		return err
+	}
+
+	if len(c.AccountIdentifierSigners) == 0 && len(r.Signers) > 0 {
+		c.AccountIdentifierSigners = make([]*AccountIdentifier, len(r.Signers))
+		for i, signer := range r.Signers {
+			c.AccountIdentifierSigners[i] = &AccountIdentifier{Address: signer}
+		}
+	}
+
+	return nil
 }
diff --git a/types/signing_payload.go b/types/signing_payload.go
index 4588893..b6176d1 100644
--- a/types/signing_payload.go
+++ b/types/signing_payload.go
@@ -12,8 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Generated by: OpenAPI Generator (https://openapi-generator.tech)
-
 package types
 
 import (
@@ -21,26 +19,35 @@ import (
 	"encoding/json"
 )
 
-// SigningPayload SigningPayload is signed by the client with the keypair associated with an address
-// using the specified SignatureType. SignatureType can be optionally populated if there is a
-// restriction on the signature scheme that can be used to sign the payload.
+// SigningPayload SigningPayload is signed by the client with the keypair associated with an
+// AccountIdentifier using the specified SignatureType. SignatureType can be optionally populated if
+// there is a restriction on the signature scheme that can be used to sign the payload.
 type SigningPayload struct {
-	// The network-specific address of the account that should sign the payload.
-	Address       string        `json:"address"`
-	Bytes         []byte        `json:"hex_bytes"`
-	SignatureType SignatureType `json:"signature_type,omitempty"`
+	AccountIdentifier *AccountIdentifier `json:"account_identifier,omitempty"`
+	Bytes             []byte             `json:"hex_bytes"`
+	SignatureType     SignatureType      `json:"signature_type,omitempty"`
 }
 
 // MarshalJSON overrides the default JSON marshaler
-// and encodes bytes as hex instead of base64.
+// and encodes bytes as hex instead of base64. It also
+// writes the deprecated "address" field to the response.
 func (s *SigningPayload) MarshalJSON() ([]byte, error) {
 	type Alias SigningPayload
+	addressString := ""
+	if s.AccountIdentifier != nil {
+		addressString = s.AccountIdentifier.Address
+	}
+
 	j, err := json.Marshal(struct {
-		Bytes string `json:"hex_bytes"`
+		// [DEPRECATED by `account_identifier` in `v1.4.4`] Address is the network-specific
+		// address of the account that should sign the payload.
+		Address string `json:"address,omitempty"`
+		Bytes   string `json:"hex_bytes"`
 		*Alias
 	}{
-		Bytes: hex.EncodeToString(s.Bytes),
-		Alias: (*Alias)(s),
+		Address: addressString,
+		Bytes:   hex.EncodeToString(s.Bytes),
+		Alias:   (*Alias)(s),
 	})
 	if err != nil {
 		return nil, err
@@ -49,11 +56,15 @@ func (s *SigningPayload) MarshalJSON() ([]byte, error) {
 }
 
 // UnmarshalJSON overrides the default JSON unmarshaler
-// and decodes bytes from hex instead of base64.
+// and decodes bytes from hex instead of base64. It also
+// reads the deprecated "address" field from the response.
 func (s *SigningPayload) UnmarshalJSON(b []byte) error {
 	type Alias SigningPayload
 	r := struct {
-		Bytes string `json:"hex_bytes"`
+		// [DEPRECATED by `account_identifier` in `v1.4.4`] Address is the network-specific
+		// address of the account that should sign the payload.
+		Address string `json:"address,omitempty"`
+		Bytes   string `json:"hex_bytes"`
 		*Alias
 	}{
 		Alias: (*Alias)(s),
@@ -67,7 +78,13 @@ func (s *SigningPayload) UnmarshalJSON(b []byte) error {
 	if err != nil {
 		return err
 	}
-
 	s.Bytes = bytes
+
+	if s.AccountIdentifier == nil && len(r.Address) > 0 {
+		s.AccountIdentifier = &AccountIdentifier{
+			Address: r.Address,
+		}
+	}
+
 	return nil
 }
diff --git a/utils/utils.go b/utils/utils.go
index 0e298e2..7d9b952 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -262,6 +262,18 @@ func ContainsString(arr []string, s string) bool {
 	return false
 }
 
+// ContainsAccountIdentifier returns a boolean indicating
+// whether the struct s is in arr.
+func ContainsAccountIdentifier(arr []*types.AccountIdentifier, s *types.AccountIdentifier) bool {
+	for _, v := range arr {
+		if types.Hash(v) == types.Hash(s) {
+			return true
+		}
+	}
+
+	return false
+}
+
 // PrettyAmount returns a currency amount in native format with
 // its symbol.
 func PrettyAmount(amount *big.Int, currency *types.Currency) string {


Test patch:
diff --git a/asserter/construction_test.go b/asserter/construction_test.go
index b18238b..5130a8a 100644
--- a/asserter/construction_test.go
+++ b/asserter/construction_test.go
@@ -190,7 +190,9 @@ func TestConstructionDeriveResponse(t *testing.T) {
 	}{
 		"valid response": {
 			response: &types.ConstructionDeriveResponse{
-				Address: "addr",
+				AccountIdentifier: &types.AccountIdentifier{
+					Address: "addr",
+				},
 				Metadata: map[string]interface{}{
 					"name": "hello",
 				},
@@ -247,7 +249,9 @@ func TestConstructionParseResponse(t *testing.T) {
 						Amount:  validAmount,
 					},
 				},
-				Signers: []string{"account 1"},
+				AccountIdentifierSigners: []*types.AccountIdentifier{
+					validAccount,
+				},
 				Metadata: map[string]interface{}{
 					"extra": "stuff",
 				},
@@ -255,12 +259,48 @@ func TestConstructionParseResponse(t *testing.T) {
 			signed: true,
 			err:    nil,
 		},
+		"duplicate signer": {
+			response: &types.ConstructionParseResponse{
+				Operations: []*types.Operation{
+					{
+						OperationIdentifier: &types.OperationIdentifier{
+							Index: int64(0),
+						},
+						Type:    "PAYMENT",
+						Account: validAccount,
+						Amount:  validAmount,
+					},
+					{
+						OperationIdentifier: &types.OperationIdentifier{
+							Index: int64(1),
+						},
+						RelatedOperations: []*types.OperationIdentifier{
+							{Index: int64(0)},
+						},
+						Type:    "PAYMENT",
+						Account: &types.AccountIdentifier{Address: "addr 2"},
+						Amount:  validAmount,
+					},
+				},
+				AccountIdentifierSigners: []*types.AccountIdentifier{
+					validAccount,
+					validAccount,
+				},
+				Metadata: map[string]interface{}{
+					"extra": "stuff",
+				},
+			},
+			signed: true,
+			err:    ErrConstructionParseResponseDuplicateSigner,
+		},
 		"nil response": {
 			err: ErrConstructionParseResponseIsNil,
 		},
 		"no operations": {
 			response: &types.ConstructionParseResponse{
-				Signers: []string{"account 1"},
+				AccountIdentifierSigners: []*types.AccountIdentifier{
+					validAccount,
+				},
 				Metadata: map[string]interface{}{
 					"extra": "stuff",
 				},
@@ -279,7 +319,9 @@ func TestConstructionParseResponse(t *testing.T) {
 						Amount:  validAmount,
 					},
 				},
-				Signers: []string{"account 1"},
+				AccountIdentifierSigners: []*types.AccountIdentifier{
+					validAccount,
+				},
 				Metadata: map[string]interface{}{
 					"extra": "stuff",
 				},
@@ -316,7 +358,7 @@ func TestConstructionParseResponse(t *testing.T) {
 			signed: true,
 			err:    ErrConstructionParseResponseSignersEmptyOnSignedTx,
 		},
-		"empty string signer": {
+		"empty account identifier signer": {
 			response: &types.ConstructionParseResponse{
 				Operations: []*types.Operation{
 					{
@@ -339,7 +381,7 @@ func TestConstructionParseResponse(t *testing.T) {
 						Amount:  validAmount,
 					},
 				},
-				Signers: []string{""},
+				AccountIdentifierSigners: []*types.AccountIdentifier{{}},
 				Metadata: map[string]interface{}{
 					"extra": "stuff",
 				},
@@ -373,7 +415,9 @@ func TestConstructionParseResponse(t *testing.T) {
 				Metadata: map[string]interface{}{
 					"extra": "stuff",
 				},
-				Signers: []string{"account 1"},
+				AccountIdentifierSigners: []*types.AccountIdentifier{
+					validAccount,
+				},
 			},
 			signed: false,
 			err:    ErrConstructionParseResponseSignersNonEmptyOnUnsignedTx,
@@ -479,8 +523,10 @@ func TestConstructionPayloadsResponse(t *testing.T) {
 				UnsignedTransaction: "tx blob",
 				Payloads: []*types.SigningPayload{
 					{
-						Address: "hello",
-						Bytes:   []byte("48656c6c6f20476f7068657221"),
+						AccountIdentifier: &types.AccountIdentifier{
+							Address: "hello",
+						},
+						Bytes: []byte("48656c6c6f20476f7068657221"),
 					},
 				},
 			},
@@ -493,8 +539,10 @@ func TestConstructionPayloadsResponse(t *testing.T) {
 			response: &types.ConstructionPayloadsResponse{
 				Payloads: []*types.SigningPayload{
 					{
-						Address: "hello",
-						Bytes:   []byte("48656c6c6f20476f7068657221"),
+						AccountIdentifier: &types.AccountIdentifier{
+							Address: "hello",
+						},
+						Bytes: []byte("48656c6c6f20476f7068657221"),
 					},
 				},
 			},
@@ -581,13 +629,17 @@ func TestSigningPayload(t *testing.T) {
 	}{
 		"valid signing payload": {
 			signingPayload: &types.SigningPayload{
-				Address: "hello",
-				Bytes:   []byte("blah"),
+				AccountIdentifier: &types.AccountIdentifier{
+					Address: "hello",
+				},
+				Bytes: []byte("blah"),
 			},
 		},
 		"valid signing payload with signature type": {
 			signingPayload: &types.SigningPayload{
-				Address:       "hello",
+				AccountIdentifier: &types.AccountIdentifier{
+					Address: "hello",
+				},
 				Bytes:         []byte("blah"),
 				SignatureType: types.Ed25519,
 			},
@@ -603,13 +655,17 @@ func TestSigningPayload(t *testing.T) {
 		},
 		"empty bytes": {
 			signingPayload: &types.SigningPayload{
-				Address: "hello",
+				AccountIdentifier: &types.AccountIdentifier{
+					Address: "hello",
+				},
 			},
 			err: ErrSigningPayloadBytesEmpty,
 		},
 		"invalid signature": {
 			signingPayload: &types.SigningPayload{
-				Address:       "hello",
+				AccountIdentifier: &types.AccountIdentifier{
+					Address: "hello",
+				},
 				Bytes:         []byte("blah"),
 				SignatureType: "blah",
 			},
@@ -639,8 +695,8 @@ func TestSignatures(t *testing.T) {
 			signatures: []*types.Signature{
 				{
 					SigningPayload: &types.SigningPayload{
-						Address: validAccount.Address,
-						Bytes:   []byte("blah"),
+						AccountIdentifier: validAccount,
+						Bytes:             []byte("blah"),
 					},
 					PublicKey:     validPublicKey,
 					SignatureType: types.Ed25519,
@@ -648,8 +704,8 @@ func TestSignatures(t *testing.T) {
 				},
 				{
 					SigningPayload: &types.SigningPayload{
-						Address: validAccount.Address,
-						Bytes:   []byte("blah"),
+						AccountIdentifier: validAccount,
+						Bytes:             []byte("blah"),
 					},
 					PublicKey:     validPublicKey,
 					SignatureType: types.EcdsaRecovery,
@@ -661,9 +717,9 @@ func TestSignatures(t *testing.T) {
 			signatures: []*types.Signature{
 				{
 					SigningPayload: &types.SigningPayload{
-						Address:       validAccount.Address,
-						Bytes:         []byte("blah"),
-						SignatureType: types.Ed25519,
+						AccountIdentifier: validAccount,
+						Bytes:             []byte("blah"),
+						SignatureType:     types.Ed25519,
 					},
 					PublicKey:     validPublicKey,
 					SignatureType: types.Ed25519,
@@ -678,8 +734,8 @@ func TestSignatures(t *testing.T) {
 			signatures: []*types.Signature{
 				{
 					SigningPayload: &types.SigningPayload{
-						Address: validAccount.Address,
-						Bytes:   []byte("blah"),
+						AccountIdentifier: validAccount,
+						Bytes:             []byte("blah"),
 					},
 					PublicKey:     validPublicKey,
 					SignatureType: types.EcdsaRecovery,
@@ -687,9 +743,9 @@ func TestSignatures(t *testing.T) {
 				},
 				{
 					SigningPayload: &types.SigningPayload{
-						Address:       validAccount.Address,
-						Bytes:         []byte("blah"),
-						SignatureType: types.Ed25519,
+						AccountIdentifier: validAccount,
+						Bytes:             []byte("blah"),
+						SignatureType:     types.Ed25519,
 					},
 					PublicKey:     validPublicKey,
 					SignatureType: types.Ed25519,
@@ -701,9 +757,9 @@ func TestSignatures(t *testing.T) {
 			signatures: []*types.Signature{
 				{
 					SigningPayload: &types.SigningPayload{
-						Address:       validAccount.Address,
-						Bytes:         []byte("blah"),
-						SignatureType: types.EcdsaRecovery,
+						AccountIdentifier: validAccount,
+						Bytes:             []byte("blah"),
+						SignatureType:     types.EcdsaRecovery,
 					},
 					PublicKey:     validPublicKey,
 					SignatureType: types.Ed25519,
diff --git a/asserter/server_test.go b/asserter/server_test.go
index 9ac43df..68a6f25 100644
--- a/asserter/server_test.go
+++ b/asserter/server_test.go
@@ -147,8 +147,8 @@ var (
 	validSignatures = []*types.Signature{
 		{
 			SigningPayload: &types.SigningPayload{
-				Address: validAccount.Address,
-				Bytes:   []byte("blah"),
+				AccountIdentifier: validAccount,
+				Bytes:             []byte("blah"),
 			},
 			PublicKey:     validPublicKey,
 			SignatureType: types.Ed25519,
@@ -159,9 +159,9 @@ var (
 	signatureTypeMismatch = []*types.Signature{
 		{
 			SigningPayload: &types.SigningPayload{
-				Address:       validAccount.Address,
-				Bytes:         []byte("blah"),
-				SignatureType: types.EcdsaRecovery,
+				AccountIdentifier: validAccount,
+				Bytes:             []byte("blah"),
+				SignatureType:     types.EcdsaRecovery,
 			},
 			PublicKey:     validPublicKey,
 			SignatureType: types.Ed25519,
@@ -172,9 +172,9 @@ var (
 	signatureTypeMatch = []*types.Signature{
 		{
 			SigningPayload: &types.SigningPayload{
-				Address:       validAccount.Address,
-				Bytes:         []byte("blah"),
-				SignatureType: types.Ed25519,
+				AccountIdentifier: validAccount,
+				Bytes:             []byte("blah"),
+				SignatureType:     types.Ed25519,
 			},
 			PublicKey:     validPublicKey,
 			SignatureType: types.Ed25519,
@@ -185,9 +185,9 @@ var (
 	emptySignature = []*types.Signature{
 		{
 			SigningPayload: &types.SigningPayload{
-				Address:       validAccount.Address,
-				Bytes:         []byte("blah"),
-				SignatureType: types.Ed25519,
+				AccountIdentifier: validAccount,
+				Bytes:             []byte("blah"),
+				SignatureType:     types.Ed25519,
 			},
 			PublicKey:     validPublicKey,
 			SignatureType: types.Ed25519,
@@ -973,6 +973,42 @@ func TestConstructionCombineRequest(t *testing.T) {
 			},
 			err: nil,
 		},
+		"valid request 2": {
+			request: &types.ConstructionCombineRequest{
+				NetworkIdentifier:   validNetworkIdentifier,
+				UnsignedTransaction: "blah",
+				Signatures: []*types.Signature{
+					{
+						SigningPayload: &types.SigningPayload{
+							AccountIdentifier: validAccount,
+							Bytes:             []byte("blah"),
+						},
+						PublicKey:     validPublicKey,
+						SignatureType: types.Ed25519,
+						Bytes:         []byte("hello"),
+					},
+				},
+			},
+			err: nil,
+		},
+		"valid request 3": {
+			request: &types.ConstructionCombineRequest{
+				NetworkIdentifier:   validNetworkIdentifier,
+				UnsignedTransaction: "blah",
+				Signatures: []*types.Signature{
+					{
+						SigningPayload: &types.SigningPayload{
+							AccountIdentifier: validAccount,
+							Bytes:             []byte("blah"),
+						},
+						PublicKey:     validPublicKey,
+						SignatureType: types.Ed25519,
+						Bytes:         []byte("hello"),
+					},
+				},
+			},
+			err: nil,
+		},
 		"invalid request wrong network": {
 			request: &types.ConstructionCombineRequest{
 				NetworkIdentifier: wrongNetworkIdentifier,
diff --git a/constructor/coordinator/coordinator_test.go b/constructor/coordinator/coordinator_test.go
index d2f896b..bddd4ea 100644
--- a/constructor/coordinator/coordinator_test.go
+++ b/constructor/coordinator/coordinator_test.go
@@ -98,7 +98,7 @@ func TestProcess(t *testing.T) {
 					Actions: []*job.Action{
 						{
 							Type:       job.FindBalance,
-							Input:      `{"address": {{random_address.account.address}}, "minimum_balance":{"value": "100", "currency": {{currency}}}}`, // nolint
+							Input:      `{"account_identifier": {{random_address.account_identifier}}, "minimum_balance":{"value": "100", "currency": {{currency}}}}`, // nolint
 							OutputPath: "loaded_address",
 						},
 					},
@@ -125,11 +125,11 @@ func TestProcess(t *testing.T) {
 						{
 							Type:       job.Derive,
 							Input:      `{"network_identifier": {{network}}, "public_key": {{key.public_key}}}`,
-							OutputPath: "address",
+							OutputPath: "account",
 						},
 						{
-							Type:  job.SaveAddress,
-							Input: `{"address": {{address.address}}, "keypair": {{key.public_key}}}`,
+							Type:  job.SaveAccount,
+							Input: `{"account_identifier": {{account.account_identifier}}, "keypair": {{key.public_key}}}`,
 						},
 					},
 				},
@@ -164,7 +164,7 @@ func TestProcess(t *testing.T) {
 						},
 						{
 							Type:       job.FindBalance,
-							Input:      `{"not_address":[{{sender.account.address}}], "minimum_balance":{"value": "0", "currency": {{currency}}}, "create_limit": 100}`, // nolint
+							Input:      `{"not_account_identifier":[{{sender.account_identifier}}], "minimum_balance":{"value": "0", "currency": {{currency}}}, "create_limit": 100}`, // nolint
 							OutputPath: "recipient",
 						},
 						{
@@ -184,7 +184,7 @@ func TestProcess(t *testing.T) {
 						},
 						{
 							Type:       job.SetVariable,
-							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","status":"","account":{"address":{{sender.account.address}}},"amount":{"value":{{sender_amount}},"currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","status":"","account":{"address":{{recipient.account.address}}},"amount":{"value":{{recipient_amount}},"currency":{{currency}}}}]`, // nolint
+							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","status":"","account":{{sender.account_identifier}},"amount":{"value":{{sender_amount}},"currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","status":"","account":{{recipient.account_identifier}},"amount":{"value":{{recipient_amount}},"currency":{{currency}}}}]`, // nolint
 							OutputPath: "transfer.operations",
 						},
 					},
@@ -235,7 +235,7 @@ func TestProcess(t *testing.T) {
 	helper.On("DatabaseTransaction", ctx).Return(dbTxFail).Once()
 	jobStorage.On("Ready", ctx, dbTxFail).Return([]*job.Job{}, nil).Once()
 	jobStorage.On("Processing", ctx, dbTxFail, "transfer").Return([]*job.Job{}, nil).Once()
-	helper.On("AllAddresses", ctx, dbTxFail).Return([]string{}, nil).Once()
+	helper.On("AllAccounts", ctx, dbTxFail).Return([]*types.AccountIdentifier{}, nil).Once()
 
 	// Start processor
 	go func() {
@@ -262,12 +262,16 @@ func TestProcess(t *testing.T) {
 		},
 		mock.Anything,
 		(map[string]interface{})(nil),
-	).Return("address1", nil, nil).Once()
+	).Return(
+		&types.AccountIdentifier{Address: "address1"},
+		nil,
+		nil,
+	).Once()
 	helper.On(
 		"StoreKey",
 		ctx,
 		dbTx,
-		"address1",
+		&types.AccountIdentifier{Address: "address1"},
 		mock.Anything,
 	).Return(nil).Once()
 	jobStorage.On("Update", ctx, dbTx, mock.Anything).Return("job1", nil).Once()
@@ -279,8 +283,10 @@ func TestProcess(t *testing.T) {
 	helper.On("DatabaseTransaction", ctx).Return(dbTxFail2).Once()
 	jobStorage.On("Ready", ctx, dbTxFail2).Return([]*job.Job{}, nil).Once()
 	jobStorage.On("Processing", ctx, dbTxFail2, "transfer").Return([]*job.Job{}, nil).Once()
-	helper.On("AllAddresses", ctx, dbTxFail2).Return([]string{"address1"}, nil).Once()
-	helper.On("LockedAddresses", ctx, dbTxFail2).Return([]string{}, nil).Once()
+	helper.On("AllAccounts", ctx, dbTxFail2).Return([]*types.AccountIdentifier{
+		{Address: "address1"},
+	}, nil).Once()
+	helper.On("LockedAccounts", ctx, dbTxFail2).Return([]*types.AccountIdentifier{}, nil).Once()
 	helper.On(
 		"Balance",
 		ctx,
@@ -309,8 +315,10 @@ func TestProcess(t *testing.T) {
 	jobStorage.On("Ready", ctx, dbTx2).Return([]*job.Job{}, nil).Once()
 	jobStorage.On("Broadcasting", ctx, dbTx2).Return([]*job.Job{}, nil).Once()
 	jobStorage.On("Processing", ctx, dbTx2, "request_funds").Return([]*job.Job{}, nil).Once()
-	helper.On("AllAddresses", ctx, dbTx2).Return([]string{"address1"}, nil).Once()
-	helper.On("LockedAddresses", ctx, dbTx2).Return([]string{}, nil).Once()
+	helper.On("AllAccounts", ctx, dbTx2).Return([]*types.AccountIdentifier{
+		{Address: "address1"},
+	}, nil).Once()
+	helper.On("LockedAccounts", ctx, dbTx2).Return([]*types.AccountIdentifier{}, nil).Once()
 	helper.On(
 		"Balance",
 		ctx,
@@ -352,8 +360,10 @@ func TestProcess(t *testing.T) {
 	dbTxExtra := db.NewDatabaseTransaction(ctx, false)
 	helper.On("DatabaseTransaction", ctx).Return(dbTxExtra).Once()
 	jobStorage.On("Ready", ctx, dbTxExtra).Return([]*job.Job{&jobExtra}, nil).Once()
-	helper.On("AllAddresses", ctx, dbTxExtra).Return([]string{"address1"}, nil).Once()
-	helper.On("LockedAddresses", ctx, dbTxExtra).Return([]string{}, nil).Once()
+	helper.On("AllAccounts", ctx, dbTxExtra).Return([]*types.AccountIdentifier{
+		{Address: "address1"},
+	}, nil).Once()
+	helper.On("LockedAccounts", ctx, dbTxExtra).Return([]*types.AccountIdentifier{}, nil).Once()
 	helper.On(
 		"Balance",
 		ctx,
@@ -384,8 +394,10 @@ func TestProcess(t *testing.T) {
 	helper.On("DatabaseTransaction", ctx).Return(dbTxFail3).Once()
 	jobStorage.On("Ready", ctx, dbTxFail3).Return([]*job.Job{}, nil).Once()
 	jobStorage.On("Processing", ctx, dbTxFail3, "transfer").Return([]*job.Job{}, nil).Once()
-	helper.On("AllAddresses", ctx, dbTxFail3).Return([]string{"address1"}, nil).Once()
-	helper.On("LockedAddresses", ctx, dbTxFail3).Return([]string{}, nil).Once()
+	helper.On("AllAccounts", ctx, dbTxFail3).Return([]*types.AccountIdentifier{
+		{Address: "address1"},
+	}, nil).Once()
+	helper.On("LockedAccounts", ctx, dbTxFail3).Return([]*types.AccountIdentifier{}, nil).Once()
 	helper.On(
 		"Balance",
 		ctx,
@@ -405,8 +417,10 @@ func TestProcess(t *testing.T) {
 		},
 		nil,
 	).Once()
-	helper.On("AllAddresses", ctx, dbTxFail3).Return([]string{"address1"}, nil).Once()
-	helper.On("LockedAddresses", ctx, dbTxFail3).Return([]string{}, nil).Once()
+	helper.On("AllAccounts", ctx, dbTxFail3).Return([]*types.AccountIdentifier{
+		{Address: "address1"},
+	}, nil).Once()
+	helper.On("LockedAccounts", ctx, dbTxFail3).Return([]*types.AccountIdentifier{}, nil).Once()
 
 	// Attempt to create recipient
 	helper.On("HeadBlockExists", ctx).Return(true).Once()
@@ -424,12 +438,16 @@ func TestProcess(t *testing.T) {
 		},
 		mock.Anything,
 		(map[string]interface{})(nil),
-	).Return("address2", nil, nil).Once()
+	).Return(
+		&types.AccountIdentifier{Address: "address2"},
+		nil,
+		nil,
+	).Once()
 	helper.On(
 		"StoreKey",
 		ctx,
 		dbTx3,
-		"address2",
+		&types.AccountIdentifier{Address: "address2"},
 		mock.Anything,
 	).Return(nil).Once()
 	jobStorage.On("Update", ctx, dbTx3, mock.Anything).Return("job3", nil).Once()
@@ -441,8 +459,11 @@ func TestProcess(t *testing.T) {
 	helper.On("DatabaseTransaction", ctx).Return(dbTx4).Once()
 	jobStorage.On("Ready", ctx, dbTx4).Return([]*job.Job{}, nil).Once()
 	jobStorage.On("Processing", ctx, dbTx4, "transfer").Return([]*job.Job{}, nil).Once()
-	helper.On("AllAddresses", ctx, dbTx4).Return([]string{"address1", "address2"}, nil).Once()
-	helper.On("LockedAddresses", ctx, dbTx4).Return([]string{}, nil).Once()
+	helper.On("AllAccounts", ctx, dbTx4).Return([]*types.AccountIdentifier{
+		{Address: "address1"},
+		{Address: "address2"},
+	}, nil).Once()
+	helper.On("LockedAccounts", ctx, dbTx4).Return([]*types.AccountIdentifier{}, nil).Once()
 	helper.On(
 		"Balance",
 		ctx,
@@ -462,8 +483,11 @@ func TestProcess(t *testing.T) {
 		},
 		nil,
 	).Once()
-	helper.On("AllAddresses", ctx, dbTx4).Return([]string{"address1", "address2"}, nil).Once()
-	helper.On("LockedAddresses", ctx, dbTx4).Return([]string{}, nil).Once()
+	helper.On("AllAccounts", ctx, dbTx4).Return([]*types.AccountIdentifier{
+		{Address: "address1"},
+		{Address: "address2"},
+	}, nil).Once()
+	helper.On("LockedAccounts", ctx, dbTx4).Return([]*types.AccountIdentifier{}, nil).Once()
 	helper.On(
 		"Balance",
 		ctx,
@@ -562,9 +586,9 @@ func TestProcess(t *testing.T) {
 	unsignedTx := "unsigned transaction"
 	signingPayloads := []*types.SigningPayload{
 		{
-			Address:       "address1",
-			Bytes:         []byte("blah"),
-			SignatureType: types.Ecdsa,
+			AccountIdentifier: &types.AccountIdentifier{Address: "address1"},
+			Bytes:             []byte("blah"),
+			SignatureType:     types.Ecdsa,
 		},
 	}
 	helper.On(
@@ -581,7 +605,7 @@ func TestProcess(t *testing.T) {
 		network,
 		false,
 		unsignedTx,
-	).Return(ops, []string{}, nil, nil).Once()
+	).Return(ops, []*types.AccountIdentifier{}, nil, nil).Once()
 	signatures := []*types.Signature{
 		{
 			SigningPayload: signingPayloads[0],
@@ -612,7 +636,12 @@ func TestProcess(t *testing.T) {
 		network,
 		true,
 		networkTx,
-	).Return(ops, []string{"address1"}, nil, nil).Once()
+	).Return(
+		ops,
+		[]*types.AccountIdentifier{{Address: "address1"}},
+		nil,
+		nil,
+	).Once()
 	txIdentifier := &types.TransactionIdentifier{Hash: "transaction hash"}
 	helper.On(
 		"Hash",
@@ -773,7 +802,7 @@ func TestProcess_Failed(t *testing.T) {
 						},
 						{
 							Type:       job.FindBalance,
-							Input:      `{"not_address":[{{sender.account.address}}], "minimum_balance":{"value": "0", "currency": {{currency}}}, "create_limit": 100}`, // nolint
+							Input:      `{"not_account_identifier":[{{sender.account_identifier}}], "minimum_balance":{"value": "0", "currency": {{currency}}}, "create_limit": 100}`, // nolint
 							OutputPath: "recipient",
 						},
 						{
@@ -793,7 +822,7 @@ func TestProcess_Failed(t *testing.T) {
 						},
 						{
 							Type:       job.SetVariable,
-							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","status":"","account":{"address":{{sender.account.address}}},"amount":{"value":{{sender_amount}},"currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","status":"","account":{"address":{{recipient.account.address}}},"amount":{"value":{{recipient_amount}},"currency":{{currency}}}}]`, // nolint
+							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","status":"","account":{{sender.account_identifier}},"amount":{"value":{{sender_amount}},"currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","status":"","account":{{recipient.account_identifier}},"amount":{"value":{{recipient_amount}},"currency":{{currency}}}}]`, // nolint
 							OutputPath: "transfer.operations",
 						},
 					},
@@ -840,8 +869,11 @@ func TestProcess_Failed(t *testing.T) {
 	helper.On("DatabaseTransaction", ctx).Return(dbTx).Once()
 	jobStorage.On("Ready", ctx, dbTx).Return([]*job.Job{}, nil).Once()
 	jobStorage.On("Processing", ctx, dbTx, "transfer").Return([]*job.Job{}, nil).Once()
-	helper.On("AllAddresses", ctx, dbTx).Return([]string{"address1", "address2"}, nil).Once()
-	helper.On("LockedAddresses", ctx, dbTx).Return([]string{}, nil).Once()
+	helper.On("AllAccounts", ctx, dbTx).Return([]*types.AccountIdentifier{
+		{Address: "address1"},
+		{Address: "address2"},
+	}, nil).Once()
+	helper.On("LockedAccounts", ctx, dbTx).Return([]*types.AccountIdentifier{}, nil).Once()
 	helper.On(
 		"Balance",
 		ctx,
@@ -861,8 +893,11 @@ func TestProcess_Failed(t *testing.T) {
 		},
 		nil,
 	).Once()
-	helper.On("AllAddresses", ctx, dbTx).Return([]string{"address1", "address2"}, nil).Once()
-	helper.On("LockedAddresses", ctx, dbTx).Return([]string{}, nil).Once()
+	helper.On("AllAccounts", ctx, dbTx).Return([]*types.AccountIdentifier{
+		{Address: "address1"},
+		{Address: "address2"},
+	}, nil).Once()
+	helper.On("LockedAccounts", ctx, dbTx).Return([]*types.AccountIdentifier{}, nil).Once()
 	helper.On(
 		"Balance",
 		ctx,
@@ -898,14 +933,6 @@ func TestProcess_Failed(t *testing.T) {
 		},
 	).Once()
 
-	// Start processor
-	go func() {
-		err := c.Process(ctx)
-		fmt.Println(err)
-		assert.True(t, errors.Is(err, context.Canceled))
-		close(processCanceled)
-	}()
-
 	// Construct Transaction
 	network := &types.NetworkIdentifier{
 		Blockchain: "Bitcoin",
@@ -966,7 +993,7 @@ func TestProcess_Failed(t *testing.T) {
 		"GetKey",
 		ctx,
 		dbTx,
-		"hello",
+		&types.AccountIdentifier{Address: "hello"},
 	).Return(
 		&keys.KeyPair{
 			PublicKey: &types.PublicKey{
@@ -980,7 +1007,7 @@ func TestProcess_Failed(t *testing.T) {
 		"GetKey",
 		ctx,
 		dbTx,
-		"hello2",
+		&types.AccountIdentifier{Address: "hello2"},
 	).Return(
 		&keys.KeyPair{
 			PublicKey: &types.PublicKey{
@@ -1014,9 +1041,9 @@ func TestProcess_Failed(t *testing.T) {
 	unsignedTx := "unsigned transaction"
 	signingPayloads := []*types.SigningPayload{
 		{
-			Address:       "address1",
-			Bytes:         []byte("blah"),
-			SignatureType: types.Ecdsa,
+			AccountIdentifier: &types.AccountIdentifier{Address: "address1"},
+			Bytes:             []byte("blah"),
+			SignatureType:     types.Ecdsa,
 		},
 	}
 	helper.On(
@@ -1042,7 +1069,7 @@ func TestProcess_Failed(t *testing.T) {
 		network,
 		false,
 		unsignedTx,
-	).Return(ops, []string{}, nil, nil).Once()
+	).Return(ops, []*types.AccountIdentifier{}, nil, nil).Once()
 	signatures := []*types.Signature{
 		{
 			SigningPayload: signingPayloads[0],
@@ -1073,7 +1100,9 @@ func TestProcess_Failed(t *testing.T) {
 		network,
 		true,
 		networkTx,
-	).Return(ops, []string{"address1"}, nil, nil).Once()
+	).Return(ops, []*types.AccountIdentifier{
+		{Address: "address1"},
+	}, nil, nil).Once()
 	txIdentifier := &types.TransactionIdentifier{Hash: "transaction hash"}
 	helper.On(
 		"Hash",
@@ -1095,6 +1124,14 @@ func TestProcess_Failed(t *testing.T) {
 	handler.On("TransactionCreated", ctx, "job", txIdentifier).Return(nil).Once()
 	helper.On("BroadcastAll", ctx).Return(nil).Once()
 
+	// Start processor
+	go func() {
+		err := c.Process(ctx)
+		fmt.Println(err)
+		assert.True(t, errors.Is(err, context.Canceled))
+		close(processCanceled)
+	}()
+
 	// Wait for transfer to complete
 	helper.On("HeadBlockExists", ctx).Return(true).Once()
 	dbTx2 := db.NewDatabaseTransaction(ctx, false)
diff --git a/constructor/worker/worker_test.go b/constructor/worker/worker_test.go
index 3efaa77..3005f8f 100644
--- a/constructor/worker/worker_test.go
+++ b/constructor/worker/worker_test.go
@@ -50,9 +50,9 @@ func TestBalanceMessage(t *testing.T) {
 			},
 			message: `looking for balance {"value":"100","currency":{"symbol":"BTC","decimals":8}}`,
 		},
-		"message with address": {
+		"message with account": {
 			input: &job.FindBalanceInput{
-				Address: "hello",
+				AccountIdentifier: &types.AccountIdentifier{Address: "hello"},
 				MinimumBalance: &types.Amount{
 					Value: "100",
 					Currency: &types.Currency{
@@ -61,12 +61,11 @@ func TestBalanceMessage(t *testing.T) {
 					},
 				},
 			},
-			message: `looking for balance {"value":"100","currency":{"symbol":"BTC","decimals":8}} on address hello`, // nolint
+			message: `looking for balance {"value":"100","currency":{"symbol":"BTC","decimals":8}} on account {"address":"hello"}`, // nolint
 		},
-		"message with address and subaccount": {
+		"message with only subaccount": {
 			input: &job.FindBalanceInput{
-				Address: "hello",
-				SubAccount: &types.SubAccountIdentifier{
+				SubAccountIdentifier: &types.SubAccountIdentifier{
 					Address: "sub hello",
 				},
 				MinimumBalance: &types.Amount{
@@ -77,12 +76,13 @@ func TestBalanceMessage(t *testing.T) {
 					},
 				},
 			},
-			message: `looking for balance {"value":"100","currency":{"symbol":"BTC","decimals":8}} on address hello with sub_account {"address":"sub hello"}`, // nolint
+			message: `looking for balance {"value":"100","currency":{"symbol":"BTC","decimals":8}} with sub_account {"address":"sub hello"}`, // nolint
 		},
-		"message with only subaccount": {
+		"message with not address": {
 			input: &job.FindBalanceInput{
-				SubAccount: &types.SubAccountIdentifier{
-					Address: "sub hello",
+				NotAddress: []string{
+					"good",
+					"bye",
 				},
 				MinimumBalance: &types.Amount{
 					Value: "100",
@@ -92,14 +92,13 @@ func TestBalanceMessage(t *testing.T) {
 					},
 				},
 			},
-			message: `looking for balance {"value":"100","currency":{"symbol":"BTC","decimals":8}} with sub_account {"address":"sub hello"}`, // nolint
+			message: `looking for balance {"value":"100","currency":{"symbol":"BTC","decimals":8}} != to addresses ["good","bye"]`, // nolint
 		},
-		"message with address and not address": {
+		"message with not account": {
 			input: &job.FindBalanceInput{
-				Address: "hello",
-				NotAddress: []string{
-					"good",
-					"bye",
+				NotAccountIdentifier: []*types.AccountIdentifier{
+					{Address: "good"},
+					{Address: "bye"},
 				},
 				MinimumBalance: &types.Amount{
 					Value: "100",
@@ -109,11 +108,11 @@ func TestBalanceMessage(t *testing.T) {
 					},
 				},
 			},
-			message: `looking for balance {"value":"100","currency":{"symbol":"BTC","decimals":8}} on address hello != to addresses ["good","bye"]`, // nolint
+			message: `looking for balance {"value":"100","currency":{"symbol":"BTC","decimals":8}} != to accounts [{"address":"good"},{"address":"bye"}]`, // nolint
 		},
-		"message with address and not coins": {
+		"message with account and not coins": {
 			input: &job.FindBalanceInput{
-				Address: "hello",
+				AccountIdentifier: &types.AccountIdentifier{Address: "hello"},
 				NotCoins: []*types.CoinIdentifier{
 					{
 						Identifier: "coin1",
@@ -127,7 +126,7 @@ func TestBalanceMessage(t *testing.T) {
 					},
 				},
 			},
-			message: `looking for balance {"value":"100","currency":{"symbol":"BTC","decimals":8}} on address hello != to coins [{"identifier":"coin1"}]`, // nolint
+			message: `looking for balance {"value":"100","currency":{"symbol":"BTC","decimals":8}} on account {"address":"hello"} != to coins [{"identifier":"coin1"}]`, // nolint
 		},
 	}
 
@@ -158,24 +157,33 @@ func TestFindBalanceWorker(t *testing.T) {
 						Decimals: 8,
 					},
 				},
-				NotAddress: []string{"addr4"},
+				NotAccountIdentifier: []*types.AccountIdentifier{
+					{Address: "addr4"},
+				},
 			},
 			mockHelper: func() *mocks.Helper {
 				helper := &mocks.Helper{}
 				helper.On(
-					"AllAddresses",
+					"AllAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2", "addr1", "addr3", "addr4"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+						{Address: "addr1"},
+						{Address: "addr3"},
+						{Address: "addr4"},
+					},
 					nil,
 				).Once()
 				helper.On(
-					"LockedAddresses",
+					"LockedAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+					},
 					nil,
 				).Once()
 				helper.On(
@@ -201,7 +209,7 @@ func TestFindBalanceWorker(t *testing.T) {
 				return helper
 			}(),
 			output: &job.FindBalanceOutput{
-				Account: &types.AccountIdentifier{
+				AccountIdentifier: &types.AccountIdentifier{
 					Address: "addr1",
 				},
 				Balance: &types.Amount{
@@ -222,26 +230,35 @@ func TestFindBalanceWorker(t *testing.T) {
 						Decimals: 8,
 					},
 				},
-				NotAddress:        []string{"addr4"},
+				NotAccountIdentifier: []*types.AccountIdentifier{
+					{Address: "addr4"},
+				},
 				CreateLimit:       100,
 				CreateProbability: 100,
 			},
 			mockHelper: func() *mocks.Helper {
 				helper := &mocks.Helper{}
 				helper.On(
-					"AllAddresses",
+					"AllAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2", "addr1", "addr3", "addr4"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+						{Address: "addr1"},
+						{Address: "addr3"},
+						{Address: "addr4"},
+					},
 					nil,
 				).Once()
 				helper.On(
-					"LockedAddresses",
+					"LockedAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+					},
 					nil,
 				).Once()
 
@@ -258,26 +275,35 @@ func TestFindBalanceWorker(t *testing.T) {
 						Decimals: 8,
 					},
 				},
-				NotAddress:        []string{"addr4"},
+				NotAccountIdentifier: []*types.AccountIdentifier{
+					{Address: "addr4"},
+				},
 				CreateLimit:       100,
 				CreateProbability: 100,
 			},
 			mockHelper: func() *mocks.Helper {
 				helper := &mocks.Helper{}
 				helper.On(
-					"AllAddresses",
+					"AllAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2", "addr1", "addr3", "addr4"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+						{Address: "addr1"},
+						{Address: "addr3"},
+						{Address: "addr4"},
+					},
 					nil,
 				).Once()
 				helper.On(
-					"LockedAddresses",
+					"LockedAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+					},
 					nil,
 				).Once()
 				helper.On(
@@ -326,24 +352,33 @@ func TestFindBalanceWorker(t *testing.T) {
 						Decimals: 8,
 					},
 				},
-				NotAddress: []string{"addr4"},
+				NotAccountIdentifier: []*types.AccountIdentifier{
+					{Address: "addr4"},
+				},
 			},
 			mockHelper: func() *mocks.Helper {
 				helper := &mocks.Helper{}
 				helper.On(
-					"AllAddresses",
+					"AllAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2", "addr1", "addr3", "addr4"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+						{Address: "addr1"},
+						{Address: "addr3"},
+						{Address: "addr4"},
+					},
 					nil,
 				).Once()
 				helper.On(
-					"LockedAddresses",
+					"LockedAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+					},
 					nil,
 				).Once()
 				helper.On(
@@ -392,25 +427,37 @@ func TestFindBalanceWorker(t *testing.T) {
 						Decimals: 8,
 					},
 				},
-				NotAddress:  []string{"addr4"},
+				NotAccountIdentifier: []*types.AccountIdentifier{
+					{Address: "addr4"},
+				},
 				CreateLimit: 100,
 			},
 			mockHelper: func() *mocks.Helper {
 				helper := &mocks.Helper{}
 				helper.On(
-					"AllAddresses",
+					"AllAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2", "addr1", "addr3", "addr4"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+						{Address: "addr1"},
+						{Address: "addr3"},
+						{Address: "addr4"},
+					},
 					nil,
 				).Once()
 				helper.On(
-					"LockedAddresses",
+					"LockedAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr1", "addr2", "addr3", "addr4"},
+					[]*types.AccountIdentifier{
+						{Address: "addr1"},
+						{Address: "addr2"},
+						{Address: "addr3"},
+						{Address: "addr4"},
+					},
 					nil,
 				).Once()
 
@@ -420,7 +467,7 @@ func TestFindBalanceWorker(t *testing.T) {
 		},
 		"simple find balance with subaccount": {
 			input: &job.FindBalanceInput{
-				SubAccount: &types.SubAccountIdentifier{
+				SubAccountIdentifier: &types.SubAccountIdentifier{
 					Address: "sub1",
 				},
 				MinimumBalance: &types.Amount{
@@ -435,19 +482,31 @@ func TestFindBalanceWorker(t *testing.T) {
 			mockHelper: func() *mocks.Helper {
 				helper := &mocks.Helper{}
 				helper.On(
-					"AllAddresses",
+					"AllAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2", "addr1", "addr3", "addr4"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+						{
+							Address: "addr1",
+							SubAccount: &types.SubAccountIdentifier{
+								Address: "sub1",
+							},
+						},
+						{Address: "addr3"},
+						{Address: "addr4"},
+					},
 					nil,
 				).Once()
 				helper.On(
-					"LockedAddresses",
+					"LockedAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+					},
 					nil,
 				).Once()
 				helper.On("Balance", ctx, mock.Anything, &types.AccountIdentifier{
@@ -469,7 +528,7 @@ func TestFindBalanceWorker(t *testing.T) {
 				return helper
 			}(),
 			output: &job.FindBalanceOutput{
-				Account: &types.AccountIdentifier{
+				AccountIdentifier: &types.AccountIdentifier{
 					Address: "addr1",
 					SubAccount: &types.SubAccountIdentifier{
 						Address: "sub1",
@@ -504,19 +563,26 @@ func TestFindBalanceWorker(t *testing.T) {
 			mockHelper: func() *mocks.Helper {
 				helper := &mocks.Helper{}
 				helper.On(
-					"AllAddresses",
+					"AllAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2", "addr1", "addr3", "addr4"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+						{Address: "addr1"},
+						{Address: "addr3"},
+						{Address: "addr4"},
+					},
 					nil,
 				).Once()
 				helper.On(
-					"LockedAddresses",
+					"LockedAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+					},
 					nil,
 				).Once()
 				helper.On("Coins", ctx, mock.Anything, &types.AccountIdentifier{
@@ -555,7 +621,7 @@ func TestFindBalanceWorker(t *testing.T) {
 				return helper
 			}(),
 			output: &job.FindBalanceOutput{
-				Account: &types.AccountIdentifier{
+				AccountIdentifier: &types.AccountIdentifier{
 					Address: "addr1",
 				},
 				Balance: &types.Amount{
@@ -591,19 +657,26 @@ func TestFindBalanceWorker(t *testing.T) {
 			mockHelper: func() *mocks.Helper {
 				helper := &mocks.Helper{}
 				helper.On(
-					"AllAddresses",
+					"AllAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2", "addr1", "addr3", "addr4"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+						{Address: "addr1"},
+						{Address: "addr3"},
+						{Address: "addr4"},
+					},
 					nil,
 				).Once()
 				helper.On(
-					"LockedAddresses",
+					"LockedAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+					},
 					nil,
 				).Once()
 				helper.On("Coins", ctx, mock.Anything, &types.AccountIdentifier{
@@ -671,19 +744,26 @@ func TestFindBalanceWorker(t *testing.T) {
 			mockHelper: func() *mocks.Helper {
 				helper := &mocks.Helper{}
 				helper.On(
-					"AllAddresses",
+					"AllAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2", "addr1", "addr3", "addr4"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+						{Address: "addr1"},
+						{Address: "addr3"},
+						{Address: "addr4"},
+					},
 					nil,
 				).Once()
 				helper.On(
-					"LockedAddresses",
+					"LockedAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+					},
 					nil,
 				).Once()
 				helper.On("Coins", ctx, mock.Anything, &types.AccountIdentifier{
@@ -751,19 +831,26 @@ func TestFindBalanceWorker(t *testing.T) {
 			mockHelper: func() *mocks.Helper {
 				helper := &mocks.Helper{}
 				helper.On(
-					"AllAddresses",
+					"AllAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2", "addr1", "addr3", "addr4"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+						{Address: "addr1"},
+						{Address: "addr3"},
+						{Address: "addr4"},
+					},
 					nil,
 				).Once()
 				helper.On(
-					"LockedAddresses",
+					"LockedAccounts",
 					ctx,
 					mock.Anything,
 				).Return(
-					[]string{"addr2"},
+					[]*types.AccountIdentifier{
+						{Address: "addr2"},
+					},
 					nil,
 				).Once()
 				helper.On("Coins", ctx, mock.Anything, &types.AccountIdentifier{
@@ -909,15 +996,15 @@ func TestJob_ComplicatedTransfer(t *testing.T) {
 			{
 				Type:       job.Derive,
 				Input:      `{"network_identifier": {{network}}, "public_key": {{key.public_key}}}`,
-				OutputPath: "address",
+				OutputPath: "account",
 			},
 			{
-				Type:  job.SaveAddress,
-				Input: `{"address": {{address.address}}, "keypair": {{key.public_key}}}`,
+				Type:  job.SaveAccount,
+				Input: `{"account_identifier": {{account.account_identifier}}, "keypair": {{key.public_key}}}`,
 			},
 			{
 				Type:  job.PrintMessage,
-				Input: `{{address.address}}`,
+				Input: `{{account.account_identifier}}`,
 			},
 		},
 	}
@@ -932,7 +1019,7 @@ func TestJob_ComplicatedTransfer(t *testing.T) {
 			},
 			{
 				Type:       job.SetVariable,
-				Input:      `[{"operation_identifier":{"index":0},"type":"","status":"","account":{"address":{{address.address}}},"amount":{"value":"-90","currency":{"symbol":"BTC","decimals":8}}},{"operation_identifier":{"index":1},"type":"","status":"","account":{"address":{{random_address}}},"amount":{"value":"100","currency":{"symbol":"BTC","decimals":8}}}]`, // nolint
+				Input:      `[{"operation_identifier":{"index":0},"type":"","status":"","account":{{account.account_identifier}},"amount":{"value":"-90","currency":{"symbol":"BTC","decimals":8}}},{"operation_identifier":{"index":1},"type":"","status":"","account":{"address":{{random_address}}},"amount":{"value":"100","currency":{"symbol":"BTC","decimals":8}}}]`, // nolint
 				OutputPath: "create_send.operations",
 			},
 			{
@@ -996,7 +1083,7 @@ func TestJob_ComplicatedTransfer(t *testing.T) {
 		Blockchain: "Bitcoin",
 		Network:    "Testnet3",
 	}
-	address := "test"
+	account := &types.AccountIdentifier{Address: "test"}
 	mockHelper.On(
 		"Derive",
 		ctx,
@@ -1004,7 +1091,7 @@ func TestJob_ComplicatedTransfer(t *testing.T) {
 		mock.Anything,
 		(map[string]interface{})(nil),
 	).Return(
-		address,
+		account,
 		nil,
 		nil,
 	).Once()
@@ -1012,7 +1099,7 @@ func TestJob_ComplicatedTransfer(t *testing.T) {
 		"StoreKey",
 		ctx,
 		dbTx,
-		address,
+		account,
 		mock.Anything,
 	).Return(
 		nil,
@@ -1030,7 +1117,7 @@ func TestJob_ComplicatedTransfer(t *testing.T) {
 
 	assertVariableEquality(t, j.State, "network", network)
 	assertVariableEquality(t, j.State, "key.public_key.curve_type", types.Secp256k1)
-	assertVariableEquality(t, j.State, "address.address", address)
+	assertVariableEquality(t, j.State, "account.account_identifier", account)
 
 	b, err = worker.Process(ctx, dbTx, j)
 	assert.NoError(t, err)
@@ -1048,9 +1135,7 @@ func TestJob_ComplicatedTransfer(t *testing.T) {
 				OperationIdentifier: &types.OperationIdentifier{
 					Index: 0,
 				},
-				Account: &types.AccountIdentifier{
-					Address: "test",
-				},
+				Account: account,
 				Amount: &types.Amount{
 					Value: "-90",
 					Currency: &types.Currency{
@@ -1083,7 +1168,7 @@ func TestJob_ComplicatedTransfer(t *testing.T) {
 
 	assertVariableEquality(t, j.State, "network", network)
 	assertVariableEquality(t, j.State, "key.public_key.curve_type", types.Secp256k1)
-	assertVariableEquality(t, j.State, "address.address", address)
+	assertVariableEquality(t, j.State, "account.account_identifier", account)
 
 	mockHelper.AssertExpectations(t)
 }
@@ -1171,7 +1256,7 @@ func TestJob_Failures(t *testing.T) {
 				Name: "create_address",
 				Actions: []*job.Action{
 					{
-						Type:  job.SaveAddress,
+						Type:  job.SaveAccount,
 						Input: `{}`,
 					},
 				},
diff --git a/keys/signer_edwards25519_test.go b/keys/signer_edwards25519_test.go
index 072cade..6789e50 100644
--- a/keys/signer_edwards25519_test.go
+++ b/keys/signer_edwards25519_test.go
@@ -31,9 +31,9 @@ func init() {
 
 func mockPayload(msg []byte, signatureType types.SignatureType) *types.SigningPayload {
 	payload := &types.SigningPayload{
-		Address:       "test",
-		Bytes:         msg,
-		SignatureType: signatureType,
+		AccountIdentifier: &types.AccountIdentifier{Address: "test"},
+		Bytes:             msg,
+		SignatureType:     signatureType,
 	}
 
 	return payload
@@ -75,9 +75,9 @@ func mockSignature(
 	msg, sig []byte,
 ) *types.Signature {
 	payload := &types.SigningPayload{
-		Address:       "test",
-		Bytes:         msg,
-		SignatureType: types.Ed25519,
+		AccountIdentifier: &types.AccountIdentifier{Address: "test"},
+		Bytes:             msg,
+		SignatureType:     types.Ed25519,
 	}
 
 	mockSig := &types.Signature{
@@ -96,9 +96,9 @@ func TestVerifyEdwards25519(t *testing.T) {
 	}
 
 	payload := &types.SigningPayload{
-		Address:       "test",
-		Bytes:         make([]byte, 32),
-		SignatureType: types.Ed25519,
+		AccountIdentifier: &types.AccountIdentifier{Address: "test"},
+		Bytes:             make([]byte, 32),
+		SignatureType:     types.Ed25519,
 	}
 	testSignature, err := signerEdwards25519.Sign(payload, types.Ed25519)
 	assert.NoError(t, err)
diff --git a/keys/signer_secp256k1_test.go b/keys/signer_secp256k1_test.go
index 3cc32c3..0869767 100644
--- a/keys/signer_secp256k1_test.go
+++ b/keys/signer_secp256k1_test.go
@@ -74,9 +74,9 @@ func mockSecpSignature(
 	msg, sig []byte,
 ) *types.Signature {
 	payload := &types.SigningPayload{
-		Address:       "test",
-		Bytes:         msg,
-		SignatureType: sigType,
+		AccountIdentifier: &types.AccountIdentifier{Address: "test"},
+		Bytes:             msg,
+		SignatureType:     sigType,
 	}
 
 	mockSig := &types.Signature{
@@ -95,14 +95,14 @@ func TestVerifySecp256k1(t *testing.T) {
 	}
 
 	payloadEcdsa := &types.SigningPayload{
-		Address:       "test",
-		Bytes:         hash("hello"),
-		SignatureType: types.Ecdsa,
+		AccountIdentifier: &types.AccountIdentifier{Address: "test"},
+		Bytes:             hash("hello"),
+		SignatureType:     types.Ecdsa,
 	}
 	payloadEcdsaRecovery := &types.SigningPayload{
-		Address:       "test",
-		Bytes:         hash("hello"),
-		SignatureType: types.EcdsaRecovery,
+		AccountIdentifier: &types.AccountIdentifier{Address: "test"},
+		Bytes:             hash("hello"),
+		SignatureType:     types.EcdsaRecovery,
 	}
 	testSignatureEcdsa, _ := signerSecp256k1.Sign(payloadEcdsa, types.Ecdsa)
 	testSignatureEcdsaRecovery, _ := signerSecp256k1.Sign(payloadEcdsaRecovery, types.EcdsaRecovery)
diff --git a/parser/intent_test.go b/parser/intent_test.go
index d354fbb..6829350 100644
--- a/parser/intent_test.go
+++ b/parser/intent_test.go
@@ -564,72 +564,135 @@ func TestExpectedOperations(t *testing.T) {
 func TestExpectedSigners(t *testing.T) {
 	var tests = map[string]struct {
 		intent   []*types.SigningPayload
-		observed []string
+		observed []*types.AccountIdentifier
 
 		err bool
 	}{
 		"simple match": {
 			intent: []*types.SigningPayload{
 				{
-					Address: "addr1",
+					AccountIdentifier: &types.AccountIdentifier{
+						Address: "addr1",
+					},
 				},
 				{
-					Address: "addr2",
+					AccountIdentifier: &types.AccountIdentifier{
+						Address: "addr2",
+					},
 				},
 				{
-					Address: "addr2",
+					AccountIdentifier: &types.AccountIdentifier{
+						Address: "addr2",
+					},
 				},
 			},
-			observed: []string{
-				"addr1",
-				"addr2",
+			observed: []*types.AccountIdentifier{
+				{
+					Address: "addr1",
+				},
+				{
+					Address: "addr2",
+				},
 			},
 		},
-		"duplicate observed signers": {
+		"complex match": {
 			intent: []*types.SigningPayload{
+				{
+					AccountIdentifier: &types.AccountIdentifier{
+						Address: "addr1",
+						SubAccount: &types.SubAccountIdentifier{
+							Address: "test",
+						},
+					},
+				},
+				{
+					AccountIdentifier: &types.AccountIdentifier{
+						Address: "addr2",
+					},
+				},
+				{
+					AccountIdentifier: &types.AccountIdentifier{
+						Address: "addr2",
+					},
+				},
+			},
+			observed: []*types.AccountIdentifier{
 				{
 					Address: "addr1",
+					SubAccount: &types.SubAccountIdentifier{
+						Address: "test",
+					},
 				},
 				{
 					Address: "addr2",
 				},
+			},
+		},
+		"missing observed signer": {
+			intent: []*types.SigningPayload{
 				{
-					Address: "addr2",
+					AccountIdentifier: &types.AccountIdentifier{
+						Address: "addr1",
+					},
+				},
+				{
+					AccountIdentifier: &types.AccountIdentifier{
+						Address: "addr2",
+					},
+				},
+				{
+					AccountIdentifier: &types.AccountIdentifier{
+						Address: "addr2",
+					},
 				},
 			},
-			observed: []string{
-				"addr1",
-				"addr2",
-				"addr2",
+			observed: []*types.AccountIdentifier{
+				{Address: "addr1"},
 			},
 			err: true,
 		},
-		"missing observed signer": {
+		"complex mismatch": {
 			intent: []*types.SigningPayload{
 				{
-					Address: "addr1",
+					AccountIdentifier: &types.AccountIdentifier{
+						Address: "addr1",
+					},
 				},
 				{
-					Address: "addr2",
+					AccountIdentifier: &types.AccountIdentifier{
+						Address: "addr2",
+					},
 				},
 				{
-					Address: "addr2",
+					AccountIdentifier: &types.AccountIdentifier{
+						Address: "addr2",
+					},
 				},
 			},
-			observed: []string{
-				"addr1",
+			observed: []*types.AccountIdentifier{
+				{
+					Address: "addr1",
+					SubAccount: &types.SubAccountIdentifier{
+						Address: "test",
+					},
+				},
+				{
+					Address: "addr2",
+				},
 			},
 			err: true,
 		},
 		"extra observed signer": {
 			intent: []*types.SigningPayload{
 				{
-					Address: "addr1",
+					AccountIdentifier: &types.AccountIdentifier{
+						Address: "addr1",
+					},
 				},
 			},
-			observed: []string{
-				"addr1",
-				"addr2",
+			observed: []*types.AccountIdentifier{
+				{Address: "addr1"},
+				{Address: "addr2"},
 			},
 			err: true,
 		},
diff --git a/storage/broadcast_storage_test.go b/storage/broadcast_storage_test.go
index 42cda72..62c55cf 100644
--- a/storage/broadcast_storage_test.go
+++ b/storage/broadcast_storage_test.go
@@ -66,9 +66,6 @@ func opFiller(sender string, opNumber int) []*types.Operation {
 			},
 			Account: &types.AccountIdentifier{
 				Address: sender,
-				SubAccount: &types.SubAccountIdentifier{
-					Address: sender,
-				},
 			},
 		}
 	}
@@ -125,11 +122,13 @@ func TestBroadcastStorageBroadcastSuccess(t *testing.T) {
 		)
 		assert.NoError(t, err)
 
-		// Check to make sure duplicate instances of address aren't reported
-		addresses, err := storage.LockedAddresses(ctx, dbTx)
+		// Check to make sure duplicate instances of accounts aren't reported
+		accounts, err := storage.LockedAccounts(ctx, dbTx)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 1)
-		assert.ElementsMatch(t, []string{"addr 1"}, addresses)
+		assert.Len(t, accounts, 1)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{
+			{Address: "addr 1"},
+		}, accounts)
 
 		assert.NoError(t, dbTx.Commit(ctx))
 
@@ -161,10 +160,12 @@ func TestBroadcastStorageBroadcastSuccess(t *testing.T) {
 		err = commitWorker(ctx)
 		assert.NoError(t, err)
 
-		addresses, err := storage.LockedAddresses(ctx, txn)
+		accounts, err := storage.LockedAccounts(ctx, txn)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 1)
-		assert.ElementsMatch(t, []string{"addr 1"}, addresses)
+		assert.Len(t, accounts, 1)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{
+			{Address: "addr 1"},
+		}, accounts)
 
 		broadcasts, err := storage.GetAllBroadcasts(ctx)
 		assert.NoError(t, err)
@@ -198,10 +199,12 @@ func TestBroadcastStorageBroadcastSuccess(t *testing.T) {
 		err = commitWorker(ctx)
 		assert.NoError(t, err)
 
-		addresses, err := storage.LockedAddresses(ctx, txn)
+		accounts, err := storage.LockedAccounts(ctx, txn)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 1)
-		assert.ElementsMatch(t, []string{"addr 1"}, addresses)
+		assert.Len(t, accounts, 1)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{
+			{Address: "addr 1"},
+		}, accounts)
 
 		broadcasts, err := storage.GetAllBroadcasts(ctx)
 		assert.NoError(t, err)
@@ -239,10 +242,13 @@ func TestBroadcastStorageBroadcastSuccess(t *testing.T) {
 		assert.NoError(t, err)
 
 		// Check to make sure duplicate instances of address aren't reported
-		addresses, err := storage.LockedAddresses(ctx, dbTx)
+		accounts, err := storage.LockedAccounts(ctx, dbTx)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 2)
-		assert.ElementsMatch(t, []string{"addr 1", "addr 2"}, addresses)
+		assert.Len(t, accounts, 2)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{
+			{Address: "addr 1"},
+			{Address: "addr 2"},
+		}, accounts)
 
 		assert.NoError(t, dbTx.Commit(ctx))
 		assert.NoError(t, storage.BroadcastAll(ctx, true))
@@ -280,10 +286,13 @@ func TestBroadcastStorageBroadcastSuccess(t *testing.T) {
 		commitWorker, err := storage.AddingBlock(ctx, block, txn)
 		assert.NoError(t, err)
 
-		addresses, err := storage.LockedAddresses(ctx, txn)
+		accounts, err := storage.LockedAccounts(ctx, txn)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 2)
-		assert.ElementsMatch(t, []string{"addr 1", "addr 2"}, addresses)
+		assert.Len(t, accounts, 2)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{
+			{Address: "addr 1"},
+			{Address: "addr 2"},
+		}, accounts)
 
 		err = txn.Commit(ctx)
 		assert.NoError(t, err)
@@ -357,10 +366,13 @@ func TestBroadcastStorageBroadcastSuccess(t *testing.T) {
 		err = commitWorker(ctx)
 		assert.NoError(t, err)
 
-		addresses, err := storage.LockedAddresses(ctx, txn)
+		accounts, err := storage.LockedAccounts(ctx, txn)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 2)
-		assert.ElementsMatch(t, []string{"addr 1", "addr 2"}, addresses)
+		assert.Len(t, accounts, 2)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{
+			{Address: "addr 1"},
+			{Address: "addr 2"},
+		}, accounts)
 
 		broadcasts, err := storage.GetAllBroadcasts(ctx)
 		assert.NoError(t, err)
@@ -404,10 +416,12 @@ func TestBroadcastStorageBroadcastSuccess(t *testing.T) {
 		commitWorker, err := storage.AddingBlock(ctx, block, txn)
 		assert.NoError(t, err)
 
-		addresses, err := storage.LockedAddresses(ctx, txn)
+		accounts, err := storage.LockedAccounts(ctx, txn)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 1)
-		assert.ElementsMatch(t, []string{"addr 2"}, addresses)
+		assert.Len(t, accounts, 1)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{
+			{Address: "addr 2"},
+		}, accounts)
 
 		err = txn.Commit(ctx)
 		assert.NoError(t, err)
@@ -452,10 +466,10 @@ func TestBroadcastStorageBroadcastSuccess(t *testing.T) {
 		commitWorker, err := storage.AddingBlock(ctx, block, txn)
 		assert.NoError(t, err)
 
-		addresses, err := storage.LockedAddresses(ctx, txn)
+		accounts, err := storage.LockedAccounts(ctx, txn)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 0)
-		assert.ElementsMatch(t, []string{}, addresses)
+		assert.Len(t, accounts, 0)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{}, accounts)
 
 		err = txn.Commit(ctx)
 		assert.NoError(t, err)
@@ -516,10 +530,10 @@ func TestBroadcastStorageBroadcastFailure(t *testing.T) {
 		dbTx := database.NewDatabaseTransaction(ctx, false)
 		defer dbTx.Discard(ctx)
 
-		addresses, err := storage.LockedAddresses(ctx, dbTx)
+		accounts, err := storage.LockedAccounts(ctx, dbTx)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 0)
-		assert.ElementsMatch(t, []string{}, addresses)
+		assert.Len(t, accounts, 0)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{}, accounts)
 	})
 
 	send1 := opFiller("addr 1", 11)
@@ -563,10 +577,14 @@ func TestBroadcastStorageBroadcastFailure(t *testing.T) {
 		assert.NoError(t, err)
 
 		// Check to make sure duplicate instances of address aren't reported
-		addresses, err := storage.LockedAddresses(ctx, dbTx)
+		accounts, err := storage.LockedAccounts(ctx, dbTx)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 3)
-		assert.ElementsMatch(t, []string{"addr 1", "addr 2", "addr 3"}, addresses)
+		assert.Len(t, accounts, 3)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{
+			{Address: "addr 1"},
+			{Address: "addr 2"},
+			{Address: "addr 3"},
+		}, accounts)
 
 		assert.NoError(t, dbTx.Commit(ctx))
 		assert.NoError(t, storage.BroadcastAll(ctx, true))
@@ -615,10 +633,10 @@ func TestBroadcastStorageBroadcastFailure(t *testing.T) {
 		dbTx := database.NewDatabaseTransaction(ctx, false)
 		defer dbTx.Discard(ctx)
 
-		addresses, err := storage.LockedAddresses(ctx, dbTx)
+		accounts, err := storage.LockedAccounts(ctx, dbTx)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 0)
-		assert.ElementsMatch(t, []string{}, addresses)
+		assert.Len(t, accounts, 0)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{}, accounts)
 
 		broadcasts, err := storage.GetAllBroadcasts(ctx)
 		assert.NoError(t, err)
@@ -703,10 +721,13 @@ func TestBroadcastStorageBehindTip(t *testing.T) {
 		assert.NoError(t, err)
 
 		// Check to make sure duplicate instances of address aren't reported
-		addresses, err := storage.LockedAddresses(ctx, dbTx)
+		accounts, err := storage.LockedAccounts(ctx, dbTx)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 2)
-		assert.ElementsMatch(t, []string{"addr 1", "addr 2"}, addresses)
+		assert.Len(t, accounts, 2)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{
+			{Address: "addr 1"},
+			{Address: "addr 2"},
+		}, accounts)
 
 		assert.NoError(t, dbTx.Commit(ctx))
 
@@ -755,10 +776,13 @@ func TestBroadcastStorageBehindTip(t *testing.T) {
 		dbTx := database.NewDatabaseTransaction(ctx, false)
 		defer dbTx.Discard(ctx)
 
-		addresses, err := storage.LockedAddresses(ctx, dbTx)
+		accounts, err := storage.LockedAccounts(ctx, dbTx)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 2)
-		assert.ElementsMatch(t, []string{"addr 1", "addr 2"}, addresses)
+		assert.Len(t, accounts, 2)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{
+			{Address: "addr 1"},
+			{Address: "addr 2"},
+		}, accounts)
 
 		broadcasts, err := storage.GetAllBroadcasts(ctx)
 		assert.NoError(t, err)
@@ -802,10 +826,13 @@ func TestBroadcastStorageBehindTip(t *testing.T) {
 		dbTx := database.NewDatabaseTransaction(ctx, false)
 		defer dbTx.Discard(ctx)
 
-		addresses, err := storage.LockedAddresses(ctx, dbTx)
+		accounts, err := storage.LockedAccounts(ctx, dbTx)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 2)
-		assert.ElementsMatch(t, []string{"addr 1", "addr 2"}, addresses)
+		assert.Len(t, accounts, 2)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{
+			{Address: "addr 1"},
+			{Address: "addr 2"},
+		}, accounts)
 
 		broadcasts, err := storage.GetAllBroadcasts(ctx)
 		assert.NoError(t, err)
@@ -865,10 +892,10 @@ func TestBroadcastStorageClearBroadcasts(t *testing.T) {
 		dbTx := database.NewDatabaseTransaction(ctx, false)
 		defer dbTx.Discard(ctx)
 
-		addresses, err := storage.LockedAddresses(ctx, dbTx)
+		accounts, err := storage.LockedAccounts(ctx, dbTx)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 0)
-		assert.ElementsMatch(t, []string{}, addresses)
+		assert.Len(t, accounts, 0)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{}, accounts)
 	})
 
 	send1 := opFiller("addr 1", 11)
@@ -902,10 +929,13 @@ func TestBroadcastStorageClearBroadcasts(t *testing.T) {
 		assert.NoError(t, err)
 
 		// Check to make sure duplicate instances of address aren't reported
-		addresses, err := storage.LockedAddresses(ctx, dbTx)
+		accounts, err := storage.LockedAccounts(ctx, dbTx)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 2)
-		assert.ElementsMatch(t, []string{"addr 1", "addr 2"}, addresses)
+		assert.Len(t, accounts, 2)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{
+			{Address: "addr 1"},
+			{Address: "addr 2"},
+		}, accounts)
 
 		assert.NoError(t, dbTx.Commit(ctx))
 
@@ -956,10 +986,10 @@ func TestBroadcastStorageClearBroadcasts(t *testing.T) {
 		dbTx := database.NewDatabaseTransaction(ctx, false)
 		defer dbTx.Discard(ctx)
 
-		addresses, err := storage.LockedAddresses(ctx, dbTx)
+		accounts, err := storage.LockedAccounts(ctx, dbTx)
 		assert.NoError(t, err)
-		assert.Len(t, addresses, 0)
-		assert.ElementsMatch(t, []string{}, addresses)
+		assert.Len(t, accounts, 0)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{}, accounts)
 	})
 }
 
diff --git a/storage/key_storage_test.go b/storage/key_storage_test.go
index 7823179..b65fc47 100644
--- a/storage/key_storage_test.go
+++ b/storage/key_storage_test.go
@@ -51,61 +51,78 @@ func TestKeyStorage(t *testing.T) {
 	assert.NoError(t, err)
 
 	t.Run("get non-existent key", func(t *testing.T) {
-		v, err := k.Get(ctx, "blah")
+		v, err := k.Get(ctx, &types.AccountIdentifier{Address: "blah"})
 		assert.Error(t, err)
 		assert.Nil(t, v)
 
-		addrs, err := k.GetAllAddresses(ctx)
+		accounts, err := k.GetAllAccounts(ctx)
 		assert.NoError(t, err)
-		assert.Len(t, addrs, 0)
+		assert.Len(t, accounts, 0)
 	})
 
 	t.Run("store and get key", func(t *testing.T) {
-		err = k.Store(ctx, "addr1", kp1)
+		err = k.Store(ctx, &types.AccountIdentifier{Address: "addr1"}, kp1)
 		assert.NoError(t, err)
 
-		v, err := k.Get(ctx, "addr1")
+		v, err := k.Get(ctx, &types.AccountIdentifier{Address: "addr1"})
 		assert.NoError(t, err)
 		assert.Equal(t, kp1, v)
 
-		addrs, err := k.GetAllAddresses(ctx)
+		// ensure account with subaccount is not accessible
+		v, err = k.Get(ctx, &types.AccountIdentifier{
+			Address: "addr1",
+			SubAccount: &types.SubAccountIdentifier{
+				Address: "blah",
+			},
+		})
+		assert.Error(t, err)
+		assert.Nil(t, v)
+
+		accounts, err := k.GetAllAccounts(ctx)
 		assert.NoError(t, err)
-		assert.Equal(t, []string{"addr1"}, addrs)
+		assert.Equal(t, []*types.AccountIdentifier{
+			{
+				Address: "addr1",
+			},
+		}, accounts)
 	})
 
 	t.Run("attempt overwrite", func(t *testing.T) {
-		err = k.Store(ctx, "addr1", kp2)
+		err = k.Store(ctx, &types.AccountIdentifier{Address: "addr1"}, kp2)
 		assert.Error(t, err)
 
-		v, err := k.Get(ctx, "addr1")
+		v, err := k.Get(ctx, &types.AccountIdentifier{Address: "addr1"})
 		assert.NoError(t, err)
 		assert.Equal(t, kp1, v)
 	})
 
 	t.Run("store and get another key", func(t *testing.T) {
-		err = k.Store(ctx, "addr2", kp2)
+		err = k.Store(ctx, &types.AccountIdentifier{Address: "addr2"}, kp2)
 		assert.NoError(t, err)
 
-		v, err := k.Get(ctx, "addr2")
+		v, err := k.Get(ctx, &types.AccountIdentifier{Address: "addr2"})
 		assert.NoError(t, err)
 		assert.Equal(t, kp2, v)
 
-		addrs, err := k.GetAllAddresses(ctx)
+		accounts, err := k.GetAllAccounts(ctx)
 		assert.NoError(t, err)
-		assert.Equal(t, []string{"addr1", "addr2"}, addrs)
+		assert.ElementsMatch(t, []*types.AccountIdentifier{
+			{Address: "addr1"},
+			{Address: "addr2"},
+		}, accounts)
 	})
 
 	t.Run("sign payloads", func(t *testing.T) {
 		payloads := []*types.SigningPayload{
 			{
-				Address:       "addr1",
-				Bytes:         hash("msg1"),
-				SignatureType: types.Ed25519,
+				AccountIdentifier: &types.AccountIdentifier{Address: "addr1"},
+				Bytes:             hash("msg1"),
+				SignatureType:     types.Ed25519,
 			},
 			{
-				Address:       "addr2",
-				Bytes:         hash("msg2"),
-				SignatureType: types.Ecdsa,
+				AccountIdentifier: &types.AccountIdentifier{Address: "addr2"},
+				Bytes:             hash("msg2"),
+				SignatureType:     types.Ecdsa,
 			},
 		}
 
@@ -119,9 +136,9 @@ func TestKeyStorage(t *testing.T) {
 	t.Run("missing address in sign", func(t *testing.T) {
 		payloads := []*types.SigningPayload{
 			{
-				Address:       "addr3",
-				Bytes:         hash("msg3"),
-				SignatureType: types.Ed25519,
+				AccountIdentifier: &types.AccountIdentifier{Address: "addr3"},
+				Bytes:             hash("msg3"),
+				SignatureType:     types.Ed25519,
 			},
 		}
 
@@ -133,8 +150,8 @@ func TestKeyStorage(t *testing.T) {
 	t.Run("missing signature type in sign", func(t *testing.T) {
 		payloads := []*types.SigningPayload{
 			{
-				Address: "addr1",
-				Bytes:   hash("msg1"),
+				AccountIdentifier: &types.AccountIdentifier{Address: "addr1"},
+				Bytes:             hash("msg1"),
 			},
 		}
 
@@ -144,32 +161,33 @@ func TestKeyStorage(t *testing.T) {
 	})
 
 	t.Run("imports accounts", func(t *testing.T) {
-		addresses, err := k.GetAllAddresses(ctx)
+		accounts, err := k.GetAllAccounts(ctx)
 		assert.NoError(t, err)
-		startingLen := len(addresses)
+		startingLen := len(accounts)
 
 		prefundedAccs := []*PrefundedAccount{
 			{
 				PrivateKeyHex: "0e842a16b2d39a4dff5c63688513cb2109e30c3c30bc4eb502cc54f4614493f6",
-				Address:       "add1",
+				Account:       &types.AccountIdentifier{Address: "add1"},
 				CurveType:     types.Edwards25519,
 			},
 			{
 				PrivateKeyHex: "42efc44bdf7b2d4d45ddd6ddb727ed498c91e7070914c9ed0d80af680ff42b3e",
-				Address:       "add2",
+				Account:       &types.AccountIdentifier{Address: "add2"},
 				CurveType:     types.Edwards25519,
 			},
 			{
 				PrivateKeyHex: "01ea48249742650907004331e85536f868e2d3959434ba751d8aa230138a9707",
-				Address:       "add3",
+				Account:       &types.AccountIdentifier{Address: "add3"},
 				CurveType:     types.Edwards25519,
 			},
 		}
 
 		err = k.ImportAccounts(ctx, prefundedAccs)
 		assert.NoError(t, err)
-		addresses, _ = k.GetAllAddresses(ctx)
-		endLen := len(addresses)
+		accounts, err = k.GetAllAccounts(ctx)
+		assert.NoError(t, err)
+		endLen := len(accounts)
 		assert.Equal(t, endLen, startingLen+len(prefundedAccs))
 	})
 
@@ -177,7 +195,7 @@ func TestKeyStorage(t *testing.T) {
 		prefundedAccs := []*PrefundedAccount{
 			{
 				PrivateKeyHex: "17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55",
-				Address:       "badadd",
+				Account:       &types.AccountIdentifier{Address: "badadd"},
 				CurveType:     types.Edwards25519,
 			},
 		}
@@ -185,14 +203,16 @@ func TestKeyStorage(t *testing.T) {
 		// No error when importing key for the first time
 		err = k.ImportAccounts(ctx, prefundedAccs)
 		assert.NoError(t, err)
-		addresses, _ := k.GetAllAddresses(ctx)
-		startingLen := len(addresses)
+		accounts, err := k.GetAllAccounts(ctx)
+		assert.NoError(t, err)
+		startingLen := len(accounts)
 
 		// No error when trying to import the key again
 		err = k.ImportAccounts(ctx, prefundedAccs)
 		assert.NoError(t, err)
-		addresses, _ = k.GetAllAddresses(ctx)
-		endLen := len(addresses)
+		accounts, err = k.GetAllAccounts(ctx)
+		assert.NoError(t, err)
+		endLen := len(accounts)
 
 		assert.Equal(t, endLen, startingLen)
 	})
diff --git a/types/marshal_test.go b/types/marshal_test.go
index 9f60791..181ac5f 100644
--- a/types/marshal_test.go
+++ b/types/marshal_test.go
@@ -98,16 +98,23 @@ func TestCustomMarshalSignature(t *testing.T) {
 
 func TestCustomMarshalSigningPayload(t *testing.T) {
 	s := &SigningPayload{
-		Address: "addr1",
-		Bytes:   []byte("hsdjkfhkasjfhkjasdhfkjasdnfkjabsdfkjhakjsfdhjksadhfjk23478923645yhsdfn"),
+		AccountIdentifier: &AccountIdentifier{
+			Address: "addr1",
+			SubAccount: &SubAccountIdentifier{
+				Address: "sub",
+			},
+		},
+		Bytes: []byte("hsdjkfhkasjfhkjasdhfkjasdnfkjabsdfkjhakjsfdhjksadhfjk23478923645yhsdfn"),
 	}
 
 	j, err := json.Marshal(s)
 	assert.NoError(t, err)
 
-	// Simple Hex Check
+	// Hex and address Check
 	simpleType := struct {
-		HexBytes string `json:"hex_bytes"`
+		AccountIdentifier *AccountIdentifier `json:"account_identifier"`
+		Address           string             `json:"address"`
+		HexBytes          string             `json:"hex_bytes"`
 	}{}
 
 	err = json.Unmarshal(j, &simpleType)
@@ -115,14 +122,14 @@ func TestCustomMarshalSigningPayload(t *testing.T) {
 
 	b, err := hex.DecodeString(simpleType.HexBytes)
 	assert.NoError(t, err)
-
 	assert.Equal(t, s.Bytes, b)
+	assert.Equal(t, s.AccountIdentifier.Address, simpleType.Address)
+	assert.Equal(t, s.AccountIdentifier, simpleType.AccountIdentifier)
 
 	// Full Unmarshal Check
 	s2 := &SigningPayload{}
 	err = json.Unmarshal(j, s2)
 	assert.NoError(t, err)
-
 	assert.Equal(t, s, s2)
 
 	// Invalid Hex Check
@@ -130,4 +137,128 @@ func TestCustomMarshalSigningPayload(t *testing.T) {
 	err = json.Unmarshal([]byte(`{"hex_bytes":"hello"}`), s3)
 	assert.Error(t, err)
 	assert.Len(t, s3.Bytes, 0)
+
+	// Unmarshal fields
+	var s4 SigningPayload
+	err = json.Unmarshal([]byte(`{"address":"hello", "hex_bytes":"74657374"}`), &s4)
+	assert.NoError(t, err)
+	assert.Equal(t, &AccountIdentifier{Address: "hello"}, s4.AccountIdentifier)
+	assert.Equal(t, []byte("test"), s4.Bytes)
+
+	// Unmarshal fields (empty address)
+	var s5 SigningPayload
+	err = json.Unmarshal([]byte(`{"hex_bytes":"74657374"}`), &s5)
+	assert.NoError(t, err)
+	assert.Nil(t, s5.AccountIdentifier)
+	assert.Equal(t, []byte("test"), s5.Bytes)
+}
+
+func TestCustomConstructionDeriveResponse(t *testing.T) {
+	s := &ConstructionDeriveResponse{
+		AccountIdentifier: &AccountIdentifier{
+			Address: "addr1",
+		},
+	}
+
+	j, err := json.Marshal(s)
+	assert.NoError(t, err)
+
+	// Address Check
+	simpleType := struct {
+		Address           string             `json:"address"`
+		AccountIdentifier *AccountIdentifier `json:"account_identifier"`
+	}{}
+
+	err = json.Unmarshal(j, &simpleType)
+	assert.NoError(t, err)
+
+	assert.Equal(t, s.AccountIdentifier.Address, simpleType.Address)
+	assert.Equal(t, s.AccountIdentifier, simpleType.AccountIdentifier)
+
+	// Full Unmarshal Check
+	s2 := &ConstructionDeriveResponse{}
+	err = json.Unmarshal(j, s2)
+	assert.NoError(t, err)
+	assert.Equal(t, s, s2)
+
+	// Unmarshal fields
+	var s3 ConstructionDeriveResponse
+	err = json.Unmarshal([]byte(`{"address":"hello", "hex_bytes":"74657374"}`), &s3)
+	assert.NoError(t, err)
+	assert.Equal(t, &AccountIdentifier{Address: "hello"}, s3.AccountIdentifier)
+
+	// Unmarshal fields (empty address)
+	var s4 SigningPayload
+	err = json.Unmarshal([]byte(`{"hex_bytes":"74657374"}`), &s4)
+	assert.NoError(t, err)
+	assert.Nil(t, s4.AccountIdentifier)
+
+	// Unmarshal fields (override)
+	var s5 ConstructionDeriveResponse
+	err = json.Unmarshal(
+		[]byte(
+			`{"address":"hello", "account_identifier":{"address":"hello2"}, "hex_bytes":"74657374"}`, // nolint
+		),
+		&s5,
+	)
+	assert.NoError(t, err)
+	assert.Equal(t, &AccountIdentifier{Address: "hello2"}, s5.AccountIdentifier)
+}
+
+func TestCustomConstructionParseResponse(t *testing.T) {
+	s := &ConstructionParseResponse{
+		AccountIdentifierSigners: []*AccountIdentifier{
+			{
+				Address: "addr1",
+				SubAccount: &SubAccountIdentifier{
+					Address: "sub",
+				},
+			},
+			{
+				Address: "addr2",
+			},
+		},
+	}
+
+	j, err := json.Marshal(s)
+	assert.NoError(t, err)
+
+	// Address Check
+	simpleType := struct {
+		Signers []string `json:"signers"`
+	}{}
+
+	err = json.Unmarshal(j, &simpleType)
+	assert.NoError(t, err)
+
+	assert.Equal(t, []string{"addr1", "addr2"}, simpleType.Signers)
+
+	// Full Unmarshal Check
+	s2 := &ConstructionParseResponse{}
+	err = json.Unmarshal(j, s2)
+	assert.NoError(t, err)
+	assert.Equal(t, s, s2)
+
+	// Unmarshal fields
+	var s3 ConstructionParseResponse
+	err = json.Unmarshal([]byte(`{"signers":["hello"], "hex_bytes":"74657374"}`), &s3)
+	assert.NoError(t, err)
+	assert.Equal(t, []*AccountIdentifier{{Address: "hello"}}, s3.AccountIdentifierSigners)
+
+	// Unmarshal fields (empty address)
+	var s4 ConstructionParseResponse
+	err = json.Unmarshal([]byte(`{"hex_bytes":"74657374"}`), &s4)
+	assert.NoError(t, err)
+	assert.Nil(t, s4.AccountIdentifierSigners)
+
+	// Unmarshal fields (override)
+	var s5 ConstructionParseResponse
+	err = json.Unmarshal(
+		[]byte(
+			`{"signers":["hello"], "account_identifier_signers":[{"address":"hello2"}], "hex_bytes":"74657374"}`, // nolint
+		),
+		&s5,
+	)
+	assert.NoError(t, err)
+	assert.Equal(t, []*AccountIdentifier{{Address: "hello2"}}, s5.AccountIdentifierSigners)
 }
diff --git a/utils/utils_test.go b/utils/utils_test.go
index abf8dfa..9a2bc79 100644
--- a/utils/utils_test.go
+++ b/utils/utils_test.go
@@ -138,6 +138,57 @@ func TestContainsString(t *testing.T) {
 	}
 }
 
+func TestContainsAccountIdentifier(t *testing.T) {
+	var tests = map[string]struct {
+		arr []*types.AccountIdentifier
+		s   *types.AccountIdentifier
+
+		contains bool
+	}{
+		"empty arr": {
+			s: &types.AccountIdentifier{Address: "hello"},
+		},
+		"single arr": {
+			arr: []*types.AccountIdentifier{
+				{Address: "hello"},
+			},
+			s:        &types.AccountIdentifier{Address: "hello"},
+			contains: true,
+		},
+		"single arr sub account mismatch": {
+			arr: []*types.AccountIdentifier{
+				{
+					Address: "hello",
+					SubAccount: &types.SubAccountIdentifier{
+						Address: "sub",
+					},
+				},
+			},
+			s: &types.AccountIdentifier{Address: "hello"},
+		},
+		"single arr no elem": {
+			arr: []*types.AccountIdentifier{
+				{Address: "hello"},
+			},
+			s: &types.AccountIdentifier{Address: "test"},
+		},
+		"multiple arr with elem": {
+			arr: []*types.AccountIdentifier{
+				{Address: "hello"},
+				{Address: "test"},
+			},
+			s:        &types.AccountIdentifier{Address: "test"},
+			contains: true,
+		},
+	}
+
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			assert.Equal(t, test.contains, ContainsAccountIdentifier(test.arr, test.s))
+		})
+	}
+}
+
 func TestBigPow10(t *testing.T) {
 	e := int32(12)
 	v := big.NewFloat(10)