cryptnox-pos 1.0.0
Standalone USDC payment terminal firmware (ESP32 + Cryptnox smart card)
Loading...
Searching...
No Matches
pos_app.h File Reference

Private to the application files (main, boot, card_io, pay, pay_evm, pay_tron): the state they share and the calls between them. More...

#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <strings.h>
#include <stdlib.h>
#include <inttypes.h>
#include <atomic>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_err.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_timer.h"
#include "nvs_flash.h"
#include "CryptnoxWallet.h"
#include "CW_Utils.h"
#include "Pn532NfcTransport.h"
#include "ESP32Logger.h"
#include "ESP32Platform.h"
#include "esp32_crypto_provider.h"
#include "CW_Tron.h"
#include "settings.h"
#include "assets.h"
#include "provision.h"
#include "ota.h"
#include "ota_version.h"
#include "wdt.h"
#include "pn532.h"
#include "keccak256.h"
#include "eth_addr.h"
#include "eth_sig.h"
#include "card_status.h"
#include "hardening.h"
#include "eth_rlp.h"
#include "eth_rpc.h"
#include "rpc_error.h"
#include "tron_rpc.h"
#include "tron_tx.h"
#include "net.h"
#include "ui.h"
#include "money.h"
#include "config_defaults.h"
Include dependency graph for pos_app.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

struct  token_t
 A token's contract, dual-stored. More...
struct  token_cfg_t
struct  sale_fee_t
struct  inflight_t
struct  WipeGuard
 Scrubs a buffer with CW_Utils::secure_wipe when it leaves scope. More...
struct  ui_msg_t
struct  pos_hw_t
 The card stack pos_boot() brought up; lives for the program. More...

Enumerations

enum  bcast_t { BCAST_FAILED = 0 , BCAST_SENT , BCAST_UNKNOWN }

Functions

static bool chain_is_tron (void)
 true when the operator has switched the terminal to Tron.
static bool chain_is_polygon (void)
 true when the terminal is charging on Polygon rather than Ethereum.
static bool chain_is_native_evm (void)
 true when charging in the network's own coin (ETH / POL), not a token.
token_t * active_token (pos_chain_t chain=settings_get_chain())
 The selection's token, or NULL for a native coin.
void token_load (const token_cfg_t *cfg, CW_CryptoProvider &crypto)
 Load one token at boot: the operator's contract if one is set and parses, config.h otherwise.
bool token_decimals_ok (pos_chain_t chain, char *err, size_t err_max)
 Before the first sale in an operator-set token, read its decimals() and refuse anything but 6.
const pos_addr_t * active_dest (void)
 The reconciled recipient for the chain currently selected.
void sale_fee_text (char *out, size_t n)
 The most network fee the customer's card can be charged on top of the sale, for the confirm screen. "" where there is no cap to state: a native TRX transfer burns bandwidth, not a fee limit.
void inflight_persist (const inflight_t *fl)
 Write the sale to NVS just before it leaves the terminal.
uint64_t wall_ms (void)
 Unix time in ms, 0 while the clock is unset.
void settle_inflight (void)
 Poll the in-flight sale for up to 120 s and show what the chain says.
bcast_t pay_sign_and_broadcast (CryptnoxWallet &wallet, Pn532NfcTransport &transport, CW_CryptoProvider &crypto, const pos_amount_t *amount, const char *pin, size_t pin_chars, inflight_t *fl, char *err_out, size_t err_max)
 Sign and broadcast the reconciled sale on whichever family is selected. The family is read once, here.
void eth_rpc_select_for (bool polygon)
 Point eth_rpc at the endpoint for the selected EVM network.
void eth_rpc_select (void)
void evm_fees_wei (bool polygon, uint64_t *max_fee, uint64_t *prio_fee)
 The EIP-1559 fees one EVM sale will offer, in wei per gas.
bool evm_balance_ok (const pos_amount_t *amount, char *err, size_t err_max)
 Refuse an EVM sale the tapped card cannot fund, before it signs.
bcast_t sign_and_broadcast (CryptnoxWallet &wallet, Pn532NfcTransport &transport, const pos_amount_t *amount, const pos_addr_t *to, const char *pin, size_t pin_chars, inflight_t *fl, char *err_out, size_t err_max)
 Sign an EVM token or coin transfer on the card and broadcast it.
void tron_addr_to_hex (const uint8_t *addr21, char *out, size_t n)
 Format a raw 21-byte Tron address as the "41..." hex the API wants.
bcast_t sign_and_broadcast_tron (CryptnoxWallet &wallet, Pn532NfcTransport &transport, CW_CryptoProvider &crypto, const pos_amount_t *amount, const pos_addr_t *to, const token_t *token, const char *pin, size_t pin_chars, inflight_t *fl, char *err_out, size_t err_max)
 Sign a Tron transfer on the card and broadcast it — TRX or TRC-20.
eth_rpc_receipt_result_t tron_receipt_as_eth (tron_receipt_t r)
 Map a Tron receipt onto the Ethereum verdicts the UI flow uses.
