25#include <ArduinoHttpClient.h>
28#include <CryptnoxWallet.h>
31#define PN532_SS_PIN (10U)
37# define CARD_PIN "000000000"
38# define CARD_PIN_LEN (9U)
59# define WIFI_CA_CERT \
60"-----BEGIN CERTIFICATE-----\n" \
61"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n" \
62"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" \
63"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n" \
64"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n" \
65"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n" \
66"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n" \
67"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n" \
68"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n" \
69"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n" \
70"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n" \
71"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n" \
72"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n" \
73"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n" \
74"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n" \
75"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n" \
76"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n" \
77"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n" \
78"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n" \
79"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n" \
80"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n" \
81"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n" \
82"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n" \
83"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n" \
84"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n" \
85"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n" \
86"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n" \
87"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n" \
88"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n" \
89"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n" \
90"-----END CERTIFICATE-----\n"
104#define ERC20_TRANSFER_SEL_0 0xa9U
105#define ERC20_TRANSFER_SEL_1 0x05U
106#define ERC20_TRANSFER_SEL_2 0x9cU
107#define ERC20_TRANSFER_SEL_3 0xbbU
109#define ERC20_INDEX_OFFSET 64U
112#define YPARITY_UNKNOWN 0xFFU
118#define TX_MAX_RETRIES 3U
120#define TX_RETRY_DELAY_MS 2000U
122#define WIFI_RETRY_MAX 20U
124#define HEX_CHAR_BUF_SIZE 3U
126#define ECRECOVER_V_PAD_CHARS 62U
128#define ECRECOVER_V_BASE 27U
147static void printHex(
const char* label,
const uint8_t* data,
size_t len) {
149 Serial.print(
F(
": 0x"));
150 char buf[3]; buf[2] =
'\0';
151 for (
size_t i = 0; i < len; i++) {
159#if defined(RPC_PROJECT_ID) && defined(RPC_API_SECRET)
161#define AUTH_CRED_BUF_SIZE 128U
163#define AUTH_HEADER_BUF_SIZE 200U
169static bool base64Encode(
const char* input,
size_t inputLen,
char* output,
size_t outputCap) {
170 static const char kAlphabet[] =
171 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
172 const size_t required = (((inputLen + 2U) / 3U) * 4U) + 1U;
173 if (outputCap < required) {
178 while (i < inputLen) {
179 uint32_t u0 =
static_cast<uint32_t
>(
static_cast<uint8_t
>(input[i]));
182 if ((i + 1U) < inputLen) {
183 u1 =
static_cast<uint32_t
>(
static_cast<uint8_t
>(input[i + 1U]));
185 if ((i + 2U) < inputLen) {
186 u2 =
static_cast<uint32_t
>(
static_cast<uint8_t
>(input[i + 2U]));
188 output[o] = kAlphabet[
static_cast<uint8_t
>(u0 >> 2U)];
190 output[o] = kAlphabet[
static_cast<uint8_t
>(((u0 & 0x03U) << 4U) | (u1 >> 4U))];
192 if ((i + 1U) < inputLen) {
193 output[o] = kAlphabet[
static_cast<uint8_t
>(((u1 & 0x0FU) << 2U) | (u2 >> 6U))];
198 if ((i + 2U) < inputLen) {
199 output[o] = kAlphabet[
static_cast<uint8_t
>(u2 & 0x3FU)];
210static void buildBasicAuthHeader(
char* buf,
size_t bufSize) {
211 static const char kPrefix[] =
"Basic ";
212 const size_t prefixLen =
sizeof(kPrefix) - 1U;
213 char cred[AUTH_CRED_BUF_SIZE];
215 int written = snprintf(cred,
sizeof(cred),
"%s:%s", RPC_PROJECT_ID, RPC_API_SECRET);
220 if ((written < 0) || ((
size_t)written >=
sizeof(cred))) {
221 Serial.println(
F(
"[fatal] RPC_PROJECT_ID + RPC_API_SECRET exceed AUTH_CRED_BUF_SIZE"));
226 if (prefixLen >= bufSize) {
227 Serial.println(
F(
"[fatal] AUTH_HEADER_BUF_SIZE too small for prefix"));
231 reinterpret_cast<const uint8_t*
>(kPrefix), prefixLen);
232 if (!base64Encode(cred, strlen(cred), buf + prefixLen, bufSize - prefixLen)) {
233 Serial.println(
F(
"[fatal] base64 output exceeds AUTH_HEADER_BUF_SIZE"));
240 if (WiFi.status() == WL_CONNECTED)
return true;
243 while ((retries > 0U) && (WiFi.status() != WL_CONNECTED)) {
247 return WiFi.status() == WL_CONNECTED;
252#define RLP_ITEM_OR_FAIL(BUF, CAP, OFF, IN, IN_LEN) \
254 uint32_t _w = RlpEncodeItem((BUF) + (OFF), (CAP) - (OFF), \
256 if (_w == 0U) { return 0U; } \
276 Serial.println(
F(
"[fatal] tx.to is not a valid 40-char hex string"));
283 if (off >= bufCap) {
return 0U; }
288static size_t rlpFinalize(uint8_t* out,
size_t outCap,
const uint8_t* buf,
size_t off) {
291 if (header_len == 0U) {
return 0U; }
293 if ((1U + header_len + off) > outCap) {
297 out[out_off++] = 0x02;
299 out_off += header_len;
301 return out_off + off;
307 if (off == 0U) {
return 0U; }
312 uint8_t* out,
size_t outCap) {
315 if (off == 0U) {
return 0U; }
318 if (w == 0U) {
return 0U; }
322 if (tmp_len == 0U) {
return 0U; }
323 w =
RlpEncodeItem(buf + off,
sizeof(buf) - off, tmp_r, (uint32_t)tmp_len);
324 if (w == 0U) {
return 0U; }
328 if (tmp_len == 0U) {
return 0U; }
329 w =
RlpEncodeItem(buf + off,
sizeof(buf) - off, tmp_s, (uint32_t)tmp_len);
330 if (w == 0U) {
return 0U; }
353 Serial.println(
F(
"[fatal] ADDR_TO is not a valid 40-char hex string"));
371 static const char requestPrefix[] =
372 "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_sendRawTransaction\","
375 static const char requestSuffix[] =
"\"]}";
377 for (uint8_t attempt = 0U; (attempt <
TX_MAX_RETRIES) && !sent; attempt++) {
382 Serial.println(
F(
"sendRawTx: WiFi reconnect failed"));
385 WiFiSSLClient wifiClient;
386#ifndef WIFI_DISABLE_CA_PINNING
394 client.beginRequest();
396 if (err != HTTP_SUCCESS) {
397 Serial.print(
F(
"sendRawTx: POST failed, err="));
403 client.sendHeader(
"Content-Type",
"application/json");
404 client.sendHeader(
"Content-Length",
405 (
int)(
sizeof(requestPrefix)-1) + 2*(
int)len + (
int)(
sizeof(requestSuffix)-1));
406#if defined(RPC_PROJECT_ID) && defined(RPC_API_SECRET)
408 char authBuf[AUTH_HEADER_BUF_SIZE];
409 buildBasicAuthHeader(authBuf,
sizeof(authBuf));
410 client.sendHeader(
"Authorization", authBuf);
414 client.print(requestPrefix);
418 byteHexStr[2] =
'\0';
419 for (
size_t i = 0; i < len; i++) {
420 byteHexStr[0] =
hexChars[raw[i] >> 4];
421 byteHexStr[1] =
hexChars[raw[i] & 0x0f];
422 client.print(byteHexStr);
424 client.print(requestSuffix);
426 int status = client.responseStatusCode();
427 String responseBody = client.responseBody();
428 Serial.print(
F(
"[RPC] HTTP ")); Serial.println(status);
433 Serial.print(
F(
"[RPC] ")); Serial.println(responseBody);
435 bool statusOk = (status ==
HTTP_OK);
439 bool noJsonError = (responseBody.indexOf(
"\"error\"") == -1);
440 sent = statusOk && noJsonError;
445 int r = responseBody.indexOf(
"\"result\":\"0x");
448 int end = responseBody.indexOf(
"\"", start);
450 Serial.print(
F(
"[tx] hash="));
451 Serial.println(responseBody.substring(start, end));
477 for (uint8_t i = 0U; i < 32U; i++) {
478 hexBuf[pos++] =
hexChars[hash[i] >> 4];
479 hexBuf[pos++] =
hexChars[hash[i] & 0x0f];
482 const uint16_t vOffset = pos;
487 for (uint8_t i = 0U; i < 32U; i++) {
488 hexBuf[pos++] =
hexChars[r[i] >> 4];
489 hexBuf[pos++] =
hexChars[r[i] & 0x0f];
491 for (uint8_t i = 0U; i < 32U; i++) {
492 hexBuf[pos++] =
hexChars[s[i] >> 4];
493 hexBuf[pos++] =
hexChars[s[i] & 0x0f];
497 static const char requestPrefix[] =
498 "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_call\","
499 "\"params\":[{\"to\":\"0x0000000000000000000000000000000000000001\","
501 static const char requestSuffix[] =
"\"},\"latest\"]}";
502 const int bodyLen = (int)(
sizeof(requestPrefix) - 1) + 258 + (int)(
sizeof(requestSuffix) - 1);
505 for (uint8_t yp = 0U; (yp <= 1U) && (result ==
YPARITY_UNKNOWN); yp++) {
513 Serial.println(
F(
"determineYParity: WiFi reconnect failed"));
516 WiFiSSLClient wifiClient;
517#ifndef WIFI_DISABLE_CA_PINNING
525 client.beginRequest();
527 if (err != HTTP_SUCCESS) {
528 Serial.print(
F(
"determineYParity: POST failed, err="));
533 client.sendHeader(
"Content-Type",
"application/json");
534 client.sendHeader(
"Content-Length", bodyLen);
535#if defined(RPC_PROJECT_ID) && defined(RPC_API_SECRET)
537 char authBuf[AUTH_HEADER_BUF_SIZE];
538 buildBasicAuthHeader(authBuf,
sizeof(authBuf));
539 client.sendHeader(
"Authorization", authBuf);
543 client.print(requestPrefix);
544 client.print(hexBuf);
545 client.print(requestSuffix);
548 int status = client.responseStatusCode();
549 String response = client.responseBody();
554 int resultIdx = response.indexOf(
"\"result\"");
558 int hexIdx = response.indexOf(
"0x", resultIdx);
566 if (response.length() < (
unsigned int)(hexIdx + 66)) {
570 String recovered = response.substring(hexIdx + 26, hexIdx + 66);
571 Serial.print(
F(
"[ecrecover] v=")); Serial.print(v);
572 Serial.print(
F(
" recovered=0x")); Serial.println(recovered);
573 Serial.print(
F(
"[ecrecover] expected=0x")); Serial.println(
F(
ADDR_FROM));
574 if (recovered.equalsIgnoreCase(
ADDR_FROM)) {
587 static const char requestPrefix[] =
588 "{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"eth_getTransactionCount\","
590 static const char requestSuffix[] =
"\",\"pending\"]}";
591 const int bodyLen = (int)(
sizeof(requestPrefix)-1) + 40 + (int)(
sizeof(requestSuffix)-1);
594 for (uint8_t attempt = 0U; (attempt <
TX_MAX_RETRIES) && (result != 0U); attempt++) {
599 Serial.println(
F(
"fetchNonce: WiFi reconnect failed"));
602 WiFiSSLClient wifiClient;
603#ifndef WIFI_DISABLE_CA_PINNING
611 client.beginRequest();
612 Serial.print(
F(
"fetchNonce: connecting to "));
616 if (err != HTTP_SUCCESS) {
617 Serial.print(
F(
"fetchNonce: POST failed, err="));
622 client.sendHeader(
"Content-Type",
"application/json");
623 client.sendHeader(
"Content-Length", bodyLen);
624#if defined(RPC_PROJECT_ID) && defined(RPC_API_SECRET)
626 char authBuf[AUTH_HEADER_BUF_SIZE];
627 buildBasicAuthHeader(authBuf,
sizeof(authBuf));
628 client.sendHeader(
"Authorization", authBuf);
632 client.print(requestPrefix);
634 client.print(requestSuffix);
637 int status = client.responseStatusCode();
638 String resp = client.responseBody();
641 int ri = resp.indexOf(
"\"result\"");
642 int xi = (ri >= 0) ? resp.indexOf(
"0x", ri) : -1;
644 uint64_t parsed = 0U;
649 bool overflowed =
false;
650 for (
int i = xi + 2; i < (int)resp.length(); i++) {
652 if (!((c>=
'0'&&c<=
'9')||(c>=
'a'&&c<=
'f')||(c>=
'A'&&c<=
'F')))
break;
653 if (digitCount >= 16) { overflowed =
true;
break; }
654 parsed = (parsed << 4) |
fromHex(c);
659 if (!overflowed && (digitCount > 0)) {
673 Serial.begin(115200);
679 Serial.println(
F(
"PN532 init failed! Halting."));
682 Serial.println(
F(
"PN532 OK"));
684#ifdef WIFI_DISABLE_CA_PINNING
685 Serial.println(
F(
"⚠️ WIFI_DISABLE_CA_PINNING is set — TLS certificate is NOT validated."));
686 Serial.println(
F(
" Connection is vulnerable to MITM. DEV ONLY, do not use in production."));
690 Serial.print(
F(
"Connecting to WiFi"));
692 uint8_t retries = 20U;
693 while ((retries > 0U) && (WiFi.status() != WL_CONNECTED)) {
696 Serial.print(
F(
"."));
699 if (WiFi.status() != WL_CONNECTED) {
700 Serial.println(
F(
"WiFi failed!"));
706 uint8_t calldata[68];
711 uint64_t fetchedNonce = 0U;
713 Serial.println(
F(
"fetchNonce failed! Halting."));
716 tx2.
nonce = fetchedNonce;
727 static const size_t kRlpBufSize = 512U;
728 uint8_t rlpUnsigned[kRlpBufSize];
737 if ((rlpLen == 0U) || (rlpLen > kRlpBufSize)) {
738 Serial.print(
F(
"[fatal] rlpUnsigned overflow or empty: ")); Serial.println(rlpLen);
743 uint8_t hashKeccak[32];
744 keccak256(
static_cast<const uint8_t*
>(rlpUnsigned), rlpLen, hashKeccak);
748 static const uint8_t eth_path[20] = {
749 0x80U,0x00U,0x00U,0x2CU,
750 0x80U,0x00U,0x00U,0x3CU,
751 0x80U,0x00U,0x00U,0x00U,
752 0x00U,0x00U,0x00U,0x00U,
753 0x00U,0x00U,0x00U,0x00U,
757 Serial.println(
F(
"Place Cryptnox card on PN532 reader..."));
759 for (uint8_t attempt = 0U; attempt < 3U; attempt++) {
764 if (!
wallet.connect(session)) {
767 Serial.println(
F(
"Card connected, secure channel established."));
770 signReq.
hash = hashKeccak;
777 signResult =
wallet.sign(signReq);
778 wallet.disconnect(session);
784 Serial.print(
F(
"Card rejected sign command (error=0x"));
786 Serial.println(
F(
") — check PIN and card initialisation. Halting."));
793 Serial.print(
F(
"Sign attempt "));
794 Serial.print(attempt + 1U);
795 Serial.print(
F(
" failed, error=0x"));
800 Serial.print(
F(
"Sign failed: error=0x"));
804 Serial.println(
F(
"Signed."));
807 const uint8_t* s = signResult.
signature + 32;
814 Serial.println(
F(
"yParity determination failed! Halting."));
817 Serial.print(
F(
"yParity: "));
818 Serial.println(yParity);
821 uint8_t rlpSigned[kRlpBufSize];
822 size_t rlpSignedLen =
rlpEncodeSignedTx(tx2, r, s, &yParity, rlpSigned, kRlpBufSize);
824 if ((rlpSignedLen == 0U) || (rlpSignedLen > kRlpBufSize)) {
825 Serial.print(
F(
"[fatal] rlpSigned overflow or empty: ")); Serial.println(rlpSignedLen);
829 Serial.println(
F(
"Sending..."));
830 if (
sendRawTx(rlpSigned, rlpSignedLen)) {
831 Serial.println(
F(
"Transaction sent successfully!"));
833 Serial.println(
F(
"Transaction FAILED."));
void setup()
Arduino setup function.
CryptnoxWallet wallet(nfc, serialAdapter, cryptoProvider, platform)
PN532Adapter nfc(serialAdapter, PN532_SS, &SPI)
ArduinoLoggerAdapter serialAdapter
ArduinoCryptoProvider cryptoProvider
void loop()
Arduino main loop.
#define CW_SIGN_NO_KEY_LOADED
#define CW_SIGN_SIG_ECDSA_LOW_S
#define CW_SIGN_DERIVE_K1
#define CW_SIGN_PIN_INCORRECT
#define PN532_SS_PIN
SPI slave-select (CS) pin connected to the PN532 module.
#define HTTP_OK
Expected HTTP 200 OK status code.
size_t rlpEncodeSignedTx(const Tx2 &tx, const uint8_t *r, const uint8_t *s, const uint8_t *v, uint8_t *out, size_t outCap)
#define ECRECOVER_V_PAD_CHARS
Number of leading zero hex characters in the ecrecover v-field padding.
size_t encodeERC20Transfer(uint8_t *out)
Encode calldata for ERC-20 transfer(address to, uint256 amount).
static const char hexChars[]
static void printHex(const char *label, const uint8_t *data, size_t len)
#define RLP_ITEM_OR_FAIL(BUF, CAP, OFF, IN, IN_LEN)
#define YPARITY_UNKNOWN
Sentinel returned by determineYParity() when recovery fails.
#define WIFI_RETRY_MAX
Maximum WiFi reconnect poll iterations (each iteration waits 500 ms).
#define TX_MAX_RETRIES
Maximum number of send-transaction attempts before giving up.
#define ERC20_INDEX_OFFSET
#define HEX_CHAR_BUF_SIZE
Buffer size for a two-hex-char + NUL string used in byte-to-hex conversion.
static size_t rlpFinalize(uint8_t *out, size_t outCap, const uint8_t *buf, size_t off)
#define ERC20_TRANSFER_SEL_3
#define ECRECOVER_V_BASE
Base value for Ethereum ecrecover v parameter (yParity=0 → v=27, yParity=1 → v=28).
#define ERC20_TRANSFER_SEL_1
size_t rlpEncodeUnsignedTx(const Tx2 &tx, uint8_t *out, size_t outCap)
bool sendRawTx(const uint8_t *raw, size_t len)
Send a raw signed transaction via JSON-RPC.
uint8_t determineYParity(const uint8_t *hash, const uint8_t *r, const uint8_t *s)
Determine EIP-1559 yParity by calling the Ethereum ecrecover precompile.
#define TX_RETRY_DELAY_MS
Delay in ms between send-transaction retry attempts.
uint8_t fetchNonce(uint64_t *nonce)
Fetch the current nonce for ADDR_FROM via eth_getTransactionCount.
#define ERC20_TRANSFER_SEL_0
static size_t rlpEncodeTxBody(uint8_t *buf, size_t bufCap, const Tx2 &tx)
#define ERC20_TRANSFER_SEL_2
CW_CryptoProvider implementation for the Arduino UNO R4 (RA4M1).
CW_Logger implementation wrapping Arduino's HardwareSerial.
static bool safe_memcpy(uint8_t *dst, size_t dstSize, const uint8_t *src, size_t count)
Safe memcpy — validates pointers, sizes, and checks for overlap.
static void secure_wipe(uint8_t *buf, size_t len)
Securely zero a buffer, guaranteed not to be optimised away.
High-level interface for interacting with a Cryptnox Hardware Wallet over NFC.
CW_NfcTransport implementation over the Adafruit_PN532 driver.
void keccak256(const uint8_t *in, size_t inlen, uint8_t out[32])
Compute Keccak-256 hash of input data.
Keccak-256 (SHA3 variant) hash function for Ethereum.
Holds cryptographic session state for reentrant secure channel operations.
Request parameters for CryptnoxWallet::sign.
const uint8_t * derivePath
uint8_t pin[CW_MAX_PIN_LENGTH]
Result of CryptnoxWallet::sign.
uint8_t signature[CW_RAW_SIGNATURE_SIZE]
Ethereum EIP-1559 transaction structure.
uint64_t maxPriorityFeePerGas
size_t trimLeadingZeros(uint8_t *out, size_t out_cap, const uint8_t *in, size_t in_len)
Trims leading zeros from a byte array.
uint32_t RlpEncodeWholeHeader(uint8_t *header_output, size_t header_cap, uint32_t total_len)
Encodes the RLP list header for a sequence of items.
int fromHex(char c)
Convert a hexadecimal character to a byte value.
bool hexToBytes(const char *hex, uint8_t *out, size_t len)
Convert a hex string to a byte array.
uint32_t RlpEncodeItem(uint8_t *output, size_t output_cap, const uint8_t *input, uint32_t input_len)
Encodes a single RLP item.
uint32_t ConvertNumberToUintArray(uint8_t *str, uint64_t val)
Converts an unsigned integer into a big-endian byte array.