Introduction
In this guide, we will see how to securly fund a third party service account, allowing the user to, for example, fund a credit / debit card, an account on a centralized exchange or some other cryptoassets custody service.
The main steps needed for the funding flow are:
- Get the user account,
- Get a unique transaction id,
- Create the operation transaction,
- Generate the payload and signature,
- Complete the exchange.
User Account
Get the user Account from which the transfer operation will be done.
You can get an account using either listAccount
or requestaccount
.
Transaction Id
The first step to start the exchange process is to call the startExchange
function with the appropriate exchangeType
. This will return a transaction ID that will be used to complete the exchange process.
const transactionId = await SDK.startExchange({
exchangeType: ExchangeType.FUND,
});
Transaction operation
Then, you will need to create the transaction associated to the operation you want to perform.
In the case of a fund operation, the transaction recipient
address is the address where you expect to receive the user funds.
// Example for a BTC transaction
const transaction: BitcoinTransaction = {
amount: new BigNumber(1), // Correspond to 1 satoshi or 0.00000001 BTC
recipient: "YOUR_BTC_ADDRESS",
family: FAMILIES.BITCOIN,
};
Payload and signature
The binaryPayload
is a protobuf message containing the transaction data. It is generated by you, the provider, and sent to the device through Ledger Live.
Your protobuf message should have the following structure:
message NewFundResponse {
string user_id = 1; // user ID
string account_name = 2; // funded account name ex: Card 1234
string in_currency = 3; // inCurrency
bytes in_amount = 4; // inAmount
string in_address = 5; // account
bytes device_transaction_id = 6; // nonce
}
Fields explanations
user_id
: A unique identifier of the user. Should be known by the user since it will be displayed to him on the device for validation purposes.
account_name
: The funded account name (for example: Card 1234
). Should be known by the user since it will be displayed to him on the device for validation purposes.
in_currency
: The currency ticker that the client wants to use to fund (example: BTC
). Will be displayed on the device for validation purposes.
in_amount
: The amount of in_currency
that the client wants to fund, in the lowest unit of the coin, encoded into a 16 bytes array in big-endian format. Will be displayed on the device for validation purposes.
in_address
: The blockchain address to which you expect to received the user funds.
device_transaction_id
: The transaction nonce generated on the device and returned by the startExchange
function, encoded in Base64 following RFC_4648.
Amounts (in_amount
) must be in the lowest unit of the coin, encoded into a 16 bytes array in big endian.
Examples:
- For 1 BTC:
- The smallest unit is a satoshi which is
10^-8
BTC.
- Multiply the amount by this smallest unit:
1 * 10^8 = 100000000
.
- Convert to hexadecimal:
100000000
in decimal is 0x5F5E100
in hexadecimal.
- Encode this value in a 16 bytes array in big endian:
[0x00, ..., 0x00, 0x05, 0xF5, 0xE1, 0x00]
.
- For 2 ETH:
- The smallest unit is a wei which is
10^-18
ETH.
- Multiply the amount by this smallest unit:
2 * 10^18 = 2000000000000000000
.
- Convert to hexadecimal:
2000000000000000000
in decimal is 0x1BC16D674EC80000
in hexadecimal.
- Encode this value in a 16 bytes array in big endian:
[0x00, ... 0x00, 0x1B, 0xC1, 0x6D, 0x67, 0x4E, 0xC8, 0x00, 0x00]
.
Payloads generation
How are binaryPayload
and it’s signature
generated?
Here is a diagram to explain this:

-
binaryPayload
: The transaction data’s parameters are assembled in a protobuf message. Then using the protobuf tools you can do a binary encoding of the protobuf to get a Byte Array. Finally, with Base64url encoding you get the binaryPayload
field.
-
signature
: Add “.” in front of the previously encoded binaryPayload
, hash it with sha256 and finally sign it with secp256r1 to get the signature
field.
Here is a code snippet to illustrate the binaryPayload and signature generation process:
/* binaryPayload generation */
// get a Byte Array (binary) representation of the protobuf data
const payload: Buffer = Buffer.from(protobufData);
// base64url encode the payload
const binaryPayload: Buffer = Buffer.from(base64url(payload));
/* signature generation */
// create the message to be signed from the binaryPayload
const message = Buffer.concat([Buffer.from('.'), binaryPayload])
// create a sha256 digest of the message
const digest: Buffer = Buffer.from(sha256.sha256.array(message));
// sign the message using your private key
const signature = Buffer.from(secp256r1.sign(digest, YOUR_PRIVATE_KEY).signature)
Complete the exchange
To complete the exchange (i.e: ask the user to verify the informations, sign the transaction and broadcast it to the network), the last step is to call the completeExchange
function, using all the information gathered or generated in the previous steps.
// Your unique ledger related provider id defined with the Ledger integration team
const provider = "YOUR_PROVIDER_ID";
const rawSignedTransaction = await SDK.completeExchange({
provider,
fromAccountId: account.id,
transaction,
binaryPayload,
signature,
feesStrategy: FeesLevel.Medium,
exchangeType: ExchangeType.FUND,
});
Working example
In order to better understand the process and test your own generated payload, feel free to have a look at our test app.