const char * pin_fail_text (Pn532NfcTransport &transport, const char *wrong)
 Why verifyPin said no: the PIN, or the card leaving the field.
bool card_connect (CryptnoxWallet &wallet, Pn532NfcTransport &transport, CW_SecureSession &session, bool setup=false)
 Wait for a card and open a secure channel, cancellable from the UI.
bool card_sign (CryptnoxWallet &wallet, CW_SecureSession &session, const uint8_t *hash, uint8_t hash_len, const uint8_t *path, uint8_t path_len, const char *pin, size_t pin_chars, uint8_t rs_out[64], char *err_out, size_t err_max)
 Have the card sign hash, then close the session.
bool card_read_payouts (CryptnoxWallet &wallet, Pn532NfcTransport &transport, CW_CryptoProvider &crypto, const char *pin, size_t pin_chars, char *eth_out, size_t eth_n, char *tron_out, size_t tron_n, char *err, size_t err_n)
 Read the card's payout addresses and put them through the panel.
void ui_event_dispatch (ui_event_t event, uint64_t payload)
 UI-task callback: forward a touch event to the main task queue.
pos_hw_t pos_boot (void)
 Everything before the main loop. Returns the card stack, which lives for the life of the program.
void boot_fault (ui_boot_err_t kind, const char *detail)
 Show a startup fault, then restart. Does not return.
void wifi_keep_or_drop (bool keep)
 Persist or discard the pending picker credentials, then scrub them.
void wait_for_ui_event (ui_event_t want)
 Block until the UI reports want, discarding anything else.
bool wifi_try_saved (void)
 Try the saved credentials, staying on the splash while it happens.
bool wifi_picker (const char *note)
 Run the panel network picker (scan → list → keyboard → connect) until connected.
bool run_wizard (CryptnoxWallet &wallet, Pn532NfcTransport &transport, CW_CryptoProvider &crypto, bool wifi_only)
 Run the browser wizard until the operator presses Finish.
bool sync_time (void)
 Block on an SNTP sync so TLS certificate validity-period checks run against real time instead of the 1970 epoch.

Variables

static const char *const TAG = "cryptnox_pos"
const uint8_t ETH_DERIVE_PATH [20]
char s_payout_eth [SETTINGS_PAYOUT_MAX]
char s_payout_tron [SETTINGS_PAYOUT_MAX]
token_t s_token [POS_CHAIN__COUNT]
const token_cfg_t TOKEN_CFG []
const size_t TOKEN_CFG_COUNT
pos_addr_t s_dest
pos_addr_t s_tron_dest
bool s_payout_bad [2]
sale_fee_t s_sale_fee
inflight_t s_inflight
const char * s_card_fault
QueueHandle_t s_ui_queue
std::atomic< bool > s_user_cancelled
const char *const NOTE_JOIN_FAILED
const char *const NOTE_NO_TIME

Detailed Description

Private to the application files (main, boot, card_io, pay, pay_evm, pay_tron): the state they share and the calls between them.

Definition in file pos_app.h.

Enumeration Type Documentation

◆ bcast_t

enum bcast_t
Enumerator
BCAST_FAILED 

Refused before or at broadcast: nothing was sent.

BCAST_SENT 

The node took it.

BCAST_UNKNOWN 

No usable answer: it may or may not be out there.

Definition at line 148 of file pos_app.h.

Function Documentation

◆ active_dest()

const pos_addr_t * active_dest ( void )

The reconciled recipient for the chain currently selected.

Definition at line 188 of file pay.cpp.

References chain_is_tron(), s_dest, and s_tron_dest.

Referenced by app_main(), and settle_inflight().

◆ active_token()

token_t * active_token ( pos_chain_t chain = settings_get_chain())

The selection's token, or NULL for a native coin.

Definition at line 45 of file pay.cpp.

References pos_asset_t::native, pos_asset_of(), POS_CHAIN__COUNT, and s_token.

Referenced by app_main(), evm_balance_ok(), pay_sign_and_broadcast(), sale_fee_text(), sign_and_broadcast(), token_decimals_ok(), and ui_refresh_addresses_for().

◆ boot_fault()

void boot_fault ( ui_boot_err_t kind,
const char * detail )

Show a startup fault, then restart. Does not return.

Most boot faults (a reader that missed its first I2C wake-up, a card stack that did not come up) clear on the next boot, and an unattended terminal must not stay stopped. On an unconfirmed update (ota_mark_valid runs after the wallet) the restart rolls back to the image that worked. A persistent fault keeps showing, 30 s at a time.

Definition at line 223 of file boot.cpp.

References BOOT_FAULT_RESTART_S, TAG, and ui_show_boot_error().

Referenced by pos_boot(), and resolve_evm_payout().

◆ card_connect()

bool card_connect ( CryptnoxWallet & wallet,
Pn532NfcTransport & transport,
CW_SecureSession & session,
bool setup )

Wait for a card and open a secure channel, cancellable from the UI.

