Skip to content

Cold wallet: demo account and QR codes for App Review - #717

Merged
n13 merged 3 commits into
mainfrom
n13/cold-wallet-review-qr
Oct 23, 2026
Merged

n13 merged 3 commits into
mainfrom
n13/cold-wallet-review-qr

Conversation

@n13

@n13 n13 commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

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/

  • README.txt: a newly generated 24-word phrase, its account (ML-DSA-87, 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.
  • qr/: one static signing request addressed to that account for each of the 72 calls in the debug call catalogue (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 a part1of2 and part2of2.
  • app-review.pdf (6 pages): the document for Apple. It covers going offline (the wallet blocks while connected, and it names Override for Testing), importing the key, and scanning and approving, followed by ten codes: send, transfer all, two reversible sends, a two-transfer batch, multisig propose/approve/execute carrying a transfer, a tech collective vote and a vesting claim. Each is headed with what the review screen will show.
  • all-calls.pdf (39 pages): the same steps and all 75 codes.
  • qr/refused/ (55 images, not in any PDF): every request the wallet must refuse. That covers the catalogue's refused nesting shapes, the audit payloads (invalidQrData), every call in refusedCallCorpus that 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.
  • qr/near/ (9 requests, 27 images, not in any PDF): the DebugNearPayloads samples, addressed to the ML-DSA-65 account at m/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.
  • generate.sh / generate_review_qr.dart: rebuild everything above. The requests are the exact payloads the simulator's Debug calls list opens, signed for the demo account instead of the vault's first account.

The phrase is public on purpose. The README and PDFs both say never to fund the account.

How the codes are built

  • Each code is the ur:quantus-sign-request v1 envelope ({v, signer, payload}) the hot wallet sends. It goes through encodeUrForQr at 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.
  • Payloads use the Planck genesis and the bundled spec/tx version, so the review screen says Planck and shows no runtime-drift warning.
  • Before writing an image, the generator decodes each request back (UR → envelope → full-policy parse) and fails if the signer is not the demo account.

Verification

  • Decoded all 75 PNGs with Core Image's QR detector. The strings then went through the scanner's own steps (isAcceptableUrPart → isCompleteUr → decodeUr → AnySigningRequest.decode → parsePayload with FullCallPolicy). 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.
  • Decoded all 82 new PNGs the same way. All 54 refused requests that name the demo account fail to parse, and the transfer for another account names a signer the wallet does not hold. The seven NEAR samples meant to sign decode with the demo ML-DSA-65 key. The ed25519 sample is refused as not a Quantus key, and the foreign ML-DSA-65 sample as a key not in this wallet. The generator itself throws if a refused request ever parses.
  • Rendered both PDFs and checked the layout: one page of instructions, then two codes per page.
  • melos run analyze: no issues.
  • Not tested: scanning with a phone camera. Before replying to Apple, import the phrase on a device and scan a code from app-review.pdf on screen.

Running the generator

quantus_sdk/test/build_library.sh once, then cold-wallet-app/tests/generate.sh. It points flutter_rust_bridge at the SDK's library through FRB_DART_LOAD_EXTERNAL_LIBRARY_NATIVE_LIB_DIR and prints the PDFs with headless Chrome or Brave. The folder sits outside test/, so CI does not run it.

n13 added 3 commits October 23, 2026 14:52
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 n13 added the bot-review Request automated review from review-bot label Oct 23, 2026

@n13 n13 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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, and near_display_test.dart, plus three artifact/generator checks.
  • Locked native SDK release build, workspace formatting (864 files unchanged), changed-file formatting, bash -n, and git diff --check passed. 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.

@n13 n13 removed the bot-review Request automated review from review-bot label Oct 23, 2026
@n13
n13 merged commit 6f6a3cc into main Oct 23, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant