cryptnox-pos 1.0.0
Standalone USDC payment terminal firmware (ESP32 + Cryptnox smart card)
Loading...
Searching...
No Matches
money.h
Go to the documentation of this file.
1/*
2 * SPDX-License-Identifier: LGPL-3.0-or-later
3 * Copyright (c) 2026 Cryptnox SA
4 */
5
19
20#ifndef MONEY_H
21#define MONEY_H
22
23#include <inttypes.h>
24#include <stdbool.h>
25#include <stddef.h>
26#include <stdint.h>
27#include <stdio.h>
28#include <string.h>
29
30#include "CW_Utils.h" /* secure_wipe / safe_memcpy (CODING_RULES §1.4) */
31#include "eth_addr.h" /* ETH_ADDR_LEN */
32#include "settings.h" /* POS_AMOUNT_UNITS_MAX_NATIVE */
33
34/******************************************************************
35 * Keypad amounts (ui.cpp)
36 ******************************************************************/
37
38/* 9999.99: plenty for a counter terminal, and it keeps the figure, the cents
39 * and the asset selector inside the amount row. */
40#define AMOUNT_CENTS_MAX 999999ULL /* 9999.99 */
41/* 18.44 — POS_AMOUNT_UNITS_MAX_NATIVE expressed in the keypad's cents. */
42#define AMOUNT_CENTS_MAX_NATIVE (POS_AMOUNT_UNITS_MAX_NATIVE / 10000ULL)
43
52static inline uint64_t amount_cents_cap(bool native)
53{
55}
56
58static inline uint64_t amount_key_digit(uint64_t cents, unsigned digit, uint64_t cap)
59{
60 const uint64_t n = (cents * 10ULL) + static_cast<uint64_t>(digit);
61 return (n <= cap) ? n : cents;
62}
63
65static inline uint64_t amount_key_00(uint64_t cents, uint64_t cap)
66{
67 const uint64_t n = cents * 100ULL;
68 return (n > cap) ? cap : n;
69}
70
72static inline uint64_t amount_key_back(uint64_t cents)
73{
74 return cents / 10ULL;
75}
76
78static inline uint64_t amount_cents_to_units(uint64_t cents)
79{
80 return cents * 10000ULL;
81}
82
84static inline void amount_format(uint64_t units, char *out, size_t n)
85{
86 uint64_t whole = units / 1000000ULL;
87 uint64_t cents = (units % 1000000ULL) / 10000ULL;
88 snprintf(out, n, "%" PRIu64 ".%02" PRIu64, whole, cents);
89}
90
91/******************************************************************
92 * Units, wei and fees (main.cpp)
93 ******************************************************************/
94
103static inline bool evm_units_to_wei(uint64_t units, uint64_t *wei)
104{
105 if (units > POS_AMOUNT_UNITS_MAX_NATIVE) { return false; }
106 *wei = units * 1000000000000ULL;
107 return true;
108}
109
121static inline void evm_fees_from_gwei(uint32_t max_gwei, uint32_t prio_gwei,
122 bool polygon, uint32_t floor_gwei,
123 uint64_t *max_fee, uint64_t *prio_fee)
124{
125 /* The user edits Gwei, so scale to wei. Keep the tip <= the cap or the tx
126 * is malformed. */
127 uint64_t max_fee_wei = (uint64_t)max_gwei * 1000000000ULL;
128 uint64_t prio_fee_wei = (uint64_t)prio_gwei * 1000000000ULL;
129 if (prio_fee_wei > max_fee_wei) { prio_fee_wei = max_fee_wei; }
130 /* Polygon drops a transfer whose tip is under ~25 Gwei, and the fee knobs are
131 * shared with Ethereum where 20 is right. Raise the floor here rather than
132 * asking the operator to retune the Tx tab every time they switch networks —
133 * and lift the cap with it, or the clamp above would only put it back. */
134 if (polygon) {
135 const uint64_t floor_wei = (uint64_t)floor_gwei * 1000000000ULL;
136 if (prio_fee_wei < floor_wei) { prio_fee_wei = floor_wei; }
137 if (max_fee_wei < prio_fee_wei) { max_fee_wei = prio_fee_wei; }
138 }
139 *max_fee = max_fee_wei;
140 *prio_fee = prio_fee_wei;
141}
142
150
159static inline evm_funds_t evm_funds_check(bool native, uint64_t have_wei,
160 uint64_t gas_cost, uint64_t units)
161{
162 if (have_wei < gas_cost) { return EVM_FUNDS_SHORT_GAS; }
163 if (!native) { return EVM_FUNDS_OK; }
164 uint64_t value_wei = 0U;
165 if (!evm_units_to_wei(units, &value_wei)) { return EVM_FUNDS_UNKNOWN; }
166 /* Subtracting rather than adding: value is capped at just under 2^64
167 * wei, so value + gas_cost is the one sum here that could overflow —
168 * and the gas is already known to be covered. */
169 if ((have_wei - gas_cost) < value_wei) { return EVM_FUNDS_SHORT_VALUE; }
170 return EVM_FUNDS_OK;
171}
172
177static inline void fmt_coin(char *out, size_t n, uint64_t v, unsigned dec,
178 const char *coin)
179{
180 uint64_t div = 1U;
181 for (unsigned i = 6U; i < dec; i++) { div *= 10U; }
182 const uint64_t micro = (v / div) + (((v % div) != 0U) ? 1U : 0U);
183 char frac[8];
184 (void)snprintf(frac, sizeof(frac), "%06" PRIu64, micro % 1000000U);
185 size_t f = strlen(frac);
186 while ((f > 0U) && (frac[f - 1U] == '0')) { frac[--f] = '\0'; }
187 (void)snprintf(out, n, "%" PRIu64 "%s%s %s", micro / 1000000U,
188 (f > 0U) ? "." : "", frac, coin);
189}
190
191/******************************************************************
192 * USDC transfer calldata (main.cpp)
193 ******************************************************************/
194
195/* ── ERC-20 transfer(address,uint256) selector + calldata ── */
196static const uint8_t TRANSFER_SELECTOR[4] = { 0xa9U, 0x05U, 0x9cU, 0xbbU };
197#define ABI_SELECTOR_LEN 4U /* transfer(address,uint256) selector */
198#define ABI_WORD_LEN 32U /* one ABI-encoded argument word */
199#define USDC_CALLDATA_LEN (ABI_SELECTOR_LEN + (2U * ABI_WORD_LEN)) /* 68 */
200#define ABI_TO_OFFSET (ABI_SELECTOR_LEN + (ABI_WORD_LEN - ETH_ADDR_LEN))
201
216static inline void build_usdc_calldata(uint8_t out[USDC_CALLDATA_LEN],
217 const uint8_t to[ETH_ADDR_LEN],
218 uint64_t amount)
219{
220 CW_Utils::secure_wipe(out, USDC_CALLDATA_LEN);
221 (void)CW_Utils::safe_memcpy(out, USDC_CALLDATA_LEN,
223 (void)CW_Utils::safe_memcpy(out + ABI_TO_OFFSET,
225 to, ETH_ADDR_LEN);
226
227 size_t j;
228 for (j = 0U; j < sizeof(amount); j++) {
229 out[(USDC_CALLDATA_LEN - 1U) - j] =
230 static_cast<uint8_t>((amount >> (8U * j)) & 0xFFU);
231 }
232}
233
234#endif /* MONEY_H */
Hex Ethereum-address parsing — a pure, dependency-free unit so it can be unit-tested and fuzzed on th...
#define ETH_ADDR_LEN
Length of a raw (binary) Ethereum address, in bytes.
Definition eth_addr.h:25
#define AMOUNT_CENTS_MAX_NATIVE
Definition money.h:42
static const uint8_t TRANSFER_SELECTOR[4]
Definition money.h:196
static void evm_fees_from_gwei(uint32_t max_gwei, uint32_t prio_gwei, bool polygon, uint32_t floor_gwei, uint64_t *max_fee, uint64_t *prio_fee)
The EIP-1559 fees one EVM sale will offer, in wei per gas, from the operator's Gwei settings.
Definition money.h:121
#define ABI_TO_OFFSET
Definition money.h:200
static uint64_t amount_cents_cap(bool native)
Ceiling on what the keypad will accept, in cents.
Definition money.h:52
static void fmt_coin(char *out, size_t n, uint64_t v, unsigned dec, const char *coin)
"0.0013 ETH": v base units of a dec-decimal coin, to six places, rounded UP — it is a ceiling,...
Definition money.h:177
#define AMOUNT_CENTS_MAX
Definition money.h:40
#define ABI_SELECTOR_LEN
Definition money.h:197
evm_funds_t
What the pre-flight balance check concluded.
Definition money.h:144
@ EVM_FUNDS_OK
Definition money.h:145
@ EVM_FUNDS_UNKNOWN
Definition money.h:148
@ EVM_FUNDS_SHORT_VALUE
Definition money.h:147
@ EVM_FUNDS_SHORT_GAS
Definition money.h:146
static uint64_t amount_key_00(uint64_t cents, uint64_t cap)
The "00" key: two zeroes shifted in, clamped to cap.
Definition money.h:65
static evm_funds_t evm_funds_check(bool native, uint64_t have_wei, uint64_t gas_cost, uint64_t units)
Can have_wei pay for this sale?
Definition money.h:159
static void build_usdc_calldata(uint8_t out[USDC_CALLDATA_LEN], const uint8_t to[ETH_ADDR_LEN], uint64_t amount)
Build the 68-byte ABI-encoded calldata for a USDC transfer call.
Definition money.h:216
static uint64_t amount_key_back(uint64_t cents)
The backspace key: the last digit dropped.
Definition money.h:72
static void amount_format(uint64_t units, char *out, size_t n)
"12.50": 6-decimal base units to two places, truncated.
Definition money.h:84
static uint64_t amount_key_digit(uint64_t cents, unsigned digit, uint64_t cap)
A digit shifted in from the right; refused whole if it passes cap.
Definition money.h:58
static uint64_t amount_cents_to_units(uint64_t cents)
Keypad cents to 6-decimal base units.
Definition money.h:78
#define USDC_CALLDATA_LEN
Definition money.h:199
static bool evm_units_to_wei(uint64_t units, uint64_t *wei)
6-decimal keypad units -> wei, for the 18-decimal coins only.
Definition money.h:103
Persistent device settings stored in NVS (backlight, Wi-Fi creds).
#define POS_AMOUNT_UNITS_MAX_NATIVE
Ceiling on a native-coin sale, in the keypad's 6-decimal base units.
Definition settings.h:60