Manual connect loop with cancel checks between PN532 polls, so a Cancel aborts within one PN532 timeout. Drives the "Tap your card" / "Processing" screens.

Parameters
[in]walletInitialised wallet instance.
[in]transportPN532 transport, polled directly.
[out]sessionOpen secure session on success.
[in]setuptrue when reading payout addresses rather than paying; shows the card-wait screen instead of the transaction one.
Returns
true with session open; false on user cancel, after 60 s, or on a card that is not set up — the three are told apart by s_user_cancelled and s_card_fault.

Definition at line 79 of file card_io.cpp.

References card_fault(), s_card_fault, s_user_cancelled, ui_show_card_wait(), ui_show_tx_status(), UI_TX_STATE_PLACE_CARD, UI_TX_STATE_PROCESSING, and wdt_feed().

Referenced by card_read_payouts(), sign_and_broadcast(), and sign_and_broadcast_tron().

◆ card_read_payouts()

bool card_read_payouts ( CryptnoxWallet & wallet,
Pn532NfcTransport & transport,
CW_CryptoProvider & crypto,
const char * pin,
size_t pin_chars,
char * eth_out,
size_t eth_n,
char * tron_out,
size_t tron_n,
char * err,
size_t err_n )

Read the card's payout addresses and put them through the panel.

The card will not export a public key without a verified PIN, so this needs the card PIN exactly as signing does — the caller collects it on the keypad first.

One tap yields both addresses (Ethereum m/44'/60'/0'/0/0, Tron m/44'/195'/0'/0/0), so a terminal is not left half configured, offering one network's payments to the compiled-in address.

Neither address is stored here. Both are proposed, through the same accept-on-the-panel handshake a browser submission goes through, because "the card said so" is not the same claim as "the operator checked it" — a card presented by a customer would otherwise redirect the takings.

Parameters
[out]eth_outEIP-55 "0x..." address, or "" if it could not be read.
[in]eth_nCapacity of eth_out.
[out]tron_outbase58 "T..." address, or "".
[in]tron_nCapacity of tron_out.
[out]errShort reason for the panel when nothing could be read.
[in]err_nCapacity of err.
Returns
true if at least one address was read.

Definition at line 233 of file card_io.cpp.

References card_connect(), eth_addr_format(), ETH_DERIVE_PATH, keccak256(), pin_fail_text(), s_card_fault, s_user_cancelled, and TAG.

Referenced by app_main(), and run_wizard().

◆ card_sign()

bool card_sign ( CryptnoxWallet & wallet,
CW_SecureSession & session,
const uint8_t * hash,
uint8_t hash_len,
const uint8_t * path,
uint8_t path_len,
const char * pin,
size_t pin_chars,
uint8_t rs_out[64],
char * err_out,
size_t err_max )

Have the card sign hash, then close the session.

Shared tail of both payment paths: build the request, copy the PIN in as late as possible, sign, scrub the PIN, close the session, map the status byte to an operator-readable message. One copy of the PIN handling and wipe.

The caller reconciles its amount and addresses immediately before calling this, which makes that check the last thing before an irreversible signature. It stays with the caller because the anomaly label and values differ per path.

The session is disconnected on every exit — including the failures — so no caller can leave a card held open.

Parameters
[in]walletInitialised wallet instance.
[in]sessionOpen secure session; disconnected before returning.
[in]hashDigest to sign (the keccak of the RLP, or a Tron txID).
[in]hash_lenLength of hash.
[in]pathBIP-32 derivation path blob.
[in]path_lenLength of path.
[in]pinOperator-entered card PIN; the caller still owns and scrubs its own copy.
[in]pin_charsNumber of PIN characters in pin.
[out]rs_out64 bytes: r || s. Wiped by the caller (WipeGuard).
[out]err_outShort UI-facing error message on failure.
[in]err_maxCapacity of err_out.
Returns
true when rs_out holds a signature.

Definition at line 160 of file card_io.cpp.

Referenced by sign_and_broadcast(), and sign_and_broadcast_tron().

◆ chain_is_native_evm()

bool chain_is_native_evm ( void )
inlinestatic

true when charging in the network's own coin (ETH / POL), not a token.

Definition at line 84 of file pos_app.h.

References pos_chain_is_native_evm(), and settings_get_chain().

Referenced by evm_balance_ok(), sale_fee_text(), and sign_and_broadcast().

◆ chain_is_polygon()

bool chain_is_polygon ( void )
inlinestatic

true when the terminal is charging on Polygon rather than Ethereum.

Definition at line 79 of file pos_app.h.

References pos_chain_is_polygon(), and settings_get_chain().

Referenced by app_main(), eth_rpc_select(), evm_balance_ok(), sale_fee_text(), and sign_and_broadcast().

◆ chain_is_tron()

bool chain_is_tron ( void )
inlinestatic

true when the operator has switched the terminal to Tron.

Definition at line 74 of file pos_app.h.

References pos_chain_is_tron(), and settings_get_chain().

Referenced by active_dest(), app_main(), pay_sign_and_broadcast(), pos_boot(), and sale_fee_text().

