@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/README.md +8 -8
- package/js/client/idl/loot.js +1 -1
- package/js/client/idl/loot.json +4 -3
- package/js/client/sizes.js +180 -66
- package/js/economy/check.mjs +36 -19
- package/js/economy/guide.mjs +1 -1
- package/js/economy/plan.mjs +32 -8
- package/js/play/index.js +15 -7
- package/package.json +1 -1
- package/release.json +5 -5
- package/skills/xsolana-economy/SKILL.md +3 -3
- package/skills/xsolana-economy/references/telling-the-person.md +2 -1
- package/types/client/sizes.d.ts +42 -8
- package/types/economy/check.d.mts +6 -2
- package/types/economy/cost.d.mts +1 -0
- package/types/economy/live.d.mts +2 -0
- package/types/economy/plan.d.mts +9 -1
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
|
|
2329
|
-
//
|
|
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
|
|
2386
|
-
//
|
|
2387
|
-
// carries the
|
|
2388
|
-
|
|
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.
|
|
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.
|
|
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.26.
|
|
13
|
-
or `npx -y @xgamesfun/solana@0.26.
|
|
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.
|
|
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
|
|
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.
|
package/types/client/sizes.d.ts
CHANGED
|
@@ -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
|
|
59
|
-
* `shares` and `fates` counted, and `bytes` (`boxBytes`) of heap reading the box
|
|
60
|
-
* be a number: that many draws among every entry. The answer's heap counts for a
|
|
61
|
-
* the oracle (`oracle`: a box that doesn't roll but one `buy` can't carry,
|
|
62
|
-
*
|
|
63
|
-
*
|
|
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`
|
|
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[];
|
package/types/economy/cost.d.mts
CHANGED
package/types/economy/live.d.mts
CHANGED
|
@@ -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
|
}[];
|
package/types/economy/plan.d.mts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
}[];
|