Skip to main content

Wallet Library Spec

Introduction#

The wallet.rs library is a stateful package with a standardized interface to build applications with IOTA value transactions. The package will be 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. An account has certain properties such as addresses and messages. An account has various possible behaviours, including moving funds, looking for new messages, and making copies of message histories. An account should also be able to provide a degree of financial privacy and this should not incur any overhead.

A similar account package was previously developed but this became obsolete with the introduction of Ed25519 signatures. The previous account package was limited to a single account. As an improvement, the new package will be able to manage multiple accounts.

To summarize, the main motivation behind this package is 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, standard Rust naming conventions are followed. 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
seed✘stringBIP-39 mnemonic. When importing an account from Stronghold backup, the seed will not be required.
id✘stringSHA-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.
indexβœ”numberAccount index in BIP-44 derivation path
alias✘stringAccount name. If not provided, Account + { index } should be used. When importing an account from Stronghold backup, the alias will be required from Stronghold.
pow✘local,remoteProof of work settings. Defaults to local. local PoW should be performed on device; remote PoW should be performed on the node.
nodesβœ”node[]A list of nodes to connect to.
quorum_size✘numberIf multiple nodes are provided, quorum size determines the number of nodes to query to check for quorum.
quorum_threshold✘numberMinimum number of nodes from the quorum pool that need to agree to consider a result true.
network✘mainnet|devnet | comnetIOTA public network.
type✘default or ledgerAccount type. Required for differentiating ledger vs non-ledger accounts.
provider✘stringNode URL.
created_at✘DateTime of account creation
messages✘Message[] Messages associated with account. Accounts can be initialized with locally stored messages.
addresses✘Address[]Address history associated with the account. Accounts can be initialized with locally stored address history

AccountObject#

