Repository navigation
Cold wallet: demo account and QR codes for App Review - #717
Conversation
App Review asked for a demo QR code (guideline 2.1(a)). cold-wallet-app/tests holds a fresh 24-word phrase, its ML-DSA-87 account, ten static signing requests addressed to that account, and app-review.pdf with the import steps and every code. generate.sh rebuilds all of it from the debug call catalogue, checking that each request decodes back to the account before writing it.
qr/ now holds a request addressed to the demo account for each of the 72 calls the debug catalogue offers, and all-calls.pdf carries all of them after the same import steps. The three 16-transfer batches do not fit one code, so each has two static parts that the scanner accumulates. app-review.pdf keeps its ten, picked from the same set; the README marks them.
qr/refused/ holds every request the wallet must refuse: the catalogue's refused shapes and audit payloads, every call naming an address form other than a plain account id, and a transfer for an account it does not hold. qr/near/ holds the NEAR samples, signed by the demo phrase's ML-DSA-65 account. Both are images only, outside the PDFs. The generator checks each refusal and writes the reason the wallet gives into the README.
n13
left a comment
There was a problem hiding this comment.
Verdict: Approve
Reviewer: GPT-6.1 Sol
Reviewed head 93ed03edc8028942a89ba9752fadf0413d53111e against base 19fe2e16aebec5fa321b3fb94eb46f20c95e7a9b. No blocking findings.
The generator reuses the debug catalogue, signing-request envelopes, UR frame sizing, and full parser policy. Both mnemonic-derived accounts and their checkphrases match the documentation. The app change only exposes the existing corpus mapper.
Validation:
- Independently decoded all 157 committed PNGs and passed their contents through the scanner's UR checks and request decoder. All 72 Quantus requests match the catalogue, parse for Planck with compatible bundled versions, and produce verifiable signatures with the demo key. The 54 malformed/policy-refused requests, one foreign signer, seven signable NEAR samples, and two refused NEAR samples match the documented outcomes. Multipart requests remain incomplete until all required parts are present.
- Rendered both PDFs and decoded all 10 App Review codes and all 75 full-catalogue codes; their contents match the committed PNGs. Inspected the instructions and representative layouts.
- Ran the actual generator in scratch storage: its README and all 157 PNGs reproduce the committed artifacts byte for byte.
- Passed 237 focused tests across
transaction_signing_test.dart,every_call_renders_test.dart,audit_regressions_test.dart, andnear_display_test.dart, plus three artifact/generator checks. - Locked native SDK release build, workspace formatting (864 files unchanged), changed-file formatting,
bash -n, andgit diff --checkpassed. GitHub Analyze and dependency-cooldown checks passed.
Local Flutter checks used the installed 3.44.4 SDK with cached dependencies and its six SDK-constrained dependency versions; the repository pins 3.47.7, so exact-lockfile bootstrap was unavailable locally. Local monorepo analysis was incomplete: the cached configuration lacked integration-test bindings, and the run was stopped at the required 10-second cap. Physical phone-camera scanning remains untested.
App Review rejected Quantus Cold Wallet 1.0 (20) under guideline 2.1(a): "We need a demo QR code to fully verify the app's functionality." This adds what they need, plus a code for every call the wallet can review, for our own device testing.
What's in
cold-wallet-app/tests/m/44'/189189'/0'/0'/0',qzkiyWnN4MCXeALXfXeNckSK44Mcoyteia7ZE9mh5MUJpSyjg, checkphrase Saddle-Chime-Goose-Fond-Alpha), the import and signing steps, and every code with what the review screen shows.DebugPayloads.byPallet), which is the corpus of every runtime call plus the composed multisig and batch wrappers. That makes 75 images: the three 16-transfer batches are too big for one code, so each has apart1of2andpart2of2.invalidQrData), every call inrefusedCallCorpusthat names an address form other than a plain account id, and a transfer addressed to an account the wallet does not hold. The README lists the reason the wallet gives for each.DebugNearPayloadssamples, addressed to the ML-DSA-65 account atm/44'/189189'/0'/0'/1'from the same phrase (qzoT1TSZ9GjQDqJYGdiMWGkZKgQKWiSnWi7pbvxBqjBVMLtUf, Marble-Cotton-Float-Donkey-Host). The README says how to add that account. Each request carries the 1952-byte key, so most take 3 or 4 static parts.The phrase is public on purpose. The README and PDFs both say never to fund the account.
How the codes are built
ur:quantus-sign-requestv1 envelope ({v, signer, payload}) the hot wallet sends. It goes throughencodeUrForQrat error correction L, the level that function sizes frames for. The frame size is set to the QR maximum, so a request becomes one static code whenever it fits. Codes are static only, no animation: the scanner keeps frames across scans, so a two-part request completes when both parts have been scanned.Verification
isAcceptableUrPart→isCompleteUr→decodeUr→AnySigningRequest.decode→parsePayloadwithFullCallPolicy). All 72 requests land on the review screen for the demo account, network Planck, spec matching the bundled one. For the two-part ones, part 1 alone is incomplete and both parts together complete.melos run analyze: no issues.app-review.pdfon screen.Running the generator
quantus_sdk/test/build_library.shonce, thencold-wallet-app/tests/generate.sh. It points flutter_rust_bridge at the SDK's library throughFRB_DART_LOAD_EXTERNAL_LIBRARY_NATIVE_LIB_DIRand prints the PDFs with headless Chrome or Brave. The folder sits outsidetest/, so CI does not run it.