@xgamesfun/solana 0.24.0 → 0.25.0

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 (92) hide show
  1. package/README.md +94 -51
  2. package/bin/xsolana.mjs +23 -8
  3. package/examples/blind-forge.json +2 -4
  4. package/examples/coin-flip-arena.json +2 -5
  5. package/examples/duel-arena.json +1 -4
  6. package/examples/fair-presale.json +1 -3
  7. package/examples/hero-trials.json +14 -2
  8. package/examples/holder-den.json +38 -0
  9. package/examples/holders-drop.json +105 -0
  10. package/examples/ideas.json +788 -788
  11. package/examples/match-predictions.json +24 -0
  12. package/examples/nft-raffle.json +1 -0
  13. package/examples/pack-league.json +108 -0
  14. package/examples/partner-vault.json +19 -3
  15. package/examples/sacrifice-raffle.json +2 -10
  16. package/examples/shelter-fair.json +1 -0
  17. package/examples/stakers-draw.json +3 -7
  18. package/examples/stride-sneakers.json +164 -0
  19. package/examples/trade-in-shop.json +2 -3
  20. package/js/approve/details.mjs +117 -48
  21. package/js/approve/ed25519.mjs +5 -1
  22. package/js/approve/live-check.mjs +83 -26
  23. package/js/approve/page.html +217 -78
  24. package/js/approve/page.js +43 -15
  25. package/js/approve/server.mjs +276 -74
  26. package/js/chain/base58.js +7 -1
  27. package/js/chain/borsh.js +15 -11
  28. package/js/chain/bytes.js +11 -8
  29. package/js/chain/core.js +21 -5
  30. package/js/chain/curve.js +1 -1
  31. package/js/chain/lookup.js +118 -36
  32. package/js/chain/mint.js +6 -2
  33. package/js/chain/pda.js +2 -2
  34. package/js/chain/programs.js +6 -6
  35. package/js/chain/rpc.js +40 -14
  36. package/js/chain/token.js +13 -2
  37. package/js/chain/token2022.js +55 -16
  38. package/js/chain/tx.js +32 -22
  39. package/js/client/accounts.js +270 -80
  40. package/js/client/compressed.js +4 -3
  41. package/js/client/grant.js +165 -40
  42. package/js/client/idl/loot.js +1 -1
  43. package/js/client/idl/loot.json +104 -16
  44. package/js/client/ix.js +2121 -457
  45. package/js/client/pda.js +20 -18
  46. package/js/client/policy.js +12 -5
  47. package/js/client/random.js +1 -1
  48. package/js/client/sizes.js +220 -65
  49. package/js/client/traits.js +14 -5
  50. package/js/connect/connect.mjs +59 -17
  51. package/js/economy/check.mjs +3873 -690
  52. package/js/economy/cost.mjs +380 -56
  53. package/js/economy/examples.mjs +176 -35
  54. package/js/economy/explain.mjs +461 -110
  55. package/js/economy/guide.mjs +282 -59
  56. package/js/economy/legal.mjs +12 -6
  57. package/js/economy/live.mjs +60 -19
  58. package/js/economy/model.mjs +320 -97
  59. package/js/economy/plan.mjs +1022 -203
  60. package/js/economy/schema.json +127 -369
  61. package/js/economy/secrets.mjs +17 -7
  62. package/js/economy/simulate.mjs +771 -178
  63. package/js/economy/txsize.mjs +15 -3
  64. package/js/economy/validate.mjs +61 -24
  65. package/js/mcp/server.mjs +42 -14
  66. package/js/mcp/tools.mjs +293 -80
  67. package/js/play/index.js +1960 -461
  68. package/js/play/keys.js +35 -9
  69. package/js/results/index.js +223 -30
  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 +30 -16
  75. package/skills/xsolana-economy/references/after-launch.md +30 -20
  76. package/skills/xsolana-economy/references/apps.md +47 -36
  77. package/skills/xsolana-economy/references/sentence-to-spec.md +148 -129
  78. package/skills/xsolana-economy/references/telling-the-person.md +9 -6
  79. package/types/chain/core.d.ts +1 -1
  80. package/types/chain/lookup.d.ts +1 -1
  81. package/types/client/accounts.d.ts +24 -3
  82. package/types/client/grant.d.ts +2 -2
  83. package/types/client/idl/loot.d.ts +22 -0
  84. package/types/client/ix.d.ts +54 -45
  85. package/types/client/pda.d.ts +3 -1
  86. package/types/economy/guide.d.mts +5 -0
  87. package/types/economy/model.d.mts +48 -3
  88. package/types/economy/plan.d.mts +44 -2
  89. package/types/play/index.d.ts +8 -19
  90. package/types/results/index.d.ts +7 -6
  91. package/types/results/server.d.ts +126 -0
  92. package/types/results/wire.d.ts +1 -1
