Skip to main content
Version: 0.1.0

Wallet Library Specifications

Introduction

The wallet.rs library is a stateful package with a standardized interface to build applications with IOTA value transactions. The package is compatible with different platforms such as web, desktop, and mobile.

The package introduces the concept of an account . An account is a reference to, or a label for, a seed. It has certain properties such as addresses and messages. An account also maintains various behaviours, including moving funds, looking for new messages, and making copies of message histories. Additionally, it provides a degree of financial privacy and thus does not incur any overhead.

A similar account package was used before but it became obsolete with the introduction of Ed25519 signatures. The previous account package was also limited to a single account, whereas the new package manages multiple accounts.

For IOTA, the motivation to use this package was to offer a simplified (stateful) approach to handle IOTA payments.

Considerations

  • Seeds should be stored and managed separately in a secure enclave and should never leave the secure environment. Secure enclaves include software enclaves such as IOTA’s Rust-based Stronghold library or hardware enclaves such as a Ledger Nano.

  • The secure enclave should have the ability to generate addresses and sign messages upon receipt, and return the output in a new message. If the secure enclave is initialized with a pre-generated seed, the sender process should immediately remove the seed traces from memory.

Naming Conventions

The primary language is Rust. Therefore, you should follow the standard Rust naming conventions. For reference, all interfaces (types) use CamelCase while all function and variable names use snake_case.

Interfaces

AccountConfiguration

Account configuration or initialization object. It should support parameters accepted by high level client libraries.