◆ eth_rpc_select()

void eth_rpc_select ( void )

Definition at line 51 of file pay_evm.cpp.

References chain_is_polygon(), and eth_rpc_select_for().

Referenced by pos_boot(), sign_and_broadcast(), and token_decimals_ok().

◆ eth_rpc_select_for()

void eth_rpc_select_for ( bool polygon)

Point eth_rpc at the endpoint for the selected EVM network.

Called at boot and at the top of every payment, since the network can change between sales. URL, credentials and pinned cert are separate statics in eth_rpc, so all three are re-applied each time (a stale cert or auth from the other network shows up as an unexplained TLS failure).

Definition at line 26 of file pay_evm.cpp.

References eth_rpc_init(), eth_rpc_set_auth(), eth_rpc_set_ca_cert(), POLY_RPC_URL, POLY_RPC_URL_MAIN, RPC_URL_MAIN, and settings_net_str().

Referenced by eth_rpc_select(), settle_inflight(), and token_decimals_ok().

◆ evm_balance_ok()

bool evm_balance_ok ( const pos_amount_t * amount,
char * err,
size_t err_max )

Refuse an EVM sale the tapped card cannot fund, before it signs.

Otherwise an underfunded token transfer is broadcast, mined, reverted and charged gas, and a short coin balance is refused only after signing.

The caller must have selected the endpoint and set the payer first. This does not call eth_rpc_select: that resets the from-address to config.h and would check the integrator's account instead of the tapped card.

A failed read is NOT a refusal: this improves a message, it does not gate payments.

Parameters
[in]amountThe reconciled sale amount, in keypad base units.
[out]errPanel-facing reason on refusal; untouched otherwise.
[in]err_maxCapacity of err.
Returns
true to let the sale proceed (funded, or unknowable).

Definition at line 91 of file pay_evm.cpp.

References active_token(), pos_amount_t::amount_minor, chain_is_native_evm(), chain_is_polygon(), eth_rpc_get_balance(), eth_rpc_get_token_balance(), evm_funds_check(), EVM_FUNDS_SHORT_GAS, EVM_FUNDS_SHORT_VALUE, GAS_LIMIT_NATIVE, pos_asset_of(), s_sale_fee, settings_get_chain(), token_t::str, and TAG.

Referenced by sign_and_broadcast().

◆ evm_fees_wei()

void evm_fees_wei ( bool polygon,
uint64_t * max_fee,
uint64_t * prio_fee )

The EIP-1559 fees one EVM sale will offer, in wei per gas.

One function so the signed transaction and the pre-flight check agree: a check against a cheaper fee than the tx carries would pass the very sale it exists to catch.

Parameters
[in]polygontrue on Polygon, which has a tip floor of its own.
[out]max_feeFee cap, wei per gas.
[out]prio_feeTip, wei per gas; never above max_fee.

Definition at line 64 of file pay_evm.cpp.

References evm_fees_from_gwei(), POLY_MIN_PRIORITY_FEE_GWEI, settings_get_max_fee_gwei(), and settings_get_priority_fee_gwei().

Referenced by app_main().

◆ inflight_persist()

void inflight_persist ( const inflight_t * fl)

Write the sale to NVS just before it leaves the terminal.

So a brownout or panic during the receipt poll does not lose whether the customer paid. Written as "broadcast not known": a record that survives a reset is by definition one whose answer nobody saw. One write per sale.

Definition at line 228 of file pay.cpp.

References inflight_t::active, inflight_t::broadcast_known, and settings_inflight_save().

Referenced by sign_and_broadcast(), and sign_and_broadcast_tron().

◆ pay_sign_and_broadcast()

bcast_t pay_sign_and_broadcast ( CryptnoxWallet & wallet,
Pn532NfcTransport & transport,
CW_CryptoProvider & crypto,
const pos_amount_t * amount,
const char * pin,
size_t pin_chars,
inflight_t * fl,
char * err_out,
size_t err_max )

Sign and broadcast the reconciled sale on whichever family is selected. The family is read once, here.

Definition at line 347 of file pay.cpp.

References active_token(), BCAST_FAILED, chain_is_tron(), s_dest, s_tron_dest, settings_get_chain(), sign_and_broadcast(), sign_and_broadcast_tron(), and token_decimals_ok().

Referenced by app_main().

◆ pin_fail_text()

const char * pin_fail_text ( Pn532NfcTransport & transport,
const char * wrong )

Why verifyPin said no: the PIN, or the card leaving the field.

verifyPin returns a bare bool, and a card pulled away mid-APDU fails exactly like a mistyped PIN. Asked before the session is dropped: if the card does not answer a SELECT, it was not the PIN.

Definition at line 52 of file card_io.cpp.

References CARD_SELECT_APDU, and TAG.

Referenced by card_read_payouts(), sign_and_broadcast(), and sign_and_broadcast_tron().

◆ pos_boot()

◆ run_wizard()

