@xgamesfun/solana 0.25.4 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +8 -8
  2. package/js/approve/details.mjs +52 -2
  3. package/js/approve/live-check.mjs +0 -15
  4. package/js/approve/server.mjs +5 -4
  5. package/js/chain/token2022.js +3 -2
  6. package/js/client/accounts.js +67 -25
  7. package/js/client/grant.js +7 -2
  8. package/js/client/idl/loot.js +1 -1
  9. package/js/client/idl/loot.json +714 -182
  10. package/js/client/ix.js +617 -113
  11. package/js/client/sizes.js +35 -3
  12. package/js/economy/check.mjs +655 -242
  13. package/js/economy/cost.mjs +2 -4
  14. package/js/economy/explain.mjs +16 -2
  15. package/js/economy/guide.mjs +12 -12
  16. package/js/economy/model.mjs +133 -14
  17. package/js/economy/plan.mjs +36 -11
  18. package/js/economy/review.mjs +50 -8
  19. package/js/economy/schema.json +73 -29
  20. package/js/economy/simulate.mjs +116 -13
  21. package/js/mcp/tools.mjs +2 -2
  22. package/js/play/index.js +847 -296
  23. package/package.json +1 -1
  24. package/release.json +5 -5
  25. package/skills/xsolana-economy/SKILL.md +12 -4
  26. package/skills/xsolana-economy/references/after-launch.md +1 -1
  27. package/skills/xsolana-economy/references/apps.md +11 -2
  28. package/skills/xsolana-economy/references/sentence-to-spec.md +3 -3
  29. package/skills/xsolana-economy/references/telling-the-person.md +21 -4
  30. package/skills/xsolana-economy/references/trust-and-cheating.md +15 -5
  31. package/types/chain/token2022.d.ts +3 -2
  32. package/types/client/accounts.d.ts +34 -11
  33. package/types/client/idl/loot.d.ts +25 -4
  34. package/types/client/ix.d.ts +217 -45
  35. package/types/economy/check.d.mts +5 -0
  36. package/types/economy/model.d.mts +75 -12
  37. package/types/economy/plan.d.mts +22 -1
  38. package/types/economy/simulate.d.mts +12 -1
  39. package/types/mcp/server.d.mts +12 -1
  40. package/types/mcp/tools.d.mts +12 -1
  41. package/types/play/index.d.ts +210 -144
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xgamesfun/solana",
3
- "version": "0.25.4",
3
+ "version": "0.26.0",
4
4
  "mcpName": "io.github.XGamesFun/solana-kit",
5
5
  "description": "Loot boxes, gacha, shops, lotteries, auctions, NFT staking, crafting and rewards on Solana without writing a smart contract: one shared program, its JS client, a CLI, and an MCP server and skill so Claude Code, Codex or any AI agent can design, check, simulate and launch an economy.",
6
6
  "license": "Apache-2.0",
package/release.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
- "version": "0.25.0",
2
+ "version": "0.26.0",
3
3
  "programId": "9cwqM1TXytPnFZsLfmUyv2dg4uW6Ch9C6UJVPns7NFQ",
4
- "sbfBytes": 1741840,
5
- "sbfSha256": "ea19d15d03211f7bd0b25179d51f75544342225127fcb631e687eae6750c9ba7",
6
- "idlSha256": "41e621990c17b8645f59bb2a170271645ad370d1c1bf9f1dbdd04f817b82d4cf",
7
- "clientSha256": "849106b5d1a022aac13e1208172945c4cee0c4a55cafbd5f1c77e1be6948de04"
4
+ "sbfBytes": 1779872,
5
+ "sbfSha256": "d5d4a2ca2bca79e8d4930b697cc3aecfcf54399aa85585c02e267ffbbdfda429",
6
+ "idlSha256": "4e1407b41c5947821d154472bd26c2df0fff9c750475239d444b2f5fd5d3d8dc",
7
+ "clientSha256": "e6b1783c6f68e342aa165474f1d3b96a7a24a3a981be4882ebcd7ae071e41e6e"
8
8
  }
@@ -9,8 +9,8 @@ An economy is one JSON spec (`xsolana.json` in the project) on **loot**, a Solan
9
9
  writes or deploys a contract: the spec is checked, simulated and priced by the `xsolana_*` tools, and the project's
10
10
  owner puts it live by approving in their own wallet. It runs on **devnet** until the program is audited.
11
11
 
12
- The tools are the `xsolana_*` MCP tools. Without MCP, the same from a terminal: `npx -y @xgamesfun/solana@0.25.4 <guide|examples|check|simulate|cost|explain|review|publish|status> [xsolana.json]`,
13
- or `npx -y @xgamesfun/solana@0.25.4 tool <name> '<json>'` for any tool (always with the exact version).
12
+ The tools are the `xsolana_*` MCP tools. Without MCP, the same from a terminal: `npx -y @xgamesfun/solana@0.26.0 <guide|examples|check|simulate|cost|explain|review|publish|status> [xsolana.json]`,
13
+ or `npx -y @xgamesfun/solana@0.26.0 tool <name> '<json>'` for any tool (always with the exact version).
14
14
 