PropertyRequiredTypeDescription
idβœ”stringSHA-256 hash of the first address on the seed (m/44'/0'/0'/0/0). Required for referencing a seed in Stronghold.
aliasβœ”stringAccount name.
created_atβœ”numberAccount creation time.
last_synced_atβœ”stringTime 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_addressβœ”AddressDeposit 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
accountsβœ”Account[]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
addressβœ”stringAddress (Bech32) string.
balanceβœ”numberAddress balance.
indexβœ”numberAddress index.
internalβœ”booleanDetermines if an address is a public or an internal (change) address. See the concept of chain node for more details.
checksumβœ”stringAddress checksum.

Node#

PropertyRequiredTypeDescription
urlβœ”stringNode URL.
powβœ”booleanDetermines if the node accepts proof of work.
username✘stringNode username. Only required if node requires authorisation.
password✘stringNode password. Only required if node requires authorisation.
networkβœ”mainnet | devnet | comnetIOTA public network name.

Timestamp#

PropertyRequiredTypeDescription
format(type: string):stringβœ”functionTransaction 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
amountβœ”numberTransfer amount.
addressβœ”stringTransfer address.
indexation_key✘Indexation Payload(Optional) Indexation payload.

Value#

PropertyRequiredTypeDescription
with_denomination():stringβœ”functionTransaction amount with unit.
without_denomination():numberβœ”functionTransaction amount without unit.

Input#

PropertyRequiredTypeDescription
typeβœ”numberInput type. Defaults to 0.
idβœ”stringBLAKE2b-256 hash of the transaction.
output_indexβœ”numberIndex of the output on the referenced transaction.

OutputAddress#

PropertyRequiredTypeDescription
typeβœ”numberSet to value 0 to denote an Ed25519 address.
addressβœ”stringIf type is set to 0, it should contain an Ed25519 address.

Output#

PropertyRequiredTypeDescription
typeβœ”numberOutput type. Defaults to 0.
addressβœ”OutputAddressOutput address.
amountβœ”numberAmount of tokens to deposit.

UnsignedDataPayload#

PropertyRequiredTypeDescription
typeβœ”numberSet to 2 to denote a unsigned data payload.
dataβœ”stringData of unsigned payload.

SignedDataPayload#

PropertyRequiredTypeDescription
typeβœ”numberSet to 3 to denote a signed data payload.
dataβœ”stringData of signed data payload.
public_keyβœ”stringEd25519 public key used to verify the signature.
signatureβœ”stringSignature of signing data.

IndexationPayload#

PropertyRequiredTypeDescription
indexβœ”stringIndexation key.
dataβœ”stringIndexation data.

UnsignedTransaction#

PropertyRequiredTypeDescription
typeβœ”numberTransaction type. Defaults to 0.
inputs_countβœ”numberAmount of inputs proceeding.
inputsβœ”Input[]Transaction inputs.
outputs_countβœ”numberAmount of outputs proceeding.
outputsβœ”Output[]Output address.
payload_lengthβœ”numberLength of optional payload.
payloadβœ”UnsignedDataPayload | SignedDataPayload | IndexationPayloadPayload containing data. As multiple payloads are not yet supported, only unsigned data payload should be used.

Ed25519Signature#

PropertyRequiredTypeDescription
typeβœ”numberSet to value 1 to denote an Ed25519 signature.
public_keyβœ”numberPublic key of the Ed25519 keypair which is used to verify the signature.
signatureβœ”stringSignature signing the serialized unsigned transaction.

SignatureUnblockBlock#

PropertyRequiredTypeDescription
typeβœ”numberSet to value 0 to denote a signature unlock block.
signatureβœ”Ed25519SignatureAn unlock block containing signature(s) unlocking input(s).

ReferenceUnblockBlock#

PropertyRequiredTypeDescription
typeβœ”numberSet to value 1 to denote a reference unlock block.
referenceβœ”numberIndex of a previous unlock block.

SignedTransactionPayload#

PropertyRequiredTypeDescription
typeβœ”numberPayload type. Defaults to 0.
transactionβœ”UnsignedTransactionEssence data making up a transaction by defining its inputs and outputs and an optional payload.
unblock_blocks_countβœ”numberNumber of inputs specifed.
unblock_blocksβœ”SignatureUnblockBlockReferenceUnblockBlock

Message#

PropertyRequiredTypeDescription
versionβœ”numberMessage version. Defaults to 1.
parentsβœ”string[]Message ids this message references.
payload_lengthβœ”numberLength of the payload.
payloadβœ”SignedTransactionPayload
UnsignedDataPayload
SignedDataPayloadTransaction amount (exposed as a custom type with additional methods).
timestampβœ”TimestampTransaction timestamp (exposed as a custom type with additional methods).
nonceβœ”stringTransaction nonce.
confirmedβœ”booleanDetermines if the transaction is confirmed.
broadcastedβœ”booleanDetermines 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.
incomingβœ”booleanDetermines if the message is an incoming transaction or not.
valueβœ”numberMessage transfer value.

StorageAdapter#

PropertyRequiredTypeDescription
get(key: string):Accountβœ”functionGets the account object for provided account name or ID.
getAll(): Account[]βœ”functionGets all account objects from storage.
set(key: string, payload: string):voidβœ”functionStores account in storage.
remove(key: string): voidβœ”functionRemoves account from storage.

Storage#

warning

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 nodes 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 nodes 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
configβœ”AccountConfigInitialization method receives a configuration object.
Returns#
NameTypeDescription
accountAccountAccount instance.
Additional Information#
NameDescription
Access modifiersPublic
ErrorsList of error messages [TBD]
Required client library methodsNone

sync_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, index = 0 and gap_limit = 10 should be sent as arguments.
  • To sync addresses from the latest address, index = latest address index and gap_limit = 1 should be sent as arguments.
Parameters#
NameRequiredTypeDescription
indexβœ”numberAddress index. By default the length of addresses stored for this account should be used as an index.
gap_limitβœ”numberNumber 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_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
idsβœ”string[]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
thresholdβœ”numberAmount user wants to spend.
addressβœ”stringRecipient 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 data 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
transferβœ”TransferTransfer 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
idβœ”stringMessage 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 sync_addresses().
  2. Sync messages using sync_messages().
  3. Store updated addresses and messages information in persistent storage (if not explicitly set otherwise by the user).
Parameters#
NameRequiredTypeDescription
index✘numberAddress index. By default the number of addresses stored for this account should be used as an index.
gap_limit✘numberNumber of address indexes that are generated.
skip_persistence✘booleanSkips 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
idβœ”stringMessage 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
aliasβœ”stringNew 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
countβœ”numberNumber of (most recent) messages.
fromβœ”numberSubset 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
countβœ”numberNumber of most recent received messages.
fromβœ”numberSubset 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
countβœ”numberNumber of (most recent) sent messages.
fromβœ”numberSubset 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
countβœ”numberNumber of (most recent) failed messages.
fromβœ”numberSubset 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
countβœ”numberNumber of (most recent) unconfirmed messages.
fromβœ”numberSubset 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
idβœ”stringMessage 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
adapter✘AdapterInitialisation 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
configβœ”AccountConfigAccount 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.
amountβœ”numberTransaction 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
destinationβœ”stringPath 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
accountsβœ”Account[]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.
idβœ”stringMessage 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 Id

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.