package/README.md CHANGED
@@ -12,13 +12,56 @@ package `@xgamesfun/solana`:
12
12
 
13
13
  An economy is a JSON spec, not a new smart contract. Every project shares the one program, and none writes its own.
14
14
 
15
- > **Status:** loot 0.24.0, program ID `9cwqM1TXytPnFZsLfmUyv2dg4uW6Ch9C6UJVPns7NFQ`, **devnet only**. The program is
16
- > not independently audited, and nothing runs on mainnet until it is. See [SECURITY.md](SECURITY.md).
15
+ > **Status:** these tools are for loot 0.25.0, program ID `9cwqM1TXytPnFZsLfmUyv2dg4uW6Ch9C6UJVPns7NFQ`, **devnet
16
+ > only** (deployed 2026-11-08, slot 519,994,814). The program is not independently audited, and nothing runs
17
+ > on mainnet until it is. See [SECURITY.md](SECURITY.md).
17
18
 
18
19
  > **Disclaimer:** provided as is, with no warranty and no guarantee that the program or the tools are safe. Use them
19
20
  > at your own risk; you are responsible for every transaction you approve and for making sure what you build is legal
20
21
  > where it runs. Nothing here is legal or financial advice. Read [DISCLAIMER.md](DISCLAIMER.md) before you use it.
21
22
 
