Stardust Exchange Integration Guide
You can easily integrate wallet.rs with your exchange, custody solution, or product.
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.
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:
- Set up the wallet.rs library.
- Create an account for each user.
- Generate a user address to deposit funds.
- Check the user balance.
- Listen to events.
- Enable withdrawals.
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
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.
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.
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:
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.
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 asinternal=false
(public). - Internal addresses (
internal=true
) are calledchange
addresses and are used to send the excess funds to them.
4. Check the Account Balance
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:
loading...
5. Listen to Events
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:
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.
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.
When sending tokens, you should consider a dust protection mechanism.