---
name: stampooor-pass
description: Mint, reveal and claim Stampooor Pass (STPASS), up to 5,555 stamp NFTs on BNB Chain (chainId 56) that pay out POSTAGE tokens. Use when the user wants to mint a Stampooor Pass, start or finish its reveal, look up a Pass tier, or claim POSTAGE for a revealed Pass.
metadata:
  version: 1.0.0
  chainId: 56
  triggers: [mint Stampooor Pass, reveal Stampooor Pass, claim Stampooor Pass, claim POSTAGE]
  requires:
    wallet: an account that can send transactions on BNB Chain (chainId 56)
---

# Stampooor Pass skill

Related files: llms.txt (overview and reference) | skill.md (step-by-step instructions for agents) | agent.json and .well-known/agent.json (the same data as JSON, identical copies) | index.html (the mint page).

Use this skill to mint a Stampooor Pass, to start or finish the reveal, and to claim POSTAGE. Each step is a contract call on BNB Chain. The rules for each step are in the sections below, and the same data is in llms.txt and agent.json.

## Addresses (BNB Chain, chainId 56)

- Pass contract (ERC-721, STPASS): `0xCFB72b0E311796C478c6b74C1cd4592BbAa17aab`
- POSTAGE token (ERC-20, 18 decimals): `0x6C310EABEdFD4eEd6148bf2C07241FB45a52FB42`
- PAYEE (a fixed address set at deployment): `0xD48Bcb88F43701C47dFDF3955da0A943d3B7860d`
- Agent ID registry: `0x60B9262456dE011F7d15a54CdD8c48bAaa8B022F`
- Official website: `https://stampooor.com` (the same address as external_url in every tokenURI).
- Mint end (Unix seconds): `1794621650`. Deployment block: `132500907`.
- Verify: https://bscscan.com/address/0xCFB72b0E311796C478c6b74C1cd4592BbAa17aab#code | https://bscscan.com/address/0x6C310EABEdFD4eEd6148bf2C07241FB45a52FB42#code | https://repo.sourcify.dev/contracts/full_match/56/0xCFB72b0E311796C478c6b74C1cd4592BbAa17aab/ | https://repo.sourcify.dev/contracts/full_match/56/0x6C310EABEdFD4eEd6148bf2C07241FB45a52FB42/

## Steps

1. Verify. Confirm chainId 56. Confirm that the Pass and POSTAGE source is verified on BscScan or Sourcify (links above) and that TOKEN() and PAYEE() on the Pass contract return the addresses listed here. If anything differs, stop.
2. Quote. Read quote(yourWallet, qty) with the address that will send the transaction. Continue only when ok is true. Note freeUnits, paidUnits and valueWei (freeUnits is 0 for every wallet once the 1,111 free passes are taken). Re-read it when the wallet, chain or quantity changes.
3. Report and simulate. If paidUnits is above 0, report the amount (valueWei, in BNB) to your owner before sending. To take only the free pass, call mint() with value 0 when freeAvailable(yourWallet) is 1. Otherwise simulate mint(qty) with value = quote.valueWei, or mint(qty, referrer) when you were given a referrer who has minted before. Check the chain, the destination (the Pass contract), the exact value, and that the transaction has no approve calls or authorization changes. Follow your wallet's transaction policy.
4. Submit and check. Submit the simulated transaction and wait for a successful receipt. Expected changes: Pass balance +qty, mintedBy(wallet) +qty, totalMinted +qty, POSTAGE +10,000 x qty (see After a successful mint). If the submission result is unclear, inspect the original transaction before sending again.
5. Reveal. After the sale ends (mintOpen() is false), read revealed() and revealBlock(). If revealBlock is 0, call reveal() (it records a block). Once the chain is past revealBlock, call reveal() again within revealBlock + 8000 blocks (about 1 hour) to finish. If that window has passed, call reveal() to record a new block and repeat. Simulate before each call. Anyone can do this.
6. Claim. After revealed() is true, list your ids with ownedIds(wallet, startId, 1000) for startId 1, 1001, 2001, 3001, 4001, 5001 (up to totalMinted). Keep ids where claimed(id) is false. Read claimable(ids), simulate claim(ids) with at most 100 ids, then submit it. Check that your POSTAGE balance rose by that amount and claimed(id) is true.

