@xgamesfun/solana 0.20.1 → 0.24.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 +10 -10
- package/examples/coin-flip-arena.json +2 -7
- package/examples/elements-set.json +2 -9
- package/examples/expedition-guild.json +1 -8
- package/examples/guild-missions.json +3 -10
- package/examples/halving-seasons.json +4 -9
- package/examples/hero-trials.json +1 -4
- package/examples/merge-2048.json +5 -5
- package/examples/paper-hands-farm.json +4 -16
- package/examples/partner-vault.json +1 -4
- package/examples/stakers-draw.json +1 -8
- package/examples/star-fleet.json +1 -8
- package/examples/starforce.json +6 -6
- package/js/approve/details.mjs +8 -7
- package/js/approve/server.mjs +20 -5
- package/js/chain/borsh.js +9 -0
- package/js/chain/core.js +14 -2
- package/js/chain/lookup.js +8 -2
- package/js/chain/rpc.js +4 -2
- package/js/client/accounts.js +448 -116
- package/js/client/grant.js +125 -66
- package/js/client/idl/loot.js +1 -1
- package/js/client/idl/loot.json +2360 -2218
- package/js/client/index.js +1 -0
- package/js/client/ix.js +1204 -473
- package/js/client/pda.js +20 -16
- package/js/client/policy.js +44 -23
- package/js/client/random.js +70 -0
- package/js/client/sizes.js +255 -12
- package/js/economy/check.mjs +455 -210
- package/js/economy/cost.mjs +45 -30
- package/js/economy/examples.mjs +2 -2
- package/js/economy/explain.mjs +24 -20
- package/js/economy/guide.mjs +31 -30
- package/js/economy/live.mjs +30 -7
- package/js/economy/model.mjs +171 -56
- package/js/economy/plan.mjs +380 -112
- package/js/economy/schema.json +291 -143
- package/js/economy/simulate.mjs +154 -87
- package/js/mcp/tools.mjs +14 -8
- package/js/play/index.js +1121 -375
- package/js/play/keys.js +167 -0
- package/package.json +1 -1
- package/release.json +5 -5
- package/skills/xsolana-economy/SKILL.md +15 -11
- package/skills/xsolana-economy/references/after-launch.md +37 -27
- package/skills/xsolana-economy/references/apps.md +55 -19
- package/skills/xsolana-economy/references/sentence-to-spec.md +30 -22
- package/skills/xsolana-economy/references/telling-the-person.md +52 -11
- package/types/chain/core.d.ts +3 -1
- package/types/chain/lookup.d.ts +5 -2
- package/types/chain/rpc.d.ts +3 -1
- package/types/client/accounts.d.ts +195 -55
- package/types/client/grant.d.ts +72 -27
- package/types/client/idl/loot.d.ts +590 -559
- package/types/client/index.d.ts +1 -0
- package/types/client/ix.d.ts +3862 -234
- package/types/client/pda.d.ts +19 -15
- package/types/client/policy.d.ts +39 -14
- package/types/client/random.d.ts +26 -0
- package/types/client/sizes.d.ts +93 -5
- package/types/economy/check.d.mts +5 -10
- package/types/economy/cost.d.mts +2 -1
- package/types/economy/live.d.mts +16 -1
- package/types/economy/model.d.mts +105 -37
- package/types/economy/plan.d.mts +193 -192
- package/types/economy/simulate.d.mts +21 -13
- package/types/mcp/tools.d.mts +21 -13
- package/types/play/index.d.ts +450 -39
- package/types/play/keys.d.ts +51 -0
|
@@ -14,50 +14,56 @@ maps to a part of the spec (`xsolana_guide` has every field).
|
|
|
14
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
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
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", "
|
|
18
|
-
| "
|
|
19
|
-
| "
|
|
20
|
-
| "
|
|
21
|
-
| "
|
|
17
|
+
| "loot box", "gacha", "mystery box", "60/25/10/4/1" | `boxes` with `outcomes` of `gives: "item"` and a `chance` each |
|
|
18
|
+
| "stop selling when the rare is gone", "keep selling what's left", "what happens when an item sells out" | a box's `whenOut`: `"stopSales"` (the default: it isn't sold while any outcome can't be given, so the odds stay as written) or `"drawLeft"` (it keeps selling, drawing among what is left, the rest's chances rising: a player can empty a common outcome to make a rare likelier). Tell the person the trade-off |
|
|
19
|
+
| "a booster of five", "a pack of 5 random cards", "three cards in one pack" | a box with `"slots": [{ "count": 5 }]`: each open draws five outcomes by their chances, all from one oracle answer, in one purchase and one wallet prompt. `"distinct": true` on the slot for five different outcomes ("a hand of different cards") |
|
|
20
|
+
| "a guaranteed rare", "one rare or better in every pack", "four commons and a rare" | slots that name outcomes by their place in `outcomes` (from 0): `[{ "count": 4, "share": 60 }, { "count": 1, "from": [8, 9, 10], "share": 40 }]` is four draws among all and one among the rares, the rare slot worth 40% of the price. Slots that name outcomes each need a `share`, 100 together: a draw that can't be made (its slot sold out) gives its slot's worth back, so a guaranteed rare keeps its value when the commons run out; ask what the rare is worth |
|
|
21
|
+
| "10-pull", "open ten at once", "buy 3 packs" | the same box: the page opens several in one purchase (`app.openBox(key, { listing, count: 10 })`), one wallet prompt and one oracle answer, each open at the price the one before it left (`maxPrice` is a pack's). Nothing to add to the spec |
|
|
22
|
+
| "a starter pack: a sword, a shield and some gold", "an item and coins together" | an outcome `{ "gives": "bundle", "prizes": [ … ] }` of any number (items, `pay` from a pool, `tokens` minted, `stash`, `nft`, `roll`). Random cards are slots, not a bundle. 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 |
|
|
23
|
+
| "random traits", "each one is different", "a random color and a power from 1 to 100", "generative" | a fresh item: its `drawn` traits, each from a table (`{ "attribute": "color", "table": "colors" }`, a row by weight among those in stock) or a range (`{ "attribute": "power", "min": 1, "max": 100 }`), and `tables`: `[{ "key": "colors", "kind": "labels", "rows": [{ "label": "red", "weight": 3 }, { "label": "gold", "weight": 1, "stock": 100 }] }]`. `{color}` and `{power}` in its `name` and `uri` take each unit's values, so the project hosts a file for every value (`telling-the-person.md`). Given only by a box's roll (a box of one outcome still rolls); never priced in the shop, granted or crafted |
|
|
24
|
+
| "a deck of thousands of cards", "a card pool with stock", "1 of 50 gold cards" | a fresh item that draws its card from a `numbers` table (`"drawn": [{ "attribute": "card", "table": "deck" }]`; each row gives its own number, from 0: the card's place in the project's own card list) with a `stock` on the scarce rows, and its `uri` naming the card (`https://…/cards/{card}.json`); a box's slots deal several a pack. A table is written once and sealed when it goes live, a batch of rows a transaction; its rent is the owner's and never comes back (about 0.00004 SOL a row of numbers, 0.0002 a row of labels) |
|
|
25
|
+
| "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 for this purchase and those waiting for the oracle (seed it a few prizes deep), and a prize won is set aside for its winner at once |
|
|
26
|
+
| "jackpot", "a jackpot that grows", "a tenth of the jackpot" | a jackpot that grows is a lottery's pot: its tickets fill it, and its places take percents of it. A box or any other prize gives a fixed amount (`gives: "pay"`, `amount`), all of it or none; a share of a pool as a prize is refused |
|
|
27
|
+
| "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 draws one by weight and takes it out of the stash at once. An NFT may carry a `weight` (1 when left out: one that weighs 10 is drawn ten times as often), so one stash can hold its own rarities; or one stash per tier, each an outcome with its chance. A compressed NFT goes in after go-live (`E-STASH`) |
|
|
22
28
|
| "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`, `
|
|
29
|
+
| "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`, `stash`), rolled for the winner. The winner or the project's keys ask the oracle for the roll and settle it; it holds no payment, so it is never refunded and never lost (an edit of the box leaves it to roll on the box as edited). Use it only for chance |
|
|
24
30
|
| "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
|
|
31
|
+
| "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 pulls it after the draw. `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
32
|
| "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
33
|
| "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
34
|
| "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", "
|
|
35
|
+
| "leave early for a 10% fee", "paper hands penalty", "compound" | a farm of tokens: a tier's `exitFee` (percent) with the farm's `exitFeeTo` (`"burn"`, `"pool:<key>"`, or `"rewards"`: its own rewards vault releases it to those who stay, over the farm's longest lock; a farm whose rewards are its staked token); leaving early keeps what the seat was credited. 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`. There is no unbonding wait and no harvest tax: say so |
|
|
30
36
|
| "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
37
|
| "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
38
|
| "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
39
|
| "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
40
|
| "only pass holders can buy it", "members only" | a `gate` of the item alone: `{ "item": "pass" }` |
|
|
35
41
|
| "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>", …] }` (
|
|
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
|
|
42
|
+
| "only these players", "just the two of them", "the tournament's entrants" | a `gate` of wallets: `{ "wallets": ["<address>", …] }` (a long list is a `list` gate instead, below) |
|
|
43
|
+
| "stake to enter", "staking tiers for a token sale", "only people who lock 100 of our token can buy" | a farm of a token that nothing funds when staking pays nothing (no `seed`, no share or schedule into `"farm:<key>"`; `W-FARM-DRY` says so), 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
44
|
| "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
45
|
| "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": "
|
|
46
|
+
| "stake to earn", "x% house edge to stakers", "fees go to holders" | a `split` share `"to": "farm:<key>"` into a `farms` entry whose `rewards` is that token (its own rewards vault, shared by weight); "1,000 a day to stakers" is a schedule `does: "issue"` `"to": "farm:<key>"`, or a `rate` with `"shared": true` (`perDay` the whole farm's, `halveEvery` to halve it) |
|
|
41
47
|
| "lock for longer, earn more", "30-day lock pays 3x" | a farm's `tiers`: `[{ "lock": "1d", "multiplier": 1 }, { "lock": "30d", "multiplier": 3 }]` |
|
|
42
48
|
| "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
|
|
49
|
+
| "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 its rewards vault while it lasts. "Only for 30 days per stake" is a tier's `earnsFor`: `"30d"` |
|
|
44
50
|
| "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
51
|
| "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
52
|
| "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
53
|
| "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
54
|
| "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
55
|
| "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 }]`
|
|
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
|
|
56
|
+
| "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 }]` |
|
|
57
|
+
| "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 its vault (the authority may take back what is free): 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
58
|
| "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 |
|
|
59
|
+
| "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. Each new end time costs whoever writes it 44 bytes of rent, not refunded |
|
|
54
60
|
| "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
61
|
| "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).
|
|
62
|
+
| "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). As many pieces as one transaction carries (`xsolana_check` sizes it). The `elements-set` example |
|
|
57
63
|
| "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
64
|
| "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
65
|
| "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
|
|
66
|
+
| "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. The program can mint one as a box's prize at the settle, but the spec's box outcomes don't take `compressed` yet (`xsolana_check` refuses it): give a box's cards as Core collectibles |
|
|
61
67
|
| "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
68
|
| "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
69
|
| "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 |
|
|
@@ -74,16 +80,18 @@ maps to a part of the spec (`xsolana_guide` has every field).
|
|
|
74
80
|
| "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
81
|
| "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
82
|
| "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;
|
|
78
|
-
| "
|
|
83
|
+
| "stake an NFT on the duel", "winner takes the loser's NFT" | a contest's `stakeNft` (a rule): each entry stakes one NFT; the winners share the losers' NFTs, each picking its share (`losersEach` caps it: 1 for "winner takes one"), and the opener gets what the winners leave. Losers send nothing |
|
|
84
|
+
| "places 2 to 101 each win 1%", "100 runners-up each get a badge" | a lottery place with `times`: `{ "percent": 1, "times": 100 }` is a hundred places alike (not for a named NFT or a roll; at most 64 runs) |
|
|
85
|
+
| "raffle 200 NFTs", "each winner gets a random NFT from the collection" | a lottery that draws once, its `nfts` (put in its own stash at go-live, a group a call) and a place `{ "prize": { "gives": "raffled" }, "times": 200 }`: each winner draws one at random |
|
|
86
|
+
| "bid with NFTs", "the most NFTs wins" | an auction's `bidNfts` (a rule): a bid is Core or Token Metadata NFTs (never compressed), the most wins, `reserve` counts NFTs |
|
|
79
87
|
| "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
88
|
| "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
89
|
| "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
|
|
90
|
+
| "ready after 7 days", "rots in 3 days", "6 uses" | an item's `life`: `readyAfter`, `lasts`, `uses`. Only recipes read `life`: the kit writes it into each recipe that takes the item, which then refuses one not ready, expired or used up; gates, odds and boosts never do |
|
|
83
91
|
| "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
92
|
| "every day mint 5,000 into the pool, halving every 90 days" | a `schedules` entry `does: "issue"`, `every: "1d"`, `halveEvery: 90` |
|
|
85
93
|
| "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>" (
|
|
94
|
+
| "pay the team 100 a week", "a landlord's rent" | a schedule `does: "pay"` from a pool `to` "wallet:<address>" or "holder:<asset>" (the project's keys, or a wallet it pays, run it when due: the page signed by one of them, or their sweep) |
|
|
87
95
|
| "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
96
|
| "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
97
|
| "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 |
|
|
@@ -109,7 +117,7 @@ maps to a part of the spec (`xsolana_guide` has every field).
|
|
|
109
117
|
| "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
118
|
| "Token-2022", "a transfer fee" | `program: "token-2022"` and `powers.transferFeePercent` |
|
|
111
119
|
| "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
|
|
120
|
+
| "mint into our existing collection", "two collections" | `collections`: Core collections the owner's wallet already controls, each with the project added as an update delegate; an item's `collection` names the one it is minted into |
|
|
113
121
|
| "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
122
|
|
|
115
123
|
Ask, in your first reply, for the numbers that define the product if the person hasn't given them: prices, supply, odds
|
|
@@ -17,8 +17,8 @@ and the spec differ (a limit, an amount, a key not set yet), change one of them
|
|
|
17
17
|
the odds players are shown don't hold until then (`W-PRIZE-SEED`).
|
|
18
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
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,
|
|
21
|
-
auction's end, claims), what only they can (a contest's result, a payout, a held item, a collectible's new level)
|
|
20
|
+
5. **Who does what while it runs** (`after-launch.md` has the table): what anyone can send (a draw, an
|
|
21
|
+
auction's end, claims), what only they can (a contest's result, a payout, a held item, a collectible's new level, each run of a schedule that isn't sealed: without them or a server holding a project key, an emission stops)
|
|
22
22
|
and how often, and what happens if nobody does. If a payout rewards a result the game reports (a clear, a win,
|
|
23
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
24
|
browser, and the program trusts the authority's signature."
|
|
@@ -39,6 +39,44 @@ and the spec differ (a limit, an amount, a key not set yet), change one of them
|
|
|
39
39
|
8. **Anything else the checker warned about**, in a sentence each (a mint that can freeze accounts, a free box bots
|
|
40
40
|
can drain, a grant with no limits or no funds).
|
|
41
41
|
|
|
42
|
+
## What stays the project's job
|
|
43
|
+
|
|
44
|
+
Say these whenever the spec has the part they name, before the person goes live. The program holds the rules and the
|
|
45
|
+
money; it holds no files and serves nothing.
|
|
46
|
+
|
|
47
|
+
- **Images and metadata are off the chain.** An item's `uri` points at a file the project hosts; the program keeps no
|
|
48
|
+
images. A fresh item's `uri` with `{attribute}` in it (`https://…/hero/{color}/{power}.json`) becomes a different link
|
|
49
|
+
for every value drawn: the project hosts a file for every value a table or range can give (every row's label, every
|
|
50
|
+
number from `min` to `max`), or a server that renders them on request. A link with nothing behind it is a card
|
|
51
|
+
that shows no picture, for good.
|
|
52
|
+
- **Large tables cost rent the owner pays, never back**: about 8 bytes a row of numbers and 40 a row of labels, at
|
|
53
|
+
5,080 lamports (0.00000508 SOL) a byte: 1,000 labels about 0.2 SOL, 100,000 numbers about 4.1 SOL. Quote
|
|
54
|
+
`xsolana_cost` for the real figure.
|
|
55
|
+
- **A pack's cards each cost a collectible's rent when settled.** Whoever settles pays what the settle mints: Core
|
|
56
|
+
keeps about 0.0031 SOL in each card the player gets. The page lends its throwaway settle key 0.006 SOL for each Core
|
|
57
|
+
card it may mint, in the purchase's one prompt, and the key sends back what it didn't spend. A plain compressed card
|
|
58
|
+
costs none. A pack of fixed cards (nothing random) is bought in one step, its cards minted in the purchase; a pack
|
|
59
|
+
too big for one transaction settles in parts (about 14 Core cards a part), which the page sends.
|
|
60
|
+
- **When an outcome runs out, the project chooses what its box does (`whenOut`), and should hear the trade-off.**
|
|
61
|
+
An outcome runs out when its item sells out, its pool can't cover its whole prize, or its stash empties. With
|
|
62
|
+
`"stopSales"` (the default) the box isn't sold while any outcome couldn't cover this purchase and those still waiting
|
|
63
|
+
for the oracle: every roll is on the odds written, nobody can buy their way to a rare once the commons are gone, and
|
|
64
|
+
the project restocks or edits the box to sell again (a pool holds its prize for every draw waiting; a named NFT is in
|
|
65
|
+
one waiting purchase at a time). With `"drawLeft"` the box keeps selling and draws among what is left, by the written
|
|
66
|
+
weights: the others' chances rise as one runs out, so anyone watching the stock can empty a common outcome and then
|
|
67
|
+
open for the rare at better odds. Put it as: "stop selling keeps your odds honest; keep selling never stalls but lets
|
|
68
|
+
a player tilt the odds". A box selling a rare prize keeps the default; `drawLeft` suits a box of alike prizes, or one
|
|
69
|
+
only other routes roll (a grant's, a mission's), where nothing is sold. `xsolana_check` says what each does to the
|
|
70
|
+
box (W-WHEN-OUT, W-OUT-OF-STOCK, W-PRIZE-SEED, W-STASH) and `xsolana_simulate` the day it stops or each outcome runs out.
|
|
71
|
+
- **A prize the program can't give right now is out of stock, not an error.** A prize in a token with a transfer hook
|
|
72
|
+
(or confidential transfers) is never moved by a roll. An outcome emptied by another route after a purchase (another
|
|
73
|
+
box sharing its pool, item or stash) can't be given: a draw that lands on it is not made, no other outcome takes its
|
|
74
|
+
place, and that draw's worth of the price goes back at the settle. With no draw made a paid open is refunded and a
|
|
75
|
+
free roll waits to be asked again, and a box with nothing in stock is refused (SoldOut).
|
|
76
|
+
- **The oracle's answer reads at most 21 accounts**, so a box's distinct prize accounts (its items, pools, stashes,
|
|
77
|
+
the mints and vaults of its tokens, a fresh item's tables) are bounded by that, however many outcomes it has.
|
|
78
|
+
`xsolana_check` counts them.
|
|
79
|
+
|
|
42
80
|
Don'ts:
|
|
43
81
|
- Don't show mints, account addresses, PDAs, lamports or instruction names unless asked.
|
|
44
82
|
- Don't promise returns, profits or "passive income". Say what the program does, with the numbers.
|
|
@@ -60,8 +98,8 @@ Don'ts:
|
|
|
60
98
|
set (`traits`): say who proves them, and when.
|
|
61
99
|
- **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
100
|
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;
|
|
64
|
-
|
|
101
|
+
loss means (burned, or given to whom). Nobody can back out of a roll once it is made; the player or the project's keys ask the oracle
|
|
102
|
+
for it, and the player, a key they named or the project's keys settle it.
|
|
65
103
|
- **NFT custody** (a farm, a mission): Escrow is the default: the NFT moves to the project while it is staked.
|
|
66
104
|
Freeze keeps it in the player's wallet, locked, with the project the only key that unlocks it; a soulbound
|
|
67
105
|
collectible needs Freeze. Watch holds nothing: say plainly that a staked NFT can be sold, that its seller then
|
|
@@ -69,12 +107,15 @@ Don'ts:
|
|
|
69
107
|
at once: a guild frozen, its missions watched. Either way the NFT issuer's own controls still apply: an issuer who
|
|
70
108
|
can block transfers, or (under Freeze) one whose later metadata or plugin changes stop the unfreeze, can keep an
|
|
71
109
|
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
|
|
73
|
-
weight as it is released
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
110
|
+
- **A farm releases everything that lands in its own rewards vault** to its stakers, over its longest lock (a day at
|
|
111
|
+
least), by weight as it is released. Something must send it rewards: its `seed`, a share `"farm:<key>"` of a sale,
|
|
112
|
+
or a schedule that pays or mints into `"farm:<key>"` (`W-FARM-DRY` when nothing does). Whoever stakes first is
|
|
113
|
+
credited all that is released while they are alone, so say early stakers share the first rewards among few. A farm
|
|
114
|
+
that `runs` takes no stake at its end, and what it has not released by then stays in its vault (the authority may
|
|
115
|
+
take back what is free there), as does what a seat forfeits after it: say to fund it at least its release span
|
|
116
|
+
before the end. What a seat earned by its term (`earnsFor`) or its fuel is exact however late it is next touched, and
|
|
117
|
+
nobody has to act at that time. Such a farm keeps a record of those times: each new one costs whoever writes it 44 bytes
|
|
118
|
+
of rent (about 0.00022 SOL), not refunded on leaving. A sharing farm of a token with a
|
|
78
119
|
great many base units (a memecoin) that pays a reward of few units rounds each seat's share to nothing
|
|
79
120
|
(`W-FARM-ROUNDING`). A farm with
|
|
80
121
|
a fixed `rate` pays the same to each NFT instead; minted, it grows the token's supply every day it runs, which
|
|
@@ -83,7 +124,7 @@ Don'ts:
|
|
|
83
124
|
(`W-FARM-MINT-ROOM`, for a rate that could get there).
|
|
84
125
|
- **Levels written by staking are attributes on the NFT** (or in the project's Sheet of it, for an NFT the project
|
|
85
126
|
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`).
|
|
127
|
+
them. They grow only when a seat is refreshed or the NFT leaves: the page refreshes (`app.refresh`). A boost that reads a level the farm's own growth writes counts each level from when it is written, so a seat refreshed often earns more than one refreshed rarely: have the page or a keeper refresh seats (anyone may, for free).
|
|
87
128
|
- **Grants pay their claims' rent from their own funds** (`fund`, in SOL, held in the grant): the player pays the
|
|
88
129
|
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
130
|
prize also opens the winner's account from the funds:
|
package/types/chain/core.d.ts
CHANGED
|
@@ -33,13 +33,15 @@ export declare function parseCoreAsset(data: any): {
|
|
|
33
33
|
frozen: boolean;
|
|
34
34
|
soulbound: boolean;
|
|
35
35
|
};
|
|
36
|
-
/** Decode the start of a Core CollectionV1: { updateAuthority, name, uri, numMinted, currentSize
|
|
36
|
+
/** Decode the start of a Core CollectionV1: { updateAuthority, name, uri, numMinted, currentSize, bubblegum (whether it
|
|
37
|
+
* carries Bubblegum's plugin, BubblegumV2: compressed NFTs can be minted into it) }. */
|
|
37
38
|
export declare function parseCoreCollection(data: any): {
|
|
38
39
|
updateAuthority: string;
|
|
39
40
|
name: string;
|
|
40
41
|
uri: string;
|
|
41
42
|
numMinted: number;
|
|
42
43
|
currentSize: number;
|
|
44
|
+
bubblegum: boolean;
|
|
43
45
|
};
|
|
44
46
|
/** getProgramAccounts filters for Core assets owned by `owner` in `collection` (no indexer needed). */
|
|
45
47
|
export declare const ownedAssetFilters: (owner: any, collection: any) => {
|
package/types/chain/lookup.d.ts
CHANGED
|
@@ -169,12 +169,14 @@ declare const pause: (ms: any) => Promise<any>;
|
|
|
169
169
|
* first (`planTable`); else the table first, then each compressed NFT's prove_leaf alone, then the rest as v0 (or the
|
|
170
170
|
* proofs then a legacy rest, when that fits with no table). The v0 transaction is compiled against the table as the
|
|
171
171
|
* chain holds it. `sign(message)` → the signed wire transaction. `rpc`: the client's (latestBlockhash, send, confirm,
|
|
172
|
-
* account, call('getSlot')). `
|
|
172
|
+
* account, call('getSlot')). `standing`: tables already made ([{ address, addresses }], a project's, active), tried
|
|
173
|
+
* first: one v0 transaction with them when they hold enough. `table: false` sends no lookup table of its own (a throwaway
|
|
174
|
+
* key holds no rent for one); `first`:
|
|
173
175
|
* addresses a table holds first (`planTable`). Throws a
|
|
174
176
|
* plain sentence when it can't be sent. Returns { signature (the last transaction's), table (its address, or null),
|
|
175
177
|
* steps (the signatures of the transactions before it) }.
|
|
176
178
|
*/
|
|
177
|
-
export declare function sendFitting({ rpc, payer, instructions, sign, table, alone, first, sleep }: {
|
|
179
|
+
export declare function sendFitting({ rpc, payer, instructions, sign, table, alone, first, standing, sleep }: {
|
|
178
180
|
alone?: (() => boolean) | undefined;
|
|
179
181
|
first?: never[] | undefined;
|
|
180
182
|
instructions: any;
|
|
@@ -182,6 +184,7 @@ export declare function sendFitting({ rpc, payer, instructions, sign, table, alo
|
|
|
182
184
|
rpc: any;
|
|
183
185
|
sign: any;
|
|
184
186
|
sleep?: typeof pause | undefined;
|
|
187
|
+
standing?: never[] | undefined;
|
|
185
188
|
table?: boolean | undefined;
|
|
186
189
|
}): Promise<{
|
|
187
190
|
signature: any;
|
package/types/chain/rpc.d.ts
CHANGED
|
@@ -18,7 +18,9 @@ export declare function notMainnet(url: any): any;
|
|
|
18
18
|
* let through). `rpc` has `call`. Returns the genesis hash.
|
|
19
19
|
*/
|
|
20
20
|
export declare function checkCluster(rpc: any): Promise<any>;
|
|
21
|
-
/** The program's own words for a failed run (`AnchorError … Error Message: …`), else the last error log line.
|
|
21
|
+
/** The program's own words for a failed run (`AnchorError … Error Message: …`), else the last error log line. A program
|
|
22
|
+
* built without its errors' text logs the error's name as its message: then the name alone (the client's IDL has
|
|
23
|
+
* the sentence: `loot.lootError`). */
|
|
22
24
|
export declare function programError(err: any, logs: any): any;
|
|
23
25
|
/** A client for one RPC URL. `fetchImpl` and `sleepImpl` are for tests. */
|
|
24
26
|
export declare function rpcClient(url: any, { fetchImpl, sleepImpl }?: {
|