bool run_wizard ( CryptnoxWallet & wallet,
Pn532NfcTransport & transport,
CW_CryptoProvider & crypto,
bool wifi_only )

Run the browser wizard until the operator presses Finish.

  1. admin code on the panel — done by the caller; its value is that it never crosses a network.
  2. QR code on the panel — a camera joins the SoftAP and the captive portal opens the page.
  3. authorise both — the browser asks; the panel takes the code.
  4. addresses in the browser
  5. Wi-Fi in the browser
  6. Finish on the panel — restarts, which applies everything.
Parameters
[in]wifi_onlySteps 5 and 6 only, for a configured terminal that lost its network. No admin code: the form can only propose what the panel must accept, and the AP passphrase on that panel is the perimeter (see prov_set_wifi_only).
Returns
false if the portal could not be raised; the caller falls back to the panel.

Definition at line 269 of file boot.cpp.

References card_read_payouts(), ui_msg_t::event, net_time_sync(), net_wifi_connect(), net_wifi_disconnect(), net_wifi_scan(), NOTE_JOIN_FAILED, NOTE_NO_TIME, PROV_ASK_PAYOUT_ETH, PROV_ASK_PAYOUT_TRON, prov_authed(), prov_mode(), PROV_MODE_OFF, PROV_MODE_WIZARD, prov_propose(), prov_set_note(), prov_set_scan(), prov_set_step(), prov_set_wifi_only(), prov_start(), prov_step(), PROV_STEP_ADDR, PROV_STEP_AUTH, PROV_STEP_DONE, PROV_STEP_WIFI, prov_stop(), s_ui_queue, s_user_cancelled, settings_has_payout(), settings_has_wifi(), SETTINGS_PAYOUT_MAX, settings_set_wifi(), TAG, UI_EVENT_CARD_PIN, ui_event_dispatch(), UI_EVENT_PROV_AUTH, UI_EVENT_PROV_CARD, UI_EVENT_PROV_FINISH, UI_EVENT_PROV_NEXT, UI_EVENT_PROV_SCAN, UI_EVENT_PROV_STOP, UI_EVENT_PROV_VALUE, UI_EVENT_PROV_VALUE_NO, UI_EVENT_PROV_VALUE_SET, UI_EVENT_WIFI_TRY, ui_set_boot_status(), ui_set_prov_note(), ui_show_card_pin(), ui_show_prov(), ui_show_prov_auth(), ui_show_prov_confirm(), ui_show_wifi_connecting(), ui_take_pin(), and ui_take_wifi_creds().

Referenced by pos_boot().

◆ sale_fee_text()

void sale_fee_text ( char * out,
size_t n )

The most network fee the customer's card can be charged on top of the sale, for the confirm screen. "" where there is no cap to state: a native TRX transfer burns bandwidth, not a fee limit.

Definition at line 201 of file pay.cpp.

References active_token(), chain_is_native_evm(), chain_is_polygon(), chain_is_tron(), fmt_coin(), GAS_LIMIT_NATIVE, s_sale_fee, and TRON_TRC20_FEE_LIMIT_SUN.

Referenced by app_main().

◆ settle_inflight()

void settle_inflight ( void )

Poll the in-flight sale for up to 120 s and show what the chain says.

Only three answers end a sale: mined and ours (Approved), mined and reverted (nothing moved), or — Tron only — expired without ever being included (nothing can ever move). Everything else, including a receipt that does not match the transfer and a broadcast whose answer was lost, is Unconfirmed: the screen keeps the hash, offers Check again, and never says Declined, because a declined sale is one the merchant charges a second time.

Definition at line 255 of file pay.cpp.

References inflight_t::active, active_dest(), inflight_t::amount, inflight_t::broadcast_known, inflight_t::decided, eth_rpc_get_tx_receipt(), ETH_RPC_RECEIPT_MISMATCH, ETH_RPC_RECEIPT_PENDING, ETH_RPC_RECEIPT_REVERTED, ETH_RPC_RECEIPT_SUCCESS, eth_rpc_select_for(), inflight_t::expiration_ms, expired(), inflight_t::hash, IS_TRUE32, inflight_t::payee, inflight_t::polygon, pos_handle_anomaly(), POS_VERDICT_APPROVED, POS_VERDICT_DECLINED, run_payment_decision(), s_inflight, settings_inflight_clear(), TAG, inflight_t::to, inflight_t::token, inflight_t::tron, tron_receipt_as_eth(), tron_rpc_get_receipt(), ui_set_tx_info(), ui_show_tx_status(), UI_TX_STATE_CONFIRMING, UI_TX_STATE_DONE, UI_TX_STATE_FAILED, UI_TX_STATE_UNCONFIRMED, wall_ms(), and wdt_feed().

Referenced by app_main().

◆ sign_and_broadcast()

bcast_t sign_and_broadcast ( CryptnoxWallet & wallet,
Pn532NfcTransport & transport,
const pos_amount_t * amount,
const pos_addr_t * to,
const char * pin,
size_t pin_chars,
inflight_t * fl,
char * err_out,
size_t err_max )

