Testnet, except where a row says otherwise. Example values are from the checked-in vector file crates/sealforge-core/tests/fixtures/vectors.json (Appendix: test vectors).
All memos are ZIP 302 text memos in the 512-byte memo field (ZIP 302). On the receive side, text is extracted by zcash_protocol::memo::Memo::from_bytes, and only Memo::Text is considered (crates/sealforge-scan/src/lib.rs, crates/sealforge-chain/src/wallet.rs). sealforge-core then trims trailing 0x00 bytes before parsing (trim_end_matches('\0')), so NUL padding from any wallet is accepted.
Auction code
The code is not a security parameter for funds: a bid only counts if paid to that auction’s own escrow address (2.4).
Bid memo: SF1
Grammar (ABNF, RFC 5234):
Limits: the encoded memo (before padding) MUST be ≤ 512 bytes (
MAX_MEMO_BYTES). With the fixed 80-byte prefix, the refund UA can be at most 432 bytes. Decoding uses splitn(4, ':'), so the refund UA is everything after the third colon, and a refund UA containing : is rejected.
The coordinator then checks the refund address: it MUST decode as a Unified Address for the auction’s network with an Orchard receiver (crates/sealforge-coordinator/src/validate.rs shielded_ua). A bid with an unusable refund address still counts; its refund goes to manual review (SECURITY-REVIEW F2).
Code: sealforge_core::memo::BidMemo::{encode, decode}; TS bidMemo in packages/bid/src/memo.ts. Tests: memo::tests::*.
Reveal memo: SF1R (sealed-reveal mode)
- Split on
:into exactly 5 fields, elseBadShape. priceMUST be canonical decimal: empty, signed (-1), or leading-zero (042000000) values are rejected (reveal_memo_round_trips). It MUST parse asu64. Selection then requires1 ≤ price ≤ deposit(2.5).- The reveal is a separate payment to the escrow. The web flow pays
REVEAL_AMOUNT_ZAT = 10_000zat (packages/bid/src/receipt.ts); a counting reveal’s amount is refunded with the deposit (see the testnet tables in evidence/testnet-genesis.md).
sealforge_core::memo::RevealMemo; TS revealMemo.
Fee-buffer memo: SF1FEE
BidMemo::decode rejects it, test fee_buffer_memo_round_trips_and_is_not_a_bid). What isn’t spent on fees returns with the payout. Code: memo::fee_buffer_memo, memo::parse_fee_buffer_memo.
ZIP 321 payment URI
amount: ZEC decimal from integer zatoshi, trailing zeros trimmed (format_zec:100_000_000 → "1",1 → "0.00000001").memo: base64url without padding (RFC 4648 §5), as ZIP 321 specifies for thememoparameter.- One URI per bid, single payment. Payable from any wallet that supports ZIP 321 and shielded memos.
crates/sealforge-core/src/zip321.rs payment_uri; TS packages/bid/src/zip321.ts. Tests: zip321::tests::*, the TS vector test.
Receipt JSON
Status:Testnet. Created and kept in the browser (IndexedDB, apps/web/src/lib/receipts.ts) and offered as a download (sealforge-receipt-*.json). Never sent to the coordinator.
Rust type:
sealforge_core::receipt::Receipt. The web app’s stored form may add wallet_address_index and paid_txid (WALLET-INTEGRATION.md).
Published result
GET /v1/auctions/{code}/result on the coordinator (also proxied by the site at /api/auctions/{code}/result). Available once the auction is published, refunding or settled. Type: sealforge_api::types::PublishedResult (crates/sealforge-api/src/types.rs).
Versioning policy
- Every memo starts with a version prefix.
SF1,SF1RandSF1FEEare the only prefixes defined. - Who rejects unknown prefixes:
sealforge-coredecoders.BidMemo::decodereturnsBadPrefixfor anything butSF1(testrejects_bad_inputusesSF2:), and the selection rules classify such a paymentinvalid(bad_memo)(2.5). An unparseable memo yields no refund address, so the payment goes to manual review rather than an automatic refund (crates/sealforge-coordinator/src/refunds.rsmodule doc). The coordinator, CLI auditor and WASM auditor all use the same decoder, so they agree. - How
SF2would coexist: a future version would get a new prefix and its own decoder, and the auction would record which version it accepts (asbid_modedoes today). AnSF1auction MUST keep treatingSF2memos as invalid, so its result never changes when new versions ship. Commitment personalizations carry their own version (_v1,leaf1), so a format change also changes every hash. The publishedalgorithm_versionnames the clearing rules.Specified: there is noSF2today.