## Verifiable contract properties

- No owner, no admin, no pause function and no price-change function; owner() returns the zero address. The contracts are not upgradeable.
- The pass cap (5,555), the paid price (0.001 BNB), the free-pass cap (1,111) and the POSTAGE supply (1,000,000,000 at deployment) are fixed in code.
- POSTAGE has no mint function after its constructor. Any holder can burn its own POSTAGE with burn(amount).
- withdraw() sends the contract's BNB to PAYEE only, and anyone can call it. PAYEE is a fixed address set at deployment. The only other BNB the Pass contract sends is the refund of an overpayment to the minter, in the same transaction.
- tokenURI is generated on-chain (base64 JSON with an SVG image).

## Mint

- Supply: up to 5,555 passes, ids 1 to 5,555, minted in order.
- Sale window: opens at deployment and ends at mintEnd or when sold out. The sale length is fixed at deployment, from 3 to 14 days. Read mintEnd(); it is authoritative.
- Price: PRICE = 0.001 BNB (1000000000000000 wei) per paid pass. price() returns the same value as PRICE().
- Limit: 15 lifetime mints per wallet, free included (MAX_PER_WALLET, counted by mintedBy(wallet), not by current holdings).
- Calls: mint() with no arguments is the same as mint(1); mint(uint256 qty) and mint(uint256 qty, address referrer) take a quantity.
- Free passes: the first 1,111 free passes (FREE_SUPPLY) go to wallets that hold a BNB Chain Agent ID: each such wallet can take one free pass (FREE_PER_WALLET = 1; once per wallet and once per handle) while the first 1,111 free passes last (freeMinted() below FREE_SUPPLY). A wallet that bought paid passes earlier can still take its free pass (until the 15-mint wallet limit is reached). Read freeMinted() and FREE_SUPPLY() for the progress. Once the 1,111 free passes are taken, freeAvailable() returns 0, quote() returns freeUnits=0 and every pass costs PRICE. The Agent ID registry is `0x60B9262456dE011F7d15a54CdD8c48bAaa8B022F`; the contract reads `balanceOf(address) returns (uint256)` (must be above 0) and `handleOf(address) returns (string)`. The free pass is available once per wallet and once per non-empty handle. If handleOf fails or returns an empty string, the wallet check alone applies. freeAvailable(wallet) returns 0 or 1 and already includes the Agent ID, wallet, handle, free-supply and wallet-cap checks, and returns 0 once the sale has closed.
- Anyone can buy paid passes: no Agent ID is needed.
- Smart-contract wallets can mint. Minting uses _mint, so no receiver callback runs. A wallet that sends more than the required value must be able to receive the BNB refund, otherwise that mint reverts. Free eligibility is read from the calling address itself.
- Payment: the required value is paidUnits x PRICE, which is quote().valueWei. msg.value must be at least that. Anything above it is returned to the caller in the same transaction, so sending unit price x qty also succeeds and only the paid passes are charged. The recommended value is quote().valueWei. A value above 0 and below it reverts with WrongValue. A call with value 0 is the free claim (next line). Mints are final.
- Free claim: call mint() or mint(qty) with value 0. You get freeAvailable(wallet) passes, which is 0 or 1 (FREE_PER_WALLET). If qty is larger than that, the call mints only the free pass and does not revert. With value 0 and freeAvailable(wallet) equal to 0, the call reverts with WrongValue. mint(qty, referrer) with value 0 behaves the same way, and a free pass earns no referral reward.
- Paid: send value = quote(wallet, qty).valueWei, or PRICE x qty (the excess is refunded). A call with a value above 0 mints exactly qty passes.
- quote(wallet, qty) describes the paid path. It is not a preview of a value-0 call; to see what a value-0 call gives you, read freeAvailable(wallet).
- Mint bonus: every minted pass, free or paid, sends 10,000 POSTAGE to the minter in the same transaction.
- Referral: mint(qty, referrer) sends 5,000 POSTAGE per paid pass to the referrer in the same transaction, from the 20,000,000 POSTAGE referral reserve. It applies when the referrer is not the zero address, is not the minter, has minted before (mintedBy(referrer) above 0), the mint includes at least one paid pass, and the reserve is not used up. If any of these does not hold, the mint goes through as if no referrer was given and does not revert. If the reserve holds less than the reward, the remainder is paid. The website uses `?ref=0x...` links.

