Validating Travel Rule transactions with Fireblocks and Notabene
Prerequisites
Overview
Note
This guide contains links to Notabene API documentation, which is password protected. If you need help accessing it, contact your Customer Success Manager.
The Travel Rule states that Virtual Asset Service Providers (VASPs), which include businesses that exchange virtual assets and companies that use Fireblocks, must provide additional data on the senders and recipients of certain transactions. Each jurisdiction decides which transactions must adhere to the Travel Rule and what data must be included.
The Fireblocks API allows you to send your transactions to Notabene to ensure they comply with the Travel Rule. After creating a key to encrypt personally identifiable information (PII) data included in the transactions, you use the Validation API to verify all the necessary data is included for the Travel Rule check. Once these API calls are set up, you can use the Fireblocks SDK to submit your transactions for Travel Rule validation.
The following transaction routes in Fireblocks are not subject to Travel Rule screening:
- Gas Station to Vault
- Vault to Network Connection(s)
- Vault to Exchange
- Vault to Vault
Learn what assets are supported for the Travel Rule integration.
Before you begin
Before you can validate Travel Rule transactions, you must create a dedicated DIDKey for encrypting customer PII data. By creating this public-private keypair, you allows other VASPs to retrieve the public key and encrypt PII data to you.
You can create a new key pair using @notabene/cli and then publishing it to the Notabene directory under the pii_didkey field.
Creating an encryption key
- Install the Notabene CLI tool.
- Install the library globally using Yarn
yarn global add @notabene/cli
or NPMnpm i -g @notabene/cli
. - Ensure the path to globally installed packages is in your $PATH environment variable.
- Install the library globally using Yarn
- Generate an M2M token.
- Log in using your Notabene issued client ID and client secret:
notabene auth:login --clientId={CLIENT_ID} --clientSecret={CLIENT_SECRET}
. - Log out of Notabene:
notabene auth:logout
. - Generate an M2M token for use with the Notabene Travel Rule gateway:
notabene auth:token
. Please note that you must be logged in first.
- Log in using your Notabene issued client ID and client secret:
- Create the encryption key.
- You can use the CLI to generate a key that can be used to encrypt PII information to be sent as part of a Travel Rule message:
notabene keys:create
. - This generates a JSON object containing an Ed25519 key and metadata which can be passed to the Notabene SDK when creating transactions to encrypt the PII.
- You can use the CLI to generate a key that can be used to encrypt PII information to be sent as part of a Travel Rule message:
{
"did":"did:key:z6MkjwpTikNZkp**\*\***\*\***\*\***\*\*\*\***\*\***\*\***\*\***",
"controllerKeyId":"519b59a6b7ebf128f6c6\***\*\*\*\*\***\*\*\***\*\*\*\*\***",
"keys":\[{"type":"Ed25519","kid":"519b59a6b7eb768**\*\*\*\***\*\***\*\*\*\***\*\*\*\***\*\*\*\***\*\***\*\*\*\***",
"publicKeyHex":"519b59a6b7ebf128f6c7**\*\***\*\***\*\***\*\*\*\***\*\***\*\***\*\***",
"meta":{"algorithms":["Ed25519","EdDSA"]},
"kms":"local",
"privateKeyHex":"0d07d8acda928f98765e4a0b80013e2be369c29564419a\***\*\*\*\*\***\*\*\***\*\*\*\*\***\*\*\***\*\*\*\*\***\*\*\***\*\*\*\*\***"}],
"services":\[],
"provider":"did:key"
}
Validation and sending Travel Rule transactions
Validating transactions ensures all the necessary information is included in the Travel Rule check. After you transaction has been validated, you can send the transaction to the counterparty.
The Travel Rule transaction flow consists of four steps:
- Initial validation
- Collecting additional data
- Validating the full transaction
- Sending the Travel Rule transaction
Step 1: Initial validation
The Transaction Validate API call checks what beneficiary details are required by your jurisdiction and the beneficiary VASP’s jurisdiction. Since Travel Rule compliance is only required for transactions above a certain threshold, the Transaction Validate API call also checks whether or not the transaction meets the threshold and must comply with the Travel Rule. Travel Rule thresholds and required data are defined by the jurisdictions of the VASPs involved.
This API works with a customerToken and may be used from the front end of your application.
Learn more about how the validation API works.
Example
{
"transactionAsset": "BTC",
"destination": "bc1qxy2kgdygjrsqtzq2n0yrf1234p83kkfjhx0wlh",
"transactionAmount": "10",
"originatorVASPdid": "did:ethr:0x44957e75d6ce4a5bf37aae117da86422c848f7c2",
"originatorEqualsBeneficiary": false
}
Response
{
"isValid": false,
"type": "NON_CUSTODIAL",
"beneficiaryAddressType": "UNKNOWN",
"addressSource": "UNKNOWN",
"errors": [
"beneficiaryNameMissing",
"beneficiaryOwnershipProofMissing"
]
}
The response to this initial validation step tells us:
- If the value of this transaction is above or below the Travel Rule threshold.
- If the destination address was identified by blockchain analytics or your address book.
If the address was not automatically identified, search and select the correct VASP from Notabene’s directory by querying Search API. You can search using the /v1/screening/travel_rule/vasp?q=Fireblocks
API endpoint.
Response
{
"isValid": "true" or "false",
"type": "BELOW_THRESHOLD" or "TRAVELRULE" or "NON_CUSTODIAL",
"beneficiaryAddressType": "UNKNOWN" or "HOSTED" or "UNHOSTED",
"addressSource": "ADDRESS_GRAPH" or "NAME_OF_BLOCKCHAIN_ANALYTICS",
"beneficiaryVASPname": "VASP_NAME",
"errors": "MISSING_FIELDS_REQUIRED_BY_YOUR_JURISDICTION"
"warnings": "MISSING_FIELDS_REQUIRED_BY_COUNTERPARTY_JURISDICTION"
}
If the transaction is below the threshold, you don’t need to collect data for the Travel Rule. Depending on the initial call response, the following scenarios can happen:
- Known VASP (address book)
- Known VASP (blockchain analytics)
- Known VASP (manually selected)
- Unknown/unlisted VASP
- Unhosted wallet
Step 2: Collecting additional data
Once you perform the initial validation step to determine if the Travel Rule data requirements apply, you have two options to collect the necessary information listed in the errors array from step 1. You can use the Notabene widget or replicate the widget's functionality using API calls.
Read the Notabene API documentation for more information on front-end data collection.
Step 3: Validating the full transaction
After reacting to the response of the initial Transaction Validate API call and collecting the necessary information about the beneficiary, you can perform a final request to confirm that you have all the data needed for the Travel Rule.
The Transaction Validate Full API call validates the beneficiary and the originator data included in your transaction. The originator data is the information about your organization and is static.
This API requires an accessToken and must be called from the back-end of your application.
Learn more about how the validation API works.
Example
{
"transactionAsset": "ETH",
"destination": "bc1qxy2kgdygjrsqtzq2n0yrf1234p83kkfjhx0wlh",
"transactionAmount": "10000000000000000000",
"originatorVASPdid": "{{vaspDID}}",
"originatorEqualsBeneficiary": false,
"beneficiaryVASPdid": "did:ethr:0x47463999eb42dc2aaacb29624c512603221227a1",
"beneficiaryName": "Bruce Wayne",
"beneficiaryAccountNumber": "bc1qxy2kgdygjrsqtzq2n0yrf1234p83kkfjhx0wlh"
}
Response
{
"isValid": true,
"type": "TRAVELRULE",
"beneficiaryAddressType": "HOSTED",
"addressSource": "ADDRESS_GRAPH",
"beneficiaryVASPname": "Notabene VASP US"
}
If all the necessary information is included, you receive the response isValid=true.
Step 4: Sending the Travel Rule transaction
Once the full transaction is validated, move the information used in the final validation request to the back-end, add information about your customer (the originator), and create the actual Travel Rule message using the Fireblocks SDK.
Fireblocks SDK
The Hybrid SafePII mode extends the End-to-End flow, where the Originator VASP further encrypts the PII data selectively using their dedicated Notabene-managed encryption key. This allows Notabene to decrypt the PII (or parts of the PII data) for in-flow pre-transaction name sanction screening.
To create a Fireblocks blockchain transaction with encrypted PII data, you must provide the necessary Notabene PII SDK credentials in the sdkOptions
field.
fireblocks = new FireblocksSDK(privateKey, userId, serverAddress, undefined, {
customAxiosOptions: {
interceptors: {
response: {
onFulfilled: (response) => {
console.log(`Request ID: ${response.headers["x-request-id"]}`);
return response;
},
onRejected: (error) => {
console.log(`Request ID: ${error.response.headers["x-request-id"]}`);
throw error;
}
}
}
},
travelRuleOptions: {
kmsSecretKey:
"75099860d284bb22a2c96a6e41ee024d04171a4ba33b2f3720d2bec17d1ced78",
authURL: "<https://auth.notabene.id">,
baseURL: "<https://api.notabene.dev">,
audience: "<https://api.notabene.dev">,
baseURLPII: "<https://pii.notabene.dev">,
audiencePII: "<https://pii.notabene.dev">,
clientId: "7iQ6MNg**\*\***\*\***\*\***\***\*\***\*\***\*\***",
clientSecret: "1rg17YZtmFT\***\*\*\*\*\***\*\*\***\*\*\*\*\***\*\*\*\***\*\*\*\*\***\*\*\***\*\*\*\*\***",
jsonDidKey: "{\"did\":\"did:key:z6MknL8ERKo2MqArMvJdA2EdZcUWehR7t3gDDc9hsbB7TCfZ\",\"controllerKeyId\":\"75099860d284bb22a2c96a6e41ee024d04171a4ba33b2f3720d2bec17d1ced78\",\"keys\":\[{\"type\":\"Ed25519\",\"kid\":\"75099860d284bb22a2c96a6e41ee024d04171a4ba33b2f3720d2bec17d1ced78\",\"publicKeyHex\":\"75099860d284bb22a2c96a6e41ee024d04171a4ba33b2f3720d2bec17d1ced78\",\"meta\":{\"algorithms\":[\"Ed25519\",\"EdDSA\"]},\"kms\":\"local\",\"privateKeyHex\":\"c0add0d8b45f704bbc8235d05ee449dc19453dce94df2b07c7bddb36cf7de59475099860d284bb22a2c96a6e41ee024d04171a4ba33b2f3720d2bec17d1ced78\"}],\"services\":\[],\"provider\":\"did:key\"}"
}
});
Then provide the Travel Rule Message data to Fireblocks Transaction in the requested format.
{
"transactionAsset": "ETH",
"transactionAmount": "10044000000000000000",
"originatorVASPdid": "{{vaspDID}}",
"beneficiaryVASPdid": "did:ethr:0xc7d10be62c7a5af366a13511fe5e0584b8918114",
"transactionBlockchainInfo": {
"txHash": "",
"origin": "5342b5234hioutewry87y78sdfghy783t4t34",
"destination": "0xDB6A31EC49D5FB35EF6BA6CE0A3B071C8BA7F7F0"
},
"originator": {
"originatorPersons": \[
{
"naturalPerson": {
"name": \[
{
"nameIdentifier": [
{
"primaryIdentifier": "Wunderland",
"secondaryIdentifier": "Alice"
}
]
}
],
"geographicAddress": [
{
"streetName": "Robinson road",
"townName": "Singapore",
"country": "SG",
"buildingNumber": "71",
"postCode": "123456"
}
],
"nationalIdentification": {
"countryOfIssue": "SG",
"nationalIdentifier": "987654321",
"nationalIdentifierType": "DRLC"
}
}
}
],
"accountNumber": [
"5342b5234hioutewry87y78sdfghy783t4t34"
]
},
"beneficiary": {
"beneficiaryPersons": \[
{
"naturalPerson": {
"name": \[
{
"nameIdentifier": [
{
"primaryIdentifier": "Bobson",
"secondaryIdentifier": "Bob"
}
]
}
]
}
}
],
"accountNumber": [
"5643jn5h34y2g7hg42jt24j890y345gfgh65"
]
}
}
const transactionWithTravelRulePayload = {
assetId: 'BTC',
source: {
type: 'VAULT_ACCOUNT',
id: '1',
virtualId: undefined,
virtualType: undefined
},
destination: {
type: 'ONE_TIME_ADDRESS',
id: undefined,
oneTimeAddress: {
address: 'bc1qdhqnuy2uejqwfsuucrwjatdtuud6c8eez0nu5z',
tag: undefined
},
virtualId: undefined,
virtualType: undefined
},
operation: 'TRANSFER',
amount: '0.001',
fee: undefined,
gasPrice: undefined,
gasLimit: undefined,
feeLevel: undefined,
maxFee: undefined,
failOnLowFee: false,
priorityFee: undefined,
note: 'Created with love by fireblocks SDK',
autoStaking: undefined,
cpuStaking: undefined,
networkStaking: undefined,
replaceTxByHash: '',
extraParameters: undefined,
destinations: undefined,
externalTxId: undefined,
treatAsGrossAmount: undefined,
travelRuleMessage: {
originatorVASPdid: 'did:ethr:0x44957e75d6ce4a5bf37aae117da86422c848f7c2',
travelRuleBehavior: false,
beneficiaryVASPdid: 'did:ethr:0xf9139d9ca3cd9824a7fb623b1d34618a155137bc',
beneficiaryVASPname: '',
originator: { originatorPersons: [Array], accountNumber: [Array] },
beneficiary: { beneficiaryPersons: [Array], accountNumber: [Array] }
}
}
const result = await fireblocks.createTransaction(transactionWithTravelRulePayload);
Get VASP details
You can use the POST {{baseUrl}}/tx/vasp/{did}
API call to get details on a specific VASP from Notabene’s database. This API call allows you to receive your VASPdid key from Notabene. Once received, you can use it to integrate your workspace with Notabene.
{
"vasps": \[
{
"did": "did:ethr:0x44957e75d6ce4a5bf37aae117da86422c848f7c2",
"name": "Fireblocks",
"verificationStatus": "PENDING",
"addressLine1": "Tel Aviv",
"addressLine2": null,
"city": "Tel Aviv",
"country": "IL",
"emailDomains": "[\"fireblocks.com\"]",
"website": "<https://fireblocks.com">,
"logo": null,
"legalStructure": "CORPORATION",
"legalName": "Fireblocks Ltd",
"yearFounded": "2018",
"incorporationCountry": "IL",
"isRegulated": "NO",
"otherNames": null,
"identificationType": null,
"identificationCountry": null,
"businessNumber": null,
"regulatoryAuthorities": null,
"jurisdictions": "IL",
"street": null,
"number": null,
"unit": null,
"postCode": null,
"state": null,
"certificates": null,
"description": null,
"travelRule_OPENVASP": null,
"travelRule_SYGNA": null,
"travelRule_TRISA": null,
"travelRule_TRLIGHT": "active",
"travelRule_EMAIL": null,
"travelRule_TRP": null,
"travelRule_SHYFT": null,
"travelRule_USTRAVELRULEWG": null,
"createdAt": "2022-12-01T09:12:11.048Z",
"createdBy": "did:ethr:0x44957e75d6ce4a5bf37aae117da86422c848f7c2",
"updatedAt": "2023-04-20T21:13:46.696Z",
"updatedBy": null,
"lastSentDate": "2023-04-20T21:13:46.678Z",
"lastReceivedDate": "2023-04-18T18:36:40.331Z",
"documents": null,
"hasAdmin": true,
"isNotifiable": true,
"issuers": {
"verificationStatus": {
"issuerDid": "did:ethr:0x19b5ff8440019b635a86bbb632db854f2ea80423"
},
"emailDomains": {
"issuerDid": "did:ethr:0xf33cbc1a777bcfba6f9f66de276e8072d18fadae"
},
"travelRule_TRLIGHT": {
"issuerDid": "did:ethr:0xf33cbc1a777bcfba6f9f66de276e8072d18fadae"
},
"jurisdictions": {
"issuerDid": "did:ethr:0xf33cbc1a777bcfba6f9f66de276e8072d18fadae"
},
"country": {
"issuerDid": "did:ethr:0xf33cbc1a777bcfba6f9f66de276e8072d18fadae"
},
"city": {
"issuerDid": "did:ethr:0xf33cbc1a777bcfba6f9f66de276e8072d18fadae"
},
"addressLine1": {
"issuerDid": "did:ethr:0xf33cbc1a777bcfba6f9f66de276e8072d18fadae"
},
"isReg
Updated 27 days ago