Nimiq Accounts Manager
The Accounts Manager (or Nimiq Accounts) provides a unified interface for
all Nimiq accounts, addresses, and contracts. It is the primary UI for Nimiq users
to manage their accounts and provides websites and apps with a concise API to
interact with their users' Nimiq addresses.
The Accounts Client library
Installation
Include the AccountsClient
JS library as a script tag in your page:
<script src="https://unpkg.com/@nimiq/accounts-client@v0.1/dist/standalone/AccountsClient.standalone.umd.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@nimiq/accounts-client@v0.1/dist/standalone/AccountsClient.standalone.umd.js"></script>
It can also be installed from NPM:
npm install @nimiq/accounts-client
yarn add @nimiq/accounts-client
Then import or require it in your module:
import AccountsClient from '@nimiq/accounts-client';
const AccountsClient = require('@nimiq/accounts-client');
Initialization
To start the client, just instantiate the class by passing it the URL of the
Accounts Manager to connect to:
const accountsClient = new AccountsClient('https://accounts.nimiq-testnet.com');
const accountsClient = new AccountsClient('https://accounts.nimiq.com');
Usage
By default, the client opens a popup window for user interactions. On mobile
devices, a new tab will be opened instead. For simplicity, we will always refer
to popups throughout this documentation.
Popups will be blocked if not opened within the context of an active user
action. Thus, it is required that API methods are called synchronously within
the context of a user action, such as a click. See example below.
document.getElementById('#checkoutButton').addEventListener('click', function(event){
accountsClient.checkout();
});
For more details about avoiding popup blocking refer to
this article.
Using top-level redirects
If you prefer top-level redirects instead of popups, you can pass an
instance of RedirectRequestBehavior
as a second parameter to either the
AccountsClient initialization or to any API method:
const redirectBehavior = new AccountsClient.RedirectRequestBehavior();
const accountsClient = new AccountsClient(<url>, redirectBehavior);
const result = accountsClient.checkout(<requestOptions>, redirectBehavior);
The RedirectRequestBehavior
accepts two optional parameters:
The first is the return URL. If no return URL is specified, the current URL
without parameters will be used.
const redirectBehavior = new RedirectRequestBehavior('https://url.to/return?to');
The second optional parameter is a plain object you can use to store data until
the request returns:
const storedData = { foo: 'I am the state' };
const redirectBehavior = new RedirectRequestBehavior(null, storedData);
For details on how to listen for redirect responses and retrieve the stored
data, see Listening for redirect responses.
API Methods
[//] TODO: Add methods 'onboard', 'changePassphrase', 'addAccount', 'rename', 'signMessage'
Note:
All API methods run asynchronously and thus return promises. Please keep in
mind that promises can also be rejected for various reasons, e.g. if the user
cancels the request by closing the popup window or clicking on a cancel
button.
An error can also occur when the request contains invalid parameters. The
Error
object will be passed on to the reject
handler.
Checkout
The checkout()
method allows your site to request a transaction from the user.
This will open a popup for the user to select the address to send from —
or cancel the request. During the payment process, the signed transaction is
sent (relayed) to the network but also returned to the caller, e.g. for
processing in your site, storage on your server or re-submittal.
const requestOptions = {
appName: 'Nimiq Shop',
recipient: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000',
value: 100 * 1e5,
};
const checkoutResult = await accountsClient.checkout(requestOptions);
The checkout()
method returns a promise which resolves to a
SignTransactionResult
:
interface SignTransactionResult {
serializedTx: Uint8Array;
sender: string;
senderType: Nimiq.Account.Type;
senderPubKey: Uint8Array;
recipient: string;
recipientType: Nimiq.Account.Type;
value: number;
fee: number;
validityStartHeight: number;
signature: Uint8Array;
extraData: Uint8Array;
flags: number;
networkId: number;
hash: string;
}
Choose Address
By using the chooseAddress()
method, you are asking the user to select one of
their addresses to provide to your website. This can be used for example to find
out, which address your app should send funds to.
Note: This method should not yet be used as a login or authentication mechanism,
as it does not provide any security that the user actually owns the provided address!
The method takes a simple request object as its only argument, which must only contain
the appName
property:
const requestOptions = {
appName: 'Nimiq Safe',
};
const providedAddress = await accountsClient.chooseAddress(requestOptions);
The request's result contains a userfriendly address string as address
and a label
:
providedAddress = {
address: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000',
label: 'Burner Address',
}
Sign transaction
The signTransaction()
method is similar to checkout, but provides a different
UI to the user. The main difference to checkout()
is that it requires the
request to already include the sender's account (wallet) ID and address as walletId
and
sender
respectively, as well as the transaction's validityStartHeight
. The
created transaction will only be returned to the caller, not sent to the network
automatically.
For brevity, most duplicate parameter explanations are omitted here, please
refer to Checkout for more details.
const requestOptions = {
appName: 'Nimiq Safe',
walletId: 'xxxxxxxx',
sender: 'NQxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx',
recipient: 'NQxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx',
value: 100 * 1e5,
validityStartHeight: 123456,
};
const signTxResult = await accountsClient.signTransaction(requestOptions);
The signTransaction()
method returns a SignTransactionResult
as well. See
Checkout for details.
Signup
The signup()
method creates a new account in the Accounts Manager. The user
will choose an Identicon and optionally set a password.
const requestOptions = {
appName: 'Nimiq Safe',
};
const newAccount = await accountsClient.signup(requestOptions);
The signup()
method returns a promise which resolves to a SignupResult
:
interface SignupResult {
walletId: string;
label: string;
type: WalletType;
accounts: Array<{
address: string;
label: string;
}>;
}
Login
The login()
method allows the user to add an existing account to the
Accounts Manager by importing their Login File, Recovery Words or
Account Access File. After an account has been imported, the
Accounts Manager automatically detects active addresses following the
BIP44
method.
const requestOptions = {
appName: 'Nimiq Safe',
};
const newAccount = await accountsClient.login(requestOptions);
The login()
method returns a promise which resolves to a LoginResult
:
interface LoginResult {
walletId: string;
label: string;
type: WalletType;
accounts: Array<{
address: string;
label: string;
}>;
}
Logout
The logout()
method removes an account from the Accounts Manager. During the
logout process, the user can retrieve the Login File or Recovery Words
before the account is deleted.
const requestOptions = {
appName: 'Nimiq Safe',
walletId: 'xxxxxxxx',
};
const logoutResult = await accountsClient.logout(requestOptions);
The logout()
method returns a promise which resolves to a simple object
containing the success
property, which is always true:
{
success: true
}
Export
Using the export()
method, a user can retrieve the Login File or
Recovery Words of an account.
const requestOptions = {
appName: 'Nimiq Safe',
walletId: 'xxxxxxxx',
};
const exportResult = await accountsClient.export(requestOptions);
The export()
method returns a promise which resolves to a simple object
containing the success
property, which is always true:
{
success: true
}
Listening for redirect responses
If you configured the AccountsClient to use
top-level redirects instead of popups, you need to
follow the four steps below to specifically listen for the redirects from the
Accounts Manager back to your site using the on()
method.
Your handler functions will be called with two parameters: the result object and
the stored data object as it was passed to the
RedirectRequestBehavior
during initialization.
const accountsClient = new AccountsClient();
const onSuccess = function(result, storedData) {
console.log("Got result from Accounts Manager:", result);
console.log("Retrieved stored data:": storedData);
}
const onError = function(error, storedData) {
console.log("Got error from Accounts Manager:", error);
console.log("Retrieved stored data:": storedData);
}
const RequestType = AccountsClient.RequestType;
accountsClient.on(RequestType.CHECKOUT, onSuccess, onError);
accountsClient.on(RequestType.SIGNTRANSACTION, onSuccess, onError);
accountsClient.on(RequestType.LOGIN, onSuccess, onError);
accountsClient.checkRedirectResponse();
The available RequestType
s, corresponding to the API methods, are:
enum AccountsClient.RequestType {
CHECKOUT = 'checkout',
SIGNTRANSACTION = 'sign-transaction',
SIGNUP = 'signup',
LOGIN = 'login',
LOGOUT = 'logout',
EXPORT = 'export',
}
Running your own Accounts Manager
TODO
Contribute
To get started with working on the source code, pull the code and install the dependencies:
Setup
git clone git@github.com:nimiq/accounts.git
cd accounts
yarn
Run
Compile and serve with hot-reload in the background for development:
yarn run serve
Compile and lint continuously in the background for development:
yarn run build --watch
Lint and fix files:
yarn run lint
Run unit tests:
yarn run test:unit
Build
Compile and minify for production:
yarn run build