Loading
PRICE DISCUSSION DOCS
APP
Updated 2026-10-12·View as Markdown

Contracts — PDStickers

PDStickers is the sticker economy's on-chain rail: one ERC-1155 contract holding every sticker and every sealed sheet, with the artwork on-chain: each sticker's full SVG, or, for PD's built-in stickers, a few bytes of parameters that the immutable PDStickerRenderer draws into the same SVG at read time. It follows the protocol's house rules — money never rests in it, metadata is self-contained, logic is immutable — and it reads the same factory the art contracts read, so the platform wallet rotates in exactly one place. Its companion, StickerSplitter, is the royalty vault (a three-way sibling of the Project PaymentSplitter).

The token model

Two kinds of token share the contract, in namespaces that cannot collide:

Token ID space What it is
Sticker 1 … (≤ 99 per sheet) One sticker design; balance = how many you hold
Sealed sheet 2^128 + sheetId An unopened sheet of its type — identical copies stack

Buying mints sealed sheets; peel burns them and mints the stickers inside. Because identical sealed sheets share one token ID, a hundred unopened packs are a single wallet line (×100), marketplaces list them as one item, and a collection stays clean until its owner chooses the reveal. uri(id) answers for both kinds with base64 JSON whose image is the on-chain SVG as a data URI — sealed sheets return their wrapper art — with nothing off-chain (a built-in sticker's SVG is drawn by PDStickerRenderer, itself on-chain and immutable). The collection is Price Discussion Stickers (name() and contractURI()), and every token is named for its kind and number: a sealed sheet is Price Discussion Sticker Sheet #<sheetId>, a sticker Price Discussion Sticker #<id>. Both kinds carry Sheet and Type traits, and a sticker's own name is its Name trait (JSON-escaped).

Purchase — exact payment, everything pushed

function purchaseSheet(uint256 sheetId, uint256 quantity) external payable;

Requires the sheet active, within its print run (maxSheets — 0 means open), and exact payment (priceWei × quantity). At most 100 sealed sheets per purchase. Every share is force-sent inside the purchase transaction, before the mint, so a payee that refuses ETH can never block a sale and nothing is left in the contract when the purchase ends:

A sheet purchase splits in-transaction: 5% to the platform wallet read live from the factory; the 95% creator side pays the collaborator's locked share first and the admin takes the remainder. purchaseSheet() exact ETH in PLATFORM · 5% factory.platformWallet(), read live — same rate as mints COLLABORATOR · their locked share collabBps of the 95% creator side, when the sheet has one ADMIN · the remainder the creator side after the collaborator's slice

The split uses the Project split's rates: 5% platform off the top (the wallet read live from factory.platformWallet(), the same rotation point mints pay), the remaining 95% is the creator side, and a sheet's collaborator — locked at createSheet, immutable forever — takes their collabBps slice of that side first. The platform share is floored, so the rounding wei goes to the creator side.

Peel — FIXED and PACK

function peel(uint256 sheetId, uint256 count) external payable returns (uint256 packPeelId);   // ≤ 10 per transaction
function revealPack(uint256 peelId) external;   // PACK only — anyone, from the next block

Burns count sealed sheets and delivers the stickers inside, one batch — at the peel for a FIXED sheet, at the reveal for a PACK. A FIXED peel sends no ETH. A PACK peel sends exactly count × revealFeeWei, the reveal fee, pushed whole and force-sent to the factory's storage-fee wallet, the wallet that sends the reveal and pays its gas; the admin can tune it only inside a corridor fixed in the code (0.00005–0.002 ETH per copy), and a peel sent at a stale fee reverts. What arrives depends on the sheet's mode, fixed before it ever went on sale:

  • FIXED (pullSize == 0) — one of every sticker in the sheet, per sealed copy. Supply was reserved at purchase, so a sealed FIXED sheet is unconditionally peelable, forever.
  • PACK (pullSize > 0) — each sealed copy draws pullSize distinct stickers from the sheet's pool. The draw is commit-then-reveal, the same recipe as PDProject and the keychain crank: peel burns the sealed copies and commits the draw to its own block, whose hash doesn't exist while the peel runs; revealPack(peelId) (or revealPacks) draws from that hash and delivers from the next block on, each sheet's peels strictly in the order they were committed, so nobody can reorder reveals to steer a capped sticker's last copy. Anyone may reveal; the hash stays readable for 8,191 blocks (about 27 hours) through the EIP-2935 history contract, and past that the peel re-commits to a fresh block. No wallet, contract or smart account can see a draw and revert it (only the builder of the commit block can grind its hash), and delivery goes straight to the keeper's balance, so refusing a draw can't buy a re-roll either. Capped stickers leave the pool as they exhaust: supply is the rarity. While a PACK sheet is live or owes any copy a draw, at least pullSize open-edition stickers must stay active (enforced at activation and whenever a sticker is retired), so no sealed pack can ever become unpeelable by exhaustion.

Royalties — StickerSplitter

royaltyInfo signals 5% on every secondary sale — stickers and sealed sheets — paid to a StickerSplitter vault carrying the Project split: 60/40 creator-side/platform, i.e. 3% / 2% of the sale, identical to the art market. Solo sheets share one vault (deployed in the constructor, creator side to the admin); each collab sheet gets its own vault at createSheet, splitting the creator side on the sheet's locked terms. StickerSplitter inherits the PaymentSplitter design wholesale: empty receive(), cumulative accounting with monotonic entitlements (no stranded wei, no underflow), permissionless withdrawals that can only pay the sheet's collaborator (immutable), the shop's current admin or the factory's current platform wallet, independent ETH and per-token ERC-20 legs, and the platform side reading the factory live, so a platform-wallet rotation also moves every share still unwithdrawn. Each vault also has redeemBlurPool(), which turns Blur Pool royalties into ETH for the ETH split.

Governance and lifecycle

Sheets are admin-published: createSheet (an inactive shell with its sealed-wrapper SVG and locked collab terms) → addSticker (a full SVG) or addGeneratedSticker (renderer parameters, checked by the renderer before they're stored) × N (a sticker's home sheet is permanent) → optional setSheetPull → setSheetActive(true). A live sheet's pool never grows — restocking means deactivating, appending, reactivating; a PACK draw reads the pool at reveal, so a restock or a sticker toggle also reaches peels committed but not yet revealed — and a sealed copy always settles on exactly the terms it was sold under: while any sealed copies are outstanding, a FIXED sheet's pool cannot grow (its supply was reserved at purchase, so a new sticker would have nothing reserved for it) and no sheet's FIXED/PACK mode can be switched. PACK sheets restock freely — they draw at reveal and check supply there. setSheetPrice retunes a sheet at any time (the ~$22 target outlives ETH drift) and applies only to the next purchase. The admin is transferable by the same two-step propose/accept the factory uses (proposing the zero address withdraws a proposal); collab vaults re-route their admin side instantly, since they read admin() live. There is no receive() — withdraw() exists only to sweep force-sent dust.

Reads worth knowing

Function Returns
sheets(sheetId) The whole sheet record: pool, price, print run, sold/peeled, mode, collab terms, vault
sealedTokenId(sheetId) The sealed token ID — what balanceOf takes
sealedOutstanding(sheetId) Sealed copies not yet delivered: still sealed, or a pack peel waiting on its reveal
packPeels(peelId) A committed pack peel awaiting its reveal: keeper, seed block, count, sheet, place in the sheet's reveal line
poolAvailable(sheetId) Stickers currently drawable (active, supply left)
canPurchaseSheet(sheetId) One-call pre-flight: can this sheet be bought right now
getStickerSVG(id) / getSheetCoverSVG(id) The on-chain art (a built-in sticker's is drawn by the renderer at read time)
generated(id) / renderer Whether a sticker is drawn from parameters, and the renderer that draws it
revealFeeWei The PACK reveal fee per sealed copy, read live

Status

PDStickers and StickerSplitter are part of the security-reviewed contract suite and covered by the repository's Foundry tests. An earlier revision was deployed to Sepolia and exercised end-to-end (purchase splits verified to the wei, peel, royalty vault withdrawals, collab sheets, and a real marketplace ERC-1155 sale against the sealed sheet); the launch build, with force-sent payouts, the 100-sheet purchase cap and in-order pack reveals, is rehearsed on Sepolia again before mainnet. Mainnet deployment rides the platform's launch.

Further reading