### quote

`quote(address wallet, uint256 qty)` returns `(uint256 freeUnits, uint256 paidUnits, uint256 valueWei, bool ok, string reason)`. Pass the address that will send the transaction. When ok is false, do not send. reason is one of:

- OK
- QTY_ZERO: qty is 0
- REVEALED: the reveal has started or finished
- CLOSED: mintEnd has passed
- SOLD_OUT: all 5,555 passes are minted
- EXCEEDS_SUPPLY: qty is above the passes left
- WALLET_CAP: the wallet would go above 15 lifetime mints

A quantity above a limit returns ok=false; it is never trimmed to a smaller number. Near the end of the free tier, another wallet can take the last free stamp between your quote and your mint; sending value = PRICE x qty always covers the price, and anything above it is refunded in the same transaction. When ok is false, freeUnits, paidUnits and valueWei are all 0. State can change between the quote and your transaction (another buyer takes the last passes, or the same handle uses its free pass first), so simulate the exact transaction before sending.

When the 1,111 free passes are taken (freeMinted() equals FREE_SUPPLY), quote returns freeUnits=0, paidUnits=qty and valueWei=qty x PRICE for every wallet.

Example eth_call result for freeUnits=1, paidUnits=2, valueWei=2000000000000000, ok=true, reason="OK" (decode as `(uint256,uint256,uint256,bool,string)`):

`0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000071afd498d0000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000024f4b000000000000000000000000000000000000000000000000000000000000`

### After a successful mint(qty)

- Your Pass balance (balanceOf) is up by qty; the new ids are firstId to firstId+qty-1 (event Minted).
- mintedBy(wallet) is up by qty, and totalMinted (and totalSupply) is up by qty.
- Your POSTAGE balance is up by 10,000 x qty.
- After a value-0 call, read qty above as the number of passes actually minted (the free pass, at most 1); the Minted event shows it.
- Every minted pass emits one ERC-721 Transfer(address(0), minter, tokenId) event, for free, paid and batch mints alike, so a mint of qty passes emits qty Transfer events plus one Minted event.
- BNB spent is quote().valueWei plus network gas. Gas grows with qty.
- With a qualifying referrer, the referrer's POSTAGE is up by 5,000 x paidUnits (event Referred).

## Reveal

- Open to any address once the sale has ended (mintEnd has passed, or all passes are minted). Claiming starts after it finishes.
- Call 1: reveal() records revealBlock = current block + 5.
- Call 2: reveal() finishes the reveal when it lands in a block after revealBlock and no later than revealBlock + 8000. BNB Chain blocks come about every 0.45 seconds, so the window is about 1 hour and the wait for call 2 is a few seconds.
- A call 2 in a block at or before revealBlock reverts with RevealTooEarly. If the window has passed (block above revealBlock + 8000), the next reveal() call records a new revealBlock instead (event RevealCommitted); then repeat call 2. Simulate before each call.
- Before the sale has ended reveal() reverts with RevealLocked; after it finished it reverts with AlreadyRevealed.
- Randomness: startingIndex = keccak256(blockhash(revealBlock), contract address) mod 5555. The contract reads revealBlock's hash with BLOCKHASH, or for blocks older than 256 from the EIP-2935 history contract `0x0000F90827F1C53a10cb7A02335B175320002935`; if the history contract has no code or answers 0, the call records a new revealBlock instead; if the history call fails or answers in a wrong format, reveal() reverts with HashReadFailed and nothing changes.
- Tier slots follow a fixed permutation: pos = (2357 x (id - 1) + startingIndex) mod 5555. pos 0 to 7 is Inverted, 8 to 32 Airmail, 33 to 182 Registered, 183 to 1182 Express, 1183 to 3054 First Class, 3055 to 5554 Postcard. So each tier has exactly the count in the tier table, whatever startingIndex is. The counts apply to all 5,555 ids.
- Before the reveal finishes, tierOf and rewardOf revert with NotRevealed, claimable returns 0 and claim reverts.

