Skip to content

Copy Transaction Parameters

C SDK API

ledger-secure-sdk/lib_standard_app/swap_entrypoints.h

/* This handle is called when the user has validated on screen the transaction
 * proposal sent by the partner and started the FROM Coin application to sign
 * the payment transaction.
 *
 * This handler needs to save in the heap the details of what has been validated
 * in Exchange. These elements will be checked against the received transaction
 * upon its reception from the Ledger Live.
 *
 * return false on error, true otherwise
 */
bool swap_copy_transaction_parameters(create_transaction_parameters_t *sign_transaction_params);

ledger-secure-sdk/lib_standard_app/swap_lib_calls.h

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

    // The amount validated on the screen by the user
    uint8_t *amount;
    uint8_t  amount_length;

    // The fees amount validated on the screen by the user
    uint8_t *fee_amount;
    uint8_t  fee_amount_length;

    // The partner address that will receive the funds
    char *destination_address;
    char *destination_address_extra_id;

    // OUTPUT //
    // /!\ This parameter is handled by the lib_standard_app, DO NOT interact
    // with it in the Coin application
    //
    // After reception and signature or refusal of the transaction, the Coin
    // application will return to Exchange. This boolean is used to inform the
    // Exchange application of the result.
    // Set to 1 if the transaction was successfully signed, 0 otherwise.
    uint8_t result;
} create_transaction_parameters_t;


Example of handle implementation in C Boilerplate

app-boilerplate/src/swap/handle_swap_sign_transaction.c

typedef struct swap_validated_s {
    bool initialized;
    uint64_t amount;
    uint64_t fee;
    char recipient[ADDRESS_LEN * 2 + 1];
    uint8_t decimals;
    char ticker[MAX_TICKER_SIZE];
} swap_validated_t;

/* Global variable used to store swap validation status */
static swap_validated_t G_swap_validated;

static bool is_token_swap(void) {
    return (strcmp(G_swap_validated.ticker, "BOL") != 0 ||
            G_swap_validated.decimals != EXPONENT_SMALLEST_UNIT);
}

bool swap_copy_transaction_parameters(create_transaction_parameters_t* params) {
    PRINTF("Inside swap_copy_transaction_parameters\n");

    if (params->destination_address == NULL) {
        PRINTF("Destination address expected\n");
        return false;
    }

    if (strlen(params->destination_address) != (ADDRESS_LEN * 2)) {
        PRINTF("Destination address wrong length\n");
        return false;
    }

    if (params->amount == NULL) {
        PRINTF("Amount expected\n");
        return false;
    }

    // first copy parameters to stack, and then to global data.
    // We need this "trick" as the input data position can overlap with app globals
    // and also because we want to memset the whole bss segment as it is not done
    // when an app is called as a lib.
    // This is necessary as many part of the code expect bss variables to
    // initialized at 0.
    swap_validated_t swap_validated;
    explicit_bzero(&swap_validated, sizeof(swap_validated));

    // Save recipient as an uppercase string
    for (int i = 0; i < ADDRESS_LEN * 2; i++) {
        if (params->destination_address[i] >= 'a' && params->destination_address[i] <= 'z') {
            swap_validated.recipient[i] = params->destination_address[i] - 'a' + 'A';
        } else {
            swap_validated.recipient[i] = params->destination_address[i];
        }
    }
    PRINTF("Validated recipient: %s\n", swap_validated.recipient);

    // Parse config and save decimals and ticker
    // If there is no coin_configuration, consider that we are doing a SOL swap
    if (params->coin_configuration == NULL) {
        memcpy(swap_validated.ticker, "BOL", sizeof("BOL"));
        swap_validated.decimals = EXPONENT_SMALLEST_UNIT;
    } else {
        if (!swap_parse_config(params->coin_configuration,
                               params->coin_configuration_length,
                               swap_validated.ticker,
                               sizeof(swap_validated.ticker),
                               &swap_validated.decimals)) {
            PRINTF("Fail to parse coin_configuration\n");
            return false;
        }
    }
    PRINTF("Validated ticker: %s, decimals: %d\n", swap_validated.ticker, swap_validated.decimals);

    // Save amount
    if (!swap_str_to_u64(params->amount, params->amount_length, &swap_validated.amount)) {
        PRINTF("Failed to convert amount to uint64_t\n");
        return false;
    }
    // Can't print u64
    PRINTF("Validated amount: %.*H\n", sizeof(swap_validated.amount), &swap_validated.amount);

    // Save fee
    if (!swap_str_to_u64(params->fee_amount, params->fee_amount_length, &swap_validated.fee)) {
        PRINTF("Failed to convert fee to uint64_t\n");
        return false;
    }
    PRINTF("Validated fee: %.*H\n", sizeof(swap_validated.fee), &swap_validated.fee);

    swap_validated.initialized = true;

    // Full reset the global variables
    os_explicit_zero_BSS_segment();

    // Commit from stack to global data, params becomes tainted but we won't access it anymore
    memcpy(&G_swap_validated, &swap_validated, sizeof(swap_validated));

    return true;
}


Rust SDK API

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

/// Retrieves parameters for the `SwapSignTransaction` command.
///
/// This function parses the raw arguments provided by `os_lib_call` and populates
/// a `CreateTxParams` struct.
///
/// # Important Side Effect
///
/// This function calls `c_reset_bss()` and `c_boot_std()`. This resets the BSS memory
/// (making heap allocation safe again) and completes the application boot process.
/// This is necessary because the signing phase allows for standard application behavior,
/// unlike the previous check/format phases.
///
/// # Arguments
///
/// * `arg0` - The argument passed to the main entry point by `os_lib_call`.
pub fn sign_tx_params<
    const COIN_CONFIG_BUF_SIZE: usize,
    const ADDRESS_BUF_SIZE: usize,
    const ADDRESS_EXTRA_ID_BUF_SIZE: usize,
>(
    arg0: u32,
) -> CreateTxParams<COIN_CONFIG_BUF_SIZE, ADDRESS_BUF_SIZE, ADDRESS_EXTRA_ID_BUF_SIZE> {



/// Parameters for the `SwapSignTransaction` command.
///
/// This struct holds the transaction details provided by the Exchange app.
/// The coin app must validate the transaction against these parameters before signing.
pub struct CreateTxParams<
    const COIN_CONFIG_BUF_SIZE: usize = DEFAULT_COIN_CONFIG_BUF_SIZE,
    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 sent (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,
    /// Fee amount (big-endian bytes, right-aligned in 16-byte buffer)
    pub fee_amount: [u8; AMOUNT_BUF_SIZE],
    /// Actual length of the fee amount data
    pub fee_amount_len: usize,
    /// Destination address (as a string)
    pub dest_address: [u8; ADDRESS_BUF_SIZE],
    /// Length of the destination address
    pub dest_address_len: usize,
    /// Extra ID for the destination address (e.g., memo, tag)
    pub dest_address_extra_id: [u8; ADDRESS_EXTRA_ID_BUF_SIZE],
    /// Length of the extra ID
    pub dest_address_extra_id_len: usize,
    /// Pointer to the result buffer (internal use)
    pub result: *mut u8,
}


Implementation in Rust Boilerplate

Unlike the C SDK where the Coin application defines the copy parameter callback, the Rust SDK directly exposes this function that returns a usable CreateTxParams struct.

As such its usage is a straightforward oneliner in the swap_main function.