@xgamesfun/solana 0.28.6 → 0.28.7

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 CHANGED
@@ -77,7 +77,7 @@ codex plugin add xsolana@xgamesfun
77
77
 
78
78
  **claude.ai, ChatGPT or Claude Desktop:** add `https://xgames.fun/mcp` as a custom connector. Nothing to install.
79
79
 
80
- **Cursor, Cline, opencode or any MCP client:** run `npx -y --package=@xgamesfun/solana@0.28.6 xsolana mcp`.
80
+ **Cursor, Cline, opencode or any MCP client:** run `npx -y --package=@xgamesfun/solana@0.28.7 xsolana mcp`.
81
81
 
82
82
  **Any agent that reads skills** (Copilot, Gemini CLI, Windsurf…): `npx skills add XGamesFun/solana-kit`.
83
83
 
@@ -88,26 +88,26 @@ are in [docs/harnesses.md](docs/harnesses.md).
88
88
 
89
89
  ```bash
90
90
  # find a worked example close to your idea
91
- npx -y @xgamesfun/solana@0.28.6 examples shop with loot boxes
91
+ npx -y @xgamesfun/solana@0.28.7 examples shop with loot boxes
92
92
 
93
93
  # using a coin you already launched? read it from the chain into its "tokens" entry
94
- npx -y @xgamesfun/solana@0.28.6 token <its mint address>
94
+ npx -y @xgamesfun/solana@0.28.7 token <its mint address>
95
95
 
96
96
  # does it work? where does the money go? what does it cost?
97
- npx -y @xgamesfun/solana@0.28.6 check xsolana.json
98
- npx -y @xgamesfun/solana@0.28.6 simulate xsolana.json
99
- npx -y @xgamesfun/solana@0.28.6 cost xsolana.json
97
+ npx -y @xgamesfun/solana@0.28.7 check xsolana.json
98
+ npx -y @xgamesfun/solana@0.28.7 simulate xsolana.json
99
+ npx -y @xgamesfun/solana@0.28.7 cost xsolana.json
100
100
 
101
101
  # the whole economy in plain words, and what a cheater could get
102
- npx -y @xgamesfun/solana@0.28.6 explain xsolana.json
103
- npx -y @xgamesfun/solana@0.28.6 review xsolana.json
102
+ npx -y @xgamesfun/solana@0.28.7 explain xsolana.json
103
+ npx -y @xgamesfun/solana@0.28.7 review xsolana.json
104
104
 
105
105
  # go live: a key file for the project (it prints the address to send SOL to, never the key)…
106
- npx -y @xgamesfun/solana@0.28.6 keygen ~/.config/xsolana/my-game.json
106
+ npx -y @xgamesfun/solana@0.28.7 keygen ~/.config/xsolana/my-game.json
107
107
  # …then everything going live would send, and a code; nothing is sent
108
- npx -y @xgamesfun/solana@0.28.6 approve xsolana.json --keypair ~/.config/xsolana/my-game.json
108
+ npx -y @xgamesfun/solana@0.28.7 approve xsolana.json --keypair ~/.config/xsolana/my-game.json
109
109
  # …and with that code, exactly what it listed is sent
110
- npx -y @xgamesfun/solana@0.28.6 approve xsolana.json --keypair ~/.config/xsolana/my-game.json --yes <code>
110
+ npx -y @xgamesfun/solana@0.28.7 approve xsolana.json --keypair ~/.config/xsolana/my-game.json --yes <code>
111
111
  ```
112
112
 
113
113
  Each command names an exact version, so you always run the release you meant to. `xsolana help` lists them all.
