@xgamesfun/solana 0.24.0 → 0.25.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.
Files changed (99) hide show
  1. package/DISCLAIMER.md +1 -1
  2. package/NOTICE +4 -4
  3. package/README.md +100 -52
  4. package/bin/xsolana.mjs +32 -11
  5. package/examples/blind-forge.json +2 -4
  6. package/examples/coin-flip-arena.json +2 -5
  7. package/examples/duel-arena.json +1 -4
  8. package/examples/fair-presale.json +1 -3
  9. package/examples/hero-trials.json +14 -2
  10. package/examples/ideas.json +788 -788
  11. package/examples/nft-raffle.json +1 -0
  12. package/examples/partner-vault.json +19 -3
  13. package/examples/sacrifice-raffle.json +2 -10
  14. package/examples/shelter-fair.json +1 -0
  15. package/examples/stakers-draw.json +4 -7
  16. package/examples/trade-in-shop.json +2 -3
  17. package/js/approve/details.mjs +117 -48
  18. package/js/approve/ed25519.mjs +5 -1
  19. package/js/approve/live-check.mjs +83 -26
  20. package/js/approve/page.html +217 -78
  21. package/js/approve/page.js +43 -15
  22. package/js/approve/server.mjs +276 -74
  23. package/js/chain/base58.js +7 -1
  24. package/js/chain/borsh.js +15 -11
  25. package/js/chain/bytes.js +11 -8
  26. package/js/chain/core.js +21 -5
  27. package/js/chain/curve.js +1 -1
  28. package/js/chain/lookup.js +118 -36
  29. package/js/chain/mint.js +6 -2
  30. package/js/chain/pda.js +2 -2
  31. package/js/chain/programs.js +6 -6
  32. package/js/chain/rpc.js +40 -14
  33. package/js/chain/token.js +13 -2
  34. package/js/chain/token2022.js +55 -16
  35. package/js/chain/tx.js +32 -22
  36. package/js/client/accounts.js +270 -80
  37. package/js/client/compressed.js +4 -3
  38. package/js/client/grant.js +165 -40
  39. package/js/client/idl/loot.js +1 -1
  40. package/js/client/idl/loot.json +104 -16
  41. package/js/client/ix.js +2121 -457
  42. package/js/client/pda.js +20 -18
  43. package/js/client/policy.js +12 -5
  44. package/js/client/random.js +2 -2
  45. package/js/client/sizes.js +220 -65
  46. package/js/client/traits.js +14 -5
  47. package/js/connect/connect.mjs +59 -17
  48. package/js/economy/check.mjs +3884 -690
  49. package/js/economy/cost.mjs +381 -57
  50. package/js/economy/examples.mjs +176 -35
  51. package/js/economy/explain.mjs +468 -110
  52. package/js/economy/guide.mjs +282 -59
  53. package/js/economy/legal.mjs +12 -6
  54. package/js/economy/live.mjs +62 -20
  55. package/js/economy/model.mjs +320 -97
  56. package/js/economy/plan.mjs +1022 -203
  57. package/js/economy/review.mjs +760 -0
  58. package/js/economy/schema.json +134 -372
  59. package/js/economy/secrets.mjs +17 -7
  60. package/js/economy/simulate.mjs +771 -178
  61. package/js/economy/trust.mjs +15 -0
  62. package/js/economy/txsize.mjs +15 -3
  63. package/js/economy/validate.mjs +61 -24
  64. package/js/index.js +1 -0
  65. package/js/mcp/server.mjs +43 -15
  66. package/js/mcp/tools.mjs +318 -80
  67. package/js/play/index.js +1960 -461
  68. package/js/play/keys.js +35 -9
  69. package/js/results/index.js +225 -32
  70. package/js/results/server.js +230 -0
  71. package/js/results/wire.js +64 -16
  72. package/package.json +6 -4
  73. package/release.json +5 -5
  74. package/skills/xsolana-economy/SKILL.md +52 -22
  75. package/skills/xsolana-economy/references/after-launch.md +32 -21
  76. package/skills/xsolana-economy/references/apps.md +47 -36
  77. package/skills/xsolana-economy/references/sentence-to-spec.md +149 -129
  78. package/skills/xsolana-economy/references/telling-the-person.md +15 -6
  79. package/skills/xsolana-economy/references/trust-and-cheating.md +126 -0
  80. package/types/chain/core.d.ts +1 -1
  81. package/types/chain/lookup.d.ts +1 -1
  82. package/types/client/accounts.d.ts +24 -3
  83. package/types/client/grant.d.ts +2 -2
  84. package/types/client/idl/loot.d.ts +22 -0
  85. package/types/client/ix.d.ts +54 -45
  86. package/types/client/pda.d.ts +3 -1
  87. package/types/economy/cost.d.mts +1 -1
  88. package/types/economy/explain.d.mts +3 -0
  89. package/types/economy/guide.d.mts +5 -0
  90. package/types/economy/model.d.mts +48 -3
  91. package/types/economy/plan.d.mts +44 -2
  92. package/types/economy/review.d.mts +45 -0
  93. package/types/economy/trust.d.mts +8 -0
  94. package/types/index.d.ts +1 -0
  95. package/types/mcp/tools.d.mts +62 -0
  96. package/types/play/index.d.ts +14 -25
  97. package/types/results/index.d.ts +8 -7
  98. package/types/results/server.d.ts +126 -0
  99. package/types/results/wire.d.ts +1 -1