## Claim

- After the reveal, the current holder of a minted id claims its tier's POSTAGE once per id. There is no deadline.
- claim(uint256[] ids) takes 1 to 100 ids. Every id must be minted, held by the caller and not yet claimed; if any id fails (BadId, NotOwner or AlreadyClaimed, including a repeated id), the whole batch reverts. The POSTAGE goes to the caller, who is the holder.
- claimable(uint256[] ids) returns one uint256: the POSTAGE (18-decimal integer) of the minted, unclaimed ids in the list. It does not check who holds them. It returns 0 before the reveal.
- tierOf(id) returns a uint8: 0 Inverted, 1 Airmail, 2 Registered, 3 Express, 4 First Class, 5 Postcard. rewardOf(id) returns the POSTAGE in 18-decimal integers (for example 75000000000000000000000 for a Postcard). tierName, tierCount and tierReward give the same table by tier index.
- Find your ids with ownedIds(owner, startId, count): count is at most 1000, so call it with startId 1, 1001, 2001, 3001, 4001 and 5001 (up to totalMinted), at most 6 calls.

## POSTAGE supply and burns

POSTAGE has 18 decimals. All 1,000,000,000 POSTAGE are created once at deployment.

| Allocation | POSTAGE | Held at |
|---|---:|---|
| Holder reserve (reveal claims) | 582,700,000 | Pass contract |
| Mint bonus reserve (10,000 per pass) | 55,550,000 | Pass contract |
| Referral reserve | 20,000,000 | Pass contract |
| PAYEE, for liquidity and treasury | 341,750,000 (34.175%) | PAYEE, at deployment |
| Total at deployment | 1,000,000,000 | |

341,750,000 POSTAGE (34.175%) at deployment to PAYEE, for liquidity and treasury. PAYEE is a fixed address set at deployment.

- After the reveal, anyone can call releaseUnminted() once. It burns the POSTAGE of ids that were never minted (total supply decreases). POSTAGE of minted ids stays in the contract until it is claimed.
- After the sale ends, anyone can call releaseLeftover() once. It burns the mint bonus and referral reserves that were not used (total supply decreases).
- None of the burned POSTAGE goes to PAYEE.

| Tier index | Tier | Ids in the tier (of 5,555) | Claim POSTAGE per pass |
|---:|---|---:|---:|
| 0 | Inverted | 8 | 1,000,000 |
| 1 | Airmail | 25 | 500,000 |
| 2 | Registered | 150 | 250,000 |
| 3 | Express | 1,000 | 150,000 |
| 4 | First Class | 1,872 | 100,000 |
| 5 | Postcard | 2,500 | 75,000 |

## Calldata examples

Hex calldata for the Pass contract. Value is the BNB sent with the transaction.

| Call | Calldata | Value |
|---|---|---|
| `mint()` | `0x1249c58b` | same as mint(1): quote(wallet, 1).valueWei, which is 0 when freeAvailable(wallet) is 1, otherwise 1000000000000000 wei; a call with value 0 takes the free pass when freeAvailable(wallet) is 1 |
| `mint(1)` | `0xa0712d680000000000000000000000000000000000000000000000000000000000000001` | quote().valueWei: 0 when freeAvailable(wallet) is 1, otherwise 1000000000000000 wei |
| `mint(3)` | `0xa0712d680000000000000000000000000000000000000000000000000000000000000003` | quote().valueWei: 2000000000000000 wei when freeAvailable(wallet) is 1 (2 paid), 3000000000000000 wei without; a value-0 mint(3) from such a wallet mints only its 1 free pass |
| `mint(3, 0x1111111111111111111111111111111111111111)` | `0x94bf804d00000000000000000000000000000000000000000000000000000000000000030000000000000000000000001111111111111111111111111111111111111111` | same value as mint(3); the referrer is 0x1111111111111111111111111111111111111111 (example address) |
| `quote(0x2222222222222222222222222222222222222222, 1)` | `0x8f79306e00000000000000000000000022222222222222222222222222222222222222220000000000000000000000000000000000000000000000000000000000000001` | eth_call, no value; the wallet is 0x2222222222222222222222222222222222222222 (example address) |
| `claim([5,9])` | `0x6ba4c1380000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000009` | no value |