15
15
  ## Rules (always)
16
16
 
@@ -103,7 +103,15 @@ or `npx -y @xgamesfun/solana@0.25.4 tool <name> '<json>'` for any tool (always w
103
103
  don't say. `"pot": 0` keeps a pot from round to round: a seed, sponsors, shares of other sales and what earlier
104
104
  rounds left all count, and the next round starts with what the round before left (a jackpot that grows, a no-loss
105
105
  lottery). `"rest": "<destination>"` pays each round from its own ticket money alone, which nothing from outside can
106
- change, and sends what the places leave there (`"treasury"`, a wallet, `"burn"`; never a pool or `"chosen"`).
106
+ change, and sends what the places leave there (`"treasury"`, a wallet, `"burn"`; never a pool or `"chosen"`). A
107
+ no-loss lottery closes its sales a while before each draw (`"closesBefore"`), and may weigh each purchase by the
108
+ time it is held (`"timeWeighted": true`). A place paid from a pool says what it gets when the pool is short
109
+ (`"whenShort"`): prizes of 1,000, 500 and 500 from a pool of 1,700 give 1,000, 500 and 0 `"inOrder"`, 850, 425 and
110
+ 425 `"inProportion"`, or `"nothing"`.
111
+ A box says what a draw on an outcome that is out does (`"whenOut"`): stop selling and refund a paid miss (the
112
+ default), draw among what is left, or give nothing. The program takes much that only risks the project itself (a
113
+ price or prize of 0, a mission with no lock, a far date, a token a player can hold up): the checker warns of each
114
+ in words, and every warning goes to the person.
107
115
  Then say what stays the project's job (`references/telling-the-person.md`): the files behind every item's link
108
116
  (every value of a fresh item's `{attribute}`), a table's rent, and the collectible rent a pack's settle pays.
109
117
  6. **Iterate on what they say.** "Make legendaries 0.5%", "a bigger seed": change, check, simulate, explain again.
@@ -113,7 +121,7 @@ or `npx -y @xgamesfun/solana@0.25.4 tool <name> '<json>'` for any tool (always w
113
121
  level would cost. Run it again after every change that touches a route that pays.
114
122
  9. **When they say "put it live"**: `xsolana_publish` lists every step and its cost and gives the command. Tell them to
115
123
  run it themselves in the project folder, exactly as `xsolana_publish` gives it (it names the exact version:
116
- `npx -y @xgamesfun/solana@0.25.4 approve xsolana.json`; never drop the version). It opens a page on their computer where
124
+ `npx -y @xgamesfun/solana@0.26.0 approve xsolana.json`; never drop the version). It opens a page on their computer where
117
125
  they connect a devnet wallet and approve; what lands is written back into `xsolana.json`. You publish nothing.
118
126
  10. **After it is live**: `xsolana_status` shows each part on chain; read it before acting on what the person says
119
127
  happened. A later change is the same loop: save, publish, and the approval sends only what changed. What only the
@@ -11,7 +11,7 @@ winner nobody pays.
11
11
  | A box's roll and settle, a shop buy | the player, in their own purchase (`js/play` does both in one prompt) | the player, a key the player named for the order (`settler`) or the project's owner or authority settle a rolled order, a normal item too (the app's backend delivers it from the `OrderSettled` receipt of a finalized transaction whose `meta.err` is null, once per order: `loot.receipts(tx)` gives none for a transaction with an error, whose logs can still read as all-success; a truncated log stays truncated when read again, so it then goes by the order's account); a roll that failed or never came refunds (`app.refund`) |
12
12
  | A lottery draw, then each part pulled | **anyone**: the page (`app.claims()` lists each available step), or a sweep. The draw's ask fixes the round's money (what its places take of the pot, or of the round's own ticket money for a lottery with `rest`; each pool's prize) and opens the next round at once; paid from a pot, it first moves in what the round before left that pot, so the next round always sees it, whoever pulls and whenever. A pull pays one part into its owner's account, opened in the same transaction when it is not there (the sender pays its rent): an entry's own (a refund, a ticket back), its places' part of the pot or their prizes (a raffled NFT, an item, tokens, a roll), or a part owed in common (a share, what the places leave: to the pot, or to `rest`; a burned `rest` stays for the close to burn) | selling stops at draw time. A round nobody draws waits, with no deadline. Once drawn, every part waits in the round for its own pull, in any order, holding up nobody else; the pull that leaves nothing owed closes the round, and the rent of its record goes back to whoever fronted it (the lottery's `roundRent`, or its first buyer). An NFT that can't move is made a hold for its winner. If the oracle never answers a draw (7 days after it was first asked for), the next ask cancels it, and every buyer's refund is pulled; the page pulls the sender's own refund in the cancel's own transaction. A due round's draw rides in front of the next ticket purchase. An empty round moves on with the next ticket. A one-time lottery ends after its round |
13
13
  | A schedule that is due (an emission, rent, a drip) | the project's owner or authority, or who it pays (a wallet, the treasury): `app.runSchedule` signed by one of them, or their sweep (a sealed burn: anyone) | **a run missed is missed**: one sent late pays once, for the latest run due, and the runs before it lapse, so keep a key of the project's sending it on time. Only a sealed schedule's fixed amount pays every run due (a backlog of years takes a few calls) |
14
- | Ending an auction | **anyone**: usually the winner, from the page | it waits: whenever it is ended, the lot goes to the highest bidder. A seller whose account can't take the money then doesn't hold up the winner: the lot goes, the auction stays open owing it (`app.auctions()`: `delivered`, `proceedsOwed`), and the next end, anyone's, pays and closes it |
14
+ | Ending an auction | **anyone**: usually the winner, from the page (`app.endAuction`). The lot and the money move together; an NFT lot that can't move right now makes the end wait, and it is sent again later. The project's keys can call such a sale off (`app.callOffSale`): the bid goes back and the lot goes home | it waits: whenever it is ended, the lot goes to the highest bidder. A seller whose account can't take the money then doesn't hold up the winner: the lot goes, the auction stays open owing it (`app.auctions()`: `delivered`, `proceedsOwed`), and the next end, anyone's, pays and closes it |
15
15
  | Catching a farm up (`walk_farm`), when it has passed more of its seats' end times than a move can walk | **anyone**, for free: `app.claims()` lists it, `app.walkFarm(key)` sends it in parts (`app.farmWalk(key)` says how many) | moves of that farm fail until it is walked; nothing is lost by the wait: what each seat earned by its end is exact |
16
16
  | A contest's entries back when it is off, or a winner's part of a contest with no prize or picks | **anyone** (each entry's money goes only to its player); a winner's first claim with a prize or picks: the **winner or the project's keys**, and past 30 days the project's keys may pick for them | they wait; nothing is lost. Each winner's part is written at the decision (the pot then, and a split prize's vault), so the order of claims changes nothing; tokens sent to the pot after it go to nobody (burned at the close), and a split prize's part a winner gives up goes back to its pool, not to later winners |
17
17
  | Opening a contest | the **authority** (the owner's wallet by default) | nothing happens |
@@ -33,7 +33,7 @@ const app = solanaApp({ spec, rpcUrl: 'https://api.devnet.solana.com', wallet: w
33
33
 
34
34
  const shop = await app.shop(); // [{ key, name, price, priceUnits, currency, active, left }]
35
35
  await app.buy('badge', { listing: shop.find(l => l.key === 'badge') }); // the price shown is the most it pays
36
- const boxes = await app.boxes(); // each box's price, outcome chances and whenOut (show it: stops selling, or draws among what is left)
36
+ const boxes = await app.boxes(); // each box's price, outcome chances and whenOut (show it: stops selling, draws among what is left, or a miss gives nothing)
37
37
  await app.openBox('chest', { listing: boxes.find(l => l.key === 'chest') }); // pays, waits for the roll, settles
38
38
  await app.pay('entry'); // a payment of the spec
39
39
  const lotteries = await app.lotteries(); // [{ key, name, price, currency, status, round, sold, drawsAt, nftTicket, … }]
@@ -159,7 +159,16 @@ assetId, tree }` after a `loot.proveLeafIx` in the same transaction. `app.stakeN
159
159
  (`{ "staked": … }`) needs nothing from the page: js/play passes the player's seat and the farm, and says plainly when
160
160
  the stake is too small or its lock is over (show the farm's locked tiers next to the gate).
161
161
  - **A no-loss lottery** (`noLoss`): once the round is drawn every ticket is owed back to its buyer (a pull, which
162
- anyone may send, so say "your tickets come back after the draw"), and the winners then collect the pot.
162
+ anyone may send, so say "your tickets come back after the draw"), and the winners then collect the pot. Sales close
163
+ `closesBefore` before each draw. A lottery weighed by time (`timeWeighted`) shows each buyer, on the buy screen,
164
+ what this purchase would weigh now (its tickets times the seconds left until sales close) and their share of the
165
+ round's weight, and that the same tickets bought later weigh less.
166
+ - **Sending for a player through a session key** (a delegate on their token account): a payment by a delegate is
167
+ refused whole when one of its shares would land back on the account it pays from (a rebate naming that player, a
168
+ share to the holder of a collectible that player holds). Pay such a purchase with the player's own signature.
169
+ - **Reading results from logs**: a transaction's log holds 10,000 bytes and a sender can fill it before the program
170
+ writes. Read the accounts as the record of what is owed and held; treat a missing receipt as unknown, not as "did
171
+ not happen".
163
172
  - **A lottery round** finishes in a few steps whatever its size: `app.claims()` lists the draw (`reveal`, whose ask
164
173
  fixes the round's money and opens the next round at once), what this player is owed (`pull`: a refund, a place's
165
174
  part of the pot, its prize; a raffled NFT; a roll asked for in the same transaction and settled in the same prompt)
@@ -16,7 +16,7 @@ maps to a part of the spec (`xsolana_guide` has every field).
16
16
  | "sealed bids", "nobody sees the bids", "second-price", "Vickrey" | loot cannot: say so, and offer an open auction (`auctions`) |
17
17
  | "loot box", "gacha", "mystery box", "60/25/10/4/1" | `boxes` with `outcomes` of `gives: "item"` and a `chance` each |
18
18
  | "pity", "guaranteed legendary by pull 90", "soft pity" | a soulbound charm each player holds, which the pull box rolls on (`rollsOn`, `then: "back"`): misses shift its `pity` +1, the rare shifts it by -1000000 (it stops at 0: a reset), the rare's `odds` add a percent a point. It never trades; a pity token minted on misses can be sold and never resets |
19
- | "stop selling when the rare is gone", "keep selling what's left", "what happens when an item sells out" | a box's `whenOut`: `"stopSales"` (the default: it isn't sold while any outcome can't be given, so the odds stay as written) or `"drawLeft"` (it keeps selling, drawing among what is left, the rest's chances rising: a player can empty a common outcome to make a rare likelier). Tell the person the trade-off |
19
+ | "stop selling when the rare is gone", "keep selling what's left", "what happens when an item sells out" | a box's `whenOut`: `"stopSales"` (the default: it isn't sold while any outcome can't be given, so the odds stay as written, and a paid miss is refunded), `"drawLeft"` (it keeps selling, drawing among what is left, the rest's chances rising: a player can empty a common outcome to make a rare likelier) or `"giveNothing"` (it keeps selling on the odds written, and a draw on an outcome that is out gives nothing, never refunded). Tell the person the trade-off |
20
20
  | "a booster of five", "a pack of 5 random cards", "three cards in one pack" | a box with `"slots": [{ "count": 5 }]`: each open draws five outcomes by their chances, all from one oracle answer, in one purchase and one wallet prompt. `"distinct": true` on the slot for five different outcomes ("a hand of different cards") |
21
21
  | "a guaranteed rare", "one rare or better in every pack", "four commons and a rare" | slots that name outcomes by their place in `outcomes` (from 0): `[{ "count": 4, "share": 60 }, { "count": 1, "from": [8, 9, 10], "share": 40 }]` is four draws among all and one among the rares, the rare slot worth 40% of the price. Slots that name outcomes each need a `share`, 100 together: a draw that can't be made (its slot sold out) gives its slot's worth back, so a guaranteed rare keeps its value when the commons run out; ask what the rare is worth |
22
22
  | "10-pull", "open ten at once", "buy 3 packs" | the same box: the page opens several in one purchase (`app.openBox(key, { listing, count: 10 })`), one wallet prompt and one oracle answer, each open at the price the one before it left (`maxPrice` is a pack's). Nothing to add to the spec |
@@ -83,7 +83,7 @@ maps to a part of the spec (`xsolana_guide` has every field).
83
83
  | "trade in any NFT of ours for 10 gold", "swap an NFT of another collection for a badge", "we keep what's traded in and give it away again" | a recipe line `{ "takes": "trade", "collection": "<address>" (left out: the project's own), "then": "burn" \| { "stash": "<key>" } \| { "giveTo": "<wallet>" } }`: the NFT is the price. Into a stash, it becomes another player's prize (a box outcome `{ "gives": "stash" }` resells it) |
84
84
  | "a trade-in box: give an NFT, roll for a new one" | a box with `rollsOn` (which NFTs it takes) and `"then": { "stash": "<key>" }` (or `"burn"`): every outcome keeps the NFT paid, and its outcomes give the prizes |
85
85
  | "tickets cost an NFT", "sacrifice an NFT for a raffle ticket" | a lottery's `ticketNft`: `{ "rule": { "item": key } or { "collection": address }, "then": "burn" or { "stash": key } }`, with no `price`, `split` or `ticketCurrency`; with `"pot": 0` the pot is its `seed` and what others send it, plus its places' prizes. An NFT spent on a ticket is never refunded (`W-NFT-PRICE`) |
86
- | "no-loss lottery", "prize savings", "you never lose your ticket", "the sponsor pays the prize" | a lottery with `"noLoss": true` and `"pot": 0`: every ticket (tokens or an NFT) goes back to its buyer once the round is drawn, and the winners take only the pot: its `seed`, a sponsor's payments, shares of other sales (`"lottery:<key>"`). No `split`, `cutPercent` or `ticketCurrency`. Say that tickets are not weighted by time: someone may buy most of them right before the draw and risk only the fees |
86
+ | "no-loss lottery", "prize savings", "you never lose your ticket", "the sponsor pays the prize" | a lottery with `"noLoss": true` and `"pot": 0`: every ticket (tokens or an NFT) goes back to its buyer once the round is drawn, and the winners take only the pot: its `seed`, a sponsor's payments, shares of other sales (`"lottery:<key>"`) (or with `"rest"`, only its places' prizes). No `split`, `cutPercent` or `ticketCurrency`. Give it `"closesBefore"` (sales close that long before each draw) and, for "the longer you hold, the better your chance", `"timeWeighted": true`: each purchase weighs its tickets times the seconds until sales close, and the buy screen shows what it weighs. Without it, say someone may buy most of the tickets right before the draw and risk only the fees |
87
87
  | "stake an NFT on the duel", "winner takes the loser's NFT" | a contest's `stakeNft` (a rule): each entry stakes one NFT; the winners share the losers' NFTs, each picking its share (`losersEach` caps it: 1 for "winner takes one"), and the opener gets what the winners leave. Losers send nothing |
88
88
  | "places 2 to 101 each win 1%", "100 runners-up each get a badge" | a lottery place with `times`: `{ "percent": 1, "times": 100 }` is a hundred places alike (not for a named NFT or a roll; at most 64 runs) |
89
89
  | "raffle 200 NFTs", "each winner gets a random NFT from the collection" | a lottery that draws once, its `nfts` (put in its own stash at go-live, a group a call) and a place `{ "prize": { "gives": "raffled" }, "times": 200 }`: each winner draws one at random |
@@ -102,7 +102,7 @@ maps to a part of the spec (`xsolana_guide` has every field).
102
102
  | "several signers must approve spending", "a multisig" | loot isn't a multisig: the money goes to their multisig's vault (for example Squads): `"treasury": "wallet:<vault>"`, or a `wallet:` share; spending from it is approved in the multisig |
103
103
  | "60% to me, 40% to my co-designer", "10% to our charity", "split with my partners" | split shares `"to": "wallet:<their wallet address>"` (ask for each address). Going live opens each wallet's account for the currency. Use `account:` only for a token account that already exists |
104
104
  | "our partner gets 4%" (one fixed wallet) | a split share `"to": "wallet:<their address>"` |
105
- | "referral links", "each streamer gets 6% of what they bring", "tip the artist I pick" | a box's, shop item's, lottery's or payment's split share `"to": "chosen"`: the buyer names the wallet when they buy (the page passes it from the link: `app.buy(key, { listing, chosenRecipient: referrer })`, `app.pay(key, { chosenRecipient: referrer })`), never their own (a second wallet of theirs still works, so a referral share is at most a rebate: price it so); with none it goes to the treasury |
105
+ | "referral links", "each streamer gets 6% of what they bring", "tip the artist I pick" | a box's, shop item's, lottery's or payment's split share `"to": "chosen"`: the buyer names the wallet when they buy (the page passes it from the link: `app.buy(key, { listing, chosenRecipient: referrer })`, `app.pay(key, { chosenRecipient: referrer })`), their own too (so a referral share is at most a rebate the buyer controls: price it so; a grant's vouchers prove a real referral); with none it goes to the treasury |
106
106
  | "a fee to the owner of the workshop I craft at", "the venue's owner gets a cut" | a split share `"to": "chosen:<item key>"`: the buyer names a collectible of that item, and whoever holds it is paid (`{ chosenRecipient: <its address> }`) |
107
107
  | "the holder of the deed gets 3%" | a split share `"to": "holder:<collectible address>"` |
108
108
  | "duel", "bet on a side", "prediction", "the winner takes the pot" | `contests` with `sides` and a rake in `split`; the authority decides, or a results server's key in `deciders` signs the result (anyone sends it) |
@@ -72,9 +72,14 @@ money; it holds no files and serves nothing.
72
72
  the project restocks or edits the box to sell again (a pool holds its prize for every draw waiting; a named NFT is in
73
73
  one waiting purchase at a time). With `"drawLeft"` the box keeps selling and draws among what is left, by the written
74
74
  weights: the others' chances rise as one runs out, so anyone watching the stock can empty a common outcome and then
75
- open for the rare at better odds. Put it as: "stop selling keeps your odds honest; keep selling never stalls but lets
76
- a player tilt the odds". A box selling a rare prize keeps the default; `drawLeft` suits a box of alike prizes, or one
77
- only other routes roll (a grant's, a mission's), where nothing is sold. `xsolana_check` says what each does to the
75
+ open for the rare at better odds. With `"giveNothing"` the box keeps selling on the odds written, and a draw that lands
76
+ on an outcome that is out gives nothing: a paid draw is not refunded, a free roll is over, and anyone who empties a
77
+ source the box shares makes its rolls miss. Put it as: "stop selling keeps your odds honest and refunds a miss; keep
78
+ selling never stalls but lets a player tilt the odds; give nothing keeps the odds and never refunds". Under
79
+ `"stopSales"`, a roll another route gives (a grant, a mission, a recipe, a lottery's place) is still given after the
80
+ box has stopped, and is asked again until it hits, so with one outcome dry it always ends on what is left. A box
81
+ selling a rare prize keeps the default; `drawLeft` suits a box of alike prizes, or one only other routes roll (a
82
+ grant's, a mission's), where nothing is sold. `xsolana_check` says what each does to the
78
83
  box (W-WHEN-OUT, W-OUT-OF-STOCK, W-PRIZE-SEED, W-STASH) and `xsolana_simulate` the day it stops or each outcome runs out.
79
84
  - **A prize the program can't give right now is out of stock, not an error.** A prize in a token with a transfer hook
80
85
  (or confidential transfers) is never moved by a roll. An outcome emptied by another route after a purchase (another
@@ -83,7 +88,19 @@ money; it holds no files and serves nothing.
83
88
  free roll waits to be asked again, and a box with nothing in stock is refused (SoldOut).
84
89
  - **The oracle's answer reads at most 21 accounts**, so a box's distinct prize accounts (its items, pools, stashes,
85
90
  the mints and vaults of its tokens, a fresh item's tables) are bounded by that, however many outcomes it has.
86
- `xsolana_check` counts them.
91
+ `xsolana_check` counts them. One answer also holds only so much of a box (measured: 128 outcomes that give
92
+ nothing, 29 of token amounts, 18 each of its own item; 41 slots; 72 odds rules; 16 token prizes or 18 collectibles
93
+ in one outcome; 340 texts in a gate): a box past that is refused when written. Put a long weighted list in a table
94
+ or a stash, drawn by one outcome.
95
+ - **The program takes any time, and the checker says a far one's date in words (W-FAR-TIME).** A time that looks like
96
+ milliseconds, or lands more than three years out, locks the bids, tickets, NFTs or vault that wait on it until then.
97
+ Read the date to the person before they sign.
98
+ - **Much of what the program used to refuse is the project's own choice now, warned of in words, never refused**: a
99
+ price or a prize of 0, a mission with no lock, a contest of NFTs whose winners take none of the losers' NFTs, a
100
+ sealed schedule of any length paying any address the program doesn't own, a lottery, contest or auction in a token a
101
+ player can hold a payment up in (a frozen default, confidential transfers), a farm that mints a token the project is
102
+ not yet the authority of (it pays nothing until it is, and that time is never paid). Tell the person each warning
103
+ `xsolana_check` gives, in its own words.
87
104
 
88
105
  Don'ts:
89
106
 
@@ -107,11 +107,21 @@ Run `xsolana_review` and go through what it reports; these are the points behind
107
107
  12. **The page is untrusted.** A page builds the player's transactions, and a cheater builds their own: the program's
108
108
  checks are what count, never the page's. Anything the page decides (a score, a reward amount, who won) is only as
109
109
  good as the server that checks it.
110
- 13. **Ordering.** A lottery's tickets are not weighted by time: one buyer can take most of a round right before the
111
- draw. Anyone, the seller included, can bid an auction up. A box that keeps selling when an outcome runs out
112
- (`drawLeft`) lets someone watching the stock empty a common outcome to make a rare likelier.
113
- 14. **Contests.** Players' stakes go to the side the authority or a decider key names: keep contests for results the
114
- server decides, or that anyone can check outside the game.
110
+ 13. **Ordering.** A lottery's tickets count one each unless it weighs them by time (`timeWeighted`, no-loss
111
+ lotteries only): then one buyer can't take most of a round right before the draw for the fees alone, and the buy
112
+ screen shows what a purchase weighs. A no-loss lottery should close its sales a while before the draw
113
+ (`closesBefore`). Anyone, the seller included, can bid an auction up; an auction with no extension or no least
114
+ raise lets two wallets topping each other in the last seconds keep every other bid out. A box that keeps selling
115
+ when an outcome runs out (`drawLeft`) lets someone watching the stock empty a common outcome to make a rare
116
+ likelier; one that gives nothing for a miss (`giveNothing`) lets anyone emptying a shared source make its rolls
117
+ miss.
118
+ 14. **Contests.** Players' stakes go to the side the authority or a decider key names, and whoever names it can also
119
+ enter: keep contests for results the server decides, or that anyone can check outside the game. A prize to each
120
+ winning entry with no stake and no gate pays one person with many wallets once a wallet.
121
+ 15. **NFTs the project doesn't control.** The creator of an NFT with a permanent delegate can pull it back out of a
122
+ hold, a stake, a loan or a listing, and a collection the project is only an update delegate of lets its owner and
123
+ every other delegate mint NFTs that read as the project's own items. Name only collections whose every writer the
124
+ project trusts.
115
125
 
116
126
  ## Telling the person
117
127
 
@@ -8,8 +8,9 @@ export declare function hookProgramOf(mintData: any): string | null;
8
8
  /**
9
9
  * Whether a mint's new accounts start frozen, or can be made to (as the program judges it): a Token-2022
10
10
  * mint carrying `DefaultAccountState` set to frozen, or set to anything while it has a freeze authority, which can switch
11
- * it to frozen whenever it likes. An auction, a contest, a lottery, an offer and a sealed schedule refuse such a mint:
12
- * each reads the setting once, as it opens, and a refund or payout opened into a frozen account would never land.
11
+ * it to frozen whenever it likes. The program takes such a mint everywhere: it is the project's choice. A refund or a
12
+ * payout opened into an account that starts frozen does not land until its issuer thaws it, so the tools warn the
13
+ * project, and a bidder or a borrower before they sign.
13
14
  */
14
15
  export declare function mayStartFrozen({ owner, data }: {
15
16
  data: any;
@@ -206,26 +206,50 @@ export declare const leftoverTo: (lotteryAddress: any, terms: any) => any;
206
206
  export declare const lotteryPots: (lottery: any) => number[];
207
207
  /** A share the buyer chooses (a referrer, a collectible's holder): paid out of the price as they buy, from their own account. */
208
208
  export declare const chosenAtPurchase: (share: any) => boolean;
209
- /** The bytes of one entry of a round: buyer, word, what arrived (`fee`: a fee mint, or a chosen share), the NFT ticket. */
210
- export declare const entryWidth: (fee: any, nft: any) => number;
209
+ /** The bytes of one entry of a round: buyer, word, what arrived (`fee`: a fee mint, a chosen share, or a weighted
210
+ * round of paid tickets), where its weight ends (`weighted`: a round that weighs its tickets by time), the NFT ticket. */
211
+ export declare const entryWidth: (fee: any, nft: any, weighted?: boolean) => number;
212
+ /** What a round's draw picks a point below: the weight sold in a weighted round, the tickets sold otherwise. */
213
+ export declare const roundTotal: (r: any) => bigint;
211
214
  /**
212
215
  * A lottery round (`["round", lottery, incarnation, round]`): its header (`status` 'Open' | 'Drawing' | 'Drawn' |
213
- * 'Cancelled', the terms it copied, `sold`, `owed`, the draw and the money the ask fixed, `nfts` the NFT of the
214
- * lottery's own stash each such place got), and `entries`, one a purchase: { buyer, start, end (its tickets), paid (its
215
- * own part's bit), amount (what reached the round), nft (its NFT ticket) }.
216
+ * 'Cancelled', the terms it copied, `sold`, `weight` (null in a round that doesn't weigh its tickets), `owed`, the draw
217
+ * and the money the ask fixed, `nfts` the NFT of the lottery's own stash each such place got), and `entries`, one a
218
+ * purchase: { buyer, start, end (its slice of the draw: its tickets, or in a weighted round its weight), tickets (how
219
+ * many it bought), paid (its own part's bit), amount (what reached the round), nft (its NFT ticket) }.
216
220
  */
217
221
  export declare function decodeRound(data: any): any;
222
+ /** When a lottery's sales close (`terms`: a Lottery's or a round's): `closes_before` seconds before its draw. */
223
+ export declare const salesCloseAt: (terms: any) => bigint;
224
+ /**
225
+ * What a purchase of `count` tickets at `now` (Unix seconds, the chain's clock) weighs in a round's draw: one a ticket,
226
+ * or in a lottery that weighs tickets by time (`terms.time_weighted`) each ticket the seconds from the purchase to the
227
+ * close of sales (`salesCloseAt`). 0 once sales have closed: nothing more is sold. The same tickets bought later weigh less.
228
+ */
229
+ export declare function ticketWeight(terms: any, count: any, now: any): bigint;
230
+ /**
231
+ * A buyer's chance before they buy: { weight, total, share } of a purchase of `count` tickets at `now` into `round` (its
232
+ * decoded record, null when nobody bought into it yet; `terms` the lottery's then): what it would weigh, the round's
233
+ * weight with it, and its share of that (a number from 0 to 1). Each place is drawn over the round's whole weight, so a
234
+ * share is the purchase's chance at one place as things stand: later purchases make it smaller.
235
+ */
236
+ export declare function purchaseOdds(round: any, terms: any, count: any, now: any): {
237
+ weight: bigint;
238
+ total: bigint;
239
+ share: number;
240
+ };
218
241
  /** How many places a lottery's (or a round's) `places` are, every run counted (`Place.times`). */
219
242
  export declare const placeCount: (places: any) => any;
220
243
  /** The places of a round that have a winner: every place, but no more than tickets. */
221
244
  export declare const roundWinners: (r: any) => number;
222
- /** The ticket that wins `place` of a drawn round: even steps around the list from first place's ticket. */
245
+ /** The point that wins `place` of a drawn round: even steps of the draw's total around the list from first place's
246
+ * (a ticket's number, or in a weighted round a point of its weight). */
223
247
  export declare function ticketOfPlace(r: any, place: any): bigint;
224
248
  /** The run of places that holds `place`, and its first place. */
225
249
  export declare function runOf(r: any, place: any): any[];
226
250
  /** The places of run `run` with a winner: [first, end). */
227
251
  export declare function runPlaces(r: any, run: any): number[];
228
- /** The places of run `run` whose ticket is in `start..end` (an entry's), in order. */
252
+ /** The places of run `run` whose point is in `start..end` (an entry's slice of the draw), in order. */
229
253
  export declare function placesIn(r: any, run: any, start: any, end: any): any[];
230
254
  /**
231
255
  * What place `place` (counted from the first place of `runs`) takes of a pass-through amount, as the program splits it:
@@ -285,11 +309,10 @@ export declare const roundFinished: (r: any) => boolean;
285
309
  export declare const placeSet: (place: any) => boolean;
286
310
  /** The address of a lottery's current round's record (there or not). */
287
311
  export declare const roundAddress: (lotteryAddress: any, lottery: any) => any;
288
- /** Whether a lottery (decoded) drew once and has ended. */
312
+ /** Whether a lottery (decoded) drew its last round and has ended: it draws once (`every` 0) and is past round `last`. */
289
313
  export declare const lotteryEnded: (lottery: any) => boolean;
290
- /** The round a lottery's close, its write again and its own stash's moves name (`lottery::check_idle`): its current
291
- * round's, or once a lottery that draws once has ended, round 0's (which keeps its stash's NFTs until its places took
292
- * them out). */
314
+ /** The round a lottery's close, its write again and its own stash's moves name (`Lottery::waits_on`): its current
315
+ * round's, or once it has ended, its last round's (which keeps its stash's NFTs until its places took them out). */
293
316
  export declare const idleRound: (lotteryAddress: any, lottery: any) => any;
294
317
  /**
295
318
  * Who rents a listed NFT at `now` (Unix seconds), from its decoded Listing (or null for none): the renter's wallet while
@@ -1006,11 +1006,20 @@ declare const _default: {
1006
1006
  args: ({
1007
1007
  name: string;
1008
1008
  type: {
1009
+ defined?: undefined;
1009
1010
  array: (string | number)[];
1010
1011
  };
1011
1012
  } | {
1012
1013
  name: string;
1013
1014
  type: string;
1015
+ } | {
1016
+ name: string;
1017
+ type: {
1018
+ array?: undefined;
1019
+ defined: {
1020
+ name: string;
1021
+ };
1022
+ };
1014
1023
  })[];
1015
1024
  } | {
1016
1025
  name: string;
@@ -3618,6 +3627,10 @@ declare const _default: {
3618
3627
  docs?: undefined;
3619
3628
  name: string;
3620
3629
  type: string;
3630
+ } | {
3631
+ name: string;
3632
+ docs: string[];
3633
+ type: string;
3621
3634
  } | {
3622
3635
  docs?: undefined;
3623
3636
  name: string;
@@ -3627,10 +3640,6 @@ declare const _default: {
3627
3640
  name: string;
3628
3641
  };
3629
3642
  };
3630
- } | {
3631
- name: string;
3632
- docs: string[];
3633
- type: string;
3634
3643
  } | {
3635
3644
  docs?: undefined;
3636
3645
  name: string;
@@ -4339,17 +4348,29 @@ declare const _default: {
4339
4348
  type: {
4340
4349
  kind: string;
4341
4350
  fields: ({
4351
+ docs?: undefined;
4342
4352
  name: string;
4343
4353
  type: string;
4344
4354
  } | {
4355
+ docs?: undefined;
4345
4356
  name: string;
4346
4357
  type: {
4358
+ defined?: undefined;
4347
4359
  option: {
4348
4360
  defined: {
4349
4361
  name: string;
4350
4362
  };
4351
4363
  };
4352
4364
  };
4365
+ } | {
4366
+ name: string;
4367
+ docs: string[];
4368
+ type: {
4369
+ option?: undefined;
4370
+ defined: {
4371
+ name: string;
4372
+ };
4373
+ };
4353
4374
  })[];
4354
4375
  variants?: undefined;
4355
4376
  };