package/js/play/index.js CHANGED
@@ -3220,7 +3220,9 @@ export function solanaApp({
3220
3220
  * `chosenRecipient`: the wallet (or collectible) the player names for a share the lottery lets them choose (a
3221
3221
  * referrer). A lottery whose ticket costs an NFT (`nft_ticket`) sells one ticket for one of the player's NFTs that
3222
3222
  * passes its rule: `nft`, its address (held by the project until the draw, then burned, given on or stashed; back to
3223
- * the player if the round is cancelled). `listProof`: for a list gate.
3223
+ * the player if the round is cancelled). `listProof`: for a list gate. `make`: { recipe, times, listing (or
3224
+ * version), assets }, a recipe crafted first in the same transaction, so a player short of what a ticket costs (an
3225
+ * entry a recipe makes) approves once for both; a recipe that rolls can't ride along.
3224
3226
  */
3225
3227
  async buyTickets(
3226
3228
  lotteryKey,
@@ -3233,12 +3235,13 @@ export function solanaApp({
3233
3235
  listing,
3234
3236
  maxPrice,
3235
3237
  acceptLeftover,
3238
+ make,
3236
3239
  ...others
3237
3240
  } = NO_OPTIONS,
3238
3241
  ) {
3239
3242
  only(
3240
3243
  others,
3241
- ['shown', 'chosenRecipient', 'listProof', 'nft', 'listing', 'maxPrice', 'acceptLeftover'],
3244
+ ['shown', 'chosenRecipient', 'listProof', 'nft', 'listing', 'maxPrice', 'acceptLeftover', 'make'],
3242
3245
  'buyTickets',
3243
3246
  );
3244
3247
  const l = part('lotteries', lotteryKey);
@@ -3368,7 +3371,24 @@ export function solanaApp({
3368
3371
  );
3369
3372
  }
3370
3373
  }
3371
- const buy = [...before, ...withSolWrap(wallet.address, mint, BigInt(terms.price) * BigInt(count), [ix])];
3374
+ // `make`: { recipe, times, listing (or version), assets }: a recipe crafted first in the same transaction, so a
3375
+ // player short of what a ticket costs (an entry a recipe makes) approves once for both. Its outputs land before
3376
+ // the purchase takes them; if either fails, neither happens.
3377
+ const made = make
3378
+ ? (
3379
+ await this.craft(make.recipe, make.assets ?? [], make.times ?? 1, {
3380
+ listing: make.listing,
3381
+ version: make.version,
3382
+ build: true,
3383
+ })
3384
+ ).instructions.filter(x => x.programId !== COMPUTE_BUDGET_PROGRAM)
3385
+ : [];
3386
+ const buy = [
3387
+ ...(made.length ? [computeUnitLimit(1_400_000)] : []),
3388
+ ...made,
3389
+ ...before,
3390
+ ...withSolWrap(wallet.address, mint, BigInt(terms.price) * BigInt(count), [ix]),
3391
+ ];
3372
3392
  return send(
3373
3393
  await closesFirst(
3374
3394
  clears,
@@ -3799,8 +3819,13 @@ export function solanaApp({
3799
3819
  * read now is never signed in its place. `extra`: the tail a Core collection's plugins need when the craft moves or
3800
3820
  * rolls on a Core NFT (sent after the Memo program's account, as on every other Core move).
3801
3821
  */
3802
- async craft(recipeKey, assets = [], times = 1, { version, listing, extra = [], ...others } = NO_OPTIONS) {
3803
- only(others, ['version', 'listing', 'extra'], 'craft');
3822
+ async craft(
3823
+ recipeKey,
3824
+ assets = [],
3825
+ times = 1,
3826
+ { version, listing, extra = [], build = false, ...others } = NO_OPTIONS,
3827
+ ) {
3828
+ only(others, ['version', 'listing', 'extra', 'build'], 'craft');
3804
3829
  const r = part('recipes', recipeKey);
3805
3830
  version ??= listing?.version ?? undefined;
3806
3831
  if (version === undefined || version === null)
@@ -3933,6 +3958,13 @@ export function solanaApp({
3933
3958
  rollOn ? loot.namedRollNonce(given.boxes[rollOn.box_id], on, nonce) : loot.prizeRollNonce(nonce),
3934
3959
  )
3935
3960
  : null;
3961
+ // `build`: its instructions, unsent, for a step that rides in another's transaction (buyTickets' `make`). A recipe
3962
+ // that rolls is settled after its transaction, so it can't ride along.
3963
+ if (build) {
3964
+ if (rolls)
3965
+ throw new Error(`“${r.name ?? recipeKey}” rolls: it is crafted on its own (craft), not with another step.`);
3966
+ return { instructions, made };
3967
+ }
3936
3968
  const { signature, rolls: rolled } = await sendRolling(instructions);
3937
3969
  return { signature, made, ...(roll ? { roll } : {}), ...(rolled.length ? { rolled: rolled[0] } : {}) };
3938
3970
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xgamesfun/solana",
3
- "version": "0.28.6",
3
+ "version": "0.28.7",
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",
@@ -11,9 +11,9 @@ signed by a key file on the owner's own machine, once they have read what will b
11
11
  **devnet**, where nothing is real money, and on **mainnet**, where everything is: an economy is on devnet unless its
12
12
  file says `"network": "mainnet"`.
13
13
 
14
- The tools are the `xsolana_*` MCP tools. Without MCP, the same from a terminal: `npx -y @xgamesfun/solana@0.28.6 <guide|examples|check|simulate|cost|explain|review|publish|status> [xsolana.json]`,
15
- `npx -y @xgamesfun/solana@0.28.6 token <mint>` for a coin made elsewhere,
16
- or `npx -y @xgamesfun/solana@0.28.6 tool <name> '<json>'` for any tool (always with the exact version).
14
+ The tools are the `xsolana_*` MCP tools. Without MCP, the same from a terminal: `npx -y @xgamesfun/solana@0.28.7 <guide|examples|check|simulate|cost|explain|review|publish|status> [xsolana.json]`,
15
+ `npx -y @xgamesfun/solana@0.28.7 token <mint>` for a coin made elsewhere,
16
+ or `npx -y @xgamesfun/solana@0.28.7 tool <name> '<json>'` for any tool (always with the exact version).
17
17
 
18
18
  ## Rules (always)
19
19
 
@@ -141,7 +141,7 @@ or `npx -y @xgamesfun/solana@0.28.6 tool <name> '<json>'` for any tool (always w
141
141
  9. **When they say "put it live"**: `xsolana_publish` lists every step and its cost and gives the commands, which you
142
142
  run in the project folder exactly as given (they name the exact version; never drop it):
143
143
  - **A new economy gets a key file**, unless the project has one:
144
- `npx -y @xgamesfun/solana@0.28.6 keygen ~/.config/xsolana/<name>.json`. Give the person the address it prints. A
144
+ `npx -y @xgamesfun/solana@0.28.7 keygen ~/.config/xsolana/<name>.json`. Give the person the address it prints. A
145
145
  Solana key file they already have and name themselves (`~/.config/solana/id.json`) works the same; never go
146
146
  looking for one.
147
147
  - **Say what the key file is** before anything is sent: the project's owner, and, unless the spec says otherwise,
@@ -156,7 +156,7 @@ or `npx -y @xgamesfun/solana@0.28.6 tool <name> '<json>'` for any tool (always w
156
156
  devnet SOL for an economy on devnet, real SOL for one on mainnet), any seed in a coin that already exists, and every NFT a stash, a raffle or an auction names. A
157
157
  collection they already have needs the key's address as its update authority. If they would rather not move
158
158
  those, offer the browser wallet.
159
- - **Show it**: `npx -y @xgamesfun/solana@0.28.6 approve xsolana.json --keypair <key file>` prints every step with
159
+ - **Show it**: `npx -y @xgamesfun/solana@0.28.7 approve xsolana.json --keypair <key file>` prints every step with
160
160
  what it pays and trusts, the warnings, the notice and a code, and sends nothing. Give the person that printout as
161
161
  it is, not a summary of it, and ask.
162
162
  - **On their yes**, the same command with `--yes <code>`. What lands is written back into `xsolana.json`. If it
@@ -38,6 +38,11 @@ await app.openBox('chest', { listing: boxes.find(l => l.key === 'chest') }); //
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, … }]
40
40
  await app.buyTickets('weekly', 2, { listing: lotteries.find(l => l.key === 'weekly') }); // an NFT ticket: { nft: asset, listing }
41
+ // tickets that cost a token a recipe makes: craft what the player lacks in the same transaction, one approval
42
+ await app.buyTickets('weekly', 10, {
43
+ listing,
44
+ make: { recipe: 'entry', times: 4, listing: recipes.find(r => r.key === 'entry') },
45
+ });
41
46
  await app.payNft(asset, { to, reference }); // pay with an NFT (burned, or given to `to`): admit the player by the receipt
42
47
  await app.stake('farm', '10', 0); // tokens, at lock tier 0; app.stakeNft('farm', asset) for a collectible
43
48
  await app.harvest('farm'); // rewards; app.unstake('farm', '10') once the lock has run
@@ -352,9 +352,11 @@ export declare function solanaApp({ spec, rpcUrl, rpc, wallet, programs, now, ke
352
352
  * `chosenRecipient`: the wallet (or collectible) the player names for a share the lottery lets them choose (a
353
353
  * referrer). A lottery whose ticket costs an NFT (`nft_ticket`) sells one ticket for one of the player's NFTs that
354
354
  * passes its rule: `nft`, its address (held by the project until the draw, then burned, given on or stashed; back to
355
- * the player if the round is cancelled). `listProof`: for a list gate.
355
+ * the player if the round is cancelled). `listProof`: for a list gate. `make`: { recipe, times, listing (or
356
+ * version), assets }, a recipe crafted first in the same transaction, so a player short of what a ticket costs (an
357
+ * entry a recipe makes) approves once for both; a recipe that rolls can't ride along.
356
358
  */
357
- buyTickets(lotteryKey: any, count?: number, { shown, chosenRecipient, listProof, nft: paidWith, listing, maxPrice, acceptLeftover, ...others }?: Readonly<Record<string, any>>): Promise<any>;
359
+ buyTickets(lotteryKey: any, count?: number, { shown, chosenRecipient, listProof, nft: paidWith, listing, maxPrice, acceptLeftover, make, ...others }?: Readonly<Record<string, any>>): Promise<any>;
358
360
  /**
359
361
  * Writes an NFT's traits from the project's traits tree (`traits` in the spec) into its Sheet, where rules read them:
360
362
  * anyone may, paying the Sheet's rent. `attributes` ([{ key, value }]) and `proof` come from the project's tree
@@ -454,7 +456,11 @@ export declare function solanaApp({ spec, rpcUrl, rpc, wallet, programs, now, ke
454
456
  * read now is never signed in its place. `extra`: the tail a Core collection's plugins need when the craft moves or
455
457
  * rolls on a Core NFT (sent after the Memo program's account, as on every other Core move).
456
458
  */
457
- craft(recipeKey: any, assets?: any[], times?: number, { version, listing, extra, ...others }?: Readonly<Record<string, any>>): Promise<{
459
+ craft(recipeKey: any, assets?: any[], times?: number, { version, listing, extra, build, ...others }?: Readonly<Record<string, any>>): Promise<{
460
+ instructions: any[];
461
+ made: any;
462
+ } | {
463
+ instructions?: undefined;
458
464
  signature: any;
459
465
  made: any;
460
466
  roll?: any;