Send value = quote().valueWei. A generic mint tool that sends unit price x qty also succeeds, because any excess is refunded in the same transaction.

## Function signatures and selectors

Selectors are the first four bytes of Keccak-256 of the canonical signature (not SHA3-256). These are the final ABI of the deployed contracts.

### Sale and minting

| Signature | Selector | Mutability | Returns | Unit / note |
|---|---|---|---|---|
| `mint()` | `0x1249c58b` | payable | `()` | payable; same as mint(1); value 0 takes the free pass when freeAvailable(wallet) is 1, otherwise send value = quote(wallet, 1).valueWei (a larger value is refunded) |
| `mint(uint256)` | `0xa0712d68` | payable | `()` | payable; send value = quote().valueWei (a larger value is refunded); value 0 mints only the free pass, or reverts with WrongValue when freeAvailable(wallet) is 0 |
| `mint(uint256,address)` | `0x94bf804d` | payable | `()` | payable; second argument is the referrer |
| `quote(address,uint256)` | `0x8f79306e` | view | `(uint256 freeUnits,uint256 paidUnits,uint256 valueWei,bool ok,string reason)` | valueWei is BNB wei; reason is one of the codes listed above |
| `freeAvailable(address)` | `0x9a5f5d31` | view | uint256 | 0 or 1 free pass in the wallet's next mint (Agent ID, wallet, handle, free-supply and wallet-cap checked); returns 0 after the sale closes |
| `mintedBy(address)` | `0x3cef28d2` | view | uint256 | passes minted by the wallet, lifetime, free included |
| `mintOpen()` | `0x24bbd049` | view | bool | true while the sale is open |
| `soldOut()` | `0x893da6c9` | view | bool | true when totalMinted has reached the cap |
| `totalMinted()` | `0xa2309ff8` | view | uint256 | passes |
| `totalSupply()` | `0x18160ddd` | view | uint256 | passes (equals totalMinted) |
| `freeMinted()` | `0xd10a1a2b` | view | uint256 | free passes minted so far; at most FREE_SUPPLY() |
| `FREE_SUPPLY()` | `0x9858cf19` | view | uint256 | passes: the most free passes in total (1,111) |
| `FREE_PER_WALLET()` | `0xcd90e5af` | view | uint256 | free passes one wallet (and one handle) can take (1) |
| `paidMinted()` | `0x2806b8fb` | view | uint256 | passes |
| `mintStart()` | `0x255e4685` | view | uint256 | Unix seconds |
| `mintEnd()` | `0xea2b4ab2` | view | uint256 | Unix seconds |
| `MAX_SUPPLY()` | `0x32cb6b0c` | view | uint256 | passes |
| `PRICE()` | `0x8d859f3e` | view | uint256 | BNB wei per paid pass |
| `price()` | `0xa035b1fe` | pure | uint256 | BNB wei per paid pass; the same value as PRICE() |
| `MAX_PER_WALLET()` | `0x0f2cdd6c` | view | uint256 | passes, lifetime, free included |

### Reveal

| Signature | Selector | Mutability | Returns | Unit / note |
|---|---|---|---|---|
| `reveal()` | `0xa475b5dd` | nonpayable | `()` | two calls; see Reveal |
| `revealBlock()` | `0x66bb81c7` | view | uint256 | block number; 0 before the first reveal() call |
| `revealed()` | `0x51830227` | view | bool | true once the reveal is finished |
| `startingIndex()` | `0xcb774d47` | view | uint256 | set at reveal; 0 to 5554 |
| `mintedAtReveal()` | `0x7141375e` | view | uint256 | passes minted when the reveal finished |
| `REVEAL_DELAY()` | `0xa7bc4e30` | view | uint256 | blocks (5) |
| `REVEAL_WINDOW()` | `0x17e931cf` | view | uint256 | blocks (8000) |
| `HISTORY_STORAGE()` | `0x72cb2273` | view | address | EIP-2935 block-hash history contract |
| `STRIDE()` | `0x41cd8781` | view | uint256 | tier permutation step (2357) |
| `TIERS()` | `0x6e748f97` | view | uint256 | number of tiers (6) |

### Tiers and claiming

