@xgamesfun/solana 0.0.0-stage → 0.20.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/DISCLAIMER.md +41 -0
- package/LICENSE +202 -0
- package/NOTICE +13 -0
- package/README.md +172 -2
- package/bin/xsolana.mjs +189 -0
- package/examples/artwork-auction.json +51 -0
- package/examples/blind-forge.json +128 -0
- package/examples/coin-flip-arena.json +108 -0
- package/examples/duel-arena.json +137 -0
- package/examples/elements-set.json +281 -0
- package/examples/event-badges.json +67 -0
- package/examples/expedition-guild.json +161 -0
- package/examples/fair-presale.json +48 -0
- package/examples/falling-drop.json +90 -0
- package/examples/guild-missions.json +144 -0
- package/examples/halving-seasons.json +112 -0
- package/examples/hero-trials.json +96 -0
- package/examples/ideas.json +789 -0
- package/examples/level-ladder.json +65 -0
- package/examples/merge-2048.json +128 -0
- package/examples/nft-raffle.json +33 -0
- package/examples/paper-hands-farm.json +56 -0
- package/examples/partner-vault.json +115 -0
- package/examples/sacrifice-raffle.json +118 -0
- package/examples/scorched-treasury.json +84 -0
- package/examples/shelter-fair.json +164 -0
- package/examples/spark-pity.json +153 -0
- package/examples/stakers-draw.json +125 -0
- package/examples/star-fleet.json +157 -0
- package/examples/starforce.json +116 -0
- package/examples/stud-farm.json +134 -0
- package/examples/team-vesting.json +70 -0
- package/examples/trade-in-shop.json +168 -0
- package/js/approve/details.mjs +262 -0
- package/js/approve/ed25519.mjs +36 -0
- package/js/approve/live-check.mjs +144 -0
- package/js/approve/page.html +86 -0
- package/js/approve/page.js +203 -0
- package/js/approve/server.mjs +711 -0
- package/js/chain/base58.js +73 -0
- package/js/chain/borsh.js +261 -0
- package/js/chain/bytes.js +141 -0
- package/js/chain/core.js +169 -0
- package/js/chain/curve.js +52 -0
- package/js/chain/lookup.js +273 -0
- package/js/chain/mint.js +96 -0
- package/js/chain/pda.js +66 -0
- package/js/chain/programs.js +102 -0
- package/js/chain/rpc.js +217 -0
- package/js/chain/sha256.js +68 -0
- package/js/chain/token.js +85 -0
- package/js/chain/token2022.js +247 -0
- package/js/chain/tx.js +224 -0
- package/js/client/accounts.js +347 -0
- package/js/client/compressed.js +46 -0
- package/js/client/grant.js +240 -0
- package/js/client/idl/loot.js +2 -0
- package/js/client/idl/loot.json +17064 -0
- package/js/client/index.js +9 -0
- package/js/client/ix.js +2156 -0
- package/js/client/pda.js +110 -0
- package/js/client/policy.js +48 -0
- package/js/client/sizes.js +54 -0
- package/js/client/traits.js +44 -0
- package/js/connect/connect.mjs +182 -0
- package/js/economy/check.mjs +1991 -0
- package/js/economy/cost.mjs +170 -0
- package/js/economy/examples.mjs +114 -0
- package/js/economy/explain.mjs +235 -0
- package/js/economy/guide.mjs +231 -0
- package/js/economy/legal.mjs +15 -0
- package/js/economy/live.mjs +107 -0
- package/js/economy/model.mjs +583 -0
- package/js/economy/plan.mjs +887 -0
- package/js/economy/schema.json +2923 -0
- package/js/economy/secrets.mjs +60 -0
- package/js/economy/simulate.mjs +952 -0
- package/js/economy/txsize.mjs +45 -0
- package/js/economy/validate.mjs +85 -0
- package/js/index.js +10 -0
- package/js/mcp/server.mjs +113 -0
- package/js/mcp/tools.mjs +479 -0
- package/js/play/index.js +2109 -0
- package/js/results/index.js +184 -0
- package/js/results/wire.js +90 -0
- package/package.json +100 -4
- package/release.json +8 -0
- package/skills/xsolana-economy/SKILL.md +105 -0
- package/skills/xsolana-economy/references/after-launch.md +114 -0
- package/skills/xsolana-economy/references/apps.md +157 -0
- package/skills/xsolana-economy/references/sentence-to-spec.md +172 -0
- package/skills/xsolana-economy/references/telling-the-person.md +93 -0
- package/types/chain/base58.d.ts +10 -0
- package/types/chain/borsh.d.ts +23 -0
- package/types/chain/bytes.d.ts +45 -0
- package/types/chain/core.d.ts +50 -0
- package/types/chain/curve.d.ts +1 -0
- package/types/chain/lookup.d.ts +191 -0
- package/types/chain/mint.d.ts +25 -0
- package/types/chain/pda.d.ts +25 -0
- package/types/chain/programs.d.ts +65 -0
- package/types/chain/rpc.d.ts +66 -0
- package/types/chain/sha256.d.ts +1 -0
- package/types/chain/token.d.ts +141 -0
- package/types/chain/token2022.d.ts +53 -0
- package/types/chain/tx.d.ts +60 -0
- package/types/client/accounts.d.ts +178 -0
- package/types/client/compressed.d.ts +29 -0
- package/types/client/grant.d.ts +125 -0
- package/types/client/idl/loot.d.ts +4467 -0
- package/types/client/index.d.ts +8 -0
- package/types/client/ix.d.ts +1687 -0
- package/types/client/pda.d.ts +91 -0
- package/types/client/policy.d.ts +23 -0
- package/types/client/sizes.d.ts +13 -0
- package/types/client/traits.d.ts +12 -0
- package/types/economy/check.d.mts +105 -0
- package/types/economy/cost.d.mts +68 -0
- package/types/economy/examples.d.mts +27 -0
- package/types/economy/explain.d.mts +19 -0
- package/types/economy/guide.d.mts +140 -0
- package/types/economy/legal.d.mts +12 -0
- package/types/economy/live.d.mts +109 -0
- package/types/economy/model.d.mts +363 -0
- package/types/economy/plan.d.mts +1013 -0
- package/types/economy/secrets.d.mts +4 -0
- package/types/economy/simulate.d.mts +156 -0
- package/types/economy/txsize.d.mts +17 -0
- package/types/economy/validate.d.mts +3 -0
- package/types/index.d.ts +9 -0
- package/types/mcp/tools.d.mts +627 -0
- package/types/play/index.d.ts +469 -0
- package/types/results/index.d.ts +155 -0
- package/types/results/wire.d.ts +122 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Building the app for a live economy
|
|
2
|
+
|
|
3
|
+
Once `xsolana.json` has a `live` section, players use the economy through a page (a game screen, a site, a mint page)
|
|
4
|
+
with their own wallet. Use the package's player module, `@xgamesfun/solana/play`: it reads the economy, builds every
|
|
5
|
+
transaction, has the player's wallet sign it, sends it and waits for the result. Don't hand-write instructions,
|
|
6
|
+
account lists or program addresses.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install @xgamesfun/solana
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```js
|
|
13
|
+
import { solanaApp, walletStandardSigner } from '@xgamesfun/solana/play';
|
|
14
|
+
|
|
15
|
+
const spec = await (await fetch('./xsolana.json')).json(); // the live spec, served with the page
|
|
16
|
+
|
|
17
|
+
// Connect a wallet (Phantom, Solflare, Backpack all register through the Wallet Standard).
|
|
18
|
+
const wallets = [];
|
|
19
|
+
const api = { register: (...w) => { wallets.push(...w); return () => {}; } };
|
|
20
|
+
window.addEventListener('wallet-standard:register-wallet', ({ detail }) => detail(api));
|
|
21
|
+
window.dispatchEvent(Object.assign(new Event('wallet-standard:app-ready'), { detail: api }));
|
|
22
|
+
const wallet = wallets.find((w) => w.chains.some((c) => c.startsWith('solana:')));
|
|
23
|
+
const { accounts: [account] } = await wallet.features['standard:connect'].connect();
|
|
24
|
+
|
|
25
|
+
const app = solanaApp({ spec, rpcUrl: 'https://api.devnet.solana.com', wallet: walletStandardSigner(wallet, account) });
|
|
26
|
+
|
|
27
|
+
const shop = await app.shop(); // [{ key, name, price, priceUnits, currency, active, left }]
|
|
28
|
+
await app.buy('badge', { listing: shop.find((l) => l.key === 'badge') }); // the price shown is the most it pays
|
|
29
|
+
const boxes = await app.boxes(); // each box's price and outcome chances
|
|
30
|
+
await app.openBox('chest', { listing: boxes.find((l) => l.key === 'chest') }); // pays, waits for the roll, settles
|
|
31
|
+
await app.pay('entry'); // a payment of the spec
|
|
32
|
+
const lotteries = await app.lotteries(); // [{ key, name, price, currency, status, round, sold, drawsAt, nftTicket, … }]
|
|
33
|
+
await app.buyTickets('weekly', 2, { listing: lotteries.find((l) => l.key === 'weekly') }); // an NFT ticket: { nft: asset, listing }
|
|
34
|
+
await app.payNft(asset, { to, reference }); // pay with an NFT (burned, or given to `to`): admit the player by the receipt
|
|
35
|
+
await app.stake('farm', '10', 0); // tokens, at lock tier 0; app.stakeNft('farm', asset) for a collectible
|
|
36
|
+
await app.harvest('farm'); // rewards; app.unstake('farm', '10') once the lock has run
|
|
37
|
+
await app.staked('farm'); // the player's seats: [{ asset, amount, freeAt, boostPercent, waiting, rewards }]
|
|
38
|
+
await app.refresh('guild', asset); // a farm with growth: writes the NFT's points and level now (anyone may)
|
|
39
|
+
const recipes = await app.recipes(); // [{ key, name, active, version }]
|
|
40
|
+
await app.craft('merge', [assetA, assetB], 1, { listing: recipes.find((r) => r.key === 'merge') }); // a changed recipe is refused
|
|
41
|
+
await app.auctions(); // each auction: high, nextBid, leading, secondsLeft, open, over, waiting (SOL)
|
|
42
|
+
await app.bid('comet', '0.6'); // the bid it tops goes back to its bidder in the same transaction
|
|
43
|
+
await app.endAuction('comet'); // once it is over, anyone: the lot to the winner { winner, asset? }
|
|
44
|
+
await app.unwrap(); // SOL back from a bid that was topped, from wrapped SOL to plain SOL
|
|
45
|
+
await app.claimGrant('welcome'); // a wallet on the grant's list (or { voucher } from your server)
|
|
46
|
+
await app.claimWins({ server, token }); // every prize a results server vouched for or listed, claimed by the winner; [{ round, signature | error }]
|
|
47
|
+
// (`token`: the player's sign-in with that server, when it asks for one: a stash drop's NFT is given only at a signed-in claim)
|
|
48
|
+
const loan = await app.loanOffer(lender, 1); // a lender's offer: { amount, repay, lasts, risks }: show every risk's `says`
|
|
49
|
+
await app.borrow(lender, 1, asset, { acceptRisks: true }); // only once the player saw the risks (refused without it)
|
|
50
|
+
for (const c of await app.claims()) await app.claim(c); // what this player can finish: a roll to collect, a refund,
|
|
51
|
+
// a contest payout, a due draw, an auction to end
|
|
52
|
+
await app.runSchedule('drip'); // a schedule that is due (anyone may run it); null when not yet
|
|
53
|
+
await app.collectibles(); // what the player owns: [{ asset, item, name, uri, attributes }] (`item`: its key)
|
|
54
|
+
await app.balances(); // what they hold of each of the economy's tokens
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- **Serving it**: a plain `index.html` with a `<script type="module">` can't import a bare `@xgamesfun/solana/play` by
|
|
58
|
+
itself. Either use a bundler (Vite: `npm create vite@latest`, then `npm install @xgamesfun/solana`), or put this import
|
|
59
|
+
map before the module script and serve the folder (`npx serve .`):
|
|
60
|
+
```html
|
|
61
|
+
<script type="importmap">{ "imports": {
|
|
62
|
+
"@xgamesfun/solana/play": "./node_modules/@xgamesfun/solana/js/play/index.js",
|
|
63
|
+
"@xgamesfun/solana/client": "./node_modules/@xgamesfun/solana/js/client/index.js"
|
|
64
|
+
} }</script>
|
|
65
|
+
```
|
|
66
|
+
Tell the person the one command that starts it, and that it serves on devnet.
|
|
67
|
+
- **What a roll looks like**: a box of several outcomes is rolled by the oracle a few seconds after the open; `openBox`
|
|
68
|
+
waits for it. Show the player that it is rolling.
|
|
69
|
+
- **What an auction looks like**: show the high bid, the least next bid (`nextBid`) and a countdown (`secondsLeft`;
|
|
70
|
+
a late bid moves the end, so read `auctions()` again after every bid). A player who was outbid has their bid back
|
|
71
|
+
already, nothing to claim: in SOL it waits in their wrapped-SOL account (`waiting`), so say "You were outbid:
|
|
72
|
+
0.5 SOL is back in your wallet" and offer `unwrap()` to turn it into plain SOL (any later purchase does it too).
|
|
73
|
+
When `over` is true, show an "End the auction" button to everyone (the winner usually presses it; it mints or
|
|
74
|
+
hands over the lot). There is no deadline: however late it is ended, the lot goes to the highest bidder.
|
|
75
|
+
- **NFTs the project has no authority over**: a gate or odds that name a collection find the player's Core NFTs of it
|
|
76
|
+
themselves; for a Token Metadata NFT pass `shown: { mint, owner }`, for a compressed one `shown: { compressed:
|
|
77
|
+
assetId, tree }` after a `loot.proveLeafIx` in the same transaction. `app.stakeNft`, `app.unstakeNft`, `app.refresh`,
|
|
78
|
+
`app.harvest`, `app.hold` and `app.endAuction` take any NFT: a Core asset, a Token Metadata mint (legacy or pNFT),
|
|
79
|
+
or a compressed NFT's asset id; the page finds its kind itself, and proves a compressed one first (its RPC must
|
|
80
|
+
offer DAS: `getAsset`, `getAssetProof`). In a farm that watches, `app.harvest` shows the NFT is still the player's,
|
|
81
|
+
and `app.stakeNft` of an NFT someone else staked and sold takes their seat over. What the project wrote about one is in
|
|
82
|
+
its Sheet (`loot.decodeSheet`).
|
|
83
|
+
- **Off-chain traits** (the spec's `traits`): the project builds the tree with `loot.traitsTree([{ nft, attributes
|
|
84
|
+
}])` from its NFTs' metadata and publishes each NFT's `attributes` and `proof` with the page; a player (or the page,
|
|
85
|
+
before a stake) calls `app.proveTraits(nft, attributes, proof)` once per NFT, and its boosts and gates read them.
|
|
86
|
+
- **Rolls on an NFT**: `app.openBox(boxKey, { on: asset })` for a box that rolls on one (an enchant). Leaving a
|
|
87
|
+
mission that rolls, `app.unstakeNft` returns `{ signature, roll }`; a recipe that rolls, `app.craft` returns `roll`.
|
|
88
|
+
`app.collect(roll)` asks the oracle when nobody has yet (`app.askRoll`), waits and settles it: show what it gave
|
|
89
|
+
(`won`, each prize the roll set aside, and `order.fate`: the numbers moved and where the NFT went). A page should offer to collect any
|
|
90
|
+
roll of the player's still waiting.
|
|
91
|
+
- **What a staking page shows**: `app.staked(farmKey)` lists the player's seats in a farm, each with what it is owed
|
|
92
|
+
now (`waiting`, in `rewards`), the program's own sum from a dry run of the seat (nothing signed or sent), and
|
|
93
|
+
when its lock is over (`freeAt`). Show that; don't work out rewards from the rate yourself (a sharing farm splits
|
|
94
|
+
its pool by weight, and an upkeep or the farm's end stops a seat). Read it when the page opens and after a stake
|
|
95
|
+
or a harvest.
|
|
96
|
+
- **Getting out of a farm**: after the farm lock ends, `app.unstakeNft(farmKey, asset)` releases the collectible;
|
|
97
|
+
`app.unstake(farmKey, amount)` returns tokens. If reward transfers are blocked, offer an explicit choice to give
|
|
98
|
+
up rewards with `{ forfeit: true }` as the last argument. Remove a token seat's booster before withdrawing all
|
|
99
|
+
its tokens. Forfeiting does not bypass the lock or the token's or NFT issuer's own controls: say which custody
|
|
100
|
+
the farm uses and never promise an unconditional exit (`telling-the-person.md`, NFT custody).
|
|
101
|
+
- **Farm token hooks and Core adapters**: `stake`, `unstake`, `stakeNft`, `unstakeNft`, and `harvest` accept
|
|
102
|
+
`extra` account metas or `transfers` keyed by mint in their options. Static Token-2022 hooks and required memos
|
|
103
|
+
resolve automatically. For dynamic hooks, provide every actual `{ source, destination, authority, amount }`
|
|
104
|
+
transfer: reward pool to vault, then vault to player when the action pays; token principal too when it moves.
|
|
105
|
+
Incoming transfer fees reduce reward credit before calculating the payout. Do not guess amounts from the gross
|
|
106
|
+
pool balance. Without that plan the app fails before signing; refresh the plan if balances or fee terms change.
|
|
107
|
+
Explicit `extra` replaces automatic resolution and must include all hook, memo and Core adapter accounts.
|
|
108
|
+
For example, `app.unstakeNft('heroes', asset, { transfers: { [rewardsMint]: rewardTransfers } })`, or
|
|
109
|
+
`app.unstakeNft('heroes', asset, { extra: adapterAndTokenAccounts })`. Harvest takes options third:
|
|
110
|
+
`app.harvest('heroes', asset, { transfers: { [rewardsMint]: rewardTransfers } })`.
|
|
111
|
+
A forfeit exit skips reward-mint reads and reward ATA creation; principal transfers still need their own mint.
|
|
112
|
+
- **Lottery steps**: `claims()` lists a due draw, the completion of its recorded tickets, each place's prize, and,
|
|
113
|
+
for a draw the oracle never answered, the cancel-and-refund (`after-launch.md` says how the money moves). Show each
|
|
114
|
+
as a button and read the list again after each one: a drawn round's tickets must be completed before its winners
|
|
115
|
+
collect.
|
|
116
|
+
- **Gates**: a gated buy, ticket, stake, entry, bid or claim finds the player's collectible that passes the gate by
|
|
117
|
+
itself ("You need a Guild Pass to buy this" when there is none). Use `collectibles()` and its `item` key to show
|
|
118
|
+
what a player holds; never match NFTs by name. A list gate (`{ "list": [...] }`) takes the player's proof: the
|
|
119
|
+
page makes it from the wallets xsolana.json lists (`loot.gateList(list).proofOf(wallet)`, which fills the list to a
|
|
120
|
+
power of two as the plan's root does), or takes it from a server that keeps a long list. A stake gate
|
|
121
|
+
(`{ "staked": … }`) needs nothing from the page: js/play passes the player's seat and the farm, and says plainly when
|
|
122
|
+
the stake is too small or its lock is over (show the farm's locked tiers next to the gate).
|
|
123
|
+
- **A no-loss lottery** (`noLoss`): completing the draw sends every ticket back to its buyer (anyone may complete
|
|
124
|
+
it, so say "your tickets come back after the draw"), and the winners then collect the pot.
|
|
125
|
+
- **A grant that pays by rank**: a voucher with an `amount` (base units of the grant's token) pays that much, up to
|
|
126
|
+
the grant's; `app.claimGrant(key, { voucher })` passes it, and `{ proof, round, personHash, amount }` for a list.
|
|
127
|
+
- **The price the player saw is the most they pay**: `app.buy` and `app.openBox` need the listing `shop()` or
|
|
128
|
+
`boxes()` showed (`{ listing }`, or `{ maxPrice }` in base units), `app.craft` the recipe's listing from `recipes()`
|
|
129
|
+
(or its `version`), `app.buyTickets` the lottery's listing from `lotteries()` (or `{ maxPrice }` a ticket), and
|
|
130
|
+
`app.buyListing` and `app.rentListing` the `listing` that `app.listing(asset)` or `app.listings()` showed. Without
|
|
131
|
+
one they refuse; a price (or recipe) that moved since is refused with the new one in words, never paid unseen. Show
|
|
132
|
+
the new price and let the player click again.
|
|
133
|
+
- **Loans against an NFT** (a lender's offer with `deal: { Lend }`): show `app.loanOffer(maker, id)` first: what is
|
|
134
|
+
lent, what repays it, by when, and every `risks[i].says`. A loan's token may have a freeze authority, a pause or a
|
|
135
|
+
transfer hook (or a permanent delegate): whoever holds one can make every repayment fail, and a lender who holds one
|
|
136
|
+
can take the NFT when it is due. The program takes such tokens, so `app.borrow` refuses one unless the player
|
|
137
|
+
accepted it (`{ acceptRisks: true }`). Lend in a plain token (SOL, or a mint with no freeze authority) to avoid it.
|
|
138
|
+
- **An NFT as the price** (a recipe's `trade` line, a box's `rollsOn` with `then`, a lottery's `ticketNft`): say on the
|
|
139
|
+
page which NFT is spent and where it goes before the player signs; it is spent at once and never refunded.
|
|
140
|
+
- **The owner's own wallet on the page**: by default the project's treasury is the owner's wallet. The owner may buy, play
|
|
141
|
+
and unwrap like anyone: a sale whose treasury account was closed opens it again.
|
|
142
|
+
- **A buyer's choice**: a share the box, lottery or payment lets the buyer name (`"chosen"`, a referrer; `"chosen:<item>"`, the
|
|
143
|
+
workshop they craft at, not in a payment) comes from the page: `app.buy(key, { listing, chosenRecipient: referrer })`, `app.openBox(key, { listing, chosenRecipient })`,
|
|
144
|
+
`app.buyTickets(key, n, { listing, chosenRecipient })`, `app.pay(key, { chosenRecipient })`. Take a referrer from the link (`?ref=<wallet>`); never let a buyer name
|
|
145
|
+
themselves (the program refuses it, and `app.pay` does). Each method refuses an option it doesn't take (`{ choice }` is
|
|
146
|
+
not `chosenRecipient`). A `"chosen:<item>"` share needs its collectible: offer only ones players hold
|
|
147
|
+
(the program refuses none, one burned, and one the project holds: staked in escrow, held, up for auction).
|
|
148
|
+
- **A "Claim" or "Collect" area** built on `claims()` is how players finish what is theirs, and how a due draw gets
|
|
149
|
+
sent. Show it. Read it when the page opens and on a Refresh button, not after every action: it searches the chain
|
|
150
|
+
several times, and devnet's free RPC turns away bursts. After an action, read only what it changed (a balance,
|
|
151
|
+
`collectibles()`), one read at a time, and keep what is on screen when one read fails.
|
|
152
|
+
- **Errors** come back as plain sentences (a sold-out item, a gate not met, a price above the most they agreed to):
|
|
153
|
+
show them.
|
|
154
|
+
- **Never in a page**: a private key, a keyed RPC link, or anything the authority signs (payouts, minting, rewriting
|
|
155
|
+
a collectible). Those are the owner's or the authority's to sign in their own wallet, or on a server they run.
|
|
156
|
+
- **Lower level**: `@xgamesfun/solana/client` has one builder per instruction and the account decoders, for what the
|
|
157
|
+
player module doesn't cover.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# From a sentence to a spec
|
|
2
|
+
|
|
3
|
+
Read the sentence for **what players pay, what they can get, where the money goes, and who decides**. Each phrase
|
|
4
|
+
maps to a part of the spec (`xsolana_guide` has every field).
|
|
5
|
+
|
|
6
|
+
| They say | Write |
|
|
7
|
+
| --- | --- |
|
|
8
|
+
| "a shop", "sell hats for 0.05 SOL", "limited to 100" | `items` with `price` (in the project's `currency`) and `supply`: bought in one transaction (`buy`), the item handed over at once. `{serial}` in an item's `uri` gives each unit its own art |
|
|
9
|
+
| "free to claim", "a free starter item" | an item with `"free": true` (a gate or a supply keeps bots out), or a grant with limits |
|
|
10
|
+
| "costs more each time", "+3% per sale" | an item's or box's `rise` (`growthPercent`, or a `step`) |
|
|
11
|
+
| "price drops every minute until it sells", "Dutch auction" | `fall` (`step`, `every`, `floor` above 0): a Dutch auction is a falling price, not `auctions`. By default a sale holds the price where it was paid and restarts the countdown, so it falls only while nobody buys; `"clock": "shared"` keeps it falling on its own clock however often it sells (a Dutch drop of many units). Say which |
|
|
12
|
+
| "drops at 6pm Friday", "on sale for a week", "the sale ends Sunday" | an item's, box's or recipe's `opens` and `closes` (times: "publish+2d" or a date) |
|
|
13
|
+
| "one per wallet", "max 2 per wallet", "5 a day each" | an item's, box's or recipe's `perWallet` (in all) and `perWalletEach` (`{ "count": 5, "every": "1d" }`): the chain counts each wallet (a person with many wallets gets around it: say so) |
|
|
14
|
+
| "auction", "highest bid wins", "bids", "losing bids are returned", "1 of 1 to the top bidder" | `auctions`: a `lot` (`{ "item": key }` for a new one; `{ "asset": address }` for one the owner already holds, once live), `reserve`, `minRaise` (percent), `runs`, `extendBy` (anti-sniping), a `split` (the artist's or a collaborator's share) |
|
|
15
|
+
| "auction my NFT", "my 1-of-1" (and you can't tell whether they already hold it) | a new item lot: an item with `supply: 1` and `lot: { "item": key }`, minted to the winner. A collectible they hold can only go up once the economy is live (`E-AUCTION`): say so, and save the spec as `xsolana.json` all the same |
|
|
16
|
+
| "sealed bids", "nobody sees the bids", "second-price", "Vickrey" | loot cannot: say so, and offer an open auction (`auctions`) |
|
|
17
|
+
| "loot box", "gacha", "mystery box", "pack", "60/25/10/4/1" | `boxes` with `outcomes` of `gives: "item"` and a `chance` each |
|
|
18
|
+
| "a starter pack: a sword, a shield and some gold", "three cards in one pack", "an item and coins together" | an outcome `{ "gives": "bundle", "prizes": [ … ] }` of up to three (items, `pay` from a pool, `tokens` minted, `stash`, `nft`, `roll`); more than three: make the third prize a roll of another box (`{ "gives": "roll", "box": key }`, that box `"active": false` and itself a bundle), so one purchase gives five or more, the rolled part after the oracle. A box of one outcome with nothing random and no NFT it rolls on is bought in one step: no order, no oracle, nothing to settle |
|
|
19
|
+
| "coin flip", "double or nothing", "plinko", "a prize" | a box whose outcomes `pay` from a pool; its `split` fills that pool. The box sells only while the pool holds every fixed prize (seed it at least that much), and a prize won is set aside for its winner at once |
|
|
20
|
+
| "jackpot", "a tenth of the jackpot" | an outcome `gives: "share"` of a pool (`percent`) |
|
|
21
|
+
| "give away our partner's NFTs", "a 5% chance at an NFT from another collection", "random NFTs from our treasury", "NFTs we already own" | a `stashes` entry (`nfts`: their addresses, held by the owner's wallet at go-live; `collection` for Core NFTs of another collection; a Token Metadata NFT as `{ "mint", "programmable" }`) and a box outcome `{ "gives": "stash", "stash": "<key>" }`: a roll picks one at random and takes it out of the stash at once. Rarity tiers: one stash per tier, each an outcome with its chance. A compressed NFT goes in after go-live (`E-STASH`) |
|
|
22
|
+
| "a prize", "the winner gets", "reward" (known in advance) on a grant, a raffle, a tournament, a quest, a mission, a recipe | a prize line given in the same transaction: `{ "gives": "item", "item": key }`, `{ "gives": "tokens", "amount": n, "from": "mint", "currency": key }` or `"from": "pool:<key>"`, `{ "gives": "share", "pool": key, "percent": p }`, `{ "gives": "nft", "stash": key, "nft": "<address>" }` (an NFT a stash lists). One step for the player, no oracle |
|
|
23
|
+
| "a random prize" for a grant, a raffle, a tournament, a quest, a mission | the prize `{ "gives": "roll", "box": "<box>" }`: a box (`"active": false`) of whatever it may give (items, `pay`, `share`, `stash`), rolled for the winner. Anyone asks the oracle for the roll and settles it; it is never refunded while it waits. Use it only for chance |
|
|
24
|
+
| "a free daily chest", "quest reward: a random item", "a promo code for a mystery box" | a grant with `"prize": { "gives": "roll", "box": "<box>" }` (its funds pay the roll's order, back to the winner at settle); a known reward is a direct `prize` instead |
|
|
25
|
+
| "raffle my NFT", "the winner gets the 1-of-1", "first place wins an NFT", "refund everyone if not enough tickets sell" | a lottery with `"raffle": "<address>"` (or `{ "nft", "collection" }` for an NFT of another collection) and no `every`: the NFT waits in the lottery's own stash, which nothing but the draw can empty, and the winner gets it before any ticket money moves. `minSold`: a round that sold fewer is cancelled at its draw and refunded. The ticket money goes where its `split` says (`"treasury"`, the seller's). A seller raffling their own NFT is the owner of a project of their own (`app.raffle`) |
|
|
26
|
+
| "sell my shield for 3 SOL", "list it at a price", "rent out my mech for a few days", "scholarships" | a listing, made from the app's page at run time, not in xsolana.json: `app.listForSale(asset, { price })`, `app.listForRent(asset, { price, lasts, maxPeriods })`; a buyer `app.buyListing(asset, { listing })`, a renter `app.rentListing(asset, { periods, listing })` (the `listing` the page showed); the app reads who rents it with `app.rentedTo(asset)` (one account read). On-chain gates don't read rentals |
|
|
27
|
+
| "presale", "fair launch", "10% of the supply split by what each put in", "allocation by contribution" | a contest with one side, a `minPot`, a split of 100% to the treasury and a token `prize` with `"split": true`: set aside whole when it is decided (or the decision is refused), each backer's part by what they put in; short of `minPot`, everyone is refunded |
|
|
28
|
+
| "vesting", "cliff then monthly", "locked team tokens", "nobody can take it back", "unlock schedule" | a schedule `{ "does": "pay", "sealed": true, "currency": key, "to": "wallet:<address>", "amount": n, "start": "publish+60d", "every": "30d", "maxRuns": 6, "fund": total, "fundFrom": "mint" }`: its own vault, never edited or stopped by anyone, the last run pays the rest. An ordinary schedule from a pool is only the owner's promise (`W-SCHEDULE-PAY`) |
|
|
29
|
+
| "leave early for a 10% fee", "paper hands penalty", "unstaking takes 7 days", "2% harvest tax", "compound" | a farm of tokens: a tier's `exitFee` (percent) with the farm's `exitFeeTo` (`"burn"` or `"pool:<key>"`: its own rewards pool releases it to those who stay, over the farm's longest lock), `unbond` ("7d"), `toll` `{ "percent": 2, "to": "burn" }`; compounding is `app.compound(farm)` when the rewards are the staked token, the lock unchanged. A farm of collectibles leaves early by a recipe that lowers its `lockAttribute` |
|
|
30
|
+
| "monthly payouts from a list", "each wallet gets a different amount", "points to tokens by list" | a grant of tokens whose `list` entries are `{ "wallet", "amount", "round" }`: each claims its amount once; the same wallet under another `round` claims again |
|
|
31
|
+
| "the tournament winner gets a trophy NFT", "a random NFT for every correct pick" | a contest's `prize`, given to each winning entry when it is claimed, besides its part of the pot: one NFT goes to the first entry claimed (`W-CONTEST-PRIZE`; a contest whose sides are the players has one winner), a roll gives each winner a random one |
|
|
32
|
+
| "auction a mystery box", "bid on a mystery lot" | an auction `lot: { "roll": "<box>" }`: the winner gets a roll of the box; the price goes to the treasury. Say what the box can give, with its odds (`W-AUCTION`) |
|
|
33
|
+
| "the expedition brings back a chest", "missions drop random loot" | a farm tier's `prize`: `{ "gives": "roll", "box": "<box>" }`, rolled when the mission is completed (its lock stayed, its upkeep never ran dry, not forfeited) while the NFT goes home; a known reward (tokens, an item, an NFT) is given as it leaves |
|
|
34
|
+
| "only pass holders can buy it", "members only" | a `gate` of the item alone: `{ "item": "pass" }` |
|
|
35
|
+
| "only holders can open it", "level 5 and up" | a `gate`: `{ "item": "pass", "attribute": "level", "atLeast": 5 }` |
|
|
36
|
+
| "only these players", "just the two of them", "the tournament's entrants" | a `gate` of wallets: `{ "wallets": ["<address>", …] }` (at most 16) |
|
|
37
|
+
| "stake to enter", "staking tiers for a token sale", "only people who lock 100 of our token can buy" | a farm of a token with no `rewards` when staking pays nothing (an empty pool is opened for it), and a `gate` `{ "staked": { "farm": "<farm of a token>", "atLeast": 100 } }`: the wallet's stake in that farm, counted only while its tier's lock runs and from the second after it is made (one stake can't move to another wallet and count again; a tier with no lock never passes, `W-STAKED`). The app passes the seat and the farm |
|
|
38
|
+
| "allowlist", "whitelist of 5,000 wallets", "a snapshot of our holders", "early supporters only" | a `gate` `{ "list": ["<address>", …] }` of any size: the program keeps only its root, the page passes each player's proof (`W-LIST`); a new list is a new root, published again |
|
|
39
|
+
| "rarer pulls for higher levels", "a charm stat improves the odds" | a box's `odds`: `{ "outcome": 3, "when": { "attribute": "charm" }, "add": { "perPointPercent": 10, "upTo": 9 } }` |
|
|
40
|
+
| "stake to earn", "x% house edge to stakers", "fees go to holders" | a `split` share `"to": "pool:<rewards>"` and a `farms` entry whose `rewards` is that pool |
|
|
41
|
+
| "lock for longer, earn more", "30-day lock pays 3x" | a farm's `tiers`: `[{ "lock": "1d", "multiplier": 1 }, { "lock": "30d", "multiplier": 3 }]` |
|
|
42
|
+
| "stake your NFT", "rarer heroes earn more" | a farm with `"stakes": "collectibles"`, `boosts` on an attribute, `maxBoostPercent` |
|
|
43
|
+
| "earn 12 a day per NFT", "daily emissions per NFT", "a fixed rate per NFT" | a farm's `rate`: `{ "perDay": 12, "minted": true }` (the reward a token the project mints), the same for each NFT whoever else stakes; without `minted` it pays from the rewards pool while it lasts |
|
|
44
|
+
| "XP", "levels up while staked", "level 5 after a week", "evolve at level 10" | a farm's `growth`: `{ "perDay": 100, "levels": [100, 300, 600] }` writes `xp` and `level` on the NFT for time staked, with nobody's signature; a boost per `level`, a gate, box `odds` or a recipe's `needs` read it (evolve: a recipe that burns the hero and makes the next form, `needs` level 10). `"streak": true` resets it when the NFT leaves |
|
|
45
|
+
| "a mission can fail", "the hero comes back hurt", "the hero can be lost / captured" | the tier's `roll`: a box (`"active": false`, `rollsOn` the mission's NFTs) whose outcomes say what happens: an item or a prize on success, `"shifts": [{ "attribute": "affection", "by": -2 }]` when hurt, `"then": "burn"` (or `"treasury"`, `{ "giveTo": "<wallet>" }`) when lost; `odds` by level make success likelier. The farm then needs freeze or escrow custody for a loss to bite (a watched NFT sold first is simply not rolled) |
|
|
46
|
+
| "enchant: 60% +1 level, 30% -1 level, 10% it breaks", "upgrade can fail" | a box with `rollsOn` (the NFT the player brings) and outcomes with `shifts` and `then`; one that can burn or give the NFT away holds it until the roll settles, so say so |
|
|
47
|
+
| "risky forge", "merge with a chance of failure" | a recipe output `{ "gives": "roll", "box": "<box>", "on": 0 }` (the box `"active": false`): on a kept collectible, or with no `on`, a roll whose outcomes are items or prizes |
|
|
48
|
+
| "merging costs a level", "lose a stat" | a recipe's `raises` with a negative `by`: `{ "place": 0, "key": "level", "by": -1 }` (never below 0) |
|
|
49
|
+
| "missions", "expeditions", "send heroes on a 3-day quest for loot", "random rewards" | a farm tier with a `lock` and a `prize`: `{ "gives": "tokens", "amount": 2, "from": "mint", "currency": "<ticket>" }` (grown by boosts); a box priced in that ticket gives the random loot, its `odds` raised by level or trait. A mission needs a lock (`E-FARM`) |
|
|
50
|
+
| "1 level a day", "+2 affection a day" | a farm's `growth` without `levels`: the points are the attribute itself, `[{ "attribute": "level", "perDay": 1 }, { "attribute": "affection", "perDay": 2 }]` (up to 4) |
|
|
51
|
+
| "a mission open for one week", "a season that ends" | a farm's `runs`: `"7d"` from when it goes live; it takes no stake at its end or after it, nor one whose lock would run past it. A sharing farm keeps what it has not released by the end in the pool (the project's): fund it at least its release span (its longest lock, a day at least) before the end. A mission that pays no tokens has `"rate": { "perDay": 0, "minted": true }`. Released later: add the farm to the spec and publish again |
|
|
52
|
+
| "level 3 to join", "needs level 100 and 6 affection" | a farm's `gate`, one rule or a list the NFT must all pass: `[{ "collection": "<address>", "attribute": "level", "atLeast": 100 }, { …, "attribute": "affection", "atLeast": 6 }]` |
|
|
53
|
+
| "fuel", "food", "upkeep", "drills need fuel to dig", "runs out", "license for 30 days" | a farm of collectibles' `upkeepAttribute` (`"fuel_until"`): a staked NFT earns and grows only until that time, and it runs out by itself; a recipe sells it: `in` the NFT (`use`) and a `pay` line, `raises`: `[{ "place": 0, "key": "fuel_until", "by": "1d", "upTo": "3d", "fromNow": true }]` (a dry tank fills from now; at most 3 days ahead). A recipe reaches an NFT staked in escrow; the play page refreshes its seat in the same transaction. A mission's prize needs the fuel to last its lock |
|
|
54
|
+
| "leave early for a fee", "early unstake penalty", "break the lock", "recall" | a farm of collectibles' `lockAttribute` (`"free_at"`: the lock is written on the NFT as the time it ends) and a recipe that lowers it for a price: `raises`: `[{ "place": 0, "key": "free_at", "by": "-1d", "fromNow": true }]` with a `pay` line burned, or paid into the farm's rewards pool (released to the seats there over the farm's longest lock, a day at least). Leaving early takes no mission prize. A farm of tokens uses its tier's `exitFee` instead |
|
|
55
|
+
| "jail", "cooldown after a raid", "arrested for 24 hours" | a roll's outcome `"shifts": [{ "attribute": "jailed", "by": "1d", "upTo": "1d", "fromNow": true }]`, and the mission's gate `{ "item": "<hero>", "attribute": "jailed", "time": "passed" }`; on a staked NFT with `lockAttribute`, a shift on that key keeps it in longer |
|
|
56
|
+
| "set bonus", "collect all four elements", "a full set earns more", "bonus for a complete set staked" | a set bound into a badge: a recipe whose `in` takes each piece `{ "takes": "staked", "item": "<piece>", "farm": "<its farm>" }` (the farm it is staked in, escrowed or frozen) and whose `out` is a badge item, with `raises` on each piece `{ "key": "bound", "by": 1, "upTo": 1 }` (one badge a set) and `{ "key": "free_at", "by": "20000d" }` (that farm has `"lockAttribute": "free_at"`, so they stay staked while the badge exists; a raise on a staked piece must move its farm's lock, or the craft is refused); the badge earns in a farm of its own. Unbinding: `in` the badge burned and the pieces `staked` in the same farm, `needs` each `bound` at least 1, `raises` `bound` by -1 and `free_at` by "-20000d", and no `pay` line (one waits while the platform suspends the project, keeping the pieces locked: charge when binding). Up to 4 pieces (8 raises; one transaction). The `elements-set` example |
|
|
57
|
+
| "only with fuel left", "pass still valid" | a rule's `"time": "ahead"` (a time still to come) or `"passed"`; a farm's gate reads a time as the NFT comes in |
|
|
58
|
+
| "the NFT stays in the guild while it goes on the mission", "can still sell it" | `"custody": "watch"` on the mission (and on any farm whose NFTs must stay free); the guild itself may freeze |
|
|
59
|
+
| "my pNFTs", "compressed NFTs", "any NFT" | nothing to add: every route takes Core, Token Metadata (legacy, pNFT) and compressed NFTs |
|
|
60
|
+
| "a badge for everyone who comes", "airdrop an NFT to 20,000 wallets", "POAPs", "mint cNFTs" | the prize `{ "gives": "item", "item": "<badge>", "compressed": true }` (a grant's, a place's, a contest's, a mission's or a recipe's): a compressed NFT of the collectible, no rent each (a Core one is about 0.003 SOL), minted into a tree the setup opens (`compressedTree: { "holds": n }`, 16,384 by default, about 0.23 SOL once). Write it before going live: the setup's collection takes compressed NFTs only if it is made with them in the spec. Not soulbound, not a box outcome; give a rare one as a Core collectible |
|
|
61
|
+
| "blue background gets 10%", "a trait in the metadata" | a boost on the attribute (`{ "when": { "collection": "<address>", "attribute": "Background", "equals": "Blue" }, "add": { "addPercent": 10 } }`) and the spec's `traits` (the root of `loot.traitsTree` of the collection's off-chain metadata): each NFT's traits are proven into its Sheet once |
|
|
62
|
+
| "pay to level up", "train", "upgrade a stat for 100 shards", "max level 10" | a recipe: `in` the collectible kept (`use`) and a `pay` line, and `raises`: `[{ "place": 0, "key": "level", "by": 1, "upTo": 10 }]`. A farm's growth never takes a bought level back down |
|
|
63
|
+
| "upgrade an NFT of another collection in our game", "stats for NFTs from another collection" | a recipe line `{ "takes": "show", "collection": "<address>" }` and `raises` on it: the stats live in the project's Sheet of that NFT (or on the NFT, when its Attributes plugin names the project); the authority imports stats that only exist off the chain with a `set` decision |
|
|
64
|
+
| "earns its power a day", "rate by rarity or stat" | a farm `rate` and a boost `perPointPercent` on the stat: `{ "when": { "attribute": "power" }, "add": { "perPointPercent": 100, "upTo": 100 } }` and `maxBoostPercent` up to 10,000 (x101) |
|
|
65
|
+
| "level-up rewards", "claim a badge at level 15" | an open grant gated by `{ "attribute": "level", "atLeast": 15 }`, `perWallet` and the NFT counted once (show the badge, claim once) |
|
|
66
|
+
| "weekly lottery", "three places", "one grand prize and seven smaller" | `lotteries` with `every: "7d"`, `places: [60, 25, 15]`. A lottery has a pot of its own (`currency`, the project's by default; `seed` fills it at go-live): only its winners are paid from it, so the owner can't drain it by mistake |
|
|
67
|
+
| "a raffle", "one draw at the end of the month" | a lottery with no `every` and a `firstDraw`: it draws once and ends |
|
|
68
|
+
| "x% of tickets go to me" | the lottery's `cutPercent` (a share to the treasury, paid after a successful draw) |
|
|
69
|
+
| "20% of every ticket to the school fund", "the referrer gets 8% of the tickets they bring" | the lottery's `split`: `{ "to": "wallet:<fund>", "percent": 20 }`, `{ "to": "chosen", "percent": 8 }` (the buyer fixes the referrer when they buy; shares are paid after a successful draw) |
|
|
70
|
+
| "max 10 tickets each" | the lottery's `perWallet` (tickets one wallet holds in a round) |
|
|
71
|
+
| "fed by 10% of every flip" | a 10% share in the box's `split` to `"lottery:<key>"` |
|
|
72
|
+
| "burn X for Y", "merge two into one", "craft", "breed", "upgrade" | `recipes`: `in` (`burn`, `use`, `renew`, `pay`, `trade`, `staked`, `show`) and `out` (`item`, `tokens`, `share`, `nft`, `roll`) |
|
|
73
|
+
| "trade in any NFT of ours for 10 gold", "swap an NFT of another collection for a badge", "we keep what's traded in and give it away again" | a recipe line `{ "takes": "trade", "collection": "<address>" (left out: the project's own), "then": "burn" \| { "stash": "<key>" } \| { "giveTo": "<wallet>" } }`: the NFT is the price. Into a stash, it becomes another player's prize (a box outcome `{ "gives": "stash" }` resells it) |
|
|
74
|
+
| "a trade-in box: give an NFT, roll for a new one" | a box with `rollsOn` (which NFTs it takes) and `"then": { "stash": "<key>" }` (or `"burn"`): every outcome keeps the NFT paid, and its outcomes give the prizes |
|
|
75
|
+
| "tickets cost an NFT", "sacrifice an NFT for a raffle ticket" | a lottery's `ticketNft`: `{ "rule": { "item": key } or { "collection": address }, "then": "burn" or { "stash": key } }`, with no `price`, `split` or `ticketCurrency`; the pot is its `seed` and its places' prizes. An NFT spent on a ticket is never refunded (`W-NFT-PRICE`) |
|
|
76
|
+
| "no-loss lottery", "prize savings", "you never lose your ticket", "the sponsor pays the prize" | a lottery with `"noLoss": true`: every ticket (tokens or an NFT) goes back to its buyer once the round is drawn, and the winners take only the pot: its `seed`, a sponsor's payments, shares of other sales (`"lottery:<key>"`). No `split`, `cutPercent` or `ticketCurrency`. Say that tickets are not weighted by time: someone may buy most of them right before the draw and risk only the fees |
|
|
77
|
+
| "stake an NFT on the duel", "winner takes the loser's NFT" | a contest's `stakeNft` (a rule): each entry stakes one NFT; each winning entry takes its own back and one loser's as it is claimed, and the opener gets what the winners leave |
|
|
78
|
+
| "bid with NFTs", "the most NFTs wins" | an auction's `bidNfts` (a rule): a bid is up to three Core or Token Metadata NFTs (never compressed), the most wins, `reserve` counts NFTs |
|
|
79
|
+
| "pay with an NFT to enter", "burn an NFT for an attempt" | not a spec part: the page calls `loot.payNftIx` (burned, or given to a wallet) and the app admits by the receipt, as for a payment |
|
|
80
|
+
| "points", "tickets", "shards", "attempts", "sparks" | a token the project mints: a `tokens` entry `source: "project"`, given by a recipe's `out` (`from: "mint"`) or a schedule |
|
|
81
|
+
| "a pass that claims daily", "cooldown" | an item with `life.cooldown`; a recipe that `use`s it and pays `tokens` from a pool |
|
|
82
|
+
| "ready after 7 days", "rots in 3 days", "6 uses" | an item's `life`: `readyAfter`, `lasts`, `uses`. Only recipes read `life` (a recipe refuses an item not ready, expired or used up); gates, odds and boosts never do |
|
|
83
|
+
| "monthly membership", "renew", "pass valid until", "expires unless renewed", "a week's access" | a pass item with a time attribute (`paid_until`), a renew recipe (`in`: the pass `use`d and a `pay` line; `raises`: `[{ "place": 0, "key": "paid_until", "by": "30d", "fromNow": true, "upTo": "60d" }]`), and gates that read `{ "item": "pass", "attribute": "paid_until", "time": "ahead" }`. Nothing charges a wallet by itself: the member renews, the page reminds them |
|
|
84
|
+
| "every day mint 5,000 into the pool, halving every 90 days" | a `schedules` entry `does: "issue"`, `every: "1d"`, `halveEvery: 90` |
|
|
85
|
+
| "burn half a percent of the pool daily", "move 5% weekly" | a schedule `does: "burn"` or `does: "move"` with `percent` |
|
|
86
|
+
| "pay the team 100 a week", "a landlord's rent" | a schedule `does: "pay"` from a pool `to` "wallet:<address>" or "holder:<asset>" (anyone may run it when due: the page, a player, a sweep) |
|
|
87
|
+
| "an entry fee", "pay per attempt" | `payments` with `amount` and a `split`. A payment has no gate, no opens/closes, no `"chosen:<item>"` share and no rising price: when the entry needs one, sell it as a box with one outcome that gives nothing (or an item), which has them all. Its split may name a `"chosen"` wallet (a referrer): `app.pay(key, { chosenRecipient })` |
|
|
88
|
+
| "tips", "donations", "pay what you want", "any amount above 0.001" | a payment with `"any": true`: `amount` is the least; the page asks the amount and calls `app.pay(key, { amount })` |
|
|
89
|
+
| "all proceeds to the school", "our DAO's vault gets the money", "the cut goes to the charity" | `"treasury": "wallet:<their address>"`: what every split leaves, and a lottery's cut, go there. Left out, the treasury is the owner's own wallet. The owner can change it later |
|
|
90
|
+
| "several signers must approve spending", "a multisig" | loot isn't a multisig: the money goes to their multisig's vault (for example Squads): `"treasury": "wallet:<vault>"`, or a `wallet:` share; spending from it is approved in the multisig |
|
|
91
|
+
| "60% to me, 40% to my co-designer", "10% to our charity", "split with my partners" | split shares `"to": "wallet:<their wallet address>"` (ask for each address). Going live opens each wallet's account for the currency. Use `account:` only for a token account that already exists |
|
|
92
|
+
| "our partner gets 4%" (one fixed wallet) | a split share `"to": "wallet:<their address>"` |
|
|
93
|
+
| "referral links", "each streamer gets 6% of what they bring", "tip the artist I pick" | a box's, shop item's, lottery's or payment's split share `"to": "chosen"`: the buyer names the wallet when they buy (the page passes it from the link: `app.buy(key, { listing, chosenRecipient: referrer })`, `app.pay(key, { chosenRecipient: referrer })`), never their own (a second wallet of theirs still works, so a referral share is at most a rebate: price it so); with none it goes to the treasury |
|
|
94
|
+
| "a fee to the owner of the workshop I craft at", "the venue's owner gets a cut" | a split share `"to": "chosen:<item key>"`: the buyer names a collectible of that item, and whoever holds it is paid (`{ chosenRecipient: <its address> }`) |
|
|
95
|
+
| "the holder of the deed gets 3%" | a split share `"to": "holder:<collectible address>"` |
|
|
96
|
+
| "duel", "bet on a side", "prediction", "the winner takes the pot" | `contests` with `sides` and a rake in `split`; the authority decides, or a results server's key in `deciders` signs the result (anyone sends it) |
|
|
97
|
+
| "1v1 wager", "both put in 10 and the winner takes it" | a contest with a wallet `gate` of the two players and a `stake` (every entry exactly that) |
|
|
98
|
+
| "crowdfund", "all or nothing", "refund everyone if we miss the goal" | a contest with `sides: 1`, a `minPot` (the goal), and a `split` of 100% to `"wallet:<recipient>"`. Short of the goal when entries close, it is off by itself and every backer claims their pledge back (anyone can send those claims). Reached: the owner (or a decider) names side 0 and the whole pot goes to the recipient. A badge for each backer: a grant whose `list` is the backers |
|
|
99
|
+
| "the game decides who cleared the level and pays them" | `decisions`: `does: "payout"` from a pool, with `when` in words |
|
|
100
|
+
| "level up my pet", "write the score on the NFT" | a decision `does: "set"` (the authority rewrites attributes) |
|
|
101
|
+
| "send caravans on trade runs, the game says how they went", "the event ends at 18:00" | `holds` (the item, `for` a time the player picks, or `until` a set time) and an `award` decision |
|
|
102
|
+
| "quests that pay", "airdrop to these wallets", "one per person" | `grants`: a `prize` (an item, tokens, an NFT, or a roll); a `list` of wallets, or `vouchers` (a results server's public key: see `after-launch.md`); `limits`. A claim of a prize it can't give is refused and keeps its round |
|
|
103
|
+
| "rewards by rank", "1st gets 400, 2nd 250", "points to tokens", "each winner a different amount" | one grant whose `prize` is a fixed amount of tokens (the most any claim takes: 1st place's) paid by `vouchers` or a list: each voucher (or list leaf) names that claim's own amount, up to the grant's (`signVoucher({ …, amount })`, `app.claimGrant(key, { voucher })`); none named pays the grant's amount. A `list` written in xsolana.json does the same: each entry `{ wallet, amount }` names its own amount |
|
|
104
|
+
| "at most once a day each", "2 wins a day per wallet" | a grant's `limits.perWalletEach`: `{ "count": 1, "every": "1d" }` |
|
|
105
|
+
| "promo codes on flyers", "a code pays once", "QR claim links" | a grant with `keys` (the public key of each code's keypair: the page derives the keypair from the typed code and signs a voucher for the player's own wallet) and `limits.perKey: 1` |
|
|
106
|
+
| "holders of the ticket claim a free badge once" | an open grant: no `vouchers`, `keys` or `list`, a `gate` of the ticket, and `limits.perWallet: 1` (each ticket shown claims once too) |
|
|
107
|
+
| "bring your own NFT", "any NFT of a partner collection can play", "holders of X get" | a `gate` (or a farm's, or odds') rule with `collection`: the collection's address; the project's notes about each NFT (a level) go in its Sheet |
|
|
108
|
+
| "our own token, 10 million, we keep it", "a fixed supply", "a community token" | `source: "project"` with `supply`: going live mints all of it to the owner and turns minting off for good; pools are seeded from it |
|
|
109
|
+
| "our token", "the coin we already launched", "this mint" | `source: "existing"` with `mint` and `decimals` (ask for the address). loot doesn't put a token on a market: for trading, launch it on a token launchpad or an exchange first |
|
|
110
|
+
| "Token-2022", "a transfer fee" | `program: "token-2022"` and `powers.transferFeePercent` |
|
|
111
|
+
| "tokens only, no NFTs" | `"collection": false`: no collection is made (no collectibles; normal items and tokens work) |
|
|
112
|
+
| "mint into our existing collection", "two collections" | `collections`: Core collections the owner's wallet already controls (up to eight in all, with the project's own), each with the project added as an update delegate; an item's `collection` names the one it is minted into |
|
|
113
|
+
| "a second game or community from the same wallet" | `projectNumber`: 1, 2, … (the first is 0); each number is a project of its own |
|
|
114
|
+
|
|
115
|
+
Ask, in your first reply, for the numbers that define the product if the person hasn't given them: prices, supply, odds
|
|
116
|
+
or multipliers, and how much SOL they can put in (their budget sets every seed). Don't wait for the answers to build:
|
|
117
|
+
save a first draft that checks, with sensible, stated defaults (a farm's lock tier of
|
|
118
|
+
"1d", a grant's `perWallet: 1`), and say which, so they react to something concrete. When you weigh two designs,
|
|
119
|
+
save the one you recommend and say what the other would change. Only what the person alone can give (their coin's
|
|
120
|
+
mint, a vault's or a partner's address) is worth waiting for.
|
|
121
|
+
|
|
122
|
+
When loot can't do exactly what they asked, follow SKILL.md rule 9: `xsolana_examples {"query": "<that part, in their
|
|
123
|
+
words>"}` returns the limit and its nearest versions. Offer them with their costs, build the one they pick, and when
|
|
124
|
+
they push back offer the next one. Don't stop at "it doesn't meet your requirement".
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
## Worked example
|
|
128
|
+
|
|
129
|
+
The person: *"A fishing game: a cast costs 0.02 SOL. Most casts catch nothing, some land a Perch to keep, and 1 cast
|
|
130
|
+
in 50 hooks the Golden Carp, worth 0.5 SOL. 5% of every cast goes to a monthly prize draw."*
|
|
131
|
+
|
|
132
|
+
- **Pays**: a cast (a box). **Gets**: nothing (68%), a Perch collectible (30%), or 0.5 SOL from a pool (2%).
|
|
133
|
+
**Money goes**: 5% to the draw's pot; they didn't say where the rest goes, so the draft sends 55% to the pond that
|
|
134
|
+
pays the carp and leaves 40% to the treasury, and says so. **Decides**: nobody, but someone sends the monthly draw
|
|
135
|
+
and pays its place when it is due: anyone can (a ticket holder from the page).
|
|
136
|
+
- `xsolana_examples {"query": "<their words>"}` gives the nearest spec to start from. The draft:
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"version": 2,
|
|
141
|
+
"network": "devnet",
|
|
142
|
+
"name": "Lakeside fishing",
|
|
143
|
+
"prompt": "A fishing game: a cast costs 0.02 SOL. Most casts catch nothing, some land a Perch to keep, and 1 cast in 50 hooks the Golden Carp, worth 0.5 SOL. 5% of every cast goes to a monthly prize draw.",
|
|
144
|
+
"currency": "sol",
|
|
145
|
+
"tokens": [{ "key": "sol", "source": "sol" }],
|
|
146
|
+
"items": [{ "key": "perch", "name": "Perch", "uri": "https://example.com/perch.json", "active": false }],
|
|
147
|
+
"pools": [{ "key": "pond", "currency": "sol", "seed": 2 }],
|
|
148
|
+
"boxes": [{
|
|
149
|
+
"key": "cast", "name": "Cast", "price": 0.02,
|
|
150
|
+
"split": [{ "to": "pool:pond", "percent": 55 }, { "to": "lottery:monthly", "percent": 5 }],
|
|
151
|
+
"outcomes": [
|
|
152
|
+
{ "gives": "nothing", "chance": 68 },
|
|
153
|
+
{ "gives": "item", "item": "perch", "chance": 30 },
|
|
154
|
+
{ "gives": "pay", "pool": "pond", "amount": 0.5, "chance": 2 }
|
|
155
|
+
]
|
|
156
|
+
}],
|
|
157
|
+
"lotteries": [{ "key": "monthly", "name": "Monthly draw", "price": 0.01, "places": [100], "every": "30d", "firstDraw": "publish+30d" }],
|
|
158
|
+
"scenario": { "days": 30, "perDay": { "box:cast": 500, "lottery:monthly": 20 } }
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
- `xsolana_check` → ready, with `W-CHANCE-PRIZE` (say it) and `W-FEE` (the 0.002 SOL player fee is 10% on top of a
|
|
163
|
+
0.02 SOL cast: say so, and that a higher price makes it a smaller part).
|
|
164
|
+
- `xsolana_simulate` → a cast costs about 0.0234 SOL in all and gives back 0.01 SOL in prizes on average (50%), plus a
|
|
165
|
+
Perch 3 casts in 10; at 500 casts a day the treasury gets about 4 SOL a day. The pond takes in 5.5 SOL a day and
|
|
166
|
+
pays out 5, but luck swings it about 4 SOL a week: on a 2 SOL seed a bad week stops the box selling, and the
|
|
167
|
+
simulation suggests about 12.4. The monthly pot reaches about 21 SOL, because casts feed it as well as tickets.
|
|
168
|
+
- `xsolana_cost` → about 2.01 SOL to go live, in one wallet approval: 2 SOL is the seed, the rest rent and fees.
|
|
169
|
+
- `xsolana_explain` → the words for the person (`telling-the-person.md`): pass on the 40% treasury share you chose, the
|
|
170
|
+
thin seed and the flags.
|
|
171
|
+
- The person: "start the pond with 12.5 SOL". Change the seed, check, simulate (the swing note is gone), explain:
|
|
172
|
+
going live is now about 12.51 SOL. `xsolana_save`, and when they say so, `xsolana_publish` and the approve command.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Telling the person what they get
|
|
2
|
+
|
|
3
|
+
The person reads your words, not the spec. After every change, run `xsolana_explain` on the spec as saved and turn it
|
|
4
|
+
into a few short lines, in this order. Describe what is in the spec, not what you meant to put there: when your words
|
|
5
|
+
and the spec differ (a limit, an amount, a key not set yet), change one of them before you answer. Use the tools' numbers; round them sensibly; name currencies by symbol.
|
|
6
|
+
|
|
7
|
+
1. **What each play costs and is worth.** Lead with the tools' "in all" number: "A cast costs about 0.0234 SOL in
|
|
8
|
+
all (0.02 SOL, the 0.002 SOL player fee, the oracle and the network, and the Perch's rent when one is caught), and
|
|
9
|
+
gives back 0.01 SOL in prizes on average (50%), plus a Perch 3 casts in 10." For a box of items, the odds; for a
|
|
10
|
+
lottery, what the pot reaches and what each place wins; for a farm, a day of rewards per unit staked and what the
|
|
11
|
+
lock tiers multiply.
|
|
12
|
+
2. **Where the money goes.** "55% of each cast goes into the pond that pays the Golden Carp, 5% into the monthly
|
|
13
|
+
draw's pot, and the other 40% to your treasury."
|
|
14
|
+
3. **How the pools fill and drain.** "The pond takes in a little more than it pays out, but luck swings it about
|
|
15
|
+
4 SOL a week: start it with about 12.5 SOL rather than 2, or a bad week stops casts until it refills." A pool that
|
|
16
|
+
runs dry is the first thing to say. A prize bigger than its pool is left out of the roll until the pool fills, so
|
|
17
|
+
the odds players are shown don't hold until then (`W-PRIZE-SEED`).
|
|
18
|
+
4. **What going live costs.** "About 12.51 SOL on devnet, in one wallet approval: 12.5 SOL is the pond's seed, the
|
|
19
|
+
rest is rent and fees."
|
|
20
|
+
5. **Who does what while it runs** (`after-launch.md` has the table): what anyone can send (a draw, a schedule, an
|
|
21
|
+
auction's end, claims), what only they can (a contest's result, a payout, a held item, a collectible's new level)
|
|
22
|
+
and how often, and what happens if nobody does. If a payout rewards a result the game reports (a clear, a win,
|
|
23
|
+
a score): "Checking that a player really did it is up to you, on your own server: a game runs in the player's
|
|
24
|
+
browser, and the program trusts the authority's signature."
|
|
25
|
+
6. **Flags for the law.** `W-CHANCE-PRIZE`: "Players pay for a chance at money. That is gambling in many places once
|
|
26
|
+
it is real money; this runs on devnet only." `W-CHANCE-ITEMS`: "Players pay for random items they can resell: some
|
|
27
|
+
places treat that as gambling, and many require the odds to be shown." `W-REVENUE-SHARE`: "Stakers are paid from
|
|
28
|
+
what players spend; regulators look at that when they decide whether a token is a security." Say them every time
|
|
29
|
+
they are raised. Before they go live, every time: "The program isn't independently audited and comes as is, with
|
|
30
|
+
no warranty: you use it at your own risk, and making sure this is legal where you offer it is up to you." The
|
|
31
|
+
approval page shows all of this too, and keeps Approve off until the owner ticks that they read it.
|
|
32
|
+
7. **For an auction**, say how it ends, in their words: "The highest bid when the 48 hours are up wins. Each bid must
|
|
33
|
+
be at least 0.5 SOL and 5% over the last; a bid in the last 10 minutes gives everyone 10 more. Whoever is outbid
|
|
34
|
+
has their SOL back at once, in the same transaction. With no bid, the lot goes back (an item's unit is never
|
|
35
|
+
made). Anyone can end it once it is over: the winner, or anyone, from the page. Nobody has to hurry: whenever it is ended,
|
|
36
|
+
the lot goes to the highest bidder." Then the shill warning (`W-AUCTION-SHILL`): "Anyone can bid, you included, and the
|
|
37
|
+
program can't tell a friend bidding the price up from a real buyer: a reserve is the honest way to set a floor."
|
|
38
|
+
For a collectible they already hold: "It stays with the project until someone wins it or the auction ends."
|
|
39
|
+
8. **Anything else the checker warned about**, in a sentence each (a mint that can freeze accounts, a free box bots
|
|
40
|
+
can drain, a grant with no limits or no funds).
|
|
41
|
+
|
|
42
|
+
Don'ts:
|
|
43
|
+
- Don't show mints, account addresses, PDAs, lamports or instruction names unless asked.
|
|
44
|
+
- Don't promise returns, profits or "passive income". Say what the program does, with the numbers.
|
|
45
|
+
- Don't hide a gap: if loot can't do what they asked, the first line says so and offers the nearest thing.
|
|
46
|
+
- Ask one question at a time, and only when the answer changes the spec (an existing coin's mint, a referrer's
|
|
47
|
+
address, how often they expect players to play).
|
|
48
|
+
- **Wrapped SOL cannot be burned**: burn a token the project mints or an existing coin, never SOL.
|
|
49
|
+
- A **box or payment in a currency other than the project's** works like any other: what its split leaves goes to the
|
|
50
|
+
treasury wallet, in that currency.
|
|
51
|
+
- **Prizes paid from a pool** need something filling it (a seed, a split, a schedule): the checker warns `W-POOL-DRY`
|
|
52
|
+
otherwise, and until it is filled those prizes pay nothing.
|
|
53
|
+
- **A token the project mints** (`source: "project"`) is how points, tickets, attempts and shards work; an existing coin
|
|
54
|
+
needs its mint address and decimals from the person (ask; never guess an address).
|
|
55
|
+
- **NFTs the project has no authority over** (any collection but its own): they count as they are. Say that
|
|
56
|
+
whatever their collection lets be written on an NFT (even by its holder) is what a gate or a boost reads, and that
|
|
57
|
+
the project's notes about one (levels, uses) live in its Sheet, which the authority writes and pays for. Every
|
|
58
|
+
route takes every kind of NFT: Core, Token Metadata (legacy, pNFT) and compressed (Bubblegum v2; a v1 tree is
|
|
59
|
+
refused). Off-chain traits (a background) count only once proven into the NFT's Sheet against the root the project
|
|
60
|
+
set (`traits`): say who proves them, and when.
|
|
61
|
+
- **Rolls that can fail**: a box, a mission or a recipe that can burn or give away the NFT it rolls on holds it from
|
|
62
|
+
the moment the roll is made until it is settled. Say the odds in plain numbers, which stats raise them, and what a
|
|
63
|
+
loss means (burned, or given to whom). Nobody can back out of a roll once it is made; anyone (the page, a keeper)
|
|
64
|
+
asks the oracle for it and settles it.
|
|
65
|
+
- **NFT custody** (a farm, a mission): Escrow is the default: the NFT moves to the project while it is staked.
|
|
66
|
+
Freeze keeps it in the player's wallet, locked, with the project the only key that unlocks it; a soulbound
|
|
67
|
+
collectible needs Freeze. Watch holds nothing: say plainly that a staked NFT can be sold, that its seller then
|
|
68
|
+
collects nothing more, and that its buyer can take the seat over. Only frozen or watched NFTs can be in two farms
|
|
69
|
+
at once: a guild frozen, its missions watched. Either way the NFT issuer's own controls still apply: an issuer who
|
|
70
|
+
can block transfers, or (under Freeze) one whose later metadata or plugin changes stop the unfreeze, can keep an
|
|
71
|
+
NFT from coming back, and forfeiting rewards doesn't get around that. Never promise an unconditional exit.
|
|
72
|
+
- **A farm releases everything in its rewards pool** to its stakers, over its longest lock (a day at least), by
|
|
73
|
+
weight as it is released: give each farm a pool of its own (`W-POOL-SHARED`). Whoever stakes first is credited all
|
|
74
|
+
that is released while they are alone, so say early stakers share the first rewards among few. A farm that
|
|
75
|
+
`runs` takes no stake at its end, and what it has not released by then stays in the pool, the project's, as does
|
|
76
|
+
what a seat gives back after it: say to fund it at least its release span before the end, and to refresh a seat
|
|
77
|
+
they suspect is dead (a watched NFT sold, an upkeep run out) a span before it. A sharing farm of a token with a
|
|
78
|
+
great many base units (a memecoin) that pays a reward of few units rounds each seat's share to nothing
|
|
79
|
+
(`W-FARM-ROUNDING`). A farm with
|
|
80
|
+
a fixed `rate` pays the same to each NFT instead; minted, it grows the token's supply every day it runs, which
|
|
81
|
+
is the person's to plan (say how much a day at the scenario's stake). A mint holds at most u64::MAX base units:
|
|
82
|
+
near that a rate that mints pays less than its rate, then nothing, and what it can't mint is never owed later
|
|
83
|
+
(`W-FARM-MINT-ROOM`, for a rate that could get there).
|
|
84
|
+
- **Levels written by staking are attributes on the NFT** (or in the project's Sheet of it, for an NFT the project
|
|
85
|
+
has no authority over): marketplaces show them, anyone can read them, and the person's other mechanics can use
|
|
86
|
+
them. They grow only when a seat is refreshed or the NFT leaves: the page refreshes (`app.refresh`).
|
|
87
|
+
- **Grants pay their claims' rent from their own funds** (`fund`, in SOL, held in the grant): the player pays the
|
|
88
|
+
0.002 SOL player fee and the network fee to claim, once, even when the prize is a roll. A first claim of a token
|
|
89
|
+
prize also opens the winner's account from the funds:
|
|
90
|
+
`xsolana_simulate` gives the funds a claim needs. When the funds run out, claims still land: whoever pays for the claim
|
|
91
|
+
(the winner, who always signs it, or anyone paying its fees for them) pays the rest. Only the winner can claim.
|
|
92
|
+
- **Never walk the person through creating, exporting or pasting a private key**, for anything (a keeper, a server,
|
|
93
|
+
a bot). Public steps need no key of theirs (`after-launch.md`).
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** The address of 32 zero bytes: what an address field holds when it names no account (the System program's address too). */
|
|
2
|
+
export declare const NO_ADDRESS = "11111111111111111111111111111111";
|
|
3
|
+
export declare function encodeBase58(bytes: any): string;
|
|
4
|
+
export declare function decodeBase58(text: any): Uint8Array<ArrayBuffer>;
|
|
5
|
+
/** Whether the text is a Solana address: base58 that decodes to exactly 32 bytes. */
|
|
6
|
+
export declare function isAddress(text: any): boolean;
|
|
7
|
+
/** Whether the text is a transaction signature: base58 that decodes to exactly 64 bytes. */
|
|
8
|
+
export declare function isSignature(text: any): boolean;
|
|
9
|
+
/** The 32 bytes of an address; throws on anything else. */
|
|
10
|
+
export declare function addressBytes(address: any): Uint8Array<ArrayBuffer>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** The Borsh bytes of one value of `type`. */
|
|
2
|
+
export declare function encodeBorsh(idl: any, type: any, value: any): Uint8Array<any>;
|
|
3
|
+
/** An instruction's data: its discriminator, then each argument in IDL order. */
|
|
4
|
+
export declare function encodeInstruction(idl: any, name: any, args: any): Uint8Array<any>;
|
|
5
|
+
/** An account's bytes as the program writes them: its discriminator, then its fields. */
|
|
6
|
+
export declare function encodeAccount(idl: any, name: any, value: any): Uint8Array<any>;
|
|
7
|
+
/** One value of `type` at `offset`: [value, next offset]. */
|
|
8
|
+
export declare const decodeBorsh: (idl: any, type: any, data: any, offset?: number) => any;
|
|
9
|
+
/** An account of the named type, after checking its discriminator. Bytes after the fields are left alone. */
|
|
10
|
+
export declare function decodeAccount(idl: any, name: any, data: any): any;
|
|
11
|
+
/** Whether the data starts with the named account's discriminator. */
|
|
12
|
+
export declare function isAccount(idl: any, name: any, data: any): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* The program's Anchor events in a transaction's logs (`Program data: <base64 of discriminator + Borsh>`):
|
|
15
|
+
* [{ name, data }]. Only lines the program itself printed count: the log names which program each line comes from
|
|
16
|
+
* (`Program <id> invoke [n]` … `success`), and any program can print a line that looks like an event. Logs alone
|
|
17
|
+
* cannot say whether the transaction succeeded (the runtime can fail it after every instruction logged success), so
|
|
18
|
+
* the caller decides that from the transaction's `meta.err` and reads events only when it is null.
|
|
19
|
+
*/
|
|
20
|
+
export declare function decodeEvents(idl: any, logs: any): {
|
|
21
|
+
name: any;
|
|
22
|
+
data: any;
|
|
23
|
+
}[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** UTF-8 bytes of a string. */
|
|
2
|
+
export declare const utf8: (text: any) => Uint8Array<ArrayBuffer>;
|
|
3
|
+
/** A string from UTF-8 bytes. */
|
|
4
|
+
export declare const fromUtf8: (bytes: any) => string;
|
|
5
|
+
/** The parts, one after another. */
|
|
6
|
+
export declare function concat(...parts: any[]): Uint8Array<any>;
|
|
7
|
+
/** Lexicographic order of two byte strings (like Buffer.compare): -1, 0 or 1. */
|
|
8
|
+
export declare function compareBytes(a: any, b: any): -1 | 0 | 1;
|
|
9
|
+
/** Whether two byte strings are equal. */
|
|
10
|
+
export declare function equalBytes(a: any, b: any): boolean;
|
|
11
|
+
export declare function toBase64(bytes: any): string;
|
|
12
|
+
/** Standard base64 to bytes; throws on anything that is not base64. */
|
|
13
|
+
export declare function fromBase64(text: any): Uint8Array<ArrayBuffer>;
|
|
14
|
+
export declare const toHex: (bytes: any) => string;
|
|
15
|
+
export declare function fromHex(text: any): Uint8Array<ArrayBuffer>;
|
|
16
|
+
/**
|
|
17
|
+
* Little-endian unsigned integer of `size` bytes, as Borsh and the Solana wire format write them. `value` is a bigint,
|
|
18
|
+
* or a number that is a safe integer: a larger number has already lost its low digits, so it is refused rather than
|
|
19
|
+
* written as some other value.
|
|
20
|
+
*/
|
|
21
|
+
export declare function uintLE(value: any, size: any): Uint8Array<any>;
|
|
22
|
+
/**
|
|
23
|
+
* Little-endian signed (two's complement) integer of `size` bytes, as Borsh writes an i32 or an i64. `value` as
|
|
24
|
+
* `uintLE` takes it; one outside the signed range is refused rather than wrapped.
|
|
25
|
+
*/
|
|
26
|
+
export declare function intLE(value: any, size: any): Uint8Array<any>;
|
|
27
|
+
/**
|
|
28
|
+
* `value` as a bigint: a bigint, a number that is a safe integer, or a string of digits. A number past 2^53 has already
|
|
29
|
+
* lost its low digits, so it is refused rather than turned into some other value.
|
|
30
|
+
*/
|
|
31
|
+
export declare function toBigInt(value: any): bigint;
|
|
32
|
+
/** A little-endian unsigned integer of `size` bytes at `offset`, as a bigint. */
|
|
33
|
+
export declare function readUintLE(bytes: any, offset: any, size: any): bigint;
|
|
34
|
+
/** Solana's compact-u16 length prefix. */
|
|
35
|
+
export declare function compactU16(value: any): Uint8Array<ArrayBuffer>;
|
|
36
|
+
/**
|
|
37
|
+
* A compact-u16 at `offset`: its value and how many bytes it took. Like Solana, it refuses a value past 0xffff and a
|
|
38
|
+
* longer form of a value that has a shorter one.
|
|
39
|
+
*/
|
|
40
|
+
export declare function readCompactU16(bytes: any, offset: any): {
|
|
41
|
+
value: number;
|
|
42
|
+
size: number;
|
|
43
|
+
};
|
|
44
|
+
/** Random bytes from the platform's CSPRNG (WebCrypto in browsers and Node 24). */
|
|
45
|
+
export declare const randomBytes: (n: any) => Uint8Array<any>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { MPL_CORE_PROGRAM } from './pda.js';
|
|
2
|
+
export { MPL_CORE_PROGRAM };
|
|
3
|
+
/**
|
|
4
|
+
* A project's Core collection. The creator is its update authority and royalty receiver; the loot project's address
|
|
5
|
+
* (`project`, for example `pda.project(creator, 0)` from the client) is an update delegate so the program can mint into it. `compressed`:
|
|
6
|
+
* it also carries Bubblegum's plugin, so compressed NFTs can be minted into it. The collection address signs too (a
|
|
7
|
+
* fresh key).
|
|
8
|
+
*/
|
|
9
|
+
export declare function createCollectionInstruction({ collection, creator, name, uri, royaltyBps, project, compressed }: {
|
|
10
|
+
collection: any;
|
|
11
|
+
compressed?: boolean | undefined;
|
|
12
|
+
creator: any;
|
|
13
|
+
name: any;
|
|
14
|
+
project: any;
|
|
15
|
+
royaltyBps?: number | undefined;
|
|
16
|
+
uri: any;
|
|
17
|
+
}): {
|
|
18
|
+
programId: string;
|
|
19
|
+
keys: {
|
|
20
|
+
pubkey: any;
|
|
21
|
+
isSigner: any;
|
|
22
|
+
isWritable: any;
|
|
23
|
+
}[];
|
|
24
|
+
data: Uint8Array<any>;
|
|
25
|
+
};
|
|
26
|
+
/** Decode a Core AssetV1: { owner, collection, name, uri, attributes, frozen, soulbound }. */
|
|
27
|
+
export declare function parseCoreAsset(data: any): {
|
|
28
|
+
owner: string;
|
|
29
|
+
collection: string | null;
|
|
30
|
+
name: string;
|
|
31
|
+
uri: string;
|
|
32
|
+
attributes: {};
|
|
33
|
+
frozen: boolean;
|
|
34
|
+
soulbound: boolean;
|
|
35
|
+
};
|
|
36
|
+
/** Decode the start of a Core CollectionV1: { updateAuthority, name, uri, numMinted, currentSize }. */
|
|
37
|
+
export declare function parseCoreCollection(data: any): {
|
|
38
|
+
updateAuthority: string;
|
|
39
|
+
name: string;
|
|
40
|
+
uri: string;
|
|
41
|
+
numMinted: number;
|
|
42
|
+
currentSize: number;
|
|
43
|
+
};
|
|
44
|
+
/** getProgramAccounts filters for Core assets owned by `owner` in `collection` (no indexer needed). */
|
|
45
|
+
export declare const ownedAssetFilters: (owner: any, collection: any) => {
|
|
46
|
+
memcmp: {
|
|
47
|
+
offset: number;
|
|
48
|
+
bytes: any;
|
|
49
|
+
};
|
|
50
|
+
}[];
|