
Research
/Security News
Critical Vulnerability in NestJS Devtools: Localhost RCE via Sandbox Escape
A flawed sandbox in @nestjs/devtools-integration lets attackers run code on your machine via CSRF, leading to full Remote Code Execution (RCE).
@notabene/javascript-sdk
Advanced tools
This library is the JavaScript SDK for loading the Notabene UX components in the front-end.
There are two options for loading the Notabene SDK:
<script id="notabene" async src="https://unpkg.com/@notabene/javascript-sdk@next/dist/notabene.js"></script>
Or installing the library:
Using Yarn:
yarn add @notabene/javascript-sdk
Using NPM:
npm install @notabene/javascript-sdk
If you installed the library into your project, you can import it into your project:
import Notabene from '@notabene/javascript-sdk';
// 1. Create Notabene instance
const notabene = new Notabene({
nodeUrl: 'https://api.notabene.id',
authToken: 'YOUR_CUSTOMER_TOKEN',
});
// 2. Create and mount withdrawal component
const withdrawal = notabene.createWithdrawalAssist({
asset: 'ETH',
destination: '0x1234...',
amountDecimal: 1.0,
});
withdrawal.mount('#nb-withdrawal');
// 3. Handle completion
const { valid, value, txCreate } = await withdrawal.completion();
if (valid) {
// Submit to your backend
}
Use the customer token endpoint with your access token to receive a token with a customer's scope.
⚠️ IMPORTANT ⚠️
When requesting the
customer token
you must pass a uniquecustomerRef
per customer for ownership proof reusability, otherwise you might encounter unwanted behavior.
Create a new Notabene instance:
const notabene = new Notabene({
nodeUrl: 'https://api.notabene.id', // use `https://api.notabene.dev` for testing
authToken: '{CUSTOMER_TOKEN}',
locale: 'de', // default locale = `en`
});
Use the same nodeUrl
that you use to interact with the Notabene API.
Each component can be used in various ways depending on your use case.
This will let you embed the component into your existing withdrawal flow.
Create an html element to contain the component:
<div id="nb-withdrawal/>
Instantiate the withdrawal element and mount it using the id from above
const withdrawal = notabene.createWithdrawalAssist(tx, options);
withdrawal.mount('#nb-withdrawal');
The simplest way to get the result is to use:
try {
const { valid, value, txCreate, ivms101, proof } =
await withdrawal.completion();
if (valid) {
// Submit result to your backend
}
} catch (e) {
console.error(e);
}
To update the component as users enter transaction details:
withdrawal.update({
asset: 'ETH',
destination: '0x8d12a197cb00d4747a1fe03395095ce2a5cc6819',
amountDecimal: 1.12,
});
To be notified once the validation is completed so you can submit the withdrawal to your back end:
withdrawal.on('complete', { valid, value, txCreate, ivms101, proof } => ...)
To be notified of any validation errors use:
withdrawal.on('error',error => ...)
To be notified when the component is Ready:
withdrawal.on('ready', ({ type }) => {
console.log(type === 'ready');
});
Calling on
returns a function that will allow you to cleanly unsubscribe.
const unsubscribe = withdrawal.on('complete', { valid, value, txCreate, ivms101, proof } => ...)
// Clean up
unsubscribe()
All components support being opened in a modal using openModal()
, which returns a promise.
const withdrawal = notabene.createWithdrawalAssist(tx, options);
try {
const { valid, value, txCreate, ivms101, proof } =
await withdrawal.openModal();
if (valid) {
// Submit result to your backend
}
} catch (e) {
console.error(e);
}
All components support being opened in a popup window using popup()
, which returns a promise.
Many embedded wallets refuse to work in an iframe. In this case it is better to use a popup window.
Unfortunately there are also some restrictions on popup windows:
const withdrawal = notabene.createWithdrawalAssist(tx, options);
try {
const { valid, value, txCreate, ivms101, proof } = await withdrawal.popup();
if (valid) {
// Submit result to your backend
}
} catch (e) {
console.error(e);
}
In some cases, in particular institutional or mobile apps you may prefer to link your customers to the component through an email or redirect the user to it in a mobile app.
const withdrawal = notabene.createWithdrawalAssist(tx, options, {
callback: /// a serverside backend url
redirectUri: // URI of website or mobile app to redirect user to after completion
});
// NodeJS redirect. Link also works in an email.
res.redirect(withdrawal.url);
Bear in mind that this is a full screen view for your users.
The two parameters that should be configured are:
callback
- a URL for your serverside. On completion this will receive an HTTP POST with the result as a json body and the authToken
as an Authorization: Bearer
header.redirectUri
- the user will be redirected here on completion. The result parameters will be json encoded in the URL fragment. You can use a mobile app schema to intercept these in your mobile app.Note for data privacy reasons the callback will be coming from your users web browser and not from our infrastructure, so no static IP is currently possible. Instead please check the authToken
provided with the request.
The Withdrawal Assist component helps you collect additional required information from your user during a standard crypto withdrawal process.
const withdrawal = notabene.createWithdrawalAssist({
asset: 'ETH',
destination: '0x...',
amountDecimal: 1.23,
assetPrice: {
currency: 'USD', // ISO currency code
price: 1700.12, // Asset price
},
customer: {
name: 'John Smith',
email: "john.smith@domain.com",
},
});
asset
: The cryptocurrency or token being transferred. See Asset Specificationdestination
: The destination or blockchain address for the withdrawal. See DestinationamountDecimal
: The amount to transfer in decimal format. See Transaction AmountassetPrice
: Optional price information in a fiat currency. See Asset Pricecustomer
: Optional Customer object containing their name and emailIf any of the required parameters are missing the component will just show the Notabene badge.
Include configuration Options as a second optional parameter:
const withdrawal = notabene.createWithdrawalAssist(
{
asset: 'ETH',
destination: '0x...',
amountDecimal: 1.23,
assetPrice: {
currency: 'USD', // ISO currency code
price: 1700.12, // Asset price
},
customer: {
name: 'John Smith',
email: "john.smith@domain.com",
},
},
{
proofs: {
microTransfer: {
destination: '0x...',
amountSubunits: '12344',
timeout: 86440,
},
},
},
);
The Connect Wallet component helps you collect and verify the address of your users self-hosted wallet in one go.
asset
: The cryptocurrency or token being transferred. See Asset Specificationconst connect = notabene.createConnectWallet({
asset: 'ETH',
});
const { proof, txCreate } = await connect.openModal();
The Deposit Request lets your customers request deposits that are fully Travel Rule compliant.
const depositRequest = notabene.createDepositRequest({
asset: 'ETH',
destination: '0x...',
amountDecimal: 1.23,
customer: {
name: 'John Smith',
},
});
asset
: The cryptocurrency or token being transferred. See Asset Specificationdestination
: The destination or blockchain address for the withdrawal. See DestinationamountDecimal
: Optional amount to deposit in decimal format. See Transaction Amountcustomer
: Optional Customer object containing their nameIf any of the required parameters are missing the component will just show the Notabene badge.
The Deposit Assist component helps you collect missing Travel Rule data after a deposit has already been recorded on-chain. For example, if the deposit arrived with incomplete originator information, you can use Deposit Assist to request this information from your end-user.
const deposit = notabene.createDepositAssist(
{
asset: 'ETH',
amountDecimal: 1.23,
source: '0x...',
destination: '0x...',
transactionId: "UUID"
},
{
// Optional transaction options
},
);
asset
: The cryptocurrency or token being transferred. See Asset Specificationsource
: The source or blockchain address for the deposit. See Origindestination
: The destination or blockchain address for the deposit. See DestinationamountDecimal
: Optional amount to deposit in decimal format. See Transaction AmounttransactionId
: Optional transactionId of a Notabene transaction. Will be returned with the payload to assist updating the TransactionIf any of the required parameters are missing the component will just show the Notabene badge.
Counterparty Assist is a feature built into the existing Withdrawal and Deposit Assist components. When enabled, it allows users to hand off data collection to a counterparty — or to another device — by sharing a secure link. This helps ensure more accurate and complete information, especially when the counterparty is best suited to provide the required data.
This feature does not function as a standalone component. Instead, it augments the Withdrawal and Deposit flows when configured.
natural
, legal
)During the counterparty data collection step, users can generate and share a link to allow third-party counterparties (individuals or organizations) to enter their own data. This ensures data accuracy and supports robust address verification by allowing the rightful owner to provide the necessary information.
self
)During the address verification step, users can share a link to complete self-hosted wallet proof submissions on another device. This is especially useful if the original device used to initiate the process doesn't support signing or wallet access.
You can enable Counterparty Assist by specifying the counterparty types you want the feature to apply to using the counterpartyAssist configuration field.
false
: Disable the feature explicitlyundefined
(not configured): Feature is disabled by default{ counterpartyTypes: [PersonType.SELF, PersonType.NATURAL, PersonType.LEGAL] }
: Enable for specific counterparty typesExample Config
import Notabene, {
PersonType,
} from '@notabene/javascript-sdk';
// Counterparty assist is enabled for specific counterparty types
const options: TransactionOptions = {
...
counterpartyAssist: {
counterpartyTypes: [
PersonType.LEGAL, // JS: 'legal'
PersonType.NATURAL, // JS: 'natural'
PersonType.SELF, // JS: 'self'
],
}
};
The component emits a response once a participant has completed their portion of the process. Depending on the party type — Third Party (natural
, legal
) or First Party (self
) — the behavior and expectations differ slightly.
natural
, legal
)When a third party completes their step after following the shared link, the host application will receive a COMPLETE
message from the component. However, because not all required data may be available at this point, the response
object will include the information gathered so far, along with a refreshSource
field. This allows the host to fetch the latest encrypted data once it's available.
Field | Type | Description |
---|---|---|
url | URI | The endpoint where the host can retrieve the encrypted data. |
key | string | The encryption key used to decrypt the PII (Personally Identifiable Information). Not stored by Notabene. |
Example Response
{
type: CMType.COMPLETE, // 'complete'
response: { // transaciton data + refresh source
destination: "0xFf9A04788972C3803959454ECAE1ed327826a216",
asset: "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
customer: {
type: "natural",
name: "sdfsd",
email: "sdjlf@sdlfj.com"
},
amountDecimal: 100,
counterparty: {
type: "natural"
},
account: {
caip10: "eip155:1:0xFf9A04788972C3803959454ECAE1ed327826a216",
blockchainAddress: "0xFf9A04788972C3803959454ECAE1ed327826a216",
chain: "eip155:1",
did: "did:pkh:eip155:1:0xFf9A04788972C3803959454ECAE1ed327826a216",
valid: true
},
refreshSource: {
url: "https://safe-connections.notabene.id/17f76e4c-9a2a-4c34-afcb-b4868e609a96", // endpoint to retreive data
key: "1Lcp5SFhaMHH7CAEILrS8IWA6BXS4tFZunPx08WU5Ok=" // key that can be used to decrypt data
}
}
}
self
)When the user is the originator (i.e., acting on their own behalf), they complete the verification process via a shared link and are then prompted to return to the original page to continue.
Upon completion, the component emits a COMPLETE
response. In this case, the component handles all necessary data updates internally, so no additional action is required from the host to retrieve updated data. The host can directly proceed to submit the transfer to the Notabene API.
(Third Parties Only: natural
, legal
)
When data submission is handed off to third parties, we cannot predict how long it will take them to complete the process. For this reason, we provide the host with all the information needed to retrieve the data and allow them to design how the user experiences this flow.
To simplify retrieval and decryption, we provide an asynchronous getRefreshResult
function. It accepts a refreshSource
and returns information about the associated transaction.
Example
import { getRefreshResult } from "@notabene/javascript-sdk";
const transaction = await getRefreshResult({
url: "https://safe-connections.notabene.id/17f76e4c-9a2a-4c34-afcb-b4868e609a96",
key: "1Lcp5SFhaMHH7CAEILrS8IWA6BXS4tFZunPx08WU5Ok="
})
getRefreshResult
Property | Type | Optional? | Description |
---|---|---|---|
id | UUID | No | Unique identifier for the transaction. |
metadata | ConnectionMetadata | No | Metadata associated with the transaction. |
status | 'active' | 'completed' | 'closed' | No | Current status of the transaction’s data collection. |
tx | T | Yes | Ongoing transaction data (available when status is active ). |
result | TransactionResponse<T> | Yes | Finalized transaction data (available when status is completed ). |
Example Active Transaction Data
{
"id": "17f76e4c-9a2a-4c34-afcb-b4868e609a96",
"metadata": {
"participants": [
"did:ethr:0x54b75d2a0925508682e65194cccb6f1e8eaafb2c"
],
"nodeUrl": "https://api-qa.eu.notabene.id",
"transactionType": "withdraw"
},
"status": "active",
"tx": {
//...
}
}
Example Completed Transaction Data
{
"id": "17f76e4c-9a2a-4c34-afcb-b4868e609a96",
"metadata": {
"participants": [
"did:ethr:0x54b75d2a0925508682e65194cccb6f1e8eaafb2c" // DID of transaction participants
],
"nodeUrl": "https://api-qa.eu.notabene.dev",
"transactionType": "withdraw"
},
"status": "completed",
"result": { // the response returned from the embedded component when all information is successfully collected
"proof": {
//...
},
"txCreate": {
//...
},
"errors": [],
"status": "pending",
"valid": true,
"value": {
//...
},
"ivms101": {
//...
}
}
}
Once the host has retrieved the completed data, they can finalize the transaction by submitting it to the Notabene API.
If any error occurs, the error
event is passed containing a message.
withdrawal.on('error', (error) => ...)
An example error object.
type Error = {
type: CMType.Error;
message: string;
description: string;
identifier: ErrorIdentifierCode;
};
Errors can be handled in this way:
component.on('error', (error) => {
if (error.type === CMType.Error) {
switch (error.identifier) {
case ErrorIdentifierCode.SERVICE_UNAVAILABLE:
// handle the error
break;
//...
}
}
});
ErrorIdentifierCode | Description/Message | In Use |
---|---|---|
SERVICE_UNAVAILABLE | The Notabene service is currently unavailable | ✅ |
TOKEN_INVALID | Auth Token is Invalid | ✅ |
WALLET_CONNECTION_FAILED | The connection to the wallet service failed, possibly due to network issues or unsupported wallet types. | ✅ |
WALLET_NOT_SUPPORTED | The wallet used does not support the required functionality or blockchain. |
The asset
field the following types of assets specified:
notabene_asset
code passed as astring
. See Notabene Assets Service.Use one of the following
amountDecimal
A number specifying the amount in decimal format. Eg. amountDecimal=1.1
would mean 1.1 of for example BTC or ETH.Specify the beneficiary address as destination
using one of the following formats:
Specify the originator address as source
using one of the following formats:
The price of the asset is used to determine certain rules based on thresholds. We recommond you pass in your price like this:
assetPrice: {
currency: 'USD', // ISO currency code
price: 1700.12, // Asset price
};
Some components can be configured using an optional TransactionOptions object.
The following shows the full set of options in typescript:
import Notabene, {
AgentType,
PersonType,
ProofTypes,
} from '@notabene/javascript-sdk';
const options: TransactionOptions = {
jurisdiction: "US", // Defaults to the jurisdiction associated with customer token
proofs: {
reuseProof: true, // Defaults true
microTransfer: {
destination: '0x...',
amountSubunits: '12344',
timeout: 86440,
},
fallbacks: [ProofTypes.Screenshot, ProofTypes.SelfDeclaration], // js ['screenshot','self-declaration']
deminimis: {
threshold: 1000,
currency: 'EUR',
proofTypes: [ProofTypes.SelfDeclaration],
},
},
allowedAgentTypes: [AgentType.PRIVATE, AgentType.VASP], // js ['WALLET','VASP']
allowedCounterpartyTypes: [
PersonType.LEGAL, // JS: 'legal'
PersonType.NATURAL, // JS: 'natural'
PersonType.SELF, // JS: 'self'
],
fields: {
naturalPerson: {
name: true, // Default true
website: { optional: true },
email: true,
phone: true,
geographicAddress: false,
nationalIdentification: false,
dateOfBirth: false,
placeOfBirth: false,
countryOfResidence: true,
},
legalPerson: {
name: true, // Default true
lei: true, // Default true
website: { optional: true }, // Default true
email: true,
phone: true,
geographicAddress: false,
nationalIdentification: false,
countryOfRegistration: true,
},
hide: [ValidationSections.ASSET, ValidationSections.DESTINATION], // Don't show specific sections of component
},
vasps: {
addUnknown: true, // Allow users to add a missing VASP - Defaults to false
onlyActive: true, // Only list active VASPs - Default false
searchable: [
VASPSearchControl.ALLOWED, // JS: 'allowed'
VASPSearchControl.PENDING, // JS: 'pending'
], // Control searches for VASPs - Defaults to undefined
},
counterpartyAssist: { // Allows users to share a link to collect counterparty data
counterpartyTypes: [
PersonType.LEGAL, // JS: 'legal'
PersonType.NATURAL, // JS: 'natural'
PersonType.SELF, // JS: 'self'
],
}
};
const withdrawal = notabene.createWithdrawalAssist(tx, options);
The options can additionally be updated dynamically with the update()
function.
withdrawal.update(
{
asset: 'ETH',
destination: '0x8d12a197cb00d4747a1fe03395095ce2a5cc6819',
amountDecimal: 1.12,
},
{
proofs: {
microTransfer: {
destination: '0x...',
amountSubunits: '12344',
timeout: 86440,
},
},
},
);
const firstParty: TransactionOptions = {
allowedCounterpartyTypes: [
PersonType.SELF, // JS: 'self'
],
};
const vasp2vasp: TransactionOptions = {
allowedAgentTypes: [AgentType.VASP], // js ['VASP']
};
const options: TransactionOptions = {
allowedAgentTypes: [AgentType.PRIVATE], // js ['WALLET']
};
The National Identifier Type is a dropdown that allows the developer configure the types of national identifiers they are supporting.
const options: TransactionOptions = {
fields: {
naturalPerson: {
name: {optional: false},
nationalIdentification: {
optional: true, // allow optional national identification
nationalIdentifierType: {
values: ['ARNU', 'CCPT', 'RAID', 'DRLC', 'TXID', 'SOCS'], // default values
},
},
},
legalPerson: {
name: true,
lei: {optional: true},
nationalIdentification: {
optional: false, // require national identification
nationalIdentifierType: {
values: ['RAID', 'TXID', 'MISC'], // default values
},
},
},
},
};
By default components support message signing proofs.
You can support Micro Transfers (aka Satoshi tests) by adding a deposit address for the test.
Your compliance team will have to determine how to handle and verify these transactions in the rules engine or individually.
const options: TransactionOptions = {
proofs: {
microTransfer: {
destination: '0x...',
amountSubunits: '1234',
timeout: 86440, // Optional timeout in seconds, which is displayed to the user
},
fallbacks: [ProofTypes.Screenshot, ProofTypes.SelfDeclaration], // js ['screenshot','self_declaration']
},
};
Notabene does not currently verify these tests automatically as you likely already have the infrastructure to do so.
You will receive a response back from the component containing a proof object. For MicroTransfers it will look like this:
type MicroTransferProof {
type: ProofTypes.MicroTransfer;
status: ProofStatus.PENDING;
did: DID;
address: CAIP10; // CAIP10 account to be verified
txhash: string; // Transaction Hash to verify
chain: CAIP2; // CAIP2 identifier of blockchain
amountSubunits: string; // Amount in subunits eg (satoshi or wei) to be verified
}
You may accept a few options if none of the other are available. We do not recommend them, as they do not provide sufficient proof. However many VASPs do allow them for now:
const options: TransactionOptions = {
proofs: {
fallbacks: [ProofTypes.Screenshot, ProofTypes.SelfDeclaration], // js ['screenshot','self_declaration']
},
};
The two options are:
screenshot
Where a user is requested to upload a screenshot of their walletself-declaration
Where a user self declares that they control the wallet addressBy default, counterparty fields are determined based on the rules of the jurisdiction associated with the VASP using the component. This ensures compliance even when fields are not explicitly configured.
If a specific jurisdiction is manually configured, the component will instead derive the fields according to that jurisdiction’s rules.
For VASPs seeking more granular control over the fields displayed to customers about counterparties, the fields object can be used to customize visibility. Required and optional fields can be configured independently for both natural and legal persons.
We recommend working closely with your compliance team when making these configurations, as regulatory requirements vary significantly across jurisdictions.
Each field can be configured like this:
true
required fieldfalse
don't show{ optional: true }
show but don't requireEg:
{
naturalPerson: {
website: { optional: true },
email: true,
phone: false,
}
}
The above will always ask the user for the following for natural persons:
name
since it is on by default (you can disable it explicitly by setting it to false
)website
is show but is optionalemail
is requiredconst options: TransactionOptions = {
fields: {
naturalPerson: {
name: true, // Default true
website: { optional: true },
email: true,
phone: true,
geographicAddress: false,
nationalIdentification: false,
dateOfBirth: {
transmit: true,
},
placeOfBirth: false,
countryOfResidence: true,
},
legalPerson: {
name: true, // Default true
lei: {optional: true }, // Default optional
website: { optional: true }, // Default optional
email: true,
phone: true,
geographicAddress: false,
nationalIdentification: false,
countryOfRegistration: true,
},
},
};
Field name | Natural | Legal | IVMS101 | description |
---|---|---|---|---|
name | ✅ | ✅ | 🟩 | Full name |
email | 🟩 | 🟩 | -- | Email (for your internal purposes) |
website | -- | ✅ | -- | Business Website (for your internal purposes) |
phone | 🟩 | 🟩 | -- | Mobile Phone (for your internal purposes) |
geographicAddress | 🟩 | 🟩 | 🟩 | Residencial or business address |
nationalIdentification | 🟩 | 🟩 | 🟩 | National Identification number |
dateOfBirth | 🟩 | -- | 🟩 | Date of birth |
placeOfBirth | 🟩 | -- | 🟩 | Place of birth |
countryOfResidence | 🟩 | -- | 🟩 | Country of Residence |
lei | -- | ✅ | 🟩 | LEI (Legal Entity Identifier) |
countryOfRegistration | -- | 🟩 | 🟩 | Country of Registration |
See locales for the list of supported locales.
You can optionally theme the UI when creating an instance of Notabene
const notabene = new Notabene({
nodeUrl: 'https://api.notabene.id',
authToken: 'YOUR_CUSTOMER_TOKEN',
theme: {
mode: 'light', // 'light', 'dark'
primaryColor: '#0a852d',
secondaryColor: '#f5f7fa',
fontFamily: 'Montserrat' // Google Fonts Supported
}
});
MIT © Notabene Inc.
FAQs
JavaScript SDK for Notabene
The npm package @notabene/javascript-sdk receives a total of 2,814 weekly downloads. As such, @notabene/javascript-sdk popularity was classified as popular.
We found that @notabene/javascript-sdk 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.
Research
/Security News
A flawed sandbox in @nestjs/devtools-integration lets attackers run code on your machine via CSRF, leading to full Remote Code Execution (RCE).
Product
Customize license detection with Socket’s new license overlays: gain control, reduce noise, and handle edge cases with precision.
Product
Socket now supports Rust and Cargo, offering package search for all users and experimental SBOM generation for enterprise projects.