| Signature | Selector | Mutability | Returns | Unit / note |
|---|---|---|---|---|
| `tierOf(uint256)` | `0x53f96df2` | view | uint8 | tier index 0 to 5; reverts NotRevealed before reveal, BadId for 0 or above 5555 |
| `rewardOf(uint256)` | `0xdaa3770c` | view | uint256 | POSTAGE, 18-decimal integer (wei scale) per pass; reverts like tierOf |
| `tierName(uint256)` | `0xddd641b0` | pure | string | name for a tier index |
| `tierCount(uint256)` | `0x48380a10` | pure | uint256 | ids in the tier, out of 5,555 |
| `tierReward(uint256)` | `0xedcfa519` | pure | uint256 | POSTAGE, 18-decimal integer (wei scale) per pass |
| `claimable(uint256[])` | `0xf53bd669` | view | `uint256 total` | POSTAGE, 18-decimal integer (wei scale), total; 0 before reveal; ownership is not checked |
| `claim(uint256[])` | `0x6ba4c138` | nonpayable | `()` | 1 to 100 ids; the caller must hold every id; POSTAGE goes to the caller |
| `claimed(uint256)` | `0xdbe7e3bd` | view | bool | true once the id has been claimed |
| `claimedAmount()` | `0x9668ceb8` | view | uint256 | POSTAGE, 18-decimal integer (wei scale), claimed in total |
| `ownedIds(address,uint256,uint256)` | `0xbf598e1e` | view | `uint256[] ids` | ids in [startId, startId+count) held now by the owner; count at most 1000 |
| `MAX_CLAIM_BATCH()` | `0x00082645` | view | uint256 | ids per claim (100) |
| `MAX_OWNED_SCAN()` | `0x016553c2` | view | uint256 | ids per ownedIds call (1000) |

### Burns and BNB

| Signature | Selector | Mutability | Returns | Unit / note |
|---|---|---|---|---|
| `releaseUnminted()` | `0xf7d7df1e` | nonpayable | `()` | after reveal, once, anyone: burns the POSTAGE of ids that were never minted |
| `releaseLeftover()` | `0xb59d4516` | nonpayable | `()` | after the sale ends, once, anyone: burns the unused mint-bonus and referral reserves |
| `unmintedBurned()` | `0x5d7df935` | view | bool | true once releaseUnminted has run |
| `leftoverBurned()` | `0xe85ce742` | view | bool | true once releaseLeftover has run |
| `withdraw()` | `0x3ccfd60b` | nonpayable | `()` | anyone can call; sends the contract's BNB to PAYEE |

### POSTAGE settings

| Signature | Selector | Mutability | Returns | Unit / note |
|---|---|---|---|---|
| `BONUS_PER_MINT()` | `0xbef8e8aa` | view | uint256 | POSTAGE, 18-decimal integer (wei scale) per minted pass |
| `REF_PER_PAID()` | `0xabd9d9d8` | view | uint256 | POSTAGE, 18-decimal integer (wei scale) per paid pass |
| `HOLDER_RESERVE()` | `0xf5228741` | view | uint256 | POSTAGE, 18-decimal integer (wei scale) |
| `BONUS_RESERVE()` | `0xed8780e6` | view | uint256 | POSTAGE, 18-decimal integer (wei scale) |
| `REF_RESERVE()` | `0x44c59173` | view | uint256 | POSTAGE, 18-decimal integer (wei scale) |
| `PAYEE_SUPPLY()` | `0x1b45db9c` | view | uint256 | POSTAGE, 18-decimal integer (wei scale), sent to PAYEE at deployment |
| `bonusRemaining()` | `0x982a96e3` | view | uint256 | POSTAGE, 18-decimal integer (wei scale), mint bonus still unspent |
| `refRemaining()` | `0x6954343b` | view | uint256 | POSTAGE, 18-decimal integer (wei scale), referral reserve still unspent |

### Addresses and registry

