Skip to content

GET_PRINTABLE_AMOUNT

This handle is called when the Exchange application wants to format for display an amount + ticker of a currency known by this application

If the formatting succeeds, result is set to the formatted string. Otherwise it is set to '\0'.


C SDK API

ledger-secure-sdk/lib_standard_app/swap_entrypoints.h

// This callback must be defined in Coin Applications that use the SWAP feature.
void swap_handle_get_printable_amount(get_printable_amount_parameters_t *params);

ledger-secure-sdk/lib_standard_app/swap_lib_calls.h

typedef struct get_printable_amount_parameters_s {
    // INPUTS //
    // Additional data when dealing with tokens
    // Content is coin application specific
    uint8_t *coin_configuration;
    uint8_t  coin_configuration_length;

    // Raw amount in big number format
    uint8_t *amount;
    uint8_t  amount_length;

    // Set to true if the amount to format is the fee of the swap.
    bool is_fee;

    // OUTPUT //
    // Set to the formatted string if the formatting succeeds. 0 otherwise.
    char printable_amount[MAX_PRINTABLE_AMOUNT_SIZE];
} get_printable_amount_parameters_t;

Example of handle implementation in C Boilerplate

app-boilerplate/src/swap/handle_get_printable_amount.c

#ifdef HAVE_SWAP
#include "swap.h"
#include "buffer.h"
#include "constants.h"
#include "format.h"
#include "os.h"
#include "tokens.h"

#include <string.h>
#include <stdio.h>

/* Format printable amount including the ticker from specified parameters.
 *
 * Must set empty printable_amount on error, printable amount otherwise
 * get_printable_amount_parameters_t is defined in C SDK as:
 * struct {
 *   // IN
 *   uint8_t *coin_configuration;
 *   uint8_t  coin_configuration_length;
 *   uint8_t *amount;
 *   uint8_t  amount_length;
 *   bool     is_fee;
 *   // OUT
 *   char printable_amount[MAX_PRINTABLE_AMOUNT_SIZE];
 * } get_printable_amount_parameters_t;
 */
void swap_handle_get_printable_amount(get_printable_amount_parameters_t* params) {
    PRINTF("Inside swap_handle_get_printable_amount\n");

    PRINTF("Amount: %.*H\n", params->amount_length, params->amount);

    explicit_bzero(params->printable_amount, sizeof(params->printable_amount));

    /// Convert params->amount into uint64_t
    uint64_t raw_amount = 0;
    if (!swap_str_to_u64(params->amount, params->amount_length, &raw_amount)) {
        PRINTF("Amount is too big\n");
        return;
    }

    uint8_t decimals = EXPONENT_SMALLEST_UNIT;
    char ticker[MAX_TICKER_SIZE] = "BOL";
    if (params->is_fee || params->coin_configuration == NULL) {
        PRINTF("Defaulting to native BOL amount\n");
    } else {
        if (!swap_parse_config(params->coin_configuration,
                               params->coin_configuration_length,
                               ticker,
                               sizeof(ticker),
                               &decimals)) {
            PRINTF("Fail to parse coin_configuration\n");
            return;
        }
    }
    char formatted_amount[30] = {0};
    format_fpu64(formatted_amount, sizeof(formatted_amount), raw_amount, decimals);
    PRINTF("Formatted amount: %s\n", formatted_amount);
    snprintf(params->printable_amount,
             sizeof(params->printable_amount),
             "%.*s %s",
             (int) strlen(formatted_amount),
             formatted_amount,
             ticker);
}
#endif  // HAVE_SWAP


Rust SDK API

ledger-device-rust-sdk/ledger_device_sdk/src/libcall/swap.rs

/// Retrieves parameters for the `SwapGetPrintableAmount` command.
///
/// This function parses the raw arguments provided by `os_lib_call` and populates
/// a `PrintableAmountParams` struct.
///
/// # Arguments
///
/// * `arg0` - The argument passed to the main entry point by `os_lib_call`.
pub fn get_printable_amount_params<
    const COIN_CONFIG_BUF_SIZE: usize,
    const ADDRESS_BUF_SIZE: usize,
    const ADDRESS_EXTRA_ID_BUF_SIZE: usize,