Sign an EVM token or coin transfer on the card and broadcast it.

Pipeline: reconcile → calldata → card connect + PIN → payer address → balance → nonce → RLP + keccak256 → reconcile → sign (cancellable) → local parity check → broadcast. The PIN is scrubbed with CW_Utils::secure_wipe right after signing; hash, signature and encoded txs via WipeGuard.

Parameters
[in]walletInitialised wallet instance.
[in]transportPN532 transport, used for the cancellable connect loop.
[in]amountReconciled amount, keypad base units (6 decimals).
[in]toReconciled recipient.
[in]pinOperator-entered card PIN (scrubbed after signing).
[in]pin_charsNumber of PIN characters in pin.
[out]flFilled once signed: the locally computed hash and what its receipt must show.
[out]err_outShort UI-facing error message on failure.
[in]err_maxCapacity of err_out.
Returns
BCAST_SENT, BCAST_UNKNOWN (no answer — poll fl), or BCAST_FAILED on refusal or user cancel (err_out is only meaningful when s_user_cancelled is clear).

Definition at line 165 of file pay_evm.cpp.

References active_token(), pos_addr_t::addr, token_t::addr, address_consistent(), inflight_t::amount, amount_consistent(), pos_amount_t::amount_minor, BCAST_FAILED, BCAST_SENT, BCAST_UNKNOWN, build_usdc_calldata(), eth_tx_t::calldata, eth_tx_t::calldata_len, card_connect(), card_sign(), eth_tx_t::chain_id, CHAIN_ID_AMOY, CHAIN_ID_MAINNET, CHAIN_ID_POLYGON, chain_is_native_evm(), chain_is_polygon(), eth_addr_format(), ETH_ADDR_LEN, ETH_DERIVE_PATH, eth_rlp_encode_signed(), eth_rlp_encode_unsigned(), eth_rpc_err_already_known(), eth_rpc_get_nonce(), eth_rpc_select(), eth_rpc_send_raw_tx(), eth_rpc_set_from(), eth_sig_parity(), eth_tx_t::eth_value, evm_balance_ok(), evm_units_to_wei(), eth_tx_t::gas_limit, GAS_LIMIT_NATIVE, inflight_t::hash, inflight_persist(), IS_TRUE32, keccak256(), eth_tx_t::max_fee, eth_tx_t::max_priority_fee, eth_tx_t::nonce, token_t::ok, inflight_t::payee, pin_fail_text(), inflight_t::polygon, pos_handle_anomaly(), rpc_error_text(), s_card_fault, s_sale_fee, s_user_cancelled, settings_get_mainnet(), SETTINGS_PAYOUT_MAX, TAG, eth_tx_t::to, inflight_t::to, inflight_t::token, inflight_t::tron, TX_BUF_SIZE, ui_set_tx_info(), ui_show_tx_status(), UI_TX_STATE_SENDING, UI_TX_STATE_SIGNING, and USDC_CALLDATA_LEN.

Referenced by pay_sign_and_broadcast().

◆ sign_and_broadcast_tron()

bcast_t sign_and_broadcast_tron ( CryptnoxWallet & wallet,
Pn532NfcTransport & transport,
CW_CryptoProvider & crypto,
const pos_amount_t * amount,
const pos_addr_t * to,
const token_t * token,
const char * pin,
size_t pin_chars,
inflight_t * fl,
char * err_out,
size_t err_max )

Sign a Tron transfer on the card and broadcast it — TRX or TRC-20.

Same shape as sign_and_broadcast, but Tron has no RLP and no local nonce: the full node serialises the transaction and we sign its txID. What the node returns is therefore verified before the card ever sees the hash (see tron_rpc.h), and the recipient handed to the node is derived from the dual-stored to right after the reconcile, never from a config literal.

TRX and TRC-20 differ only in which transaction the node builds, so they share one function and the security checks cannot drift apart.

Parameters
[in]walletInitialised wallet instance.
[in]transportPN532 transport (cancellable connect loop).
[in]amountDual-stored amount, 6 decimals — sun for TRX, token base units for TRC-20.
[in]toDual-stored recipient (20-byte key hash).
[in]tokenToken to charge in, or NULL for native TRX.
[in]pinOperator-entered card PIN (scrubbed after signing).
[in]pin_charsNumber of PIN characters in pin.
[out]flFilled once built: the txID and its expiration.
[out]err_outShort UI-facing error message on failure.
[in]err_maxCapacity of err_out.
Returns
BCAST_SENT, BCAST_UNKNOWN (broadcast not confirmed — poll fl until it expires), or BCAST_FAILED on refusal or user cancel.

Definition at line 125 of file pay_tron.cpp.

