Coins & Decimals
Which coins are supported by Notabene?
When your customer is performing a withdrawal, it is necessary to provide the virtual asset type and the amount with the correct number of decimal points so that we can calculate the current FIAT value of the transfer. Different jurisdictions have different thresholds for when an accompanying travel rule message needs to be sent.
- How many decimals should I use in
transactionAmount
?- You can check how many decimals a coin needs by calling the asset service API.
- What about coins that you do not support?
- You should compare the coins you currently support against ours and see if there is any we are missing.
- To request new or missing coins, see "Coins not supported" below.
- Do I need to check your decimals every time?
- The content of the asset service seldom changes, so you can cache a copy of it once every day.
- You do not need to store all the fields, just the "notabene_..."-fields.
Hardcoding
We recommend that you do not hardcode the decimals from the Notabene asset service as these might change if they were reported incorrectly. A daily sync of that you store locally should be fine to base your decimals on.
Checking travel rule thresholds
When we receive the initial request through our widget or the validation API, we get the FIAT value in your local currency IF it is supported by Coingecko. This is then checked against the threshold in your jurisdiction:
"thresholds": [
{
"conditions": [
{
"fields": [
{
"field": "originatorName"
},
.....
],
"conditions": [
{
"fields": [
.....
],
"conditions": [],
"type": "OR"
}
],
"type": "AND"
}
],
"threshold": 1500
},
{
"conditions": [
{
"fields": [
{
......
}
],
"type": "AND"
}
],
"threshold": 0.000001
},....
"countryCode": "SG",
"currency": "SGD"
If it is above, travel rule information needs to be collected.
The final check happens when the transaction is created where the FIAT value of the virtual asset is reported in USD (chargedQuantity), and the currency of the the originator and beneficiary:
Decimals
When creating or validating a travel rule transfer, our APIs do not support commas or dots and therefore all amounts have to be expressed as integers of the smallest denominator of a coin. For example, if you want to create a transaction of 1 ETH in the payload of txCreate, you will have to express it in WEI by putting 1 followed by 18 zeros to get the correct amount.
Examples
1 ETH = 1000000000000000000 WEI
1 BTC = 100000000 SATOSHI
We recommend using a library such as ethers or viem to convert floats to int using the decimal places you find on our asset API below.
Polymorphic asset field
We support three different ways of providing the transactionAsset in txCreate:
Format 1 - using Notabene coin standard: "asset": "USDC-SOL"
{
"transactionAsset": "USDC-SOL",
"transactionAmount": "1700000000000000000",
"originatorVASPdid": "{{vaspDID}}",
"beneficiaryVASPdid": "{{vaspDIDee}}",
"transactionBlockchainInfo": {
"origin": "{{$randomBitcoin}}",
"destination": "{{$randomBitcoin}}"
},
Format 2 - using Coingeko API ID + the network: "asset": { "coingeckoId": "usd-coin", "network": "solana" }
{
"transactionAsset":
{
"coingeckoId": "usd-coin",
"network": "solana"
},
"transactionAmount": "1700000000000000000",
"originatorVASPdid": "{{vaspDID}}",
"travelRuleBehavior": false,
"beneficiaryVASPdid": "{{vaspDIDee}}",
"beneficiaryVASPname":"",
"transactionBlockchainInfo": {
"origin": "{{$randomBitcoin}}",
"destination": "{{$randomBitcoin}}"
},
Format 3 - using the CAIP19 standard: "asset": { "caip19": "solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" }
{
"transactionAsset":
{
"caip19": "solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
},
"transactionAmount": "1700000000000000000",
"originatorVASPdid": "{{vaspDID}}",
"travelRuleBehavior": false,
"beneficiaryVASPdid": "{{vaspDIDee}}",
"beneficiaryVASPname":"",
"transactionBlockchainInfo": {
"origin": "{{$randomBitcoin}}",
"destination": "{{$randomBitcoin}}"
},
Coins with memo or tag
A destination tag is a special identifier used in XRP transactions. In Stellar, the destination tag is called memo. Many VASPs have one XRP and XLM address for all customers and the only way to identify who should receive the payment is this identifier which is unique for every user.
To be able to include this information in the travel rule message you send, we recommend that the memo/tag is added after the address separated by :
Address: rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY
Tag: 12345
so your destination address will look as:
"destination": "rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY:12345"
and the body of a request might look like this:
{
"transactionAsset": "xrp",
"transactionAmount": "72000000000",
"originatorVASPdid": "{{vaspDID}}",
"beneficiaryVASPdid": "did:ethr:0x270d4f239359471f1d1c80781a53cf8105f7d08f",
"transactionBlockchainInfo": {
"txHash": "",
"origin": "",
"destination": "rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY:12345"
},
"beneficiary": {
"beneficiaryPersons": [
{
"naturalPerson": {
"name": [
{
"nameIdentifier": [
{
"primaryIdentifier": "Bruce",
"secondaryIdentifier": "Wayne",
"nameIdentifierType": "LEGL"
}
]
}
]
}
}
],
"accountNumber": [
"rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY:12345"
]
}
}
Some of the virtual assets that require a memo or tag now also have an integrated address format where everything is part of one string. If you are storing the old format in your internal address book and your counterparty is using the new, you need to decode it before looking it up to confirm that it's yours:
XRP X-address encode/decode
The new 𝗫-address format aims to replace the use of a separate destination tag when sending funds to a multi-user wallet the XRP ledger, like those of exchanges and custodial services.
If you are using the integrated address instead of the separated version, you use it as you would any other wallet address:
{
"transactionAsset": "xrp",
"transactionAmount": "72000000000",
"originatorVASPdid": "{{vaspDID}}",
"beneficiaryVASPdid": "did:ethr:0x270d4f239359471f1d1c80781a53cf8105f7d08f",
"transactionBlockchainInfo": {
"txHash": "",
"origin": "",
"destination": "XV5sbjUmgPpvXv4ixFWZ5ptAYZ6PD28Sq49uo34VyjnmK5H"
},
If you are not able to separate the tag/memo into separate parts, you can pass the information in the "destination" with a colon like this: rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY:12345
Updated 12 days ago