Sell | Developers

Sell

Estimated reading time: 7 minutes

Introduction

In this guide, we will see how to securly sell cryptoassets to a third party service in exchange for fiat currency.

The main steps needed for the selling 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.SELL,
});

Transaction operation

Then, you will need to create the transaction associated to the operation you want to perform.

In the case of a sell 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:

// (coefficient) * 10^(- exponent)
message UDecimal {
    bytes  coefficient = 1;
    uint32 exponent = 2;
}
message NewSellResponse {
    string   trader_email = 1;           // traderEmail
    string   in_currency = 2;            // inCurrency
    bytes    in_amount = 3;              // inAmount
    string   in_address = 4;             // account
    string   out_currency = 5;           // outCurrency
    UDecimal out_amount = 6;             // outAmount
    bytes    device_transaction_id = 7;  // nonce
}

Fields explanations

  • trader_email: An email address used to identify of the user on the third party service provider. Should be known by the user since it will be displayed on the device for validation purposes.
  • in_currency: The currency ticker that the client wants to sell (example: BTC). Will be displayed on the device for validation purposes.
  • in_amount: The amount of in_currency that the client wants to sell, 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 cryptoassets.
  • out_currency: The fiat currency ticker that the client wants to sell his assets against (example: USD). Will be displayed on the device for validation purposes.
  • out_amount: The amount of out_currency that the client is expected to receive, as a UDecimal protobuf message (cf. definition above). Will be displayed on the device for validation purposes.
  • 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 and out_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].

The coefficient part is, like the in_amount above, the hexadecimal amount encoded into a 16 bytes array in big-endian format.
For example, to represent 10.25, which is 1025 * 10^(-2), the coefficient would be 1025 (encoded into a 16 bytes array in big-endian format) and the exponent would be 2.

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.SELL,
});

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?



Broadcast
Fund
Getting Started
Theme Features
Customization