Security News
Weekly Downloads Now Available in npm Package Search Results
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
@nimiq/hub-api
Advanced tools
The Nimiq Hub 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 Nimiq Hub 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.
Include the HubApi
JS library as a script tag in your page:
<!-- From CDN -->
<script src="https://unpkg.com/@nimiq/hub-api@v0.4/dist/standalone/HubApi.standalone.umd.js"></script>
<!-- or -->
<script src="https://cdn.jsdelivr.net/npm/@nimiq/hub-api@v0.4/dist/standalone/HubApi.standalone.umd.js"></script>
It can also be installed from NPM:
npm install @nimiq/hub-api
# or with yarn
yarn add @nimiq/hub-api
Then import or require it in your module:
import HubApi from '@nimiq/hub-api';
// or
const HubApi = require('@nimiq/hub-api');
To start the client, just instantiate the class by passing it the URL of the Nimiq Hub to connect to:
// Connect to testnet
const hubApi = new HubApi('https://hub.nimiq-testnet.com');
// Connect to mainnet
const hubApi = new HubApi('https://hub.nimiq.com');
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){
hubApi.checkout(/* see details below */);
});
For more details about avoiding popup blocking refer to this article.
Note: To use redirects instead of popups, your app must run under a HTTPS domain!
If you prefer top-level redirects instead of popups, you can pass an
instance of RedirectRequestBehavior
as a second parameter to either the
HubApi initialization or to any API method:
Note: The way to configure top-level redirects will change in an upcoming version of the Hub API!
const redirectBehavior = new HubApi.RedirectRequestBehavior();
// Pass the behavior as a second parameter to the HubApi
const hubApi = new HubApi(<url>, redirectBehavior);
// Or pass it as a second parameter to any API method
const result = hubApi.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.
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. when 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 request promise will be rejected with an
Error
object.
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 = {
// The name of your app, should be as short as possible.
appName: 'Nimiq Shop',
// [optional] The path to an image on the same origin as the request is sent
// from, must be square and will be displayed with up to 146px width and hight.
shopLogoUrl: 'https://your.domain.com/path/to/an/image.jpg',
// The human-readable address of the recipient (your shop/app).
recipient: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000',
// [optional] Nimiq.Account.Type of the recipient.
// Only required if the recipient is a vesting (1) or HTLC (2) contract.
// Default: Nimiq.Account.Type.BASIC (0)
//recipientType: Nimiq.Account.Type.HTLC,
// Value of the transaction, in luna.
value: 100 * 1e5, // 100 NIM
// [optional] Transaction fee in luna.
// Default: 0
//fee: 138,
// [optional] Extra data that should be sent with the transaction.
// Type: string | Uint8Array | Nimiq.SerialBuffer
// Default: new Uint8Array(0)
//extraData: 'Hello Nimiq!',
// [optional] Human-readable address of the sender.
// If the address exists in the user's Hub, this parameter
// forwards the user directly to the transaction-signing after the
// balance check.
// Default: undefined
//sender: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000',
// [optional] Whether to force the submitted sender address
// If this parameter is true, an exception is thrown when either the
// submitted sender address does not exist or does not have sufficient
// balance. When false, the user will be shown the address selector
// for the above conditions instead.
// (Only relevant in connection with the `sender` parameter)
// Default: false
//forceSender: true,
// [optional] Nimiq.Transaction.Flag, only required if the transaction
// creates a contract.
// Default: Nimiq.Transaction.Flag.NONE (0)
//flags: Nimiq.Transaction.Flag.CONTRACT_CREATION,
// [optional] The duration (in number of blocks) that the signed transaction
// should be valid for. The maximum is 120.
// Default: 120
//validityDuration?: number;
};
// All client requests are async and return a promise
const signedTransaction = await hubApi.checkout(requestOptions);
The checkout()
method returns a promise which resolves to a
SignedTransaction
:
interface SignedTransaction {
serializedTx: string; // HEX signed and serialized transaction
hash: string; // HEX transaction hash
raw: {
signerPublicKey: Uint8Array; // Serialized public key of the signer
signature: Uint8Array; // Serialized signature of the signer
sender: string; // Human-readable address of sender
senderType: Nimiq.Account.Type; // 0, 1, 2 - see recipientType above
recipient: string; // Human-readable address of recipient
recipientType: Nimiq.Account.Type; // 0, 1, 2 - see above
value: number;
fee: number;
validityStartHeight: number; // Automatically determined validity
// start height of the transaction
extraData: Uint8Array;
flags: number;
networkId: number;
}
}
The serializedTx
can be handed to a Nimiq JSON-RPC's sendRawTransaction
method.
The raw
object can be handed to the NanoApi's relayTransaction
method.
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 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 basic request object as its only argument, which must only contain
the appName
property:
const requestOptions = {
// The name of your app, should be as short as possible.
appName: 'My App',
};
// All client requests are async and return a promise
const address = await hubApi.chooseAddress(requestOptions);
The request's result contains an address string as address
and a label
:
interface Address {
address: string; // Human-readable address
label: string; // The address's label (name)
}
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 address as sender
, 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: 'My App',
// Sender information
sender: 'NQxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx',
recipient: 'NQxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx',
value: 100 * 1e5, // 100 NIM
// Optional attributes, see checkout method for details
//senderType: Nimiq.Account.Type.BASIC,
//recipientType: Nimiq.Account.Type.BASIC,
//fee: 0,
//extraData: new Uint8Array(0),
//flags: Nimiq.Transaction.Flag.NONE,
// The transaction's validity start height.
// A transaction is only valid for 120 blocks after its validityStartHeight.
// Transactions with a validityStartHeight higher than <current network
// block height + 1> are rejected and need to be sent again later during
// their validity window.
validityStartHeight: 123456,
};
// All client requests are async and return a promise
const signedTransaction = await hubApi.signTransaction(requestOptions);
The signTransaction()
method returns a SignedTransaction
. See
Checkout for details.
The signup()
method creates a new account in the Hub. The user
will choose an Identicon and optionally set a password.
const requestOptions = {
// The name of your app, should be as short as possible.
appName: 'My App',
};
// All client requests are async and return a promise
const account = await hubApi.signup(requestOptions);
The signup()
method returns a promise which resolves to an Account
:
interface Account {
accountId: string; // Automatically generated account ID
label: string; // The label (name) generated for the account
type: WalletType; // 1 for in-browser multi-address accounts,
// 2 for Ledger hardware accounts
fileExported: boolean; // These two flags signal if the user already
wordsExported: boolean; // has the Login File or the recovery words
addresses: Array<{ // During signup, only one address is added to the account
address: string; // Human-readable address
label: string; // The label (name) of the address
}>;
}
The login()
method allows the user to add an existing account to the
Hub by importing their Login File, Recovery Words or
old Account Access File. After an account has been imported, the
Hub automatically detects active addresses following the
BIP44
method.
const requestOptions = {
// The name of your app, should be as short as possible.
appName: 'My App',
};
// All client requests are async and return a promise
const account = await hubApi.login(requestOptions);
The login()
method returns a promise which resolves to an Account
. Please see
the result type for signup()
for details.
The onboard()
method presents a choice menu between Signup, Login, and
Connect Ledger to the user and is thus a general purpose onboarding method.
Just like the direct methods, it only requires a simple request object:
const requestOptions = {
// The name of your app, should be as short as possible.
appName: 'My App',
};
// All client requests are async and return a promise
const account = await hubApi.onboard(requestOptions);
Since onboard()
is a wrapper around Signup, Login and Ledger, it also returns an
Account
result type. Please see the result type for signup()
for details.
The logout()
method removes an account from the Hub. During the
logout process, the user can export the Login File or Recovery Words before
the account is deleted.
const requestOptions = {
// The name of your app, should be as short as possible.
appName: 'My App',
// The ID of the account that should be removed
accountId: 'xxxxxxxx',
};
// All client requests are async and return a promise
const logoutResult = await hubApi.logout(requestOptions);
The logout()
method returns a promise which resolves to a simple object
containing the success
property, which is always true:
{ success: true }
Using the export()
method, a user can retrieve the Login File or
Recovery Words of an account.
const requestOptions = {
// The name of your app, should be as short as possible.
appName: 'My App',
// The ID of the account to export
accountId: 'xxxxxxxx',
// [optional] Limit the export flow to Login File download
// Default: false,
//fileOnly: true,
// [optional] Limit the export flow to Recovery Words export
// Default: false,
//wordsOnly: true,
};
// All client requests are async and return a promise
const exportResult = await hubApi.export(requestOptions);
The export()
method returns a promise which resolves to an object that
contains flags for each export type:
interface ExportResult {
fileExported: boolean;
wordsExported: boolean;
}
With the changePassword()
method, a user can change the password of an account:
const requestOptions = {
// The name of your app, should be as short as possible.
appName: 'My App',
// The ID of the account whose password should be changed
accountId: 'xxxxxxxx',
};
// All client requests are async and return a promise
const result = await hubApi.changePassword(requestOptions);
The changePassword()
method returns a promise which resolves to a simple object
containing the success
property, which is always true:
{ success: true }
By using the addAddress()
method, the user is able to derive and add an additional
address to their account. The method returns the added address and its label.
The method takes a simple request object as its argument:
const requestOptions = {
// The name of your app, should be as short as possible.
appName: 'My App',
// The ID of the account to which an address should be added
accountId: 'xxxxxxxx',
};
// All client requests are async and return a promise
const address = await hubApi.addAddress(requestOptions);
The request's result contains an address string as address
and a label
:
interface Address {
address: string; // Human-readable address
label: string; // The address's label (name)
}
To rename a user's account or addresses, you can call the rename()
method. The
UI for the rename action always presents the given account and all its addresses
to the user. By sending an optional address with the request, that address's label
will be already pre-selected for the user.
This method takes the following request object as its only argument:
const requestOptions = {
// The name of your app, should be as short as possible.
appName: 'My App',
// The ID of the account which should be renamed, or to which the
// address, which should be renamed, belongs
accountId: 'xxxxxxxx',
// [optional] The human-readable address which should be pre-selected
// for the user to be renamed
address: 'NQxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx';
};
// All client requests are async and return a promise
const account = await hubApi.rename(requestOptions);
Since more than one label can be renamed during the rename request, the result
contains the whole account, including all visible addresses. Please see the
result type for signup()
for details about the Account
object.
To let the user sign an arbitrary message with any of their addresses, you can
call signMessage()
with the following request object. If you do not include
a signer
property, the user will be prompted to select an address from their
available accounts. The message can be either a string or a Uint8Array byte array.
const requestOptions = {
// The name of your app, should be as short as possible.
appName: 'My App',
// The message to sign. Can either be string of valid UTF-8 or a
// byte array to sign arbitrary data
message: 'String to sign' || new Uint8Array([...]),
// [optional] The human-readable address with which to sign
// When not passed, an address selector will be displayed to the user.
// Default: undefined,
//signer: 'NQxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx';
};
// All client requests are async and return a promise
const signedMessage = await hubApi.signMessage(requestOptions);
The method returns a SignedMessage
object containing the following properties:
interface SignedMessage {
signer: string; // Userfriendly address
signerPublicKey: Uint8Array; // The public key of the signer
signature: Uint8Array; // Signature for the message
}
Note: To prevent users from signing valid transactions or other blockchain-related proofs which could be used to impersonate them, the Nimiq Keyguard prefixes additional data to the message before signing. This prefix consists of
'\x16Nimiq Signed Message:\n'
, available as HubApi.MSG_PREFIX
)This data is then hashed with SHA256 before being signed. Together, this leads to the following data structure:
sign( sha256( '\x16Nimiq Signed Message:\n' + message.length + message ) );
Verifying a signed message could go like this:
const signature = new Nimiq.Signature(signedMessage.signature);
const publicKey = new Nimiq.PublicKey(signedMessage.signerPublicKey);
// For string messages:
const data = HubApi.MSG_PREFIX
+ message.length
+ message;
const dataBytes = Nimiq.BufferUtils.fromUtf8(data);
const hash = Nimiq.Hash.computeSha256(dataBytes);
// Check signature against the hashed message
const isValid = signature.verify(publicKey, hash);
If you configured the HubApi to use
top-level redirects instead of popups, you need to
follow the four steps below to specifically listen for the redirects from the
Hub 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.
// 1. Initialize an Hub client instance
const hubApi = new HubApi(/* ... */);
// 2. Define your handler functions
const onSuccess = function(result, storedData) {
console.log("Got result from Hub:", result);
console.log("Retrieved stored data:": storedData);
}
const onError = function(error, storedData) {
console.log("Got error from Hub:", error);
console.log("Retrieved stored data:": storedData);
}
// 3. Listen for the redirect responses you expect
hubApi.on(HubApi.RequestType.CHECKOUT, onSuccess, onError);
hubApi.on(HubApi.RequestType.SIGN_TRANSACTION, onSuccess, onError);
hubApi.on(HubApi.RequestType.LOGIN, onSuccess, onError);
// 4. After setup is complete, check for a redirect response
hubApi.checkRedirectResponse();
The available RequestType
s, corresponding to the API methods, are:
enum HubApi.RequestType {
CHECKOUT = 'checkout',
CHOOSE_ADDRESS = 'choose-address',
SIGN_TRANSACTION = 'sign-transaction',
SIGNUP = 'signup',
LOGIN = 'login',
ONBOARD = 'onboard',
LOGOUT = 'logout',
EXPORT = 'export',
CHANGE_PASSWORD = 'change-password',
ADD_ADDRESS = 'add-address',
RENAME = 'rename',
SIGN_MESSAGE = 'sign-message',
}
TODO
If you want to run your own instance of Hub, you also need to run an instance of the Keyguard.
To get started with working on the source code, pull the code and install the dependencies:
git clone https://github.com/nimiq/hub.git
cd hub
yarn
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
Compile and minify for production:
yarn run build
The following values can be changed via configuration files:
list()
.The default config file is config.local.ts
. To use a different file
(especially useful for deployment), set an environment variable
build
. E.g. export build='testnet'
to use config.testnet.ts
. To set
environment variables permanently, please refer to your server's documentation,
e.g. for Apache.
FAQs
The Nimiq Hub 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 npm package @nimiq/hub-api receives a total of 138 weekly downloads. As such, @nimiq/hub-api popularity was classified as not popular.
We found that @nimiq/hub-api demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
Security News
A Stanford study reveals 9.5% of engineers contribute almost nothing, costing tech $90B annually, with remote work fueling the rise of "ghost engineers."
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.