PropertyRequiredTypeDescription
seedstringBIP-39 mnemonic. When importing an account from Stronghold backup, the seed will not be required.
idstringSHA-256 hash of the first address on the seed (m/44'/0'/0'/0'/0'). Required for referencing a seed in Stronghold. The ID should be provided by Stronghold.
indexnumberAccount index in BIP-44 derivation path
aliasstringAccount name. If not provided, Account + { index } should be used. When importing an account from Stronghold backup, the alias will be required from Stronghold.
powlocal,remoteProof of work settings. Defaults to local. local PoW should be performed on device; remote PoW should be performed on the node.
nodesnode[]A list of nodes to connect to.
quorum_sizenumberIf multiple nodes are provided, quorum size determines the number of nodes to query to check for quorum.
quorum_thresholdnumberMinimum number of nodes from the quorum pool that need to agree to consider a result true.
networkmainnet|devnet | comnetIOTA public network.
typedefault or ledgerAccount type. Required for differentiating ledger vs non-ledger accounts.
providerstringNode URL.
created_atDateTime of account creation
messagesMessage[] Messages associated with account. Accounts can be initialized with locally stored messages.
addressesAddress[]Address history associated with the account. Accounts can be initialized with locally stored address history

AccountObject

PropertyRequiredTypeDescription
idstringSHA-256 hash of the first address on the seed (m/44'/0'/0'/0/0). Required for referencing a seed in Stronghold.
aliasstringAccount name.
created_atnumberAccount creation time.
last_synced_atstringTime the account was last synced with the Tangle.
sync()functionSyncs account with the Tangle.
reattach()functionReattaches unconfirmed transaction to the Tangle.
total_balance()functionGets total account balance.
available_balance()functionGets available account balance.
set_alias()functionUpdates account name.
list_messages()functionGets messages.
list_received_messages()functionGets all received messages.
list_sent_messages()functionGets all sent messages.
list_failed_messages()functionGets all failed messages.
list_unconfirmed_messages()functionGets all unconfirmed messages.
get_message()functionGets message for providedID.
list_addresses()functionGets all addresses.
list_unspent_addresses()functionGets all unspent input addresses.
generate_address()functionGets the latest unused address.

SyncedAccountObject

PropertyRequiredTypeDescription
deposit_addressAddressDeposit address. Only exposed on successful completion of account syncing process.
send()functionSend transaction method. Only exposed on successful completion of account syncing process.
retry()functionRebroadcasts failed transaction. Only exposed on successful completion of account syncing process.

AccountManagerObject

PropertyRequiredTypeDescription
accountsAccount[]Account objects.
add_account()functionAdds a new account.
remove_account()functionRemoves an account.
sync_accounts()functionSyncs all stored accounts with the Tangle.
move()functionInititates an internal transaction between accounts.
backup()functionCreates a backup to a provided destination.
import_accounts()functionImports backed up accounts.
get_account()functionReturns the account associated with the provided address.
reattach()functionReattaches an unconfirmed transaction.

Address

Useful reference for address management in Hierarchical Deterministic (HD) wallets.

PropertyRequiredTypeDescription
addressstringAddress (Bech32) string.
balancenumberAddress balance.
indexnumberAddress index.
internalbooleanDetermines if an address is a public or an internal (change) address. See the concept of chain node for more details.
checksumstringAddress checksum.

Node

PropertyRequiredTypeDescription
urlstringNode URL.
powbooleanDetermines if the node accepts proof of work.
usernamestringNode username. Only required if node requires authorisation.
passwordstringNode password. Only required if node requires authorisation.
networkmainnet | devnet | comnetIOTA public network name.

Timestamp

PropertyRequiredTypeDescription
format(type: string):stringfunctionTransaction timestamp in various formats.
For example: MM-DD-YYYY, DD MM YYYY hh:mm:ss.

Transfer

Transfer object required for creating a transaction. It allows end-users to specify the transaction amount and recipient address.

info

Currently, it is not possible to send multiple payloads as part of the message. That is why the tag property is omitted from this interface. You can find more details in this GitHub pull request.

PropertyRequiredTypeDescription
amountnumberTransfer amount.
addressstringTransfer address.
indexation_keyIndexation Payload(Optional) Indexation payload.

Value

PropertyRequiredTypeDescription
with_denomination():stringfunctionTransaction amount with unit.
without_denomination():numberfunctionTransaction amount without unit.

Input

PropertyRequiredTypeDescription
typenumberInput type. Defaults to 0.
idstringBLAKE2b-256 hash of the transaction.
output_indexnumberIndex of the output on the referenced transaction.

OutputAddress

PropertyRequiredTypeDescription
typenumberSet to value 0 to denote an Ed25519 address.
addressstringIf type is set to 0, it should contain an Ed25519 address.

Output

PropertyRequiredTypeDescription
typenumberOutput type. Defaults to 0.
addressOutputAddressOutput address.
amountnumberAmount of tokens to deposit.

UnsignedDataPayload

PropertyRequiredTypeDescription
typenumberSet to 2 to denote a unsigned data payload.
datastringData of unsigned payload.

SignedDataPayload

PropertyRequiredTypeDescription
typenumberSet to 3 to denote a signed data payload.
datastringData of signed data payload.
public_keystringEd25519 public key used to verify the signature.
signaturestringSignature of signing data.

IndexationPayload

PropertyRequiredTypeDescription
indexstringIndexation key.
datastringIndexation data.

UnsignedTransaction

PropertyRequiredTypeDescription
typenumberTransaction type. Defaults to 0.
inputs_countnumberAmount of inputs proceeding.
inputsInput[]Transaction inputs.
outputs_countnumberAmount of outputs proceeding.
outputsOutput[]Output address.
payload_lengthnumberLength of optional payload.
payloadUnsignedDataPayload | SignedDataPayload | IndexationPayloadPayload containing data. As multiple payloads are not yet supported, only unsigned data payload should be used.

Ed25519Signature

PropertyRequiredTypeDescription
typenumberSet to value 1 to denote an Ed25519 signature.
public_keynumberPublic key of the Ed25519 keypair which is used to verify the signature.
signaturestringSignature signing the serialized unsigned transaction.

SignatureUnblockBlock

PropertyRequiredTypeDescription
typenumberSet to value 0 to denote a signature unlock block.
signatureEd25519SignatureAn unlock block containing signature(s) unlocking input(s).

ReferenceUnblockBlock

PropertyRequiredTypeDescription
typenumberSet to value 1 to denote a reference unlock block.
referencenumberIndex of a previous unlock block.

SignedTransactionPayload

PropertyRequiredTypeDescription
typenumberPayload type. Defaults to 0.
transactionUnsignedTransactionEssence data making up a transaction by defining its inputs and outputs and an optional payload.
unblock_blocks_countnumberNumber of inputs specifed.
unblock_blocksSignatureUnblockBlockReferenceUnblockBlockHolds the unlock blocks unlocking inputs within an Unsigned Transaction

Message

PropertyRequiredTypeDescription
versionnumberMessage version. Defaults to 1.
parentsstring[]Message ids this message references.
payload_lengthnumberLength of the payload.
payloadSignedTransactionPayload
UnsignedDataPayload
SignedDataPayloadTransaction amount (exposed as a custom type with additional methods).
timestampTimestampTransaction timestamp (exposed as a custom type with additional methods).
noncestringTransaction nonce.
confirmedbooleanDetermines if the transaction is confirmed.
broadcastedbooleanDetermines if the transaction was broadcasted to the network. This will be true if the transaction was fetched from the network or if the transaction was successfully broadcasted from the client itself. This property may only be required for clients with persistent storage.
incomingbooleanDetermines if the message is an incoming transaction or not.
valuenumberMessage transfer value.

StorageAdapter

PropertyRequiredTypeDescription
get(key: string):AccountfunctionGets the account object for provided account name or ID.
getAll(): Account[]functionGets all account objects from storage.
set(key: string, payload: string):voidfunctionStores account in storage.
remove(key: string): voidfunctionRemoves account from storage.

Storage

danger

Using Stronghold for storage is currently under research/development.

You should consider multiple storage options should for managing data that requires persistence:

  • You can use a simple key-value storage could be leveraged for wallet basic metadata, such as user settings or theming options.
  • For transactions and address data management you could use a relational database such as SQLite.

What follows is an Entity Relationship Diagram (ERD) that shows the logical representation of the data. An account is the basic entity in this database design. It has a one-to-many relationship with addresses. This means an account could have multiple addresses , but an address can only belong to a single account. An account has a many-to-many relationship with transactions . Therefore, an account could have multiple transactions, but it is possible that a transaction belongs to multiple accounts. To accommodate this behaviour, an additional table that stores account IDs against transaction IDs (hashes) was added.

A storage adapter is required by the Rust layer to handle all the storage operations (read/write) from that layer. A generic storage adapter is defined in the storage adapter section.

Storage - Entity Relationship Diagram

Storage Adapter

The package should have a default opinionated storage mechanism but should also provide the ability for users to override the storage by specifying an adapter. As a default option, a relational database such as SQLite can be used.

See storage adapter for adapter interface.

Account

API

Initialisation

Initializes account There are several scenarios in which an account can be initialized:

  • Seed generated outside the Stronghold: In this case, the account should be initialized with a seed. It should communicate with the Stronghold using the import_accounts method and should expect an ID as a response.
  • Seed generated inside the Stronghold: In this case, the account should be initialized without a seed. It should communicate with the Stronghold using its create_account method and should expect an “id” in response;
  • Importing accounts from Stronghold backup: In this case, the account should receive all the initialization properties from the Stronghold. Please note that during backup, these configuration settings should be passed to the Stronghold. See import_accounts().

The following should be considered when initializing an account:

  • An account should never be initialized directly. The only way an account can be initialized is through the add_account() method.
  • An account should always be initialized after a successful response from the Stronghold. If the Stronghold fails to create an account , the account initialization should error out. If the Stronghold successfully creates an account , the account should be stored in the persistent storage. Upon a successful store operation, the user should be returned an account object.
  • If a provider property is not passed, a random node should be selected from the nodesA node is any computer that communicates with other nodes in the network using specific software. Essentially, nodes act as connection points for data transfers. The Tangle employs various node types, including full nodes (Hornet, Bee), permanodes (Chronicle), and smart contract nodes (Wasp). property.
  • If a type property is not passed, default should be used as an account type.
  • quorum_size and quorum_threshold should be validated. For example, quorum_size should not be greater than the number of nodes provided by the user.
  • The nodesA node is any computer that communicates with other nodes in the network using specific software. Essentially, nodes act as connection points for data transfers. The Tangle employs various node types, including full nodes (Hornet, Bee), permanodes (Chronicle), and smart contract nodes (Wasp). property should validate and remove duplicate node URLs.
  • All the properties of the returned account object should be read-only. It should not be possible to manipulate them directly.
Parameters
NameRequiredTypeDescription
configAccountConfigInitialization method receives a configuration object.
Returns
NameTypeDescription
accountAccountAccount instance.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

check_for_new_used_addresses()

Sync addresses with the Tangle. The method ensures that the wallet's local state contains all used addresses and an unused address.

The following should be considered when implementing this method:

  • The updated address history should not be written down in the database/persistent storage. Instead, the method should only return the updated address history (with transaction hashes). This ensures that there are no partial writes to the database.
  • To sync addresses for an account from scratch, gap_limit = 10 should be sent as arguments.
  • To sync addresses from the latest address, gap_limit = 1 should be sent as arguments.
Parameters
NameRequiredTypeDescription
gap_limitnumberNumber of address indexes that are generated.
Returns
NameTypeDescription
addressesAddress[]Address history up to latest unused address.
idsstring[]Message IDs associated with the addresses.
Additional Information
NameDescription
Access modifiersPrivate
ErrorsList of error messages [TBD]
Required client library methodsget_address_balances()| find_messages() | find_outputs()

sync_addresses_and_messages()

Sync messages with the Tangle. The method should ensure that the wallet's local state has all messages associated with the address history.

The following should be considered when implementing this method:

  • The updated message history should not be written down in the database/persistent storage. Instead, the method should only return the updated message history (with message IDs).
  • This method should check if there are any local messages (with “broadcasted: false”) matching the messages fetched from the network. If there are such messages, their “broadcasted” property should be set to true.
  • For newly-confirmed messages, the method should ensure that it updates the “confirmed” property of all its reattachments.
Parameters
NameRequiredTypeDescription
idsstring[]Message IDs. New message IDs should be calculated by running a difference of local message IDs with latest message IDs on the Tangle.
Returns
NameTypeDescription
messagesMessage[]Message history
Additional Information
NameDescription
Access modifiersPrivate
ErrorsList of error messages [TBD]
Required client library methodsfind_messages()
Required client library methods

select_inputs()

Select inputs for funds transfer.

info

This method should only be used internally by send(). The input selection method should also ensure that the recipient address doesn't match the remainder address.

See Input Selection Process for implementation details.

Parameters
NameRequiredTypeDescription
thresholdnumberAmount user wants to spend.
addressstringRecipient address.
Returns
NameTypeDescription
inputsAddress[]Selected Inputs
remainderAddressRemainder address object. Empty or null if there's no need for a remainder
Additional Information
NameDescription
Access modifiersPrivate
ErrorsList of error messages [TBD]
Required client library methodsNone

send()

Sends a message to the Tangle.

info

This method should only be used after a successful response from sync().

Currently, it is not possible to send multiple payloads.

If you want to send a value transaction, please follow this process:

  1. Ensure amount is not set to zero.
  2. Ensure amount does not exceed the total balance.
  3. Ensure recipient address has correct checksum.
  4. Validate dataThe Tangle proves the integrity of data (verifiability of completeness and origin) in a reliable manner. Current cryptographic methods for this have repeated security vulnerabilities, making data prone to manipulation. This is especially problematic in cloud computing where sometimes third-party audit tools are even used (for a fee) to ensure this data integrity. IOTA and Shimmer offer a relatively straightforward and fee-free solution to this with their protocol. property semantics and size.
  5. Select inputs by using select_inputs().
  6. Pass the serialized unsigned transaction to the Stronghold for signing with its “signTransaction” method.
  7. Perform proof-of-work. The pow property in the account object should determine if the proof of work should be offloaded.
  8. Once proof-of-work is successfully performed, the message should be validated and stored in the persistent storage.
  9. After persisting the transaction, the transaction should be broadcast to the network.
  10. In the event of a broadcast error, there should be three attempts for automatic rebroadcasting. If all attempts fail, the send process should terminate, and it should be left to the user to retry the failed message. For failed messages, the “broadcasted” property in the transaction objects should be set to false.
Parameters
NameRequiredTypeDescription
transferTransferTransfer object.
Returns
NameTypeDescription
messageMessageNewly made message.
Additional Information
NameDescription
Access modifiersPrivate
ErrorsList of error messages [TBD]
Required client library methodsfind_messages() | send()

retry()

Rebroadcasts failed message.

info

This method should only be used after a successful response from sync().

If you want to retry broadcasting a failed message, you can use the following process:

  1. Get the message by using get_message().
  2. Rebroadcast the message.
  3. Update the account in persistent storage.
Parameters
NameRequiredTypeDescription
idstringMessage ID
Returns
NameTypeDescription
messageMessageNewly made message.
Additional Information
NameDescription
Access modifiersPrivate
ErrorsList of error messages [TBD]
Required client library methodspost_message()

sync()

Syncs an account with the Tangle. The account syncing process should ensure that the latest metadata (balance, messages) associated with an account is retrieved from the Tangle and stored locally.
Please note that it is a proposed design decision to enforce account syncing before every send. An alternative way would be to have the send method always exposed and internally ensuring that the account is synced before every message.

If you want to sync an account, you can use the following process:

  1. Sync addresses using check_for_new_used_addresses().
  2. Sync messages using sync_addresses_and_messages().
  3. Store updated addresses and messages information in persistent storage (if not explicitly set otherwise by the user).
Parameters
NameRequiredTypeDescription
indexnumberAddress index. By default the number of addresses stored for this account should be used as an index.
gap_limitnumberNumber of address indexes that are generated.
skip_persistencebooleanSkips write to the database. This will be useful if a user wants to scan the Tangle for further addresses to find balance. You can find more details in the snapshot transition feature provided by Trinity.
Returns
NameTypeDescription
accountSyncedAccountSynced account object.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsfind_messages() | get_address_balances()

reattach()

Reattaches unconfirmed message to the Tangle. The following should be considered when implementing this method:

  • Only an unconfirmed message can be reattached. The method should validate the confirmation state of the provided transaction. If a confirmed message ID is provided, the method should throw an error.
  • The method should also validate if a reattachment is necessary, by checking if the message falls below max depth. The criteria for whether the message has fallen below max depth is determined through its timestamp. If 11 minutes have passed since the timestamp of the most recent reattachment, the message can be reattached. See this implementation for reference.
  • Once reattached, the message should be stored in the persistent storage.
  • If the message was reattached via polling, a reattachment event should be emitted to notify all subscribers.
Parameters
NameRequiredTypeDescription
idstringMessage ID.
Returns
NameTypeDescription
messageMessageNewly reattached message.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsreattach()

total_balance()

Gets total account balance.

The total balance should be read directly from local storage. To read the latest account balance from the network, sync() should be used first.

Returns
TypeDescription
ValueAccount total balance.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

available_balance()

Gets available account balance. The available account balance is the amount users are allowed to spend. It should subtract the pending balance from the total balance.

For example, if a user with 50i total account balance has made a transaction spending 30i, the available balance should be 20i (i.e. 50i - 30i).

The available balance should be read directly from local storage. If you want to read the latest account balance from the network, you should use sync() first.

Returns
TypeDescription
ValueThe accounts available balance.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

set_alias()

Updates an account's alias/name.

Parameters
NameRequiredTypeDescription
aliasstringNew account name.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

list_messages()

Gets messages. Messages should be read directly from local storage. To ensure the local database is updated with the latest messages, you should use sync() first.

Parameters
NameRequiredTypeDescription
countnumberNumber of (most recent) messages.
fromnumberSubset of messages. For example: count = 10, from = 5, it should return ten messages skipping the most recent five messages.
Returns
NameTypeDescription
messagesMessage[]All messages.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

list_received_messages()

Gets all received messages.

Messages should be read directly from local storage. To ensure the local database is updated with the latest messages, you should use sync() first.

Parameters
NameRequiredTypeDescription
countnumberNumber of most recent received messages.
fromnumberSubset of received messages.
Returns
NameTypeDescription
messagesMessage[]All received messages.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

list_sent_messages()

Gets all sent messages.

Messages should be read directly from local storage. To ensure the local database is updated with the latest messages, you should use sync() first.

Parameters
NameRequiredTypeDescription
countnumberNumber of (most recent) sent messages.
fromnumberSubset of sent messages.
Returns
NameTypeDescription
messagesMessage[]All sent messages.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

list_failed_messages()

Gets all failed (broadcasted = false) messages. Messages should be read directly from local storage.

Parameters
NameRequiredTypeDescription
countnumberNumber of (most recent) failed messages.
fromnumberSubset of failed messages.
Returns
NameTypeDescription
messagesMessage[]All failed messages.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

list_unconfirmed_messages()

Gets all unconfirmed (confirmed = false) messages. Messages should be read directly from local storage.

Returns
NameRequiredTypeDescription
countnumberNumber of (most recent) unconfirmed messages.
fromnumberSubset of unconfirmed messages.
Returns
NameTypeDescription
messagesMessage[]All unconfirmed messages.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

get_message()

Gets message for provided ID.

Messages should be read directly from local storage. To ensure the local database is updated with the latest messages, you should use sync() first.

Parameters
NameRequiredTypeDescription
idstringMessage ID.
Returns
NameTypeDescription
messageMessageMessage object.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

list_addresses()

Gets all addresses.

Returns
NameTypeDescription
addressesAddress[]All addresses.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

list_unspent_addresses()

Gets all unspent input addresses

Returns
NameTypeDescription
addressesAddress[]All unspent input addresses.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

generate_address()

Gets the latest unused address.

Returns
NameTypeDescription
addressAddressA new address object.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

Account Manager

An account manager class should be publicly available for users. With the account manager, the user can create, update, delete or manage multiple accounts. The implementation details of a specific account should be abstracted using this account manager wrapper.

API

Initialisation

Initializes the account manager. Account manager initialization should validate the adapter object semantics and return an AccountManager instance.

Parameters
NameRequiredTypeDescription
adapterAdapterInitialisation method receives an optional storage adapter.
Returns
NameTypeDescription
managerAccountManagerAccount manager instance.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

add_account()

Adds new account

See account initialisation for detailed implementation guidelines.

Parameters
NameRequiredTypeDescription
configAccountConfigAccount configuration object.
Returns
NameTypeDescription
accountsAccountNewly created account.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

remove_account()

Removes an account.

The following should be considered when removing an account:

  • An account should first be removed from the Stronghold using its removeAccount method.
  • Once the account references have been removed from the Stronghold, the account should be deleted from the persistent storage.
Parameters
NameRequiredTypeDescription
identifier{ address: <string> } | { alias: <string> } | { ID: <number> } | { index: <number }Identifier. Could be one of address, alias, ID or index.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

sync_accounts()

Syncs all stored accounts with the Tangle. Syncing should get the latest balance for all accounts and should find any new messages associated with the stored account.

See Accounts Syncing Process for further details.

Returns
NameTypeDescription
accountSyncedAccount[]Synced accounts.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodssync()

move()

Moves funds from one account to another. This method should use the send() method from the sender account and initiate a message to the receiver account.

Parameters
NameRequiredTypeDescription
from{ address: <string> } | { alias: <string> } | { ID: <number>| { index: <number> }Identifier. Could be one of address, alias, ID or index.
to{ address: <string> } | { alias: <string> } | { ID: <number> } | { index: <number> }Identifier. Could be one of address, alias, ID or index.
amountnumberTransaction amount.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

backup()

Safely creates a backup of the accounts to a destination. The file could simply be JSON containing the address & transaction histories for accounts.

This method should provide the Stronghold instance with the metadata of all accounts.

Parameters
NameRequiredTypeDescription
destinationstringPath where the backup should be stored.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

import_accounts

Import (backed up) accounts.

The implementation details are not finalized.

Parameters
NameRequiredTypeDescription
accountsAccount[]Account object.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

get_account()

Returns the account associated with the provided identifier.

Parameters
NameRequiredTypeDescription
identifier{ address: <string> } | { alias: <string> } | { ID: <number> } | { index: <number> }Identifier. Could be one of address, alias, ID or index.
Returns
NameTypeDescription
accountAccountAccount associated with identifier.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

reattach()

Reattaches an unconfirmed message.

See reattach() method for implementation details. This method is a wrapper method provided for convenience. A user could directly access the reattach() method on an account object.

Parameters
NameRequiredTypeDescription
identifier{ address: <string> } | { alias: <string> } | { ID: <number> } | { index: <number }Identifier. Could be one of address, alias, ID or index.
idstringMessage ID.
Returns
NameTypeDescription
messageMessageNewly reattached message.
Additional Information
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

Events

Events can have two categories:

  1. Reactive messages emitted from the node software whenever the state on the node changes. For example, emitting new messages received by the node. Clients (Wallet) can subscribe to these events to get notified if any relevant change occurs on the node. For further details, please visit the Firefly GitHub repository.
  2. Messages emitted from the wallet library whenever there are any important state changes. Please note that in cases where a user triggered action leads to a state change, the messages will not be emitted. For example, if a user explicitly triggers a sync() action leading to a state change, an explicit event is not necessary.

Category 1 events

On every update sent from the node software via an event, the wallet library should update internal (persistent) storage and should also emit events via category 2 events.

Monitor address for balance changes

EventReturned Data
< Address : Balance>Index 1: Address | Index 2: New balance on the address

Monitor address for new messages

EventReturned Data
<Address : Message>Index 1: Address | Index 2: Id of the new message

Monitor message for confirmation state

EventReturned Data
<MessageId>Index 1: Message IdIndex 2: Confirmation state

Category 2 events

They could be triggered via events from category 1 or through polling.

Monitor for balance changes

EventReturned Data
balances[{ accountId, address, balance }]

Monitor for new messages

EventReturned Data
messages[{ accountId, messages }]

Monitor for confirmation state

EventReturned Data
confirmations[{ accountId, messages }]

Monitor for reattachments

EventReturned Data
reattachments[{ accountId, messages }]

Monitor for broadcasts

EventReturned Data
broadcasts[{ accountId, messages }]

Monitor for errors

EventReturned Data
error{ type, error }

Privacy

To maintain the financial privacy of wallet users, you should enforce strategies in the application/wallet that will guarantee a certain level of anonymity:

  • The wallet should only use a single address per message. If an address has already been used in a message, it should not be used as a deposit address. Instead, a new address should be generated.- The input selection strategy should expose as little information as possible. Please see the input selection process for further details.

Some other privacy enhancing techniques can be found in this document.

Input Selection

The goal of input selection is to avoid remainder addresses. The remainder output leaves a clue to the user's future spends. There should be a standardized input selection strategy used by the wallet.

The steps for input selection are as follows:

  1. Try to select an input with an exact match. For example, if a user intends to spend X iotas, the wallet should try to find an address that has X iotas as available balance.
  2. If the previous step fails, try to select a combination of inputs that satisfy the amount leaving no change. For example, consider a scenario where the wallet with account name Foo has three addresses A, B and C with 10, 20 and 50 IOTA respectively. If a user intends to spend X = 30 IOTA, the application should search for an exact match (step no. 1). In this case, no address balance matches X. Therefore, the wallet should search for a subset of addresses with an accumulated balance of X. In this scenario, A and B.
  3. If both the previous steps fail, the wallet should select a combination of inputs that produce the minimum remainder.

A reference implementation of different input selection algorithms for Bitcoin can be found in this project.

The implementation of step no. 2 is also quite similar to the subset sum problem. Given a total and a set of non-negative numbers (inputs), we need to determine if there is a subset which adds up to the total.

Account Syncing Process

The account syncing process should detect all used accounts on a seed with their corresponding address and message history. Once, all accounts and histories are detected, the wallet should accumulate the total balance. The syncing process should work as follows:

  1. Start with the account at index 0, generate gap limit number of addresses. This defaults to 20.
  2. Check for messages and balances on the generated addresses.
  3. If there are no messages and balances of 0 on all addresses, the process for generating addresses and finding messages and balances should be stopped.
  4. If any address has balance or associated messages, generate gap limit number of addresses from the index of the last address with messages or balance.
  5. Steps (1-4) should also be performed for the account at index 1. The general idea is that n + 1 accounts should be checked if account n has any messages or balance.

Treat accounts like addresses. Only allow 1 latest unused account.

Scenario 1: The wallet message and address history stored in Stronghold backup

  1. Start syncing from the latest address index stored in the Stronghold backup
  2. Run the “Full sync” function to resync from index 0 across all accounts
  3. Run the “Find more history” function to sync a further 50 addresses

Scenario 2: User has no backup file

  1. Start syncing from account 0 address 0

Polling

A background process that automatically performs several tasks periodically should be part of the wallet library. The goal of the background process is to perform the following tasks:

  • Sync accounts: The background process should sync all accounts with the network. This should be done using the sync_accounts() method.
    • If new messages are detected, a messages event should be used to notify all subscribers.
    • If new balances are detected, a balances event should be used to notify all subscribers.
    • If new confirmations are detected, a confirmations event should be used to notify all subscribers.
      info

      If there are multiple failed messages, priority should be given to the old ones.

  • Reattach: The background process should check if there are any unconfirmed messages that require reattachment. The detailed implementation flow for reattachment can be found in the reattach section.

The following should be considered for implementation:

  • Invoking a task explicitly that is already being performed through polling should lead to an error. For example, if the polling process is already syncing accounts, and a user explicitly calls sync(), it should throw an error.
  • Errors during the polling process should be communicated to subscribers via error events.

The background process should have a recurring checker that sequentially performs all the above tasks. The implementation should ensure that future tasks can easily be added to the background process. For reference, see Trinity's implementation of the poll component.