@@ -8,14 +8,17 @@ import { auctionSeller, plan, setupContext } from './plan.mjs';
8
8
 
9
9
  /** A key for each fresh account the plan creates. Their secret halves are made by whoever signs (the page). */
10
10
  export function freshAddresses(names, random = randomBytes) {
11
- return Object.fromEntries(names.map((n) => [n, encodeBase58(random(32))]));
11
+ return Object.fromEntries(names.map(n => [n, encodeBase58(random(32))]));
12
12
  }
13
13
 
14
14
  /**
15
15
  * The transactions that put `economy` live for `owner`, with `keys` the addresses of the fresh accounts (each signs
16
16
  * the transaction that creates it). `live`: what is already live (a change publishes only what differs).
17
17
  */
18
- export function prepare(economy, { owner, keys, live = null, now = Math.floor(Date.now() / 1000), rent, only = null, slot = null }) {
18
+ export function prepare(
19
+ economy,
20
+ { owner, keys, live = null, now = Math.floor(Date.now() / 1000), rent, only = null, slot = null },
21
+ ) {
19
22
  const m = model(economy, { now });
20
23
  // `slot`: a recent slot (in the slot hashes now), which the project's standing lookup table is made at.
21
24
  const ctx = { ...setupContext(m, { owner, keys, live: live ?? {}, rent }), slot };
@@ -24,12 +27,19 @@ export function prepare(economy, { owner, keys, live = null, now = Math.floor(Da
24
27
  // Going live always makes the project's standing lookup table (the plan's "table" step). Without a recent slot it
25
28
  // can't be made: refused when a part's widest transaction needs it (`wideParts`), so no player is left with a
26
29
  // transaction only a table carries in one approval.
27
- const wide = p.steps.some((s) => s.id === 'table') && slot == null ? wideParts(m, p.warnings) : [];
30
+ const wide = p.steps.some(s => s.id === 'table') && slot == null ? wideParts(m, p.warnings) : [];
28
31
  if (wide.length)
29
32
  return {
30
33
  ok: false,
31
34
  warnings: p.warnings,
32
- refusals: [{ code: 'E-NO-TABLE', where: wide[0].where, message: `Going live makes the project’s standing lookup table, and no recent slot was read to make it at: ${wide.map((x) => x.why).join('; ')}. Without the table each of those is a wallet’s own table first (more approvals), or can’t be sent at all by a wallet that sends legacy transactions only.`, fix: 'Try again once the cluster answers (the approval page reads a recent slot from its slot hashes).' }],
35
+ refusals: [
36
+ {
37
+ code: 'E-NO-TABLE',
38
+ where: wide[0].where,
39
+ message: `Going live makes the project’s standing lookup table, and no recent slot was read to make it at: ${wide.map(x => x.why).join('; ')}. Without the table each of those is a wallet’s own table first (more approvals), or can’t be sent at all by a wallet that sends legacy transactions only.`,
40
+ fix: 'Try again once the cluster answers (the approval page reads a recent slot from its slot hashes).',
41
+ },
42
+ ],
33
43
  };
34
44
  return { ...p, ctx };
35
45
  }
@@ -41,9 +51,20 @@ export function prepare(economy, { owner, keys, live = null, now = Math.floor(Da
41
51
  * collection's NFTs (a pNFT's stake and exit carry its token record and rule set). [{ where, why }]
42
52
  */
43
53
  export function wideParts(m, warnings = []) {
44
- const out = warnings.filter((w) => w.code === 'W-TX-V0' || w.code === 'W-SETTLE-V0').map((w) => ({ where: w.where, why: `${w.where} (${w.code === 'W-TX-V0' ? 'its open, buy or craft' : 'its settle'} passes a legacy transaction)` }));
45
- for (const a of m.auctions) if (a.bidNfts) out.push({ where: `economy.auctions.${a.key}`, why: `the auction “${a.name}” (its bids are NFTs)` });
46
- for (const f of m.farms) if (f.stakes === 'collectibles' && listOf(f.gate).some((g) => g?.collection)) out.push({ where: `economy.farms.${f.key}`, why: `the farm “${f.name}” (it stakes another collection’s NFTs, a pNFT’s stake and exit pass a legacy transaction)` });
54
+ const out = warnings
55
+ .filter(w => w.code === 'W-TX-V0' || w.code === 'W-SETTLE-V0')
56
+ .map(w => ({
57
+ where: w.where,
58
+ why: `${w.where} (${w.code === 'W-TX-V0' ? 'its open, buy or craft' : 'its settle'} passes a legacy transaction)`,
59
+ }));
60
+ for (const a of m.auctions)
61
+ if (a.bidNfts) out.push({ where: `economy.auctions.${a.key}`, why: `the auction “${a.name}” (its bids are NFTs)` });
62
+ for (const f of m.farms)
63
+ if (f.stakes === 'collectibles' && listOf(f.gate).some(g => g?.collection))
64
+ out.push({
65
+ where: `economy.farms.${f.key}`,
66
+ why: `the farm “${f.name}” (it stakes another collection’s NFTs, a pNFT’s stake and exit pass a legacy transaction)`,
67
+ });
47
68
  return out;
48
69
  }
49
70
 
@@ -53,20 +74,25 @@ export function wideParts(m, warnings = []) {
53
74
  */
54
75
  export function recordLanded(economy, { ctx, signatures = [], at = new Date().toISOString(), now }) {
55
76
  const m = model(economy, { now });
56
- const ids = (list, field = 'id') => list.map((x) => x[field]);
57
- const withIds = (key, list, field = 'id') => (economy[key] ?? []).map((part, i) => ({ ...part, [field]: ids(list, field)[i] }));
77
+ const ids = (list, field = 'id') => list.map(x => x[field]);
78
+ const withIds = (key, list, field = 'id') =>
79
+ (economy[key] ?? []).map((part, i) => ({ ...part, [field]: ids(list, field)[i] }));
58
80
  const out = { ...economy };
59
81
  out.items = (economy.items ?? []).map((it, i) => {
60
- const shop = m.shop.find((b) => b.shopOf === it.key);
82
+ const shop = m.shop.find(b => b.shopOf === it.key);
61
83
  return { ...it, id: m.items[i].id, ...(shop ? { shopBox: shop.id } : {}) };
62
84
  });
63
85
  out.pools = withIds('pools', m.pools, 'index');
64
86
  out.boxes = withIds('boxes', m.boxes);
65
- for (const k of ['stashes', 'tables', 'recipes', 'schedules', 'lotteries', 'farms', 'grants', 'auctions']) if (economy[k]) out[k] = withIds(k, m[k]);
87
+ for (const k of ['stashes', 'tables', 'recipes', 'schedules', 'lotteries', 'farms', 'grants', 'auctions'])
88
+ if (economy[k]) out[k] = withIds(k, m[k]);
66
89
  // An auction's address names its seller: the page finds it by both.
67
- if (economy.auctions) out.auctions = out.auctions.map((a) => ({ ...a, seller: a.seller ?? auctionSeller(a, ctx) }));
90
+ if (economy.auctions) out.auctions = out.auctions.map(a => ({ ...a, seller: a.seller ?? auctionSeller(a, ctx) }));
68
91
  for (const k of ['items', 'pools', 'boxes']) if (!economy[k]) delete out[k];
69
- const mints = { ...(economy.live?.mints ?? {}), ...Object.fromEntries([...m.tokens.values()].filter((t) => t.source === 'game').map((t) => [t.key, ctx.mints[t.key]])) };
92
+ const mints = {
93
+ ...(economy.live?.mints ?? {}),
94
+ ...Object.fromEntries([...m.tokens.values()].filter(t => t.source === 'game').map(t => [t.key, ctx.mints[t.key]])),
95
+ };
70
96
  const { live: _, previous: __, ...published } = out;
71
97
  out.live = {
72
98
  program: LOOT_PROGRAM,
@@ -80,12 +106,23 @@ export function recordLanded(economy, { ctx, signatures = [], at = new Date().to
80
106
  ...(Object.keys(mints).length ? { mints } : {}),
81
107
  at,
82
108
  published,
83
- transactions: [...(economy.live?.transactions ?? []), ...signatures.map((signature) => ({ signature }))].slice(-60),
109
+ transactions: [...(economy.live?.transactions ?? []), ...signatures.map(signature => ({ signature }))].slice(-60),
84
110
  };
85
111
  return out;
86
112
  }
87
113
 
88
- const PART_LISTS = { items: 'item', pools: 'pool', stashes: 'stash', boxes: 'box', recipes: 'recipe', schedules: 'schedule', lotteries: 'lottery', farms: 'farm', grants: 'grant', auctions: 'auction' };
114
+ const PART_LISTS = {
115
+ items: 'item',
116
+ pools: 'pool',
117
+ stashes: 'stash',
118
+ boxes: 'box',
119
+ recipes: 'recipe',
120
+ schedules: 'schedule',
121
+ lotteries: 'lottery',
122
+ farms: 'farm',
123
+ grants: 'grant',
124
+ auctions: 'auction',
125
+ };
89
126
  /** Parts that never change once live: a farm and an auction are never edited, a pool's token is fixed. */
90
127
  const FIXED = new Set(['farm', 'pool', 'auction']);
91
128
 
@@ -100,9 +137,10 @@ export function changedParts(economy) {
100
137
  const only = new Set();
101
138
  const fixed = [];
102
139
  const removed = [];
103
- const strip = ({ id, index, shopBox, seller, ...rest }) => JSON.stringify(rest);
140
+ // A grant's `trust` is the spec's own note about its server, never sent on chain: changing it publishes nothing.
141
+ const strip = ({ id, index, shopBox, seller, trust, ...rest }) => JSON.stringify(rest);
104
142
  for (const [list, kind] of Object.entries(PART_LISTS)) {
105
- const before = new Map((published[list] ?? []).map((p) => [p.key, p]));
143
+ const before = new Map((published[list] ?? []).map(p => [p.key, p]));
106
144
  for (const part of economy[list] ?? []) {
107
145
  const was = before.get(part.key);
108
146
  if (!was) {
@@ -117,14 +155,18 @@ export function changedParts(economy) {
117
155
  }
118
156
  for (const key of before.keys()) removed.push(`${kind}:${key}`);
119
157
  }
120
- for (const t of economy.tokens ?? []) if (!(published.tokens ?? []).some((x) => x.key === t.key)) only.add(`token:${t.key}`);
158
+ for (const t of economy.tokens ?? [])
159
+ if (!(published.tokens ?? []).some(x => x.key === t.key)) only.add(`token:${t.key}`);
121
160
  // The first compressed prize opens the project's tree.
122
161
  if (!economy.live.tree && compressedTree(model(economy))) only.add('tree');
123
162
  // A traits root set again replaces the last.
124
- for (const t of economy.traits ?? []) if (!(published.traits ?? []).some((x) => x.collection === t.collection && x.root === t.root)) only.add(`traits:${t.collection}`);
163
+ for (const t of economy.traits ?? [])
164
+ if (!(published.traits ?? []).some(x => x.collection === t.collection && x.root === t.root))
165
+ only.add(`traits:${t.collection}`);
125
166
  const settings = [];
126
167
  if ((published.keys?.authority ?? 'owner') !== (economy.keys?.authority ?? 'owner')) settings.push('its authority');
127
168
  if ((published.treasury ?? null) !== (economy.treasury ?? null)) settings.push('its treasury');
128
- if (JSON.stringify(published.collections ?? []) !== JSON.stringify(economy.collections ?? [])) settings.push('the collections it takes');
169
+ if (JSON.stringify(published.collections ?? []) !== JSON.stringify(economy.collections ?? []))
170
+ settings.push('the collections it takes');
129
171
  return { only, fixed, removed, settings };
130
172
  }