References pos_addr_t::addr, token_t::addr, address_consistent(), amount_consistent(), pos_amount_t::amount_minor, BCAST_FAILED, BCAST_SENT, BCAST_UNKNOWN, card_connect(), card_sign(), ETH_ADDR_LEN, inflight_t::expiration_ms, tron_tx_ctx_t::expiration_ms, inflight_t::hash, inflight_persist(), IS_TRUE32, token_t::ok, pin_fail_text(), pos_handle_anomaly(), s_card_fault, s_user_cancelled, TAG, inflight_t::tron, TRON_ADDR_HEX_LEN, tron_addr_to_hex(), tron_balance_ok(), tron_rpc_broadcast(), tron_rpc_create_transfer(), tron_rpc_create_trc20_transfer(), TRON_TRC20_FEE_LIMIT_SUN, tron_tx_ctx_t::txid, tron_tx_ctx_t::txid_hex, ui_show_tx_status(), UI_TX_STATE_SENDING, and UI_TX_STATE_SIGNING.

Referenced by pay_sign_and_broadcast().

◆ sync_time()

bool sync_time ( void )

Block on an SNTP sync so TLS certificate validity-period checks run against real time instead of the 1970 epoch.

Returns
true once the clock is set, false after TIME_SYNC_ATTEMPTS rounds — flaky uplinks often need a second try.

Definition at line 483 of file boot.cpp.

References net_time_sync(), TAG, and TIME_SYNC_ATTEMPTS.

Referenced by pos_boot().

◆ token_decimals_ok()

bool token_decimals_ok ( pos_chain_t chain,
char * err,
size_t err_max )

Before the first sale in an operator-set token, read its decimals() and refuse anything but 6.

Every amount is signed in 6-decimal base units, so an 18-decimal contract would be charged 10^-12 of the figure on the screen. Checked here rather than when the contract is proposed: the config page runs with the station down, so no node can be asked then. A read that fails refuses the sale too, and the next sale asks again. Once per boot per token.

Returns
true when the token may be charged in.

Definition at line 115 of file pay.cpp.

References active_token(), pos_addr_t::addr, token_t::addr, token_t::checked, ETH_ADDR_LEN, eth_rpc_get_token_decimals(), eth_rpc_select(), eth_rpc_select_for(), pos_chain_is_polygon(), pos_chain_is_tron(), token_t::str, TAG, TRON_ADDR_HEX_LEN, tron_addr_to_hex(), and tron_rpc_get_trc20_decimals().

Referenced by app_main(), and pay_sign_and_broadcast().

◆ token_load()

void token_load ( const token_cfg_t * cfg,
CW_CryptoProvider & crypto )

Load one token at boot: the operator's contract if one is set and parses, config.h otherwise.

Non-fatal by design: a placeholder or a typo leaves ok false and that one asset is refused when selected, rather than stopping a terminal that charges in something else from booting.

Definition at line 84 of file pay.cpp.

References token_cfg_t::chain, token_t::checked, token_cfg_t::main, token_cfg_t::name, pos_asset_t::net, pos_asset_of(), pos_chain_is_tron(), pos_net_info(), s_token, settings_get_contract(), settings_net_str(), token_t::str, TAG, token_cfg_t::test, pos_asset_t::ticker, and token_parse().

Referenced by pos_boot().

◆ tron_addr_to_hex()

void tron_addr_to_hex ( const uint8_t * addr21,
char * out,
size_t n )

Format a raw 21-byte Tron address as the "41..." hex the API wants.

Parameters
[in]addr2121-byte address (0x41 prefix included).
[out]outTRON_ADDR_HEX_LEN chars + NUL.
[in]nCapacity of out.

Definition at line 25 of file pay_tron.cpp.

References TRON_ADDR_HEX_LEN.

Referenced by sign_and_broadcast_tron(), and token_decimals_ok().

◆ tron_receipt_as_eth()

eth_rpc_receipt_result_t tron_receipt_as_eth ( tron_receipt_t r)

Map a Tron receipt onto the Ethereum verdicts the UI flow uses.

Definition at line 309 of file pay_tron.cpp.

References ETH_RPC_RECEIPT_PENDING, ETH_RPC_RECEIPT_REVERTED, ETH_RPC_RECEIPT_RPC_ERROR, ETH_RPC_RECEIPT_SUCCESS, TRON_RECEIPT_FAILED, TRON_RECEIPT_PENDING, and TRON_RECEIPT_SUCCESS.

Referenced by settle_inflight().

◆ ui_event_dispatch()

void ui_event_dispatch ( ui_event_t event,
uint64_t payload )

UI-task callback: forward a touch event to the main task queue.

Runs in the UI task context. A Cancel tap also raises the atomic s_user_cancelled flag so the in-flight signing flow can abort without waiting for the queue to drain.

Parameters
[in]eventUI event identifier.
[in]payloadEvent payload (amount in USDC base units, or 0).

Definition at line 39 of file main.cpp.

References s_ui_queue, s_user_cancelled, and UI_EVENT_CONFIRM_CANCEL.

Referenced by pos_boot(), and run_wizard().

◆ wait_for_ui_event()

void wait_for_ui_event ( ui_event_t want)

Block until the UI reports want, discarding anything else.

For the modal first-run steps. The queue is flushed on the way out so a repeated tap is not read by the next stage (wifi_picker() would rescan and throw away a half-typed password).

