Fund | Developers

Fund

Estimated reading time: 6 minutes

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:
    1. The smallest unit is a satoshi which is 10^-8 BTC.
    2. Multiply the amount by this smallest unit: 1 * 10^8 = 100000000.
    3. Convert to hexadecimal: 100000000 in decimal is 0x5F5E100 in hexadecimal.
    4. Encode this value in a 16 bytes array in big endian: [0x00, ..., 0x00, 0x05, 0xF5, 0xE1, 0x00].
  • For 2 ETH:
    1. The smallest unit is a wei which is 10^-18 ETH.
    2. Multiply the amount by this smallest unit: 2 * 10^18 = 2000000000000000000.
    3. Convert to hexadecimal: 2000000000000000000 in decimal is 0x1BC16D674EC80000 in hexadecimal.
    4. 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:

Payload and Payload Signature generation diagram

  • 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.


Did you find this page helpful?


How would you improve this page for developers?



Getting Started
Theme Features
Customization