| Signature | Selector | Mutability | Returns | Unit / note |
|---|---|---|---|---|
| `PAYEE()` | `0x7e306053` | view | address | address |
| `TOKEN()` | `0x82bfefc8` | view | address | POSTAGE address |
| `AGENT_ID_REGISTRY()` | `0x841b6ccc` | view | address | address |
| `owner()` | `0x8da5cb5b` | pure | address | always the zero address: the contract has no owner |
| `freeUsed(address)` | `0x2626c4bf` | view | bool | true once the wallet's free pass is used |
| `freeUsedByHandle(bytes32)` | `0x28008a0c` | view | bool | keccak256 of the registry handle |

### ERC-721

| Signature | Selector | Mutability | Returns | Unit / note |
|---|---|---|---|---|
| `name()` | `0x06fdde03` | view | string | Stampooor Pass |
| `symbol()` | `0x95d89b41` | view | string | STPASS |
| `balanceOf(address)` | `0x70a08231` | view | uint256 | passes held now |
| `ownerOf(uint256)` | `0x6352211e` | view | address | reverts for an id that is not minted |
| `tokenURI(uint256)` | `0xc87b56dd` | view | string | on-chain base64 JSON with an SVG image |
| `supportsInterface(bytes4)` | `0x01ffc9a7` | view | bool | ERC-165 |
| `approve(address,uint256)` | `0x095ea7b3` | nonpayable | `()` | standard |
| `getApproved(uint256)` | `0x081812fc` | view | address | standard |
| `isApprovedForAll(address,address)` | `0xe985e9c5` | view | bool | standard |
| `setApprovalForAll(address,bool)` | `0xa22cb465` | nonpayable | `()` | standard |
| `transferFrom(address,address,uint256)` | `0x23b872dd` | nonpayable | `()` | standard |
| `safeTransferFrom(address,address,uint256)` | `0x42842e0e` | nonpayable | `()` | standard |
| `safeTransferFrom(address,address,uint256,bytes)` | `0xb88d4fde` | nonpayable | `()` | standard |

### POSTAGE token (`0x6C310EABEdFD4eEd6148bf2C07241FB45a52FB42`)

| Signature | Selector | Mutability | Returns | Unit / note |
|---|---|---|---|---|
| `name()` | `0x06fdde03` | view | string | Stampooor Postage |
| `symbol()` | `0x95d89b41` | view | string | POSTAGE |
| `decimals()` | `0x313ce567` | view | uint8 | 18 |
| `totalSupply()` | `0x18160ddd` | view | uint256 | 1,000,000,000 x 10^18 at deployment; decreases when POSTAGE is burned |
| `MAX_SUPPLY()` | `0x32cb6b0c` | view | uint256 | the deployment supply, 1,000,000,000 x 10^18 |
| `balanceOf(address)` | `0x70a08231` | view | uint256 | wei scale |
| `allowance(address,address)` | `0xdd62ed3e` | view | uint256 | standard |
| `transfer(address,uint256)` | `0xa9059cbb` | nonpayable | bool | standard |
| `approve(address,uint256)` | `0x095ea7b3` | nonpayable | bool | standard |
| `transferFrom(address,address,uint256)` | `0x23b872dd` | nonpayable | bool | standard |
| `burn(uint256)` | `0x42966c68` | nonpayable | `()` | destroys the caller's own POSTAGE; total supply decreases |

## Events and errors