>(
    arg0: u32,
) -> PrintableAmountParams<COIN_CONFIG_BUF_SIZE, ADDRESS_BUF_SIZE, ADDRESS_EXTRA_ID_BUF_SIZE> {



/// Parameters for the `SwapGetPrintableAmount` command.
///
/// This struct holds an amount to be formatted for display.
pub struct PrintableAmountParams<
    const COIN_CONFIG_BUF_SIZE: usize = DEFAULT_COIN_CONFIG_BUF_SIZE,
    // Unused const generic parameter here, to allow type inference in `swap_return` fn
    const ADDRESS_BUF_SIZE: usize = DEFAULT_ADDRESS_BUF_SIZE,
    const ADDRESS_EXTRA_ID_BUF_SIZE: usize = DEFAULT_ADDRESS_EXTRA_ID_BUF_SIZE,
> {
    /// Coin configuration
    pub coin_config: [u8; COIN_CONFIG_BUF_SIZE],
    /// Length of the coin configuration
    pub coin_config_len: usize,
    /// Amount to be formatted (big-endian bytes, right-aligned in 16-byte buffer)
    pub amount: [u8; AMOUNT_BUF_SIZE],
    /// Actual length of the amount data
    pub amount_len: usize,
    /// Pointer to the output string buffer (internal use)
    pub amount_str: *mut i8,
    /// Whether this is a fee amount (true) or the main amount (false)
    pub is_fee: bool,
}


Example of handle implementation in Rust Boilerplate

app-boilerplate-rust/src/swap.rs

/// Format an amount for display in the Exchange app UI.
///
/// The Exchange app calls this to get human-readable strings for amounts and fees.
/// This is used during swap transactions to show the user what amounts they're
/// exchanging.
///
/// # Amount Format
///
/// The amount is provided as big-endian bytes in `params.amount`:
/// - Right-aligned in a 16-byte buffer (AMOUNT_BUF_SIZE)
/// - Actual length is in `params.amount_len`
/// - Padded to 32 bytes (uint256) for SDK formatting helpers
///
/// # Arguments
///
/// * `params` - Contains:
///   - `amount`: Big-endian encoded amount bytes (right-aligned in 16-byte buffer)
///   - `amount_len`: Actual number of significant bytes
///   - `coin_config`: Coin configuration (unused - hardcoded to CRAB in this template)
///   - `is_fee`: Whether this is a fee amount
///
/// # Returns
///
/// Stack-allocated string formatted as "CRAB {value}" (e.g., "CRAB 1.5")
///
/// # Memory Safety
///
/// Uses `ArrayString` (stack-allocated) to avoid heap allocation, as this function
/// runs under BSS memory restrictions.
///
/// # Production Notes
///
/// For a production app, you should:
/// - Parse `coin_config` to extract ticker and decimals dynamically
/// - Handle different coin types
/// - Support both u64 and u128 amounts
fn get_printable_amount(params: &PrintableAmountParams) -> ArrayString<40> {
    // Convert amount from 16-byte buffer to 32-byte buffer (uint256 format)
    // The amount is right-aligned in params.amount, we need to copy it to a
    // 32-byte buffer that's also right-aligned (big-endian)
    let mut amount_u256: [u8; 32] = [0; 32];
    let src_start = params.amount.len() - params.amount_len;
    let dst_start = 32 - params.amount_len;
    amount_u256[dst_start..].copy_from_slice(&params.amount[src_start..]);

    debug_print("Amount bytes (u256): ");
    debug_hex("", &amount_u256);

    // CRAB uses 9 decimals (similar to SUI, which also uses 9 decimals)
    // For production: parse decimals from params.coin_config
    const CRAB_DECIMALS: usize = 9;
    const CRAB_TICKER: &str = "CRAB";

    // Use SDK helper to format amount with decimals
    let amount_str = uint256_to_float(&amount_u256, CRAB_DECIMALS);

    // Format as "CRAB {value}" using stack-allocated ArrayString
    let mut printable = ArrayString::<40>::new();
    let _ = write!(&mut printable, "{} {}", CRAB_TICKER, amount_str.as_str());

    debug_print("Formatted amount: ");
    debug_print(printable.as_str());
    debug_print("\n");

    printable
}