@xgamesfun/solana 0.26.0 → 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/js/play/index.js CHANGED
@@ -1489,6 +1489,11 @@ export function solanaApp({
1489
1489
  const g = await projectNow();
1490
1490
  const box = await read('LootBox', loot.pda.box(project, boxId));
1491
1491
  if (!Number.isInteger(count) || count < 1) throw new Error('Buy a whole number, one at least (count).');
1492
+ // One `buy` prices at most so many sales (`PackTooLarge` past it): more is several purchases.
1493
+ if (count > loot.MOST_SALES_A_BUY)
1494
+ throw new Error(
1495
+ `One purchase buys at most ${loot.MOST_SALES_A_BUY.toLocaleString('en-US')} (count): buy the rest in another.`,
1496
+ );
1492
1497
  const bound =
1493
1498
  boundOf({ ...box, currencyKey: part?.currency }, part?.name ?? `box ${boxId}`, { maxPrice, listing }, part?.key) *
1494
1499
  BigInt(count);
@@ -2325,8 +2330,9 @@ export function solanaApp({
2325
2330
  const e = i == null ? null : r.entries[i];
2326
2331
  const out = [];
2327
2332
  const rolls = [];
2328
- // Whether a pull here wants the largest heap (a run of rolls), and whether one stops short of the part (a raffle's
2329
- // NFTs past what one pull takes out, a run of rolls past what one transaction carries): a next pull carries on.
2333
+ // Whether a pull here wants the largest heap (a run of rolls, or several NFTs out of the round's own stash), and
2334
+ // whether one stops short of the part (a raffle's NFTs past what one pull takes out or one transaction carries, a
2335
+ // run of rolls past what one transaction carries): a next pull carries on.
2330
2336
  let heap = false;
2331
2337
  let partial = false;
2332
2338
  const g = await projectNow();
@@ -2382,10 +2388,12 @@ export function solanaApp({
2382
2388
  projectMint: mintOf(),
2383
2389
  };
2384
2390
  more.nonce = pullNonce(key, i);
2385
- // A run of places that each give a roll: two accounts a place, as many as one transaction carries, on the
2386
- // largest heap. The pull is sized from a dry run, largest first; the next pull carries on, and only the last
2387
- // carries the close.
2388
- if (given.Roll) {
2391
+ // A run of places that each give a roll (two accounts a place), or several NFTs out of the round's own stash
2392
+ // (each NFT's accounts, and heap the program does not give back: 8 Core moves in a round of some 125 places or
2393
+ // more run past the default 32 KiB): as many places as one transaction carries, on the largest heap. The pull
2394
+ // is sized from a dry run, largest first; the next pull carries on while the round still owes the entry, and
2395
+ // only the last carries the close.
2396
+ if (given.Roll || (stash && loot.prizePlacesOwed(r, i, run).length > 1)) {
2389
2397
  const owed = loot.prizePlacesOwed(r, i, run).length;
2390
2398
  const sized = async most => {
2391
2399
  const ix = loot.pullIx({
@@ -4928,7 +4936,7 @@ export function solanaApp({
4928
4936
  heap ||= built.heap;
4929
4937
  partial ||= built.partial;
4930
4938
  }
4931
- // A run of rolls asks for the largest heap.
4939
+ // A run of rolls, or several NFTs out of the round's own stash, asks for the largest heap.
4932
4940
  if (heap) instructions.splice(1, 0, requestHeapFrame(loot.LARGE_SETTLE_HEAP));
4933
4941
  // A pull that stopped short of its part (a raffle's NFTs, 64 a pull; a run of rolls): sent, then the next carries
4934
4942
  // on while the round moves (its NFTs out, its places paid) and still owes it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xgamesfun/solana",
3
- "version": "0.26.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.26.0",
2
+ "version": "0.26.1",
3
3
  "programId": "9cwqM1TXytPnFZsLfmUyv2dg4uW6Ch9C6UJVPns7NFQ",
4
- "sbfBytes": 1779872,
5
- "sbfSha256": "d5d4a2ca2bca79e8d4930b697cc3aecfcf54399aa85585c02e267ffbbdfda429",
6
- "idlSha256": "4e1407b41c5947821d154472bd26c2df0fff9c750475239d444b2f5fd5d3d8dc",
7
- "clientSha256": "e6b1783c6f68e342aa165474f1d3b96a7a24a3a981be4882ebcd7ae071e41e6e"
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.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).
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
 
@@ -121,7 +121,7 @@ or `npx -y @xgamesfun/solana@0.26.0 tool <name> '<json>'` for any tool (always w
121
121
  level would cost. Run it again after every change that touches a route that pays.
122
122
  9. **When they say "put it live"**: `xsolana_publish` lists every step and its cost and gives the command. Tell them to
123
123
  run it themselves in the project folder, exactly as `xsolana_publish` gives it (it names the exact version:
124
- `npx -y @xgamesfun/solana@0.26.0 approve xsolana.json`; never drop the version). It opens a page on their computer where
124
+ `npx -y @xgamesfun/solana@0.26.1 approve xsolana.json`; never drop the version). It opens a page on their computer where
125
125
  they connect a devnet wallet and approve; what lands is written back into `xsolana.json`. You publish nothing.
126
126
  10. **After it is live**: `xsolana_status` shows each part on chain; read it before acting on what the person says
127
127
  happened. A later change is the same loop: save, publish, and the approval sends only what changed. What only the
@@ -90,7 +90,8 @@ money; it holds no files and serves nothing.
90
90
  the mints and vaults of its tokens, a fresh item's tables) are bounded by that, however many outcomes it has.
91
91
  `xsolana_check` counts them. One answer also holds only so much of a box (measured: 128 outcomes that give
92
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
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
94
95
  or a stash, drawn by one outcome.
95
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
96
97
  milliseconds, or lands more than three years out, locks the bids, tickets, NFTs or vault that wait on it until then.
@@ -41,6 +41,19 @@ export declare function entryRows(entries: any, items?: {}): any;
41
41
  * gives items. A pack's order grows with its cards.
42
42
  */
43
43
  export declare function orderBytes(lootBox: any, shares: number | undefined, opens: number | undefined, items: any): number;
44
+ /** The most draws one `open_box` takes in all, its count times the box's draws: what one answer could weigh, each draw
45
+ * at its weighing at least (`ROLL_BUDGET / COST_WEIGH`, 300). More is refused before any price is added up
46
+ * (`PackTooLarge`). */
47
+ export declare const MOST_DRAWS_A_PURCHASE: number;
48
+ /** The most sales one `buy` takes (`count`): what one transaction can price (`MAX_TX_CU / COST_SALE`, rounded down:
49
+ * 25,454). More is refused before any price is added up (`PackTooLarge`). */
50
+ export declare const MOST_SALES_A_BUY: number;
51
+ /**
52
+ * The tries counted for finding a pool's or a stash's address when the project is not known yet (a spec checked before
53
+ * any wallet is named): 2, what an address takes on average (half are found at the first try, a quarter at the second,
54
+ * and so on). With the project known, the count is exact: `256 - bump` tries an address.
55
+ */
56
+ export declare const ASSUMED_TRIES = 2;
44
57
  /**
45
58
  * The heap reading a box takes beyond its entries (`order::box_bytes`): its odds, gate, shares, subject, slots and
46
59
  * fates, read whole with it, by their bytes and their number. `box`: the box's args, as `upsertBoxIx` takes them.
@@ -53,20 +66,30 @@ export declare function boxBytes({ odds, gate, shares, subject, slots, fates }:
53
66
  slots?: never[] | undefined;
54
67
  subject?: null | undefined;
55
68
  }): bigint;
69
+ /**
70
+ * The tries finding a pool's (`pool` true) or a stash's address takes (`order::searched`: `256 - bump`), for `project`'s
71
+ * pool or stash `id`. With no project, `ASSUMED_TRIES`.
72
+ */
73
+ export declare function searchTries(project: any, pool: any, id: any): number;
56
74
  /**
57
75
  * Whether all of `opens` opens of `entries` by `slots` fit one oracle answer (`order::draws_fit`), each draw at the
58
- * dearest its slot could make, with what is known from the box alone (no table rows, a stash of one group), its `odds`,
59
- * `shares` and `fates` counted, and `bytes` (`boxBytes`) of heap reading the box takes beyond its entries. `slots` may
60
- * be a number: that many draws among every entry. The answer's heap counts for a box that rolls, or one sold through
61
- * the oracle (`oracle`: a box that doesn't roll but one `buy` can't carry, `buyFits`). The program refuses a box whose
62
- * one open doesn't fit at its upsert, and a purchase that doesn't at `open_box`, where it also reads what the accounts
63
- * hold (`PackTooLarge`).
76
+ * dearest its slot could make, with what is known from the box alone (no table rows, a stash of one group, a named
77
+ * NFT's stash of one row), its `odds`, `shares` and `fates` counted, and `bytes` (`boxBytes`) of heap reading the box
78
+ * takes beyond its entries. `slots` may be a number: that many draws among every entry. The answer's heap counts for a
79
+ * box that rolls, or one sold through the oracle (`oracle`: a box that doesn't roll but one `buy` can't carry,
80
+ * `buyFits`). `project`: the project the box is in, whose pools' and stashes' addresses the write searches for (each
81
+ * search counted at `256 - bump` tries of 1,500 units); without it, at `ASSUMED_TRIES` tries an address. The program
82
+ * refuses a box whose one open doesn't fit at its upsert (`PackTooLarge`). `purchase`: counted as `open_box` counts a
83
+ * purchase of `opens` instead (the answer's heap always; a draw's own stock read again only when there are several; at
84
+ * most `MOST_DRAWS_A_PURCHASE` draws in all), from what the box alone tells: the program reads the accounts too.
64
85
  */
65
- export declare function drawsFit(entries: any, slots: any, opens?: number, { odds, shares, fates, bytes, oracle }?: {
86
+ export declare function drawsFit(entries: any, slots: any, opens?: number, { odds, shares, fates, bytes, oracle, project, purchase }?: {
66
87
  bytes?: bigint | undefined;
67
88
  fates?: number | undefined;
68
89
  odds?: number | undefined;
69
90
  oracle?: boolean | undefined;
91
+ project?: null | undefined;
92
+ purchase?: boolean | undefined;
70
93
  shares?: number | undefined;
71
94
  }): boolean;
72
95
  /**
@@ -76,10 +99,21 @@ export declare function drawsFit(entries: any, slots: any, opens?: number, { odd
76
99
  * (`drawsFit` with `oracle`), and can't give a roll (`PackTooLarge`). `box`: the box's args.
77
100
  */
78
101
  export declare function buyFits(box: any, draws?: any): boolean;
102
+ /**
103
+ * Whether the program writes `box` (its args, as `upsertBoxIx` takes them, or a box read from the chain) as far as what
104
+ * it costs goes (`catalog::loot_box`, `PackTooLarge`): a box that doesn't roll sells by `buy` when one transaction carries
105
+ * the whole purchase (`buyFits`), else through the oracle, whose heap then counts and which can't give a roll; and every
106
+ * draw of one open fits one answer (`drawsFit`). `project`: whose pools and stashes the box names (a box read from the
107
+ * chain says); without it, each search at `ASSUMED_TRIES` tries.
108
+ */
109
+ export declare function boxFits(box: any, { project }?: {
110
+ project?: any;
111
+ }): boolean;
79
112
  /** The most program rent and fee one order takes from its player, in lamports (0.01 SOL): a client cap (`opensAnOrder`). */
80
113
  export declare const ORDER_COST_CAP = 10000000n;
81
114
  /**
82
- * The most opens of `lootBox` a client puts in one order: what one oracle answer draws (`drawsFit`), and what keeps the
115
+ * The most opens of `lootBox` a client puts in one order: what one oracle answer draws (`drawsFit` as a purchase counts
116
+ * it, the box's project's searches counted when the box names it: a box read from the chain does), and what keeps the
83
117
  * order's rent and the player's fee within `ORDER_COST_CAP` (`orderBytes` with `items`, so with the box's shares). At
84
118
  * least one: a box whose one open is over the cap is still bought one open an order. A purchase of more is several
85
119
  * orders, each its own answer.
@@ -87,12 +87,16 @@ export declare const FAR_SECONDS: number;
87
87
  /** A Unix time in words, in English: "14 March 2031". Past what a date can show, the year alone. */
88
88
  export declare function dateWords(seconds: any): string;
89
89
  /**
90
- * Checks an economy spec. `now` (Unix seconds) dates "publish".
90
+ * Checks an economy spec. `now` (Unix seconds) dates "publish". `project`: the project's address, once a wallet is named
91
+ * (the plan for a real setup passes it; a live economy's file names it): a box's write searches for each of its pools'
92
+ * and stashes' addresses, and the checker counts each search exactly with it, else at an assumed 2 tries an address
93
+ * (`ASSUMED_TRIES`).
91
94
  * Returns { ok, refusals: [{ code, where, message, fix }], warnings: [{ code, where, message, fix? }] }.
92
95
  */
93
- export declare function check(economy: any, { network, now }?: {
96
+ export declare function check(economy: any, { network, now, project }?: {
94
97
  network?: string | undefined;
95
98
  now?: number | undefined;
99
+ project?: any;
96
100
  }): {
97
101
  ok: boolean;
98
102
  refusals: any[];
@@ -40,6 +40,7 @@ export declare function cost(economy: any, { now, only, live }?: {
40
40
  steps: any[];
41
41
  built: any[];
42
42
  instructions: any;
43
+ compute: boolean;
43
44
  signers: any[];
44
45
  alone: boolean;
45
46
  }[];
@@ -33,6 +33,7 @@ export declare function prepare(economy: any, { owner, keys, live, now, rent, on
33
33
  steps: any[];
34
34
  built: any[];
35
35
  instructions: any;
36
+ compute: boolean;
36
37
  signers: any[];
37
38
  alone: boolean;
38
39
  }[];
@@ -68,6 +69,7 @@ export declare function prepare(economy: any, { owner, keys, live, now, rent, on
68
69
  steps: any[];
69
70
  built: any[];
70
71
  instructions: any;
72
+ compute: boolean;
71
73
  signers: any[];
72
74
  alone: boolean;
73
75
  }[];
@@ -1031,12 +1031,19 @@ export declare function setupContext(m: any, { owner: ownerKey, keys, live: give
1031
1031
  };
1032
1032
  /** The bytes of a transaction of `instructions` the owner pays for: what `pack` holds against the budget. */
1033
1033
  export declare const stepBytes: (instructions: any, ctx: any) => number;
1034
- /** Packs steps into transactions under the size budget, one signer set at a time; each step whole. */
1034
+ /** The bytes of a transaction of `step` alone, as `pack` sends it (its compute limit included). */
1035
+ export declare const stepAloneBytes: (step: any, ctx: any) => number;
1036
+ /**
1037
+ * Packs steps into transactions under the size budget, one signer set at a time; each step whole. A transaction that
1038
+ * carries a step needing more compute than an instruction has by default (`compute`: a box's write) asks for all a
1039
+ * transaction can have, first.
1040
+ */
1035
1041
  export declare function pack(steps: any, ctx: any): {
1036
1042
  signer: string;
1037
1043
  steps: any[];
1038
1044
  built: any[];
1039
1045
  instructions: any;
1046
+ compute: boolean;
1040
1047
  signers: any[];
1041
1048
  alone: boolean;
1042
1049
  }[];
@@ -1067,6 +1074,7 @@ export declare function plan(economy: any, { now, ctx, only, live }?: {
1067
1074
  steps: any[];
1068
1075
  built: any[];
1069
1076
  instructions: any;
1077
+ compute: boolean;
1070
1078
  signers: any[];
1071
1079
  alone: boolean;
1072
1080
  }[];