@xgamesfun/solana 0.25.4 → 0.26.1
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.
- package/README.md +8 -8
- package/js/approve/details.mjs +52 -2
- package/js/approve/live-check.mjs +0 -15
- package/js/approve/server.mjs +5 -4
- package/js/chain/token2022.js +3 -2
- package/js/client/accounts.js +67 -25
- package/js/client/grant.js +7 -2
- package/js/client/idl/loot.js +1 -1
- package/js/client/idl/loot.json +716 -183
- package/js/client/ix.js +617 -113
- package/js/client/sizes.js +210 -64
- package/js/economy/check.mjs +691 -261
- package/js/economy/cost.mjs +2 -4
- package/js/economy/explain.mjs +16 -2
- package/js/economy/guide.mjs +12 -12
- package/js/economy/model.mjs +133 -14
- package/js/economy/plan.mjs +68 -19
- package/js/economy/review.mjs +50 -8
- package/js/economy/schema.json +73 -29
- package/js/economy/simulate.mjs +116 -13
- package/js/mcp/tools.mjs +2 -2
- package/js/play/index.js +855 -296
- package/package.json +1 -1
- package/release.json +5 -5
- package/skills/xsolana-economy/SKILL.md +12 -4
- package/skills/xsolana-economy/references/after-launch.md +1 -1
- package/skills/xsolana-economy/references/apps.md +11 -2
- package/skills/xsolana-economy/references/sentence-to-spec.md +3 -3
- package/skills/xsolana-economy/references/telling-the-person.md +22 -4
- package/skills/xsolana-economy/references/trust-and-cheating.md +15 -5
- package/types/chain/token2022.d.ts +3 -2
- package/types/client/accounts.d.ts +34 -11
- package/types/client/idl/loot.d.ts +25 -4
- package/types/client/ix.d.ts +217 -45
- package/types/client/sizes.d.ts +42 -8
- package/types/economy/check.d.mts +11 -2
- package/types/economy/cost.d.mts +1 -0
- package/types/economy/live.d.mts +2 -0
- package/types/economy/model.d.mts +75 -12
- package/types/economy/plan.d.mts +31 -2
- package/types/economy/simulate.d.mts +12 -1
- package/types/mcp/server.d.mts +12 -1
- package/types/mcp/tools.d.mts +12 -1
- 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.
|
|
3
|
+
"version": "0.26.1",
|
|
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.
|
|
2
|
+
"version": "0.26.1",
|
|
3
3
|
"programId": "9cwqM1TXytPnFZsLfmUyv2dg4uW6Ch9C6UJVPns7NFQ",
|
|
4
|
-
"sbfBytes":
|
|
5
|
-
"sbfSha256": "
|
|
6
|
-
"idlSha256": "
|
|
7
|
-
"clientSha256": "
|
|
4
|
+
"sbfBytes": 1783800,
|
|
5
|
+
"sbfSha256": "af318ccfa4d2901076b85fcd1b4467b03253adf0de16e73a9dfc1ef8077b2c14",
|
|
6
|
+
"idlSha256": "0e88fc826a585cf17917cbc654e33b68232771769fe9aae1d5899a257940ff47",
|
|
7
|
+
"clientSha256": "01d85ff69999efc8b0641ce684dd060cf0ad421a8e125f91d620fef50bb5aedd"
|
|
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.
|
|
13
|
-
or `npx -y @xgamesfun/solana@0.
|
|
12
|
+
The tools are the `xsolana_*` MCP tools. Without MCP, the same from a terminal: `npx -y @xgamesfun/solana@0.26.1 <guide|examples|check|simulate|cost|explain|review|publish|status> [xsolana.json]`,
|
|
13
|
+
or `npx -y @xgamesfun/solana@0.26.1 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.
|
|
124
|
+
`npx -y @xgamesfun/solana@0.26.1 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
|
|
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,
|
|
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)
|
|
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`.
|
|
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 })`),
|
|
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.
|
|
76
|
-
|
|
77
|
-
|
|
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,20 @@ 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; fewer where outcomes name pools or stashes, since the write counts finding
|
|
94
|
+
each one's address): a box past that is refused when written. Put a long weighted list in a table
|
|
95
|
+
or a stash, drawn by one outcome.
|
|
96
|
+
- **The program takes any time, and the checker says a far one's date in words (W-FAR-TIME).** A time that looks like
|
|
97
|
+
milliseconds, or lands more than three years out, locks the bids, tickets, NFTs or vault that wait on it until then.
|
|
98
|
+
Read the date to the person before they sign.
|
|
99
|
+
- **Much of what the program used to refuse is the project's own choice now, warned of in words, never refused**: a
|
|
100
|
+
price or a prize of 0, a mission with no lock, a contest of NFTs whose winners take none of the losers' NFTs, a
|
|
101
|
+
sealed schedule of any length paying any address the program doesn't own, a lottery, contest or auction in a token a
|
|
102
|
+
player can hold a payment up in (a frozen default, confidential transfers), a farm that mints a token the project is
|
|
103
|
+
not yet the authority of (it pays nothing until it is, and that time is never paid). Tell the person each warning
|
|
104
|
+
`xsolana_check` gives, in its own words.
|
|
87
105
|
|
|
88
106
|
Don'ts:
|
|
89
107
|
|
|
@@ -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
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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.
|
|
12
|
-
*
|
|
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,
|
|
210
|
-
|
|
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`, `
|
|
214
|
-
* lottery's own stash each such place got), and `entries`, one a
|
|
215
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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 (`
|
|
291
|
-
* round's, or once
|
|
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
|
};
|