Skip to main content

Stardust Exchange Integration Guide

note

You can easily integrate wallet.rs with your exchange, custody solution, or product.

danger

Shimmer allows for complex tokenization schemes, but they can lead to monetary losses if used incorrectly. Transaction outputs may have multiple unlocking conditions which may require returning some or all of the amount, which could expire if not claimed in time, or which may not be unlockable for a very long time. You can lose money if you do not check the unlock conditions before accepting a deposit!

Integration Guide

This guide explains how to integrate the Wallet library in your exchange.

Features of the Wallet Library:

  • Secure seed management.
  • Account management with multiple accounts and multiple addresses.
  • Confirmation monitoring.
  • Deposit address monitoring.
  • Backup and restore functionality.

How Does it Work?

The Wallet Library is a stateful package with a standardized interface for developers to build applications involving value transactions. It offers abstractions to handle payments and can optionally interact with Stronghold for seed handling, seed storage, and state backup.

note

If you are not familiar with the wallet.rs library, you can find more information in the documentation.

You can use the following examples as a guide to implementing the multi-account approach using the NodeJS binding:

  1. Set up the wallet.rs library.
  2. Create an account for each user.
  3. Generate a user address to deposit funds.
  4. Check the user balance.
  5. Listen to events.
  6. Enable withdrawals.
note

If you are looking for other languages, please read the wallet.rs library overview.

Since all wallet.rs bindings are based on core principles provided by the wallet.rs library, the outlined approach is very similar regardless of the programming language you choose.

1. Set Up the Wallet.rs Library

First, you should install the components that are needed to use wallet.rs and the binding of your choice; it may vary a bit from language to language. In the case of the NodeJs binding, it is straightforward since it is distributed via the npm package manager.

You can read more about backup and security in this guide.

npm install @iota/wallet dotenv

1 Generate a mnemonic

wallet/bindings/nodejs/examples/exchange/0-generate-mnemonic.js
loading...

You can then create a .env by running the following command:

touch .env

You can now add your SH_PASSWORD and MNEMONIC to the .env file.

SH_PASSWORD="here is your super secure password"
MNEMONIC="here is your super secure 24 word mnemonic, it's only needed here the first time"

After you have updated the .env file, you can initialize the AccountManager instance with a secret manager(Stronghold by default) and client options.

note

Manage your password with the utmost care.

By default, the Stronghold file will be called wallet.stronghold. It will store the seed (derived from the mnemonic) that serves as a cryptographic key from which all accounts and related addresses are generated.

One of the key principles behind the stronghold is that no one can get a seed out of it, so you should also back up your 24-word mnemonic in a secure place because there is no way to recover it from the .stronghold file. You deal with accounts using the AccountManager instance exclusively, where all complexities are hidden under the hood and are dealt with securely.

note

Keep the stronghold password and the stronghold database on separate devices. See the backup and security guide for more information.

2 Create an account

You can import the Wallet Library and create an account manager using the following example:

wallet/bindings/nodejs/examples/exchange/1-create-account.js
loading...

The Alias must be unique and can be whatever fits your use case. The Alias is typically used to identify an account later on. Each account is also represented by an index which is incremented by one every time a new account is created. You can refer to any account via its index, or alias.

You get an instance of any created account using AccountManager.getAccount(accountId|alias) or get all accounts with AccountManager.getAccounts().

Common methods of account instance include:

  • account.addresses() - returns list of addresses related to the account.
  • account.generateAddress() - generate a new address for the address index incremented by 1.
  • account.balance() - returns the balance for the given account.
  • account.sync() - sync the account information with the tangle.

3. Generate a User Address to Deposit Funds

Wallet.rs is a stateful library. This means it caches all relevant information in storage to provide performance benefits while dealing with, potentially, many accounts and addresses.

wallet/bindings/nodejs/examples/exchange/2-generate-address.js
loading...

Every account can have multiple addresses. Addresses are represented by an index which is incremented by one every time a new address is created. You can access the addresses using the account.address() method:

const addresses = account.addresses();

console.log('Need a refill? Send it to this address:', addresses[0]);

You can use the Faucet to add test tokens and test your account.

There are two types of addresses, internal and public (external). This approach is known as a BIP32 Hierarchical Deterministic wallet (HD Wallet).

  • Each set of addresses is independent of each other and has an independent index id.
  • Addresses that are created by account.generateAddress() are indicated as internal=false (public).
  • Internal addresses (internal=true) are called change addresses and are used to send the excess funds to them.

4. Check the Account Balance

Unlock Conditions

Outputs may have multiple UnlockConditions, which may require returning some or all of the transferred amount. The outputs could also expire if not claimed in time, or may not be unlockable for a predefined period.

To get outputs with only the AddressUnlockCondition, you should synchronize with the option syncOnlyMostBasicOutputs: true.

If you are synchronizing outputs with other unlock conditions, you should check the unlock conditions carefully before crediting users any balance.

You can find an example illustrating how to check whether an output has only the address unlock condition and whether this address belongs to the account in the Check Unlock Conditions how-to guide.

You can get the available account balance across all addresses of the given account using the following example:

wallet/bindings/nodejs/examples/exchange/3-check-balance.js
loading...

5. Listen to Events

Unlock Conditions

Outputs may have multiple UnlockConditions, which may require returning some or all of the transferred amount. The outputs could also expire if not claimed in time, or may not be unlockable for a predefined period.

To get outputs with only the AddressUnlockCondition, you should synchronize with the option syncOnlyMostBasicOutputs: true.

If you are synchronizing outputs with other unlock conditions, you should check the unlock conditions carefully before crediting users any balance.

You can find an example illustrating how to check whether an output has only the address unlock condition and whether this address belongs to the account in the Check Unlock Conditions how-to guide.

The Wallet.rs library supports several events for listening. A provided callback is triggered as soon as an event occurs (which usually happens during syncing).

You can use the following example to listen to new output events:

wallet/bindings/nodejs/examples/exchange/4-listen-events.js
loading...

Example output:

NewOutput: {
output: {
outputId: '0x2df0120a5e0ff2b941ec72dff3464a5b2c3ad8a0c96fe4c87243e4425b9a3fe30000',
metadata: [Object],
output: [Object],
isSpent: false,
address: [Object],
networkId: '1862946857608115868',
remainder: false,
chain: [Array]
},
transaction: null,
transactionInputs: null
}

Alternatively you can use account.outputs() to get all outputs that are stored in the account, or account.unspentOutputs(), to get only unspent outputs.

6. Enable Withdrawals

You can use the following example to send tokens to an address.

wallet/bindings/nodejs/examples/exchange/5-send-amount.js
loading...

The full function signature is account.sendAmount(outputs[, options]).

Default options are fine and successful; however, you can provide additional options, such as remainderValueStrategy, which can have the following values:

  • changeAddress: Send the remainder value to an internal address.
  • reuseAddress: Send the remainder value back to its original address.
  • customAddress: Send the remainder value back to a provided account address.
TransactionOptions {
remainderValueStrategy?: RemainderValueStrategy;
taggedDataPayload?: ITaggedDataPayload;
customInputs?: string[];
}

The account.sendAmount() function returns a transaction with it's id. The blockId can be used later for checking a confirmation status. You can obtain individual transactions related to the given account using the account.transactions() function.

Dust Protection

When sending tokens, you should consider a dust protection mechanism.