Repository navigation
v2.10.0 — NFT drop learnings: safe CLI scripting, bulk mint, rng callback safety
Learnings from an AtomicAssets drop on XPR Network: a proton-tsc claim contract using the rng oracle, a
full-size testnet rehearsal (3,333 assets, 2026-09-24/25), and the mainnet mint that followed (2026-09-25).
Tools: @proton/cli 0.1.98 (source checked against 0.1.99), proton-tsc 0.3.58, @proton/vert 0.3.24,
@proton/web-sdk / @proton/link 5.1.0. MINOR bump: new reference content and some corrected notes. The
measured numbers come from one collection, so treat them as rough.
Added — contracts (smart-contracts.md, testing-debugging.md, troubleshooting.md)
- Known SDK bug:
getCodeHash()inproton-tsc0.3.58 /as-chaindeclaresget_code_hashwith 3 params, but the node intrinsic takes 4.setcodefails withwrong type for imported function get_code_hash. Added a workaround that ran on testnet and mainnet: an@external4-param import, plus the exact 43-byte packed layout (varuint32 struct_version | u64 code_sequence | checksum256 code_hash | u8 vm_type | u8 vm_version, no account field; hash at bytes 9..40). The helper fails safe on any unexpected length or version. - The local test VM does not check intrinsic signatures: vert 0.3.24 implements the same 3-param
get_code_hash, and 15/15 tests passed with the broken import. Added "deploy to testnet early". - Troubleshooting entries for the
get_code_hasherror and for actions that report"status": "executed"on an account with an ABI but no code. - Chain vs vert auth wording: a node says
missing authority of X, vert saysmissing required authority X. Fixed the vert test example, which used the chain wording and would not match. - Testnet checklist: oracle callbacks, a claimer with contract code, and "rehearse at full size".
Added — RNG (oracles-randomness.md)
- The
rngoracle is live on testnet. The callback arrived in about 1–3 s, as a separate transaction. - Best practices: use a never-reused request id as
assoc_idand ignore stale or cancelled callbacks (tested with a cancel and a retry in flight); nocheck()on normal paths inreceiverand(it runs insidesetrand); don't auto-send results to accounts with contract code, which could reject the transfer and stall or re-roll the draw.
Added — CLI (cli-reference.md, testing-debugging.md, SKILL.md, safety-guidelines.md)
contract:setpromptsContinue? (y/N)and has no--yesflag. Scripts must pipeecho y |.contract:setcan deploy the ABI when the WASM is rejected, and still exit 0. Checkget_code_hashafter every deploy (all zeros = no code). Added this check to the automated testing script.- New section, Scripting the CLI for bulk or high-value jobs:
transaction:push/action/contract:setprint errors and exit 0.- The CLI signs with
expireSeconds: 3000. - Header fields you supply (
expiration,ref_block_*) override the CLI's. - How to tell "never broadcast" (the error names a read call) from "rejected by the node" from "ambiguous".
- Resend only after irreversible time passes the expiration, reading state from the same endpoint.
- The selected chain is global: check it before every signing step.
- Don't grep CLI output for
error, because successes include"error_code": null. transaction:pushneeds{"actions":[…]}. Plain action data goes throughproton action.- Buying a
resourcesplan from the CLI (deposit, thenbuyplan). Faucet: 1,000 test XPR per account per 24 h. - SKILL.md safety reminder: a push response is not proof.
Added — accounts (accounts-permissions.md, cli-reference.md)
proton account:create-funded NAME -c CREATOR -k PUB_K1_… -o BACKUP_OWNERcreates accounts with no email step and prints no private key when-kis given. It is the recommended path for agent-driven testnet rehearsals.
Added — resources (staking-governance.md, troubleshooting.md, nfts-atomicassets.md)
buyplanpays from a deposit: transfer XPR toresourcesfirst (credited to the sender inresources::accounts), then callbuyplan. Verified on mainnet with plan 0 (Basic, 744 h).- Troubleshooting for
transaction net usage is too high: X > Y: bulk jobs from one account use up the free NET allowance (~450 KB / 24 h observed on testnet). The node rejected the transaction, so retrying after buying a plan is safe.
Added — NFTs (nfts-atomicassets.md)
- In action data,
ipfs-typed attributes use the variant typestring(["string","Qm…"]), including collectionimgincreatecoldata (the example now shows it).["ipfs",…]fails client-side withtype "ipfs" is not valid for variant. The value must be a CIDv0. - Template
immutable_datakeys must exist in the schema. For collections where every asset is unique, use an empty template withmax_supplyandburnable: false. - New Bulk Minting Safely pattern, used for 3,333 editions on testnet (with a deliberate crash and a real ambiguous push) and on mainnet (167/167 batches, no duplicates):
issued_supplyis the progress counter, and editions are minted in order.- Every attempt is journaled with its own expiration before the push.
- The chain decides the outcome, not the CLI.
- Ambiguous batches are resent only after they can no longer land.
- A lock file, resource checks, and a verify step.
template_mintfrom the AtomicAssets API is back-filled (0 at first). Verify mint order byasset_idinstead, and wait for the indexer count to matchissued_supply.- Measured:
- ~280 B RAM per asset (3,333 assets, ~15 attributes, empty template data).
- ~470–610 µs CPU per 10-asset mint.
- ~5.6 KB NET per 20-asset mint. Free NET ran out after ~80 batches.
Added — RPC (rpc-queries.md, troubleshooting.md)
get_table_rowscan return fewer rows thanlimit(about 1,000 per call observed on AtomicAssetsassets, testnet). A one-page read missed 2,300 of 3,300 rows. Always loop onmore/next_key.- Troubleshooting: A push returned a
transaction_id, but nothing changed on chain. A non-producing API node executes the transaction speculatively against its own state. The producer rejected it, and it was dropped without an error (observed on testnet). Confirm by reading state.
Fixed — web SDK (web-sdk.md, troubleshooting.md, SKILL.md)
@proton/web-sdk5.1.0 is GA (npmlatest). The version note had called 5.x "rc" and recommended pinning@4. Added the 5.x options shape (uiOptions.appInfo.{name,logo,logoRounded},selectorOptionslimited towalletType/enabledWalletTypes), and the advice to match@proton/link@5.1.0. The 4.x examples are kept and labelled.- New pattern: login and transact in separate clicks, with nothing async before
transact()in the click. Awaiting login and then callingsession.transact()in the same click gets the WebAuth signing popup blocked. - The 5.1.0 WebAuth link opens its window synchronously in
transact(). A blocked popup leaveslink.childWindownull, and the promise never settles (added a detection snippet). A window closed by hand also leaves it pending. Treat that as an unknown outcome, not a cancel. storagePrefix: key format documented. Scope saved sessions per chain and contract, so testnet and mainnet builds on one origin don't restore each other's sessions.- 5.1.0 login and cancel shapes (
{ error }return,E_CANCEL/E_WALLET_TYPE,'Closed') replace the "User cancelled" string check.