| Event | topic0 | Note |
|---|---|---|
| `Approval(address indexed owner, address indexed approved, uint256 indexed tokenId)` | `0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925` | ERC-721 |
| `ApprovalForAll(address indexed owner, address indexed operator, bool approved)` | `0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31` | ERC-721 |
| `Claimed(address indexed owner, uint256 indexed tokenId, uint256 amount)` | `0x987d620f307ff6b94d58743cb7a7509f24071586a77759b77c2d4e29f75a2f9a` | amount is POSTAGE wei |
| `LeftoverBurned(uint256 amount)` | `0xfb08b47cfd605f6210fabb724266f1a7a9ecb70276f12753802256f5324ff883` | POSTAGE wei burned |
| `Minted(address indexed minter, uint256 firstId, uint256 qty, uint256 freeUnits, uint256 paidUnits, uint256 valuePaid)` | `0x9045b28c8427bc83571801c0916c82f9ec27d9233ce8c25ac55e60cdf1ba3008` | valuePaid is the BNB kept for paid passes (refunds excluded); ids are firstId to firstId+qty-1 |
| `Referred(address indexed referrer, address indexed minter, uint256 paidUnits, uint256 amount)` | `0x37ad3f0dfd230f119d4a647760b82837e0a6bdd1e154b75990f170f3567196dc` | amount is POSTAGE wei sent to the referrer |
| `RevealCommitted(uint256 revealBlock)` | `0xf1f26af95d1e8cc525acaeb28483a05a915ddfd514713d30cf50744887c154c4` | first reveal() call, or a restart after the window |
| `Revealed(uint256 startingIndex)` | `0x15120e52505e619cbf6c2af910d5cf7f9ee1befa55801b078c33e93880b2d609` | reveal finished |
| `Transfer(address indexed from, address indexed to, uint256 indexed tokenId)` | `0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef` | ERC-721; emitted once per minted pass as Transfer(address(0), minter, tokenId) |
| `UnmintedBurned(uint256 amount)` | `0xc126243656db33a57c8fd6816147cfbf5903f48302e5f36ae1d57569646b4b97` | POSTAGE wei burned |
| `Withdrawn(uint256 amount)` | `0x430648de173157e069201c943adb2d4e340e7cf5b27b1b09c9cb852f03d63b56` | BNB wei sent to PAYEE |
| `MetadataUpdate(uint256 _tokenId)` | `0xf8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7` | EIP-4906: metadata of a pass changed (emitted on claim) |
| `BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId)` | `0x6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c` | EIP-4906: metadata of minted passes changed (emitted on reveal) |

POSTAGE token events:

| Event | topic0 | Note |
|---|---|---|
| `Approval(address indexed owner, address indexed spender, uint256 value)` | `0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925` | standard |
| `Transfer(address indexed from, address indexed to, uint256 value)` | `0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef` | a burn is a Transfer to the zero address |

Custom errors of the Pass contract:

| Error | Selector | Meaning |
|---|---|---|
| `PayeeZero()` | `0x600c0b1c` | constructor: payee is the zero address |
| `BadDuration()` | `0x4d84c2e9` | constructor: duration outside 3 to 14 days |
| `QtyZero()` | `0xd481977e` | qty is 0 (quote reason QTY_ZERO) |
| `Closed()` | `0x1cdde67b` | sale ended (CLOSED) |
| `RevealStarted()` | `0x3bcc93d3` | reveal has started (REVEALED) |
| `SoldOut()` | `0x52df9fe5` | all passes minted (SOLD_OUT) |
| `ExceedsSupply()` | `0x2d573a55` | qty is above the passes left (EXCEEDS_SUPPLY) |
| `HashReadFailed()` | `0x60d773fd` | reveal(): the EIP-2935 history contract call failed or answered in a wrong format; nothing changes, try again |
| `WalletCap()` | `0x94048683` | would exceed 15 lifetime mints (WALLET_CAP) |
| `WrongValue()` | `0x98d4901c` | msg.value below paidUnits x PRICE |
| `RefundFailed()` | `0xf0c49d44` | the refund of an overpayment could not be delivered to the caller |
| `RevealLocked()` | `0x5b8e912f` | reveal() or releaseLeftover() before the sale has ended |
| `AlreadyRevealed()` | `0xa89ac151` | reveal already finished |
| `RevealTooEarly()` | `0xc349402d` | second reveal() call in a block at or before revealBlock |
| `NotRevealed()` | `0xb2c9bc88` | needs a finished reveal |
| `BadId()` | `0x08e235ff` | id is 0, above 5555, or not minted where a minted id is required |
| `NotOwner()` | `0x30cd7471` | caller does not hold the id |
| `AlreadyClaimed()` | `0x646cf558` | the id was claimed already (also for a repeated id) |
| `BadBatch()` | `0x86bdc25c` | ids length is 0 or above 100 |
| `AlreadyReleased()` | `0x63b4904e` | that release already ran |
| `WithdrawFailed()` | `0x750b219c` | PAYEE did not accept the BNB |
| `ScanTooLarge()` | `0xffce22e4` | ownedIds count above 1000 |

## Transaction policy

Verify the deployed source and bytecode, the chain, the destination, the quote and the exact native value; simulate or preview; confirm there are no approve or authorization changes; follow your wallet's transaction policy before submitting.
