16#define TX_BUF_SIZE 300U
31#ifdef POLY_CA_CERT_PEM
38#if defined(RPC_PROJECT_ID) && defined(RPC_API_SECRET)
64void evm_fees_wei(
bool polygon, uint64_t *max_fee, uint64_t *prio_fee)
95 const char *coin = polygon ?
"POL" :
"ETH";
101 const uint64_t gas_cost =
104 uint64_t have_wei = 0U;
106 ESP_LOGW(
TAG,
"pre-flight: balance read failed - letting the sale run");
114 (void)snprintf(err, err_max,
"Not enough %s for the network fee", coin);
122 (void)snprintf(err, err_max,
"Not enough %s for this amount", coin);
129 uint64_t have_units = 0U;
131 ESP_LOGW(
TAG,
"pre-flight: token balance read failed - letting it run");
134 if (have_units < amount->amount_minor) {
135 (void)snprintf(err, err_max,
"Not enough %s on the card",
166 Pn532NfcTransport &transport,
169 const char *pin,
size_t pin_chars,
171 char *err_out,
size_t err_max)
177 if ((tk != NULL) && !tk->
ok) {
178 (void)snprintf(err_out, err_max,
"Token contract not configured");
197 (void)snprintf(err_out, err_max,
"Integrity check failed");
205 uint64_t native_wei = 0U;
207 (void)snprintf(err_out, err_max,
"Amount too large for this asset");
218 CW_SecureSession session;
221 (void)snprintf(err_out, err_max,
"%s",
234 if (!wallet.verifyPin(session,
reinterpret_cast<const uint8_t *
>(pin),
235 static_cast<uint8_t
>(pin_chars))) {
236 (void)snprintf(err_out, err_max,
"%s",
pin_fail_text(transport,
"Wrong PIN"));
237 wallet.disconnect(session);
250 uint8_t key_hash[32];
251 WipeGuard g_kh(key_hash,
sizeof(key_hash));
255 wallet.disconnect(session);
256 (void)snprintf(err_out, err_max,
"Cannot read card address");
259 keccak256(pubkey,
sizeof(pubkey), key_hash);
265 wallet.disconnect(session);
266 (void)snprintf(err_out, err_max,
"Cannot read card address");
269 ESP_LOGI(
TAG,
"EVM sender (this card): %s", from_addr);
274 wallet.disconnect(session);
281 wallet.disconnect(session);
282 (void)snprintf(err_out, err_max,
"RPC: get nonce failed");
287 CW_Utils::secure_wipe(
reinterpret_cast<uint8_t *
>(&tx),
sizeof(tx));
305 tx.
calldata = native ? NULL : calldata;
307 (void)CW_Utils::safe_memcpy(tx.
to,
sizeof(tx.
to),
308 (token != NULL) ? token->
addr : to->
addr,
313 if (unsigned_len == 0U) {
314 wallet.disconnect(session);
315 (void)snprintf(err_out, err_max,
"RLP encode overflow");
319 uint8_t hash[CW_HASH_SIZE];
320 keccak256(unsigned_tx, unsigned_len, hash);
324 WipeGuard g_unsigned(unsigned_tx,
sizeof(unsigned_tx));
334 (void)snprintf(err_out, err_max,
"Integrity check failed");
335 wallet.disconnect(session);
341 if (!
card_sign(wallet, session, hash,
static_cast<uint8_t
>(CW_HASH_SIZE),
344 pin, pin_chars, rs, err_out, err_max)) {
347 const uint8_t *sig_r = rs;
348 const uint8_t *sig_s = rs + 32U;
359 ESP_LOGE(
TAG,
"signature does not verify under the card's own key");
360 (void)snprintf(err_out, err_max,
"Signature check failed");
365 WipeGuard g_signed(signed_tx,
sizeof(signed_tx));
367 signed_tx,
sizeof(signed_tx));
368 if (signed_len == 0U) {
369 (void)snprintf(err_out, err_max,
"RLP signed overflow");
387 for (
size_t i = 0U; i <
sizeof(h); i++) {
388 (void)snprintf(&fl->
hash[2U + (2U * i)], 3U,
"%02x", h[i]);
392 fl->
token = (token != NULL);
393 (void)CW_Utils::safe_memcpy(fl->
to,
sizeof(fl->
to),
394 (token != NULL) ? token->
addr : to->
addr,
396 (void)CW_Utils::safe_memcpy(fl->
payee,
sizeof(fl->
payee), to->
addr,
398 fl->
amount = amount_units;
404 char node_err[128] =
"";
405 char node_hash[72] =
"";
407 node_err,
sizeof(node_err))) {
410 ESP_LOGW(
TAG,
"node already has %s", fl->
hash);
413 if (node_err[0] ==
'\0') {
416 ESP_LOGW(
TAG,
"no answer to the broadcast - polling %s", fl->
hash);
419 ESP_LOGE(
TAG,
"broadcast refused: %s", node_err);
423 if (strcasecmp(node_hash, fl->
hash) != 0) {
426 ESP_LOGE(
TAG,
"node answered hash %s for %s", node_hash, fl->
hash);
static const pos_asset_t * pos_asset_of(pos_chain_t chain)
The row describing chain.
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.
const char * s_card_fault
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.
const char * pin_fail_text(Pn532NfcTransport &transport, const char *wrong)
Why verifyPin said no: the PIN, or the card leaving the field.
#define POLY_MIN_PRIORITY_FEE_GWEI
#define POLY_RPC_URL_MAIN
bool eth_addr_format(const uint8_t addr[ETH_ADDR_LEN], char *out, size_t n)
Render a 20-byte address as an EIP-55 mixed-case "0x..." string.
#define ETH_ADDR_LEN
Length of a raw (binary) Ethereum address, in bytes.
size_t eth_rlp_encode_signed(const eth_tx_t *tx, uint8_t v, const uint8_t r[32], const uint8_t s[32], uint8_t *out, size_t out_max)
Encode a signed EIP-1559 transaction: 0x02 || RLP([..., v, r, s]).
size_t eth_rlp_encode_unsigned(const eth_tx_t *tx, uint8_t *out, size_t out_max)
Encode an unsigned EIP-1559 transaction: 0x02 || RLP([chainId, nonce, ...]).
bool eth_rpc_get_nonce(uint64_t *nonce_out)
Fetch the confirmed transaction count (nonce) for from_addr.
static const char *const TAG
void eth_rpc_init(const char *rpc_url, const char *from_addr)
Set the RPC URL and the from-address used for nonce queries and ecrecover comparison.
void eth_rpc_set_auth(const char *project_id, const char *api_secret)
Optional: set Infura-style HTTP Basic Auth credentials.
bool eth_rpc_get_balance(uint64_t *wei_out)
Fetch the native balance of from_addr, in wei.
bool eth_rpc_get_token_balance(const char *token_addr, uint64_t *units_out)
Fetch from_addr's balance of an ERC-20, via balanceOf over eth_call.
bool eth_rpc_send_raw_tx(const uint8_t *tx, size_t tx_len, char *tx_hash_out, size_t tx_hash_max, char *err_out, size_t err_max)
Broadcast a raw signed transaction (type-prefixed RLP bytes).
bool eth_rpc_err_already_known(const char *node_err)
true if a broadcast error message means the node already HAS this transaction ("already known",...
void eth_rpc_set_ca_cert(const char *ca_pem)
Optional: pin the RPC endpoint's TLS certificate.
bool eth_rpc_set_from(const char *addr)
Replace the from-address — the account a sale spends from.
bool eth_sig_parity(const uint8_t hash[32], const uint8_t r[32], const uint8_t s[32], const uint8_t pub64[64], uint8_t *v_out)
Verify (r, s) over hash against pub64 and return its y-parity.
void pos_handle_anomaly(const char *where)
Report an anomaly: bump a self-checked persisted counter, log it, and keep a local ring buffer for on...
static bool32 amount_consistent(const pos_amount_t *a)
static bool32 address_consistent(const pos_addr_t *a)
void keccak256(const uint8_t *input, size_t length, uint8_t digest[32])
Compute the Keccak-256 digest of a buffer.
std::atomic< bool > s_user_cancelled
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.
evm_funds_t
What the pre-flight balance check concluded.
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?
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.
#define USDC_CALLDATA_LEN
static bool evm_units_to_wei(uint64_t units, uint64_t *wei)
6-decimal keypad units -> wei, for the 18-decimal coins only.
token_t * active_token(pos_chain_t chain)
The selection's token, or NULL for a native coin.
const uint8_t ETH_DERIVE_PATH[20]
void inflight_persist(const inflight_t *fl)
Write the sale to NVS just before it leaves the terminal.
void eth_rpc_select_for(bool polygon)
Point eth_rpc at the endpoint for the selected EVM network.
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.
void eth_rpc_select(void)
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 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.
Private to the application files (main, boot, card_io, pay, pay_evm, pay_tron): the state they share ...
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.
static void rpc_error_text(const char *node_msg, bool native, bool polygon, char *out, size_t n)
Render a broadcast refusal for the Declined screen.
const char * settings_net_str(const char *testnet, const char *mainnet)
Pick the string belonging to the network the terminal is on.
pos_chain_t settings_get_chain(void)
Selected chain, or POS_CHAIN_ETH_USDC if never set.
uint32_t settings_get_priority_fee_gwei(void)
EIP-1559 max priority fee (tip) per gas, in Gwei.
bool settings_get_mainnet(void)
true when the terminal is on the production networks.
uint32_t settings_get_max_fee_gwei(void)
EIP-1559 max fee per gas, in Gwei.
#define SETTINGS_PAYOUT_MAX
Longest payout address plus NUL — "0x" + 40 hex, or 34 base58 Tron.
Scrubs a buffer with CW_Utils::secure_wipe when it leaves scope.
EIP-1559 (type 2) transaction parameters.
uint64_t max_priority_fee
uint8_t payee[ETH_ADDR_LEN]
uint8_t addr[ETH_ADDR_LEN]
A token's contract, dual-stored.
char str[SETTINGS_PAYOUT_MAX]
void ui_show_tx_status(ui_tx_state_t state, const char *info)
Switch to the transaction-status screen.
void ui_set_tx_info(const char *info)
Replace the transaction screen's info line without rebuilding it.