23
+ ## Before you use it
24
+
25
+ - **Devnet only, and not independently audited.** Nothing here is for real money yet. Use a devnet wallet.
26
+ - **The program can be upgraded, and an upgrade can mean starting again.** Its upgrade authority can replace the
27
+ program's code, which is power over every economy on it (on mainnet that key is to be a multisig). No version so far
28
+ has carried accounts over from the one before: an economy made on 0.23 was made again for 0.24. Pin the package
29
+ version (each one names the program build it is for in `release.json`) and expect to set an economy up again after an
30
+ upgrade.
31
+ - **Players trust the project.** Its owner and authority are trusted keys with no caps: the authority mints, moves the
32
+ project's pool funds, decides contests and writes its NFTs. The program protects players from each other, from the
33
+ platform and from mistakes, not from the project they chose to play. Money held for a record (an order, a bid, a
34
+ farm's stakes, a contest's or lottery's pot) leaves only by that record's rules.
35
+ - **Players also trust the token and the collection.** A mint's issuer (a freeze authority, a pause, a transfer hook, a
36
+ permanent delegate) or a collection's controller can stop or move what loot holds. USDC has a freeze authority.
37
+ - **The chain can't see a game.** Results a game reports (a win, a score, a clear) are vouched for by a key the project
38
+ trusts, a results server or deciders. Checking that a player really did it is the project's job, on its own server.
39
+ - **The hosted results server is optional.** `https://play.xgames.fun/xsolana` (`@xgamesfun/solana/results`'s
40
+ default) signs vouchers for games without a server of their own. It can only pay what a grant allows, and only for a
41
+ grant whose owner named its key in `vouchers`. It sends nothing on chain, and it is offered as is: it can be down, and
42
+ it is behind for a while after each program upgrade. A game with its own server signs its own vouchers
43
+ (`signVoucher`), which is the better choice once it has one.
44
+ - **Nothing runs by itself.** Someone sends every step: settles (the player, the project's keys, or a key the player
45
+ named), lottery draws, auction ends, refunds (anyone), contest results (the authority or its deciders) and schedule
46
+ runs (the project's keys). A run nobody sends on time is missed, except a sealed schedule's fixed amount. Apps built
47
+ with `@xgamesfun/solana/play` send the steps a player needs. Don't count on a hosted keeper.
48
+ - **The rules are strict.** A prize is a fixed amount: a jackpot that grows is a lottery's pot. A lottery says where its
49
+ places are paid from: a pot that carries over (gifts and what earlier rounds left count), or each round's own ticket
50
+ money, which nothing from outside can change. A prize never shifts to a later round. A draw uses the odds
51
+ as written. When an outcome runs out, a box either stops selling (`stopSales`, the default) or keeps selling and
52
+ draws among what's left (`drawLeft`, which lets a player empty a common outcome to make a rare one likelier).
53
+ - **Chance comes from an oracle.** MagicBlock's VRF answers every random roll and draw. If it stops answering, rolls wait. An
54
+ order still unanswered after 7 days can be refunded. Once a roll has waited 10 minutes, a buyer who sees a late
55
+ answer coming can take a refund instead.
56
+ - **Fees and rent.** The platform sets two flat fees: by default 0.002 SOL a player action and 0.00001 SOL a project
57
+ action. Each is capped at 0.01 SOL, and a raise waits 7 days. Accounts cost rent: going live, and each settle that
58
+ opens or mints something. `xsolana cost` gives the totals. The platform can suspend purchases, but it never stops
59
+ payouts, refunds or unstakes (the one exception is a recipe that charges).
60
+ - **Some things aren't built,** and some limits are kept by design. Read
61
+ [Known limits](docs/program.md#known-limits) before promising players something unusual.
62
+ - **The law is yours to check.** The checker's legal flags (chance at money, resellable random items, revenue
63
+ sharing) are reminders, not advice. A spec with no flag isn't therefore legal.
64
+
22
65
  ## What an economy can be made of
23
66
 
24
67
  - payments and splits, in tokens or with an NFT
@@ -59,13 +102,13 @@ codex plugin add xsolana@xgamesfun
59
102
  ```
60
103
 
61
104
  **Any agent with MCP** (Cursor, opencode, Cline…): run the server with
62
- `npx -y --package=@xgamesfun/solana@0.24.0 xsolana mcp`. Or let `npx -y @xgamesfun/solana@0.24.0 connect` register it, with
105
+ `npx -y --package=@xgamesfun/solana@0.25.0 xsolana mcp`. Or let `npx -y @xgamesfun/solana@0.25.0 connect` register it, with
63
106
  the skill, for Claude Code and Codex.
64
107
 
65
- Then ask for an economy, for example *"a fishing game where each cast costs 0.02 SOL, with a rare golden fish and a
66
- monthly draw"*.
108
+ Then ask for an economy, for example _"a fishing game where each cast costs 0.02 SOL, with a rare golden fish and a
109
+ monthly draw"_.
67
110
 
68
- Going live is always yours: `npx -y @xgamesfun/solana@0.24.0 approve` opens a page on your computer where you connect a
111
+ Going live is always yours: `npx -y @xgamesfun/solana@0.25.0 approve` opens a page on your computer where you connect a
69
112
  devnet wallet and approve each transaction. The agent never signs and never asks for a key.
70
113
 
71
114
  ## Use it from a terminal
@@ -73,12 +116,12 @@ devnet wallet and approve each transaction. The agent never signs and never asks
73
116
  Every tool runs from the CLI too, on a spec file (`xsolana.json` by default):
74
117
 
75
118
  ```bash
76
- npx -y @xgamesfun/solana@0.24.0 examples shop with loot boxes # the nearest worked examples
77
- npx -y @xgamesfun/solana@0.24.0 check xsolana.json # refusals and warnings
78
- npx -y @xgamesfun/solana@0.24.0 simulate xsolana.json # where the money goes, day by day
79
- npx -y @xgamesfun/solana@0.24.0 cost xsolana.json # what going live and each action cost
80
- npx -y @xgamesfun/solana@0.24.0 explain xsolana.json # the economy in plain words
81
- npx -y @xgamesfun/solana@0.24.0 approve xsolana.json # go live, approved in your own wallet
119
+ npx -y @xgamesfun/solana@0.25.0 examples shop with loot boxes # the nearest worked examples
120
+ npx -y @xgamesfun/solana@0.25.0 check xsolana.json # refusals and warnings
121
+ npx -y @xgamesfun/solana@0.25.0 simulate xsolana.json # where the money goes, day by day
122
+ npx -y @xgamesfun/solana@0.25.0 cost xsolana.json # what going live and each action cost
123
+ npx -y @xgamesfun/solana@0.25.0 explain xsolana.json # the economy in plain words
124
+ npx -y @xgamesfun/solana@0.25.0 approve xsolana.json # go live, approved in your own wallet
82
125
  ```
83
126
 
84
127
  `xsolana help` lists every command. Every command names the exact version, so `npx` runs that release and never whatever
@@ -95,45 +138,45 @@ import * as loot from '@xgamesfun/solana/client';
95
138
  const ix = loot.openBoxIx({ player, project, box, nonce }); // one builder per instruction, no Solana SDK
96
139
  ```
97
140
 
98
- | Import | What |
99
- | --- | --- |
100
- | `@xgamesfun/solana` | the economy tools: `check`, `simulate`, `plan`, `cost`, `explainEconomy`, `model` |
101
- | `@xgamesfun/solana/client` | every PDA, one builder per instruction, account and receipt decoders, grant vouchers |
102
- | `@xgamesfun/solana/play` | `solanaApp`: what a player does with a live economy, from a browser app |
103
- | `@xgamesfun/solana/results` | results and vouchers a game's server signs |
104
- | `@xgamesfun/solana/chain/*` | browser-safe Solana primitives: base58, Borsh, transactions, PDAs, tokens, Core |
105
- | `@xgamesfun/solana/idl.json` | the program's IDL, the authority for account and argument layouts |
106
- | `@xgamesfun/solana/release.json` | the program build, IDL and client this release is pinned to |
141
+ | Import | What |
142
+ | -------------------------------- | ------------------------------------------------------------------------------------ |
143
+ | `@xgamesfun/solana` | the economy tools: `check`, `simulate`, `plan`, `cost`, `explainEconomy`, `model` |
144
+ | `@xgamesfun/solana/client` | every PDA, one builder per instruction, account and receipt decoders, grant vouchers |
145
+ | `@xgamesfun/solana/play` | `solanaApp`: what a player does with a live economy, from a browser app |
146
+ | `@xgamesfun/solana/results` | results and vouchers a game's server signs |
147
+ | `@xgamesfun/solana/chain/*` | browser-safe Solana primitives: base58, Borsh, transactions, PDAs, tokens, Core |
148
+ | `@xgamesfun/solana/idl.json` | the program's IDL, the authority for account and argument layouts |
149
+ | `@xgamesfun/solana/release.json` | the program build, IDL and client this release is pinned to |
107
150
 
108
151
  The client, chain and play modules run in browsers and Node 22 or later, with no bundler required. The package
109
152
  ships TypeScript declarations for every entry point.
110
153
 
111
154
  ## Documentation
112
155
 
113
- | | |
114
- | --- | --- |
115
- | [`docs/program.md`](docs/program.md) | the program: who signs, accounts, every instruction, fees, limits, receipts, costs |
116
- | [`docs/nfts.md`](docs/nfts.md) | NFTs and tokens, with and without the project's authority |
117
- | [`docs/harnesses.md`](docs/harnesses.md) | how the plugin reaches Claude Code and Codex, and the release checklist |
118
- | [`skills/xsolana-economy`](skills/xsolana-economy/SKILL.md) | the skill an agent follows |
119
- | [`examples/`](examples) | whole economies that check and go live in the tests; `ideas.json`, 112 real economies rated |
120
- | [`evals/`](evals/README.md) | fresh-session evals of agents building on loot |
121
- | [`CHANGELOG.md`](CHANGELOG.md), [`SECURITY.md`](SECURITY.md) | versions, the trust model, reporting a vulnerability |
156
+ | | |
157
+ | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------- |
158
+ | [`docs/program.md`](docs/program.md) | the program: who signs, accounts, every instruction, fees, limits, receipts, costs |
159
+ | [`docs/nfts.md`](docs/nfts.md) | NFTs and tokens, with and without the project's authority |
160
+ | [`docs/harnesses.md`](docs/harnesses.md) | how the plugin reaches Claude Code and Codex, and the release checklist |
161
+ | [`skills/xsolana-economy`](skills/xsolana-economy/SKILL.md) | the skill an agent follows |
162
+ | [`examples/`](examples) | whole economies that check and go live in the tests; `ideas.json`, 112 real economies rated |
163
+ | [`evals/`](evals/README.md) | fresh-session evals of agents building on loot |
164
+ | [`CHANGELOG.md`](CHANGELOG.md), [`SECURITY.md`](SECURITY.md) | versions, the trust model, reporting a vulnerability |
122
165
 
123
166
  ## Layout
124
167
 
125
- | Path | What |
126
- | --- | --- |
127
- | `js/chain/` | browser-safe Solana primitives, no SDK |
128
- | `js/client/` | the client; `idl/` is the program's IDL |
129
- | `js/economy/` | the economy spec (version 2, `schema.json`), checker, simulation, setup plan, cost, explainer, guide |
130
- | `js/play/`, `js/results/` | a player's side of a live economy; results a server vouches for |
131
- | `js/approve/`, `js/mcp/`, `js/connect/` | the approval page, the MCP server and its tools, `xsolana connect` |
132
- | `js/sim/`, `js/test/` | LiteSVM running the real program; the tests and their pinned fixtures |
133
- | `bin/xsolana.mjs` | the CLI |
134
- | `skills/`, `plugin/` | the skill (copied to `plugin/skills`, `.claude/skills`, `.agents/skills`) and the plugin |
135
- | `scripts/` | fixtures, skill sync, IDL sync, release manifest, a devnet end-to-end run |
136
- | `release.json` | the program binary, IDL and client hashes this version is pinned to |
168
+ | Path | What |
169
+ | --------------------------------------- | ---------------------------------------------------------------------------------------------------- |
170
+ | `js/chain/` | browser-safe Solana primitives, no SDK |
171
+ | `js/client/` | the client; `idl/` is the program's IDL |
172
+ | `js/economy/` | the economy spec (version 2, `schema.json`), checker, simulation, setup plan, cost, explainer, guide |
173
+ | `js/play/`, `js/results/` | a player's side of a live economy; results a server vouches for |
174
+ | `js/approve/`, `js/mcp/`, `js/connect/` | the approval page, the MCP server and its tools, `xsolana connect` |
175
+ | `js/sim/`, `js/test/` | LiteSVM running the real program; the tests and their pinned fixtures |
176
+ | `bin/xsolana.mjs` | the CLI |
177
+ | `skills/`, `plugin/` | the skill (copied to `plugin/skills`, `.claude/skills`, `.agents/skills`) and the plugin |
178
+ | `scripts/` | fixtures, skill sync, IDL sync, release manifest, a devnet end-to-end run |
179
+ | `release.json` | the program binary, IDL and client hashes this version is pinned to |
137
180
 
138
181
  ## Development
139
182
 
@@ -143,20 +186,20 @@ Node 22 or later.
143
186
  npm ci
144
187
  npm run fixtures # downloads the loot program from devnet and the programs it calls from mainnet, each checked by hash
145
188
  npm test # every instruction the client builds, and every example's whole setup, against the real program in LiteSVM
146
- npm run lint # ESLint
189
+ npm run lint # ESLint and Prettier (npm run format fixes the formatting)
147
190
  npm run check # fixtures, skill copies and release.json in step, lint, then the tests
148
191
  ```
149
192
 
150
193
  Nothing in the tests deploys or spends SOL.
151
194
 
152
- | Variable | What it does |
153
- | --- | --- |
154
- | `XSOLANA_RPC_URL` | a devnet RPC of your own for `xsolana approve` and `xsolana status` (a mainnet URL is refused) |
155
- | `XSOLANA_NO_OPEN` | `xsolana approve` prints its link without opening a browser |
156
- | `XSOLANA_DEBUG` | the CLI prints a failure's whole stack, not one line |
157
- | `XSOLANA_PROGRAM_DIR` | the tests run that program build's `target/deploy/loot.so`, and check the IDL against it | To try the tools in your own agent from a clone,
158
- `node bin/xsolana.mjs connect --local` registers this checkout's MCP server and the skill with Claude Code and Codex
159
- (`--remove` takes them out again).
195
+ | Variable | What it does |
196
+ | -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
197
+ | `XSOLANA_RPC_URL` | a devnet RPC of your own for `xsolana approve` and `xsolana status` (a mainnet URL is refused) |
198
+ | `XSOLANA_NO_OPEN` | `xsolana approve` prints its link without opening a browser |
199
+ | `XSOLANA_DEBUG` | the CLI prints a failure's whole stack, not one line |
200
+ | `XSOLANA_PROGRAM_DIR` | the tests run that program build's `target/deploy/loot.so`, and check the IDL against it | To try the tools in your own agent from a clone, |
201
+ | `node bin/xsolana.mjs connect --local` registers this checkout's MCP server and the skill with Claude Code and Codex |
202
+ | (`--remove` takes them out again). |
160
203
 
161
204
  After editing `skills/`, run `npm run sync-skills`. Changes to tool texts, the skill or the plugin are best measured
162
205
  with the evals before and after ([`evals/README.md`](evals/README.md)). [`AGENTS.md`](AGENTS.md) has the rules for
package/bin/xsolana.mjs CHANGED
@@ -74,7 +74,8 @@ function parseArgs(rest) {
74
74
  flags.add(name);
75
75
  if (eq > 0) values.set(name, a.slice(eq + 1));
76
76
  else if (VALUED.has(name)) {
77
- if (i + 1 >= rest.length || rest[i + 1].startsWith('--')) throw new UsageError(`${name} needs a value (${name} <value> or ${name}=<value>).`);
77
+ if (i + 1 >= rest.length || rest[i + 1].startsWith('--'))
78
+ throw new UsageError(`${name} needs a value (${name} <value> or ${name}=<value>).`);
78
79
  values.set(name, rest[++i]);
79
80
  }
80
81
  }
@@ -85,7 +86,8 @@ function parseArgs(rest) {
85
86
  function portOf(value) {
86
87
  if (value === undefined) return undefined;
87
88
  const n = Number(value);
88
- if (!/^\d+$/.test(String(value)) || n < 1 || n > 65535) throw new UsageError(`--port must be a whole number from 1 to 65535, not ${JSON.stringify(value)}.`);
89
+ if (!/^\d+$/.test(String(value)) || n < 1 || n > 65535)
90
+ throw new UsageError(`--port must be a whole number from 1 to 65535, not ${JSON.stringify(value)}.`);
89
91
  return n;
90
92
  }
91
93
 
@@ -93,8 +95,11 @@ async function main(argv) {
93
95
  const [command, ...rest] = argv;
94
96
  const { flags, values, positional } = parseArgs(rest);
95
97
  const takes = Object.hasOwn(FLAGS, command) ? FLAGS[command] : null;
96
- const unknown = takes && [...flags].find((f) => !takes.includes(f));
97
- if (unknown) throw new UsageError(`“${command}” takes no ${unknown} flag (${takes.length ? `only ${takes.join(', ')}` : 'it takes none'}).`);
98
+ const unknown = takes && [...flags].find(f => !takes.includes(f));
99
+ if (unknown)
100
+ throw new UsageError(
101
+ `“${command}” takes no ${unknown} flag (${takes.length ? `only ${takes.join(', ')}` : 'it takes none'}).`,
102
+ );
98
103
 
99
104
  async function tool(name, input) {
100
105
  const { runTool } = await import('../js/mcp/server.mjs');
@@ -135,7 +140,13 @@ async function main(argv) {
135
140
  }
136
141
  case 'connect': {
137
142
  const { connect } = await import('../js/connect/connect.mjs');
138
- process.exitCode = await connect({ agent: values.get('--agent'), local: flags.has('--local'), remove: flags.has('--remove'), dryRun: flags.has('--dry-run'), scope: values.get('--scope') });
143
+ process.exitCode = await connect({
144
+ agent: values.get('--agent'),
145
+ local: flags.has('--local'),
146
+ remove: flags.has('--remove'),
147
+ dryRun: flags.has('--dry-run'),
148
+ scope: values.get('--scope'),
149
+ });
139
150
  break;
140
151
  }
141
152
  case 'tool': {
@@ -145,12 +156,15 @@ async function main(argv) {
145
156
  break;
146
157
  }
147
158
  const from = values.get('--args-file');
148
- const raw = from ? await readFile(from, 'utf8') : positional[1] ?? '{}';
159
+ const raw = from ? await readFile(from, 'utf8') : (positional[1] ?? '{}');
149
160
  let input;
150
161
  try {
151
162
  input = JSON.parse(raw);
152
163
  } catch (e) {
153
- throw new UsageError(`The arguments ${from ? `in ${from} ` : ''}are not JSON (${e.message}). Give them as one word, as in: xsolana tool xsolana_check '{"file":"xsolana.json"}'.`, { cause: e });
164
+ throw new UsageError(
165
+ `The arguments ${from ? `in ${from} ` : ''}are not JSON (${e.message}). Give them as one word, as in: xsolana tool xsolana_check '{"file":"xsolana.json"}'.`,
166
+ { cause: e },
167
+ );
154
168
  }
155
169
  await tool(positional[0], input);
156
170
  break;
@@ -176,7 +190,8 @@ async function main(argv) {
176
190
  /** One line for a failure: what went wrong, without a stack (a missing file names the file). */
177
191
  function oneLine(e) {
178
192
  if (e?.code === 'ENOENT' && e.path) return `${e.path} does not exist.`;
179
- if (e?.code === 'EADDRINUSE') return `Port ${e.port} is in use: pick another with --port, or leave it out for any free one.`;
193
+ if (e?.code === 'EADDRINUSE')
194
+ return `Port ${e.port} is in use: pick another with --port, or leave it out for any free one.`;
180
195
  return String(e?.message ?? e);
181
196
  }
182
197
 
@@ -109,12 +109,10 @@
109
109
  {
110
110
  "key": "buyers-draw",
111
111
  "name": "Buyers' draw",
112
+ "pot": 0,
112
113
  "price": 1,
113
114
  "cutPercent": 5,
114
- "places": [
115
- 70,
116
- 30
117
- ],
115
+ "places": [70, 30],
118
116
  "every": "7d"
119
117
  }
120
118
  ],
@@ -65,12 +65,9 @@
65
65
  {
66
66
  "key": "weekly",
67
67
  "name": "Weekly draw",
68
+ "pot": 0,
68
69
  "price": 0.01,
69
- "places": [
70
- 50,
71
- 30,
72
- 20
73
- ],
70
+ "places": [50, 30, 20],
74
71
  "every": "7d",
75
72
  "firstDraw": "publish+7d"
76
73
  }
@@ -59,10 +59,7 @@
59
59
  "name": "Duel",
60
60
  "currency": "usdc",
61
61
  "sides": 2,
62
- "sideNames": [
63
- "Red",
64
- "Blue"
65
- ],
62
+ "sideNames": ["Red", "Blue"],
66
63
  "entriesClose": "10m",
67
64
  "resultDue": "1h",
68
65
  "every": "1h",
@@ -24,9 +24,7 @@
24
24
  "name": "The raise",
25
25
  "currency": "sol",
26
26
  "sides": 1,
27
- "sideNames": [
28
- "Back it"
29
- ],
27
+ "sideNames": ["Back it"],
30
28
  "entriesClose": "7d",
31
29
  "resultDue": "8d",
32
30
  "minPot": 50,
@@ -26,7 +26,13 @@
26
26
  { "gives": "nothing", "chance": 25, "shifts": [{ "attribute": "level", "by": -1 }] },
27
27
  { "gives": "nothing", "chance": 5, "then": "burn" }
28
28
  ],
29
- "odds": [{ "outcome": 0, "when": { "item": "hero", "attribute": "level", "atLeast": 0 }, "add": { "perPointPercent": 10, "upTo": 10 } }]
29
+ "odds": [
30
+ {
31
+ "outcome": 0,
32
+ "when": { "item": "hero", "attribute": "level", "atLeast": 0 },
33
+ "add": { "perPointPercent": 10, "upTo": 10 }
34
+ }
35
+ ]
30
36
  },
31
37
  {
32
38
  "key": "expedition-result",
@@ -39,7 +45,13 @@
39
45
  { "gives": "nothing", "chance": 30, "shifts": [{ "attribute": "affection", "by": -2 }] },
40
46
  { "gives": "nothing", "chance": 10, "then": "treasury" }
41
47
  ],
42
- "odds": [{ "outcome": 0, "when": { "item": "hero", "attribute": "level", "atLeast": 0 }, "add": { "perPointPercent": 20, "upTo": 10 } }]
48
+ "odds": [
49
+ {
50
+ "outcome": 0,
51
+ "when": { "item": "hero", "attribute": "level", "atLeast": 0 },
52
+ "add": { "perPointPercent": 20, "upTo": 10 }
53
+ }
54
+ ]
43
55
  },
44
56
  {
45
57
  "key": "forge",
@@ -0,0 +1,38 @@
1
+ {
2
+ "version": 2,
3
+ "network": "devnet",
4
+ "name": "Holder Den",
5
+ "prompt": "Holders of our NFT collection stake them without the NFT leaving their wallet and earn 10 DUST a day each. Locking for 30 days doubles it, 90 days triples it. NFTs with a gold background earn 25% more. Every day staked adds a point to the NFT, which the game shows as its rank.",
6
+ "currency": "dust",
7
+ "tokens": [{ "key": "dust", "source": "project", "name": "Den Dust", "symbol": "DUST", "decimals": 6 }],
8
+ "collection": false,
9
+ "farms": [
10
+ {
11
+ "key": "den",
12
+ "name": "The Den",
13
+ "stakes": "collectibles",
14
+ "custody": "freeze",
15
+ "gate": { "collection": "5N3f6KXYGvtX4zCVD3DeQoEGbzahifQiFtkxfTfEyiRz" },
16
+ "rewards": "dust",
17
+ "rate": { "perDay": 10, "minted": true },
18
+ "tiers": [
19
+ { "lock": "0d", "multiplier": 1 },
20
+ { "lock": "30d", "multiplier": 2 },
21
+ { "lock": "90d", "multiplier": 3 }
22
+ ],
23
+ "boosts": [
24
+ {
25
+ "when": {
26
+ "collection": "5N3f6KXYGvtX4zCVD3DeQoEGbzahifQiFtkxfTfEyiRz",
27
+ "attribute": "Background",
28
+ "equals": "Gold"
29
+ },
30
+ "add": { "addPercent": 25 }
31
+ }
32
+ ],
33
+ "maxBoostPercent": 25,
34
+ "growth": { "attribute": "points", "perDay": 1 }
35
+ }
36
+ ],
37
+ "scenario": { "days": 90, "perDay": { "farm:den": 30 }, "staked": { "den": 1500 } }
38
+ }
@@ -0,0 +1,105 @@
1
+ {
2
+ "version": 2,
3
+ "network": "devnet",
4
+ "name": "Holders Drop",
5
+ "prompt": "Only holders of our collection get in. They buy a holders' crate for 0.02 SOL with a chance at a limited relic, claim a free holder badge once, and enter a raffle for our one-of-one NFT at 0.005 SOL a ticket. The raffle draws in a week, and only if at least 50 tickets sold; the ticket money goes to the treasury.",
6
+ "currency": "sol",
7
+ "tokens": [
8
+ {
9
+ "key": "sol",
10
+ "source": "sol"
11
+ }
12
+ ],
13
+ "collection": {
14
+ "name": "Holders Drop"
15
+ },
16
+ "items": [
17
+ {
18
+ "key": "trinket",
19
+ "name": "Holder Trinket",
20
+ "rarity": "Common"
21
+ },
22
+ {
23
+ "key": "relic",
24
+ "name": "Holder Relic",
25
+ "rarity": "Epic",
26
+ "supply": 50
27
+ },
28
+ {
29
+ "key": "badge",
30
+ "name": "Holder Badge",
31
+ "soulbound": true
32
+ }
33
+ ],
34
+ "boxes": [
35
+ {
36
+ "key": "crate",
37
+ "name": "Holders' Crate",
38
+ "price": 0.02,
39
+ "gate": {
40
+ "collection": "9gyC3T5ynfwHK2EwKrxvmayntaDXjgcbLTaPwMwdeYuh"
41
+ },
42
+ "perWalletEach": {
43
+ "count": 3,
44
+ "every": "1d"
45
+ },
46
+ "outcomes": [
47
+ {
48
+ "gives": "item",
49
+ "item": "trinket",
50
+ "chance": 95
51
+ },
52
+ {
53
+ "gives": "item",
54
+ "item": "relic",
55
+ "chance": 5
56
+ }
57
+ ]
58
+ }
59
+ ],
60
+ "grants": [
61
+ {
62
+ "key": "badge",
63
+ "name": "Holder Badge",
64
+ "prize": {
65
+ "gives": "item",
66
+ "item": "badge"
67
+ },
68
+ "gate": {
69
+ "collection": "9gyC3T5ynfwHK2EwKrxvmayntaDXjgcbLTaPwMwdeYuh"
70
+ },
71
+ "limits": {
72
+ "perWallet": 1,
73
+ "total": 2000
74
+ },
75
+ "wallets": 2000,
76
+ "fund": 0.2
77
+ }
78
+ ],
79
+ "lotteries": [
80
+ {
81
+ "key": "raffle",
82
+ "name": "One-of-one raffle",
83
+ "price": 0.005,
84
+ "raffle": {
85
+ "nft": "2oguAYJzxA6isSBtu4b8kzNPExdnvCuSwKjnB9dRkdxg",
86
+ "collection": "9gyC3T5ynfwHK2EwKrxvmayntaDXjgcbLTaPwMwdeYuh"
87
+ },
88
+ "minSold": 50,
89
+ "firstDraw": "publish+7d",
90
+ "gate": {
91
+ "collection": "9gyC3T5ynfwHK2EwKrxvmayntaDXjgcbLTaPwMwdeYuh"
92
+ },
93
+ "perWallet": 20,
94
+ "rest": "treasury"
95
+ }
96
+ ],
97
+ "scenario": {
98
+ "days": 7,
99
+ "perDay": {
100
+ "box:crate": 60,
101
+ "grant:badge": 100,
102
+ "lottery:raffle": 40
103
+ }
104
+ }
105
+ }