The shape of it
The one organising principle
Anything that can be tested without a phone or a validator, is. That is not tidiness — it is the only reason most of this repo is verifiable at all. StrongBox needs a handset. LiteSVM needs a built program. So the arithmetic that would otherwise hide inside those is pulled into plain packages with plain tests:
The residue — account plumbing, native modules, RPC — is thin by design, because it is the part that cannot be checked here.
How a sale moves
1
Merchant enters an amount
apps/merchant holds it in minor units as a bigint, so no float ever touches a price. @nelo/pay converts to token base units at the current rate.2
A Solana Pay request is shown
encodeTransferRequest() builds the URL; a random 32-byte reference goes in it as the marker the terminal will watch for.3
The customer pays from any wallet
Nothing Nelo-specific on their side.
4
The terminal validates
awaitPayment() polls for a transaction naming the reference, then validates it against what was asked — right payee, right mint, enough money, no failure. Finding a transaction is not proof of payment: anyone can name your reference.5
It lands in the day-book and the balance
@nelo/ledger groups it by the merchant’s local day. The balance row re-reads the chain.6
Settlement posts to the ledger
services/settle books the sale double-entry: custody up, merchant payable up, fee to revenue, reserve and rebate accrued — one transaction, because it is one event.How an offline sale moves
This is the path the product exists for.1
The payer pre-loads a vault
deposit locks USDC into a PDA on chain. This is the prepaid balance.2
Both phones go offline
The payer’s device holds a P-256 key in StrongBox; the merchant’s device holds cached enrolment and revocation lists.
3
The payer emits a voucher
202 bytes, signed by the secure element over the first 105. Transported as a QR code — it fits version 10 at ECC level M with room to spare.
4
The merchant verifies, with no network
Signature against the carried device key; key in the cached enrolment list; vault not revoked; amount within the floor limit; not expired;
remaining_after consistent with the last balance seen.5
Both reconnect, the voucher redeems
redeem_voucher asserts the secp256r1 precompile verified this key over these bytes, consumes the sequence from a 128-slot replay window, and moves the collateral.6
A replay is refused
Same sequence twice →
SequenceAlreadyRedeemed. Two different vouchers at one sequence → permissionless proof that freezes the vault.What the merchant cannot check offline
Stated plainly rather than implied away: Can, with no network: signature verifies; device key is enrolled; vault is not revoked; amount within the floor limit; not expired;remaining_after is consistent with the last vault balance seen.
Cannot, by definition: whether that sequence was already spent at another stall thirty seconds ago. No offline system can — EMV included.
So the exposure is priced rather than denied. Worst case per vault per offline session is roughly floor_limit × merchants_reached − locked_balance, every attempt is cryptographically attributable to a hardware-attested key, and the vault freezes permanently on the first conflict. The residual is an ordinary insurance line.
Trust boundaries
Never trusted
Nelo’s servers. There is no server in the value path — the chain verifies the secure element directly.
Trusted, and attested
The handset’s StrongBox. Its attestation chain is verified at enrolment; a device without one degrades to online-only rather than falling back to software.
Trusted, and bounded
The risk authority — it publishes prices and reputation. It is held separately from the program upgrade authority, and every input it publishes is range-checked on chain.
Trusted, and declared
The payout partner. Currently a stub that labels itself as one at every call site, so nothing can mistake it for a live disbursement.