Definition at line 94 of file boot.cpp.

References ui_msg_t::event, and s_ui_queue.

Referenced by pos_boot().

◆ wall_ms()

uint64_t wall_ms ( void )

Unix time in ms, 0 while the clock is unset.

Definition at line 237 of file pay.cpp.

Referenced by app_main(), and settle_inflight().

◆ wifi_keep_or_drop()

void wifi_keep_or_drop ( bool keep)

Persist or discard the pending picker credentials, then scrub them.

Parameters
[in]keeptrue once the clock is set — the only proof the network is actually usable; false to drop them unpersisted.

Definition at line 77 of file boot.cpp.

References s_join_pass, s_join_ssid, settings_set_wifi(), and TAG.

Referenced by pos_boot().

◆ wifi_picker()

bool wifi_picker ( const char * note)

Run the panel network picker (scan → list → keyboard → connect) until connected.

Blocks until a connection succeeds. The fallback to the browser setup (run_wizard): reached from the settings menu, or when the SoftAP could not come up.

A network joined here is not persisted: the credentials are staged for wifi_keep_or_drop, which the caller invokes once the clock proves the uplink usable.

Parameters
[in]noteOne-line reason shown above the picker, or NULL.
Returns
true always: the picker took the screen, so restore the splash.

Definition at line 151 of file boot.cpp.

References ui_msg_t::event, net_wifi_connect(), net_wifi_scan(), NOTE_JOIN_FAILED, ok(), s_join_pass, s_join_ssid, s_ui_queue, UI_EVENT_WIFI_SCAN, UI_EVENT_WIFI_TRY, ui_show_wifi_connecting(), ui_show_wifi_list(), and ui_take_wifi_creds().

Referenced by pos_boot().

◆ wifi_try_saved()

bool wifi_try_saved ( void )

Try the saved credentials, staying on the splash while it happens.

Unattended, so it reports through ui_set_boot_status. config.h Wi-Fi credentials are intentionally not used (NVS only).

Returns
true if a saved network was joined.

Definition at line 113 of file boot.cpp.

References net_wifi_connect(), ok(), settings_get_wifi(), TAG, ui_set_boot_status(), and WIFI_SAVED_ATTEMPTS.

Referenced by pos_boot().

Variable Documentation

◆ ETH_DERIVE_PATH

const uint8_t ETH_DERIVE_PATH[20]
extern

Definition at line 16 of file pay.cpp.

Referenced by card_read_payouts(), and sign_and_broadcast().

◆ NOTE_JOIN_FAILED

const char* const NOTE_JOIN_FAILED
extern

Definition at line 58 of file boot.cpp.

Referenced by app_main(), run_wizard(), and wifi_picker().

◆ NOTE_NO_TIME

const char* const NOTE_NO_TIME
extern

Definition at line 60 of file boot.cpp.

Referenced by app_main(), pos_boot(), and run_wizard().

◆ s_card_fault

const char* s_card_fault
extern

◆ s_dest

pos_addr_t s_dest
extern

Definition at line 177 of file pay.cpp.

Referenced by active_dest(), pay_sign_and_broadcast(), and resolve_evm_payout().

◆ s_inflight

inflight_t s_inflight
extern

Definition at line 219 of file pay.cpp.

Referenced by app_main(), and settle_inflight().

◆ s_payout_bad

bool s_payout_bad[2]
extern

Definition at line 182 of file pay.cpp.

Referenced by app_main(), and pos_boot().

◆ s_payout_eth

char s_payout_eth[SETTINGS_PAYOUT_MAX]
extern

Definition at line 27 of file pay.cpp.

Referenced by app_main(), pos_boot(), resolve_evm_payout(), and ui_refresh_addresses_for().

◆ s_payout_tron

char s_payout_tron[SETTINGS_PAYOUT_MAX]
extern

Definition at line 28 of file pay.cpp.

Referenced by app_main(), pos_boot(), and ui_refresh_addresses_for().

◆ s_sale_fee

sale_fee_t s_sale_fee
extern

Definition at line 192 of file pay.cpp.

Referenced by app_main(), evm_balance_ok(), sale_fee_text(), and sign_and_broadcast().

◆ s_token

◆ s_tron_dest

pos_addr_t s_tron_dest
extern

Definition at line 185 of file pay.cpp.

Referenced by active_dest(), pay_sign_and_broadcast(), and pos_boot().

◆ s_ui_queue

QueueHandle_t s_ui_queue
extern

◆ s_user_cancelled

std::atomic<bool> s_user_cancelled
extern

◆ TAG

const char* const TAG = "cryptnox_pos"
static

Definition at line 69 of file pos_app.h.

◆ TOKEN_CFG

const token_cfg_t TOKEN_CFG[]
extern

Definition at line 32 of file pay.cpp.

Referenced by pos_boot().

◆ TOKEN_CFG_COUNT

const size_t TOKEN_CFG_COUNT
extern

Definition at line 41 of file pay.cpp.

Referenced by pos_boot().