@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.
- package/README.md +94 -51
- package/bin/xsolana.mjs +23 -8
- package/examples/blind-forge.json +2 -4
- package/examples/coin-flip-arena.json +2 -5
- package/examples/duel-arena.json +1 -4
- package/examples/fair-presale.json +1 -3
- package/examples/hero-trials.json +14 -2
- package/examples/holder-den.json +38 -0
- package/examples/holders-drop.json +105 -0
- package/examples/ideas.json +788 -788
- package/examples/match-predictions.json +24 -0
- package/examples/nft-raffle.json +1 -0
- package/examples/pack-league.json +108 -0
- package/examples/partner-vault.json +19 -3
- package/examples/sacrifice-raffle.json +2 -10
- package/examples/shelter-fair.json +1 -0
- package/examples/stakers-draw.json +3 -7
- package/examples/stride-sneakers.json +164 -0
- package/examples/trade-in-shop.json +2 -3
- package/js/approve/details.mjs +117 -48
- package/js/approve/ed25519.mjs +5 -1
- package/js/approve/live-check.mjs +83 -26
- package/js/approve/page.html +217 -78
- package/js/approve/page.js +43 -15
- package/js/approve/server.mjs +276 -74
- package/js/chain/base58.js +7 -1
- package/js/chain/borsh.js +15 -11
- package/js/chain/bytes.js +11 -8
- package/js/chain/core.js +21 -5
- package/js/chain/curve.js +1 -1
- package/js/chain/lookup.js +118 -36
- package/js/chain/mint.js +6 -2
- package/js/chain/pda.js +2 -2
- package/js/chain/programs.js +6 -6
- package/js/chain/rpc.js +40 -14
- package/js/chain/token.js +13 -2
- package/js/chain/token2022.js +55 -16
- package/js/chain/tx.js +32 -22
- package/js/client/accounts.js +270 -80
- package/js/client/compressed.js +4 -3
- package/js/client/grant.js +165 -40
- package/js/client/idl/loot.js +1 -1
- package/js/client/idl/loot.json +104 -16
- package/js/client/ix.js +2121 -457
- package/js/client/pda.js +20 -18
- package/js/client/policy.js +12 -5
- package/js/client/random.js +1 -1
- package/js/client/sizes.js +220 -65
- package/js/client/traits.js +14 -5
- package/js/connect/connect.mjs +59 -17
- package/js/economy/check.mjs +3873 -690
- package/js/economy/cost.mjs +380 -56
- package/js/economy/examples.mjs +176 -35
- package/js/economy/explain.mjs +461 -110
- package/js/economy/guide.mjs +282 -59
- package/js/economy/legal.mjs +12 -6
- package/js/economy/live.mjs +60 -19
- package/js/economy/model.mjs +320 -97
- package/js/economy/plan.mjs +1022 -203
- package/js/economy/schema.json +127 -369
- package/js/economy/secrets.mjs +17 -7
- package/js/economy/simulate.mjs +771 -178
- package/js/economy/txsize.mjs +15 -3
- package/js/economy/validate.mjs +61 -24
- package/js/mcp/server.mjs +42 -14
- package/js/mcp/tools.mjs +293 -80
- package/js/play/index.js +1960 -461
- package/js/play/keys.js +35 -9
- package/js/results/index.js +223 -30
- package/js/results/server.js +230 -0
- package/js/results/wire.js +64 -16
- package/package.json +6 -4
- package/release.json +5 -5
- package/skills/xsolana-economy/SKILL.md +30 -16
- package/skills/xsolana-economy/references/after-launch.md +30 -20
- package/skills/xsolana-economy/references/apps.md +47 -36
- package/skills/xsolana-economy/references/sentence-to-spec.md +148 -129
- package/skills/xsolana-economy/references/telling-the-person.md +9 -6
- package/types/chain/core.d.ts +1 -1
- package/types/chain/lookup.d.ts +1 -1
- package/types/client/accounts.d.ts +24 -3
- package/types/client/grant.d.ts +2 -2
- package/types/client/idl/loot.d.ts +22 -0
- package/types/client/ix.d.ts +54 -45
- package/types/client/pda.d.ts +3 -1
- package/types/economy/guide.d.mts +5 -0
- package/types/economy/model.d.mts +48 -3
- package/types/economy/plan.d.mts +44 -2
- package/types/play/index.d.ts +8 -19
- package/types/results/index.d.ts +7 -6
- package/types/results/server.d.ts +126 -0
- 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.
|
|
16
|
-
>
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
77
|
-
npx -y @xgamesfun/solana@0.
|
|
78
|
-
npx -y @xgamesfun/solana@0.
|
|
79
|
-
npx -y @xgamesfun/solana@0.
|
|
80
|
-
npx -y @xgamesfun/solana@0.
|
|
81
|
-
npx -y @xgamesfun/solana@0.
|
|
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
|
|
99
|
-
|
|
|
100
|
-
| `@xgamesfun/solana`
|
|
101
|
-
| `@xgamesfun/solana/client`
|
|
102
|
-
| `@xgamesfun/solana/play`
|
|
103
|
-
| `@xgamesfun/solana/results`
|
|
104
|
-
| `@xgamesfun/solana/chain/*`
|
|
105
|
-
| `@xgamesfun/solana/idl.json`
|
|
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)
|
|
116
|
-
| [`docs/nfts.md`](docs/nfts.md)
|
|
117
|
-
| [`docs/harnesses.md`](docs/harnesses.md)
|
|
118
|
-
| [`skills/xsolana-economy`](skills/xsolana-economy/SKILL.md)
|
|
119
|
-
| [`examples/`](examples)
|
|
120
|
-
| [`evals/`](evals/README.md)
|
|
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
|
|
126
|
-
|
|
|
127
|
-
| `js/chain/`
|
|
128
|
-
| `js/client/`
|
|
129
|
-
| `js/economy/`
|
|
130
|
-
| `js/play/`, `js/results/`
|
|
131
|
-
| `js/approve/`, `js/mcp/`, `js/connect/` | the approval page, the MCP server and its tools, `xsolana connect`
|
|
132
|
-
| `js/sim/`, `js/test/`
|
|
133
|
-
| `bin/xsolana.mjs`
|
|
134
|
-
| `skills/`, `plugin/`
|
|
135
|
-
| `scripts/`
|
|
136
|
-
| `release.json`
|
|
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
|
|
153
|
-
|
|
|
154
|
-
| `XSOLANA_RPC_URL`
|
|
155
|
-
| `XSOLANA_NO_OPEN`
|
|
156
|
-
| `XSOLANA_DEBUG`
|
|
157
|
-
| `XSOLANA_PROGRAM_DIR`
|
|
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('--'))
|
|
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)
|
|
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(
|
|
97
|
-
if (unknown)
|
|
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({
|
|
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(
|
|
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')
|
|
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
|
|
package/examples/duel-arena.json
CHANGED
|
@@ -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": [
|
|
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": [
|
|
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
|
+
}
|