@domino-sdk/relay 1.67.0 → 1.69.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/CHANGELOG.md +65 -0
- package/dist/{accounts-C2O8mpB8.d.ts → accounts-DD4gAOCe.d.ts} +232 -3
- package/dist/{action-9Xl0StBg.d.ts → action-BDFKqGlH.d.ts} +3 -2
- package/dist/{attempt-captures-ClHAoo_n.d.ts → attempt-captures-CbuCmthd.d.ts} +11 -2
- package/dist/authoring.d.ts +534 -48
- package/dist/authoring.js +192 -29
- package/dist/{automatic-outcomes-BEnPn4Uh.d.ts → automatic-outcomes-DE3vfi8g.d.ts} +11 -3
- package/dist/avatar.d.ts +2 -1
- package/dist/browser.d.ts +15 -13
- package/dist/browser.js +12 -13
- package/dist/{capability-error-CuI_F7yM.d.ts → capability-error-db7kO8-P.d.ts} +2 -1
- package/dist/{catalog-CbiftcxQ.d.ts → catalog-Cf3fHDNn.d.ts} +69 -3
- package/dist/{chunk-5DZNMLDM.js → chunk-2A3BJ262.js} +2 -2
- package/dist/{chunk-BQXRCJ3N.js → chunk-5RLIPTL7.js} +1 -1
- package/dist/{chunk-MVQQDPQX.js → chunk-AZDZEQFK.js} +14 -2
- package/dist/{chunk-JK3XV2CR.js → chunk-BRROI4CH.js} +50 -8
- package/dist/{chunk-P33ZAJMS.js → chunk-CQVKOD3B.js} +1266 -677
- package/dist/{chunk-QYC3CZVD.js → chunk-CZ23KRVK.js} +2 -2
- package/dist/{chunk-X3FMVRMM.js → chunk-DIXYVGXO.js} +1 -1
- package/dist/{chunk-RDUSFUXX.js → chunk-GEVARKEV.js} +9 -1
- package/dist/{chunk-2HHZV7U3.js → chunk-LDO5GP7G.js} +1 -1
- package/dist/{chunk-Z2Z5DBNW.js → chunk-LGGD7S5T.js} +45 -9
- package/dist/{chunk-U66GUVSG.js → chunk-LZXDYSUH.js} +2 -0
- package/dist/{chunk-NRMQVK2W.js → chunk-ME2KUAVJ.js} +1 -1
- package/dist/{chunk-KUNPCDN6.js → chunk-ODJGTFPY.js} +16 -9
- package/dist/{chunk-DSJ46QLB.js → chunk-PO2ZDY27.js} +1 -1
- package/dist/{chunk-RWR2LOPA.js → chunk-RKTMSTT7.js} +2 -2
- package/dist/{chunk-5SBA6X5X.js → chunk-SWLW4VN3.js} +2 -2
- package/dist/{chunk-BMJHFWVT.js → chunk-TCREFRPK.js} +10 -43
- package/dist/{chunk-Q5QVAF7R.js → chunk-UGM6DUB2.js} +42 -25
- package/dist/chunk-VAGGGB6B.js +87 -0
- package/dist/{chunk-FGXLXENM.js → chunk-VNEJH4NT.js} +47 -3
- package/dist/chunk-WNEQEDMU.js +427 -0
- package/dist/{chunk-Q6KTTVOI.js → chunk-WVK3G2J2.js} +1 -1
- package/dist/{client-CZPewB9h.d.ts → client-6eSYOl8h.d.ts} +159 -11
- package/dist/{dashboards-jGPM3AxF.d.ts → dashboards-DCOAXL54.d.ts} +6 -5
- package/dist/{deployments-BVFd7ddA.d.ts → deployments-BIfdCMd5.d.ts} +911 -309
- package/dist/{host-environment-FN4zg3hw.d.ts → host-environment-DUw5-Cvj.d.ts} +2 -1
- package/dist/{identity-integration-W1Vo3Ql9.d.ts → identity-integration-BlfOn8rc.d.ts} +3 -2
- package/dist/index.d.ts +16 -15
- package/dist/index.js +10 -10
- package/dist/internal.d.ts +155 -17
- package/dist/internal.js +266 -19
- package/dist/{limits-C4RzrOV4.d.ts → limits-Ctaeq23J.d.ts} +8 -2
- package/dist/{offers-BpYenB9i.d.ts → offers-COqHGbHm.d.ts} +4 -3
- package/dist/{participant-activity-DXRq4s6K.d.ts → participant-activity-BznrcePT.d.ts} +2 -1
- package/dist/{participant-rewards-BcWkBRn_.d.ts → participant-rewards-s5Gn23qb.d.ts} +23 -1
- package/dist/portal-proxy.d.ts +7 -4
- package/dist/portal-proxy.js +14 -18
- package/dist/profiles-CLDKZVFx.d.ts +370 -0
- package/dist/react/index.d.ts +71 -30
- package/dist/react/index.js +12 -13
- package/dist/{request-error-D_dUPLwo.d.ts → request-error-C24SY7n9.d.ts} +2 -1
- package/dist/{reward-deliveries-BOWpMytr.d.ts → reward-deliveries-DFhYGJdx.d.ts} +3 -2
- package/dist/runtime/authoring.js +1 -1
- package/dist/runtime/chunks/HKAI5OQA.js +1 -0
- package/dist/runtime/manifest.json +3 -3
- package/dist/runtime/schema.js +1 -1
- package/dist/schema.d.ts +556 -180
- package/dist/schema.js +70 -8
- package/dist/server/index.d.ts +15 -14
- package/dist/server/index.js +2053 -893
- package/dist/{sign-in-providers-BRYVOSlG.d.ts → sign-in-providers-C97mRNbG.d.ts} +4 -3
- package/dist/solana.d.ts +287 -0
- package/dist/solana.js +699 -0
- package/dist/{streaks-0YuipsUQ.d.ts → streaks-MAqKHbHC.d.ts} +2 -1
- package/dist/{submission-input-C152xIdn.d.ts → submission-input-BD7nORuh.d.ts} +4 -3
- package/dist/{telegram-browser-657QqiQl.d.ts → telegram-browser-gRMgGs7B.d.ts} +8 -109
- package/dist/telegram.d.ts +14 -12
- package/dist/telegram.js +2 -2
- package/dist/testing.d.ts +99 -18
- package/dist/testing.js +101 -11
- package/dist/{tracker-data-BqY-9Vu-.d.ts → tracker-data-DQgX3FIc.d.ts} +493 -1
- package/dist/{transport-CthYgZXK.d.ts → transport-BLU8jrhg.d.ts} +7340 -2134
- package/dist/vite.d.ts +2 -1
- package/dist/{wallet-Btng186v.d.ts → wallet-CPAjrW4k.d.ts} +339 -308
- package/dist/wallet-browser-BzPt_Qb5.d.ts +107 -0
- package/package.json +21 -3
- package/dist/chunk-UWUS4OOV.js +0 -59
- package/dist/chunk-UYVJZR33.js +0 -34
- package/dist/runtime/chunks/C2MGLQXC.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,70 @@
|
|
|
1
1
|
# @domino-sdk/relay
|
|
2
2
|
|
|
3
|
+
## 1.69.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ed8461e: **The billing read names an offer Checkout applies by itself.** `management.billing.read()` (and `domino billing show`) answers `offer` while Checkout would apply one to the project: its `id`, the `plan` and `interval` it applies to, and how many months are free (`freeMonths`). It is absent otherwise. The one offer today is `badge-builder`: an organization that built a project from the `badge` starter, started its trial between 2026-11-03 and 2026-11-30 UTC and has had a live quest completion gets 3 months of Launch free, billed monthly, once, with no code to enter. Billing stays experimental.
|
|
8
|
+
- e4891cb: A new starter, `badge`: a game for the Breakpoint 2026 badge (Solana OS). The game is a Lua app in `badge/`, played in the preview on the badge's own firmware, built to WebAssembly from a pinned commit. Rounds played on a paired badge pay Domino points and rank on a leaderboard. `pnpm check` compiles the app with the App Store's compiler, and `pnpm badge:export` packages it for the owner to upload to the App Store themselves. Console offers it, and `domino create --template badge` creates it.
|
|
9
|
+
- 8b6b175: **Check a Reward QR and hand a reward over from the CLI.** `domino collection resolve CODE` prints what a member's collection pass names, changing nothing: the entitlement with its member, reward, tier and revision, the pickup inventory, and `expiresAt`. A malformed code fails before any request. `domino collection begin CODE --expected-revision N`, or `domino handover begin ENTITLEMENT --expected-revision N`, starts the handover (`--item ITEM --pickup-revision N` picks a pickup variant), and `domino handover confirm ENTITLEMENT --handover HANDOVER` collects it. They call `management.collection.resolve` and `begin` and `management.handover.begin` and `confirm`, and need the `handover` permission. Resolve only reads; the other three are saved operations (`--operation-id`) or replay with `--action-id`. In live, a `domino login` session is refused with `console-approval-required`; use an API key with the permission there.
|
|
10
|
+
|
|
11
|
+
**Entitlements name their reward's key.** Management reads and writes that answer an entitlement (`entitlements.list` and `get`, `members.get`, `collection.resolve` and `begin`, the handover methods and entitlement corrections) now carry `reward.key`: the key given to `defineTieredReward`, or `null` for a reward created without one. Apps that show authored names no longer need an `offers.list()` per scan. The `Entitlement` type has it as an optional `key`, since participant reads leave it out.
|
|
12
|
+
|
|
13
|
+
- a0ac75a: **Compressed NFT badges on Solana (experimental).** `@domino-sdk/relay/solana` can mint quest badges as compressed NFTs (Metaplex Bubblegum v2) for large drops: rent is paid once for a Merkle tree, then each badge costs its transaction fee plus Bubblegum's 0.00009 SOL mint fee. `createSolanaBadgeTree({ authority, rpc, maxDepth, maxBufferSize, collection: { name, uri } })` creates the tree and a Core collection in one transaction, after printing the rent it costs (or asking `confirmRent`). The collection is permanently frozen, so its badges can't be transferred or burned, unless you pass `collection.transferable: true`. Pass `badge: { kind: "cnft", tree, collection, metadata: { name, symbol?, uri } }` instead of `mint` to `awardSolanaBadge` or `awardOwedSolanaBadges`: each completion mints one cNFT to the member's proven wallet, once however often the event arrives, and the `badge` receipt's `asset` is the cNFT's asset ID. The RPC needs `getTransaction` too (`SolanaCompressedBadgeRpc`), and no DAS methods. Quests check the badge with `ctx.wallet.nfts()` on the collection. See "Compressed NFT badges for large drops" in the wallets guide.
|
|
14
|
+
- 1784198: **Reward Seeker owners, and any Token-2022 token group.** A Solana collection for `ctx.wallet.nfts()`, `settings.nft()` and `wallet.nfts()` trackers can now be a Token-2022 token group's mint, as well as a Metaplex Core collection or a Token Metadata collection NFT. A mint counts when a linked Solana wallet holds exactly 1 of it, it has no decimals and a supply of 1, and its own token group member extension names the group. Publication accepts a token group's mint. A single member mint is refused with a message naming its group, and a mint that is neither kind of collection is refused with a message naming both. Solana results gain an optional `kind`, an open string: `"core"`, `"token-metadata"` or `"token-group"`. `standard` stays `"metaplex"` for every Solana collection. The new `seeker-holder` snippet accepts members whose linked Solana wallets hold a Seeker Genesis Token, the token every Solana Mobile Seeker comes with. Its collection defaults to the SGT group, `GT22s89nU4iWFkNXj1Bw6uYhJJWDRPpShHt4Bk8f99Te`.
|
|
15
|
+
- 22abcb8: **Reward keeping a position in a Solana program for days.** `wallet.programAccounts({ program, account, seeds, field, chain, idl })` is a tracker source of daily samples of one integer field of the account a Solana program keeps for each member's wallet, such as a staked amount, that Relay writes itself, sampled the way `wallet.balances()` samples balances: once per project-calendar day, at a time members can't predict, every read reaching the run's pinned slot. Each wallet's account is the program-derived address of `seeds`, read and decoded as `ctx.wallet.programAccount({ address })` reads it. Relay stores the IDL it decodes with when the tracker is published: `idl` when given, else the IDL the program publishes, and publication refuses an account type the IDL lacks or a field that is not an integer. Each record is one wallet, keyed by its CAIP-10 account on the cluster; its `data` is `WalletProgramAccountSample` (`amount`, the field as a decimal string or null when the wallet has no account, `pda` and `block`). Reward keeping a position with `automatic({ tracker, where: { path: "amount", gte: ... } })` and `streak({ days })`. In SDK 1.x it is a custom source on the reserved namespace `wallet-program-accounts`, with its settings and stored IDL in the new optional `programAccount` field of custom sources (`walletProgramAccountSourceSchema`); ingestion refuses the namespace, and a project and environment can have at most 10 such trackers besides its other sampled trackers (`limit-reached`).
|
|
16
|
+
|
|
17
|
+
`management.trackers.create()` takes `programAccountTrackerCreateSchema` input (`source: { kind: "wallet-program-accounts", chain, program, account, seeds, field, idl? }`), and `domino trackers create --source wallet-program-accounts` takes `--program`, `--account`, `--seeds`, `--field`, and optionally `--chain` and `--idl <file>`. Creating a tracker as data now reads Solana programs' IDLs as a deployment does, so a `wallet.programActivity()` tracker created as data has its instruction checked against the IDL too.
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- 9cbc12d: **OAuth connect through an identity integration uses only `__Host-relay_auth_return`.** Relay now sets, clears and reads the OAuth return binding under the `__Host-` name for identity-integration requests too, as it already did for hosted apps, so a cookie planted under the bare `relay_auth_return` no longer counts. `createParticipantBackend` forwards only `__Host-relay_auth_return`. Apps on SDK versions before 1.62.0, which forward only the bare name, can no longer complete OAuth connect through an identity integration; upgrade to 1.62.0 or later. An OAuth connect started just before the deploy fails once and starts over.
|
|
22
|
+
- 3165aaa: **A quest that cannot be described fails `domino build`, naming the quest and the field.** `domino build`, `domino deploy` (including `--preview` and `--dry-run`) and `domino check` now describe each quest with its `relay.json` settings, and each quest type with none, as Relay does at deployment. A quest whose definition fails (a schema failure, or an error naming the quest) stops the command before anything is sent; an error that is not about the definition, such as a global Node lacks, is left for Relay: `build` and `deploy` exit with `BUNDLE_FAILED`, and `details.diagnostics` lists a `DEFINITION_INVALID` for each failing quest, with its `file` and `export`. `defineQuest` and `defineQuestType` now throw when `rewards` is missing, with `Quest "core": rewards is missing. Return [] for a quest that gives none.`, instead of building and then failing the deployment with 422 `expected array, received undefined`. Give a quest that grants nothing `rewards: []` or `rewards: () => []`. A schema failure while a quest is described now names the quest and the field path, such as `Quest "core" is invalid: rewards.0: Invalid input`, locally and in the deployment's 422 `validation-failed`. For quests built with an earlier SDK, the 422 names the quest as well.
|
|
23
|
+
- 1747cdd: **The SDK's declaration files are the same in every build of a release.** Two builds of the same source used to print inferred union members and object properties in a different order, and name shared declaration chunks differently. The API they declare is unchanged.
|
|
24
|
+
|
|
25
|
+
## 1.68.0
|
|
26
|
+
|
|
27
|
+
### Minor Changes
|
|
28
|
+
|
|
29
|
+
- 393abb2: **Record what your server did for a completion (experimental).** `management.completions.recordReceipt(completion, { key, network, signature, asset?, recipient?, label? })` records a receipt against a completion: the CAIP-2 network and transaction of something your server did off-platform for it, such as minting a badge. It needs `points` permission and works in live. A completion holds one receipt per `key`. Recording the same values again answers the stored receipt, and different values fail with 409 `receipt-conflict`. A completion holds at most 20 receipts (409 `receipt-limit`). `management.completions.receipts(completion)` and `domino completions receipts COMPLETION` list them. Console shows them on the accepted attempt, with a Solana Explorer link on mainnet and devnet, and marks receipts of a revoked completion. Audit history records each new receipt, and erasing a member deletes their receipts.
|
|
30
|
+
- d677eee: **Badges for members who link a wallet later (experimental).** A new webhook event, `wallet.linked` (`activityTypes.walletLinked`), fires once when a member links a wallet they proved, by signing with it or signing in with it. Its `data` is `{ member, chain, network, address }` (`walletLinkedEventDataSchema`). A wallet a member import or an app login vouched for sends nothing until the member proves it. Proving a wallet that is already linked sends nothing again, and neither does an operator's relink. Subscribe to it in Console under **Settings → Webhooks** (group Members) or with `domino webhooks`. It is not in the member activity feed. `awardSolanaBadge({ ..., whenNoWallet: "defer" })` answers `{ status: "deferred" }` for a member without a proven Solana wallet. The new `awardOwedSolanaBadges({ management, event, ...badge })` from `@domino-sdk/relay/solana` handles `wallet.linked` for a Solana wallet: it mints each badge the member is owed for their counting completions of `quests`, once per completion, so a redelivered event mints nothing new. To find those completions, `management.completions.list({ member, quest?, cursor?, limit? })` and `domino completions list MEMBER [--quest QUEST]` page through a member's unrevoked completions (`read` permission).
|
|
31
|
+
- 1335216: **Check-in stations on a quest (experimental).** A quest a signed Solana transaction completes can list `stations: [{ key, label, opensAt?, closesAt? }]` in `defineQuest`, up to 200. Each key is the `reference` of the station's memo (`solanaActionMemo({ quest, reference })`), and the optional window is in epoch milliseconds. Domino then takes a transaction only when its memo names one of the stations and that station was open at the transaction's block time: otherwise `management.attempts.submitSolanaTransaction()` is refused with 422 `station-unknown` or 409 `station-closed`, and a member's own submission of the quest with `station-unknown`. `perStation()` completes the quest once per member per station. Publishing refuses duplicate keys, more than 200 stations, a window that closes before it opens, and stations on any quest but a member-submitted `manual()` quest without input, or on a reusable quest type. Stations are part of the release, so promotion copies them to live and `management.quests.list()` returns them. `runQuest` refuses an unknown station, or one closed at `now`, as Domino does.
|
|
32
|
+
- 30a5bae: **.sol names.** Domino reads the primary `.sol` name (Solana Name Service, on mainnet) of every linked Solana wallet, proved, sign-in or vouched, and keeps it for a day. It counts only while the wallet owns the domain, directly or by holding its NFT, so a primary domain since transferred shows nothing; an RPC outage keeps the last name read. Operators see it beside the address: `management.members.get(member)` returns an optional `name` on Solana wallets, such as `"toly.sol"`, and Console's member page shows it. The address stays the wallet's identity, and nothing looks a wallet up by its name.
|
|
33
|
+
|
|
34
|
+
Members and projects can use it as a profile name. `relay.profiles.update({ nameSource: "sol" })` follows the member's `.sol` name (422 without one); a `displayName`, or `nameSource: null`, stops following it, and an operator's **Reset name** clears it. `relay.profiles.mine()` lists the name in `nameOptions` and reports `nameSource`. Profile settings take the full order, `"sol"` included, in the new optional `defaultSources` (`profileDefaultSourcesSchema`) beside `defaults`, which still lists accounts only; a change that sends only `defaults`, as earlier SDKs do, keeps `.sol` names at their place. Console's profile settings show them as **.sol names**, and they supply a name only. Settings reads answer both fields, so `ProfileDefaultProvider` and the other closed enums gain no value. Leaderboards show the name under the existing profile rules, and a hidden profile still shows its alias. The new fields are `@experimental`. `domino stage` and `domino doctor --remote` count `.sol` names as a default source.
|
|
35
|
+
|
|
36
|
+
- c071cec: **Soulbound Solana badges from your own server (experimental).** The new `@domino-sdk/relay/solana` entry has `createSolanaBadgeMint()`, which creates a non-transferable Token-2022 mint with on-mint metadata, and `awardSolanaBadge()`, which, given a verified `completion.created` event, mints one badge to the member's most recently linked proven Solana wallet with your mint authority and records the transaction as the completion's `badge` receipt. It mints once per completion, however often the event arrives: it records the signed transaction as a `badge-attempt-<n>` receipt before sending, so a redelivery while the mint is still confirming sends nothing and throws `SolanaBadgePendingError`, and a retry after a failed receipt write records the landed transaction instead of minting again. The entry needs the new optional peer dependencies `@solana/kit` and `@solana-program/token-2022`; other entries load neither, so nothing changes for apps that don't import it, including apps on `@solana/kit` 7. `./solana` itself needs kit 8.5 or later, which `@solana-program/token-2022` requires. The `solana-badge` snippet has the webhook route, the mint script and a quest for badge holders. See "Award on-chain badges on Solana" in the wallets guide.
|
|
37
|
+
- 59e592e: **Solana programs written without Anchor decode too.** A `settings.solanaProgram()` setting now stores and uses a Codama IDL or a Shank IDL as well as an Anchor one, from the Program Metadata Program's canonical `idl` account or pasted in Console, so native, Pinocchio and Steel programs get decoded `instructions` in `ctx.wallet.transactions()`, decoded fields from `ctx.wallet.programAccount()`, and an `instruction` filter in `wallet.programActivity()` trackers. The decoded shape is the same as for Anchor: names as the IDL spells them, 64- and 128-bit integers as decimal strings, enums as `{ Variant: fields }`; a Codama map reads as an object keyed by each key as a string. On-chain IDLs stored with the Program Metadata format "none" are now read as JSON too. A pasted IDL in none of these formats is still refused, and the message now lists the supported formats.
|
|
38
|
+
- 457f4e7: **NFT checks count compressed NFTs on Solana.** `ctx.wallet.nfts()` on a Solana collection now also counts the compressed NFTs (Metaplex Bubblegum) each linked wallet owns whose collection is the setting's and verified: Bubblegum v1 cNFTs in a Token Metadata collection and v2 cNFTs in a Core collection, on mainnet and devnet. They are added to each wallet's `count` and to `total`; the result is unchanged, with `standard` still `"metaplex"`. A compressed NFT collection is set like any other, as `solana:<cluster>/nft:<collection>`. Unverified and burnt cNFTs don't count.
|
|
39
|
+
- f67451b: **NFT checks read Metaplex Core collections on Solana.** A `settings.nft()` collection can now name a Core collection's address, `solana:<cluster>/nft:<collection>`, on mainnet or devnet, and `ctx.wallet.nfts()` counts the Core assets each linked Solana wallet owns in it. Only the collection's update authority or a delegate it approved can add an asset, so every one counts. The result is unchanged: `standard` stays `"metaplex"`, and `at` is the lowest slot read. Setting a Core asset's address is refused with a message naming its collection, as an NFT in a Token Metadata collection already is. Token Metadata collections read as before. Compressed NFTs are still not counted.
|
|
40
|
+
- 7e3cdef: **Quests read the SOL a member delegates to a validator.** `ctx.wallet.stake({ voteAccount })` finds every native stake account a linked Solana wallet controls, as its staker or its withdrawer, and returns the lamports delegated at the current epoch, summed, per wallet and per vote account. `total` is active stake; stake activating or deactivating this epoch is reported apart, so a quest can count new stake at once or wait for the epoch boundary. An account two linked wallets control counts once. Use it for "delegate at least X SOL to our validator": the new `validator-stake` snippet does that. The testing kit answers it from `facts.wallet.stake`. A malformed vote account and an unlinked `address` fail as `author-error`; RPC outages, including a node refusing the read, as `provider-unavailable`, never as zero stake.
|
|
41
|
+
- 19190a4: **Check members in at a quest's stations with Solana Pay QR codes (experimental).** `defineStationAction({ quest, rpcUrl, linkUrl, chain?, label?, icon?, instructions?, waitUntil?, findTimeout?, management?, env? })` from `@domino-sdk/relay/server` serves a quest's check-in stations as Solana Pay transaction requests from your app, the station named by `?station=<key>`. `GET` answers the wallet's `{ label, icon }`; `POST { account }` answers `{ transaction, message }`, a transaction the member pays only the network fee for: your optional instructions, a 0-lamport transfer to the member's own wallet that names a one-time reference account, and the quest's memo with the station's key. A missing, unknown or closed station, and a quest that is closed or has no stations, are refused before anything is built. After answering, the handler looks the reference up through `rpcUrl` for up to 90 seconds and submits the signed transaction to Domino, kept running by `waitUntil` (Next.js `after`) or a Worker's execution context, where Cloudflare's 30-second `waitUntil` limit stops it after 28 seconds. `domino quests stations qr <quest> --url <https URL> [--out dir] [--force]` writes a labelled SVG QR code per station encoding `solana:` and the station's URL; its `--json` result lists the files. The new guide is "In-person quests with Solana Pay".
|
|
42
|
+
- 964965f: **Quests read the state a Solana program keeps for each member's wallet.** `ctx.wallet.programAccount({ program, account, seeds })` derives the program-derived address from `seeds`, with `{ wallet: true }` standing for each linked Solana wallet's address, reads every account at one slot, and decodes its fields with the program's Anchor IDL: the IDL a `settings.solanaProgram()` setting stores, from chain or pasted in Console. A wallet without the account reads `fields: null`. Use it for "staked at least N in our program": the new `solana-program-state` snippet checks JUP staked in Jupiter's locked voter. The testing kit answers it from `facts.wallet.programAccount`. An account owned by another program or holding another account type, an account type the IDL doesn't name, a program without an IDL and seeds that don't fit fail as `author-error`; RPC outages as `provider-unavailable`.
|
|
43
|
+
- 2125539: **Serve a quest as a Solana Action, a blink (experimental).** `defineQuestAction({ quest, build, linkUrl, chain?, metadata?, reference?, management?, env?, confirmTimeout? })` from `@domino-sdk/relay/server` returns a Web-standard `(request, env?) => Promise<Response>` handler for one Action URL, for a Next.js route handler or a Worker. `GET` answers the Action's card from the quest's title, instructions and image, which `metadata` overrides; an archived or paused quest, or one that can't take a transaction, gets a disabled card and its transaction is refused before the member signs. `POST { account }` calls your `build()`, which returns an unsigned `@solana/kit` transaction message (legacy or version 0), kit's compiled transaction or its bytes; the helper appends only the quest's SPL Memo (`solanaActionMemo`) and signs nothing. Throw `QuestActionRefusal` from `build` to answer the member without calling Domino. After the member signs, the blink's callback submits the signature with `management.attempts.submitSolanaTransaction()`, retries a transaction that is not final yet (and `attempt-pending`, `conflict` and `unavailable`) for about 45 seconds, and answers a completed action with the points awarded, the check's reason, or a note to check the app in a minute; an unlinked wallet gets buttons to open `linkUrl` and to check again. Every response carries the Actions CORS headers, `X-Action-Version: 2.4` and `X-Blockchain-Ids`. `actionsJson(rules?)` serves `/actions.json`, and `solanaActionHeaders()` and `decodeSolanaTransaction()` are exported too. The wallets guide and the on-chain reference describe it. `POST /management/v1/attempts/solana-transaction` now counts each Solana read against a budget of 1,500 per project environment and 3,000 across Domino every 10 minutes, since a blink exposes it to anyone: past it, it answers 429 `rate-limited` with `Retry-After` before reading, and a failed read answers 502 `unavailable` instead of 503.
|
|
44
|
+
- 3b36041: **Pay a leaderboard's token pool on Solana (experimental).** For an approved settlement whose pool is in SOL or an SPL Token or Token-2022 token, `management.leaderboards.buildSolanaPayout(settlement, { network, mint, payer })` returns unsigned v0 transfer batches that pay each winner's unpaid share to their most recently linked proven Solana wallet, creating missing token accounts, with the rent and fees the paying wallet spends. The team signs and sends them; Relay holds no key. `confirmSolanaPayout(settlement, { signatures })` reads the finalized transactions and marks each share they paid, and `solanaPayouts(settlement)` lists the payouts. A failed or expired batch leaves its shares payable again, and a share is never planned twice. Refusals: `settlement-not-approved`, `pool-not-token`, `decimals-mismatch`, `insufficient-balance` and `unsupported-mint` (a Token-2022 transfer fee or hook). Settlement reads and the export gain an optional `payment` per row with a non-zero token share: `unpaid`, `pending` or `paid`, with the paying transaction. The CLI adds `domino leaderboard-settlements pay-solana` (prints the batches, or with `--keypair` signs, sends through `--rpc` and confirms them on your machine), `confirm-solana` and `payout-status`.
|
|
45
|
+
- 7691ca9: **Solana smart accounts link by transaction (experimental).** A Squads vault, or any Solana account that cannot sign messages, links by executing a transaction: `relay.wallets.transactionChallenge({ chain: "solana", address })` answers a memo text, open 7 days and bound to the member, and once the account executes an SPL Memo v2 instruction carrying that text with itself as signer, `relay.wallets.linkByTransaction({ chain: "solana", address, signature })` links it after Relay reads the transaction finalized on Solana mainnet. The account links as a Solana wallet that counts toward the five, with `provedBy: "transaction"` and `proofTransaction` on `LinkedWallet`; `members get` and Console show both. New refusal codes: `transaction-not-final` (retry the same signature), `transaction-not-found`, `transaction-failed`, `proof-mismatch` and `challenge-used`, listed in `RelayErrorCode`. Transaction reads are limited to 30 per member and 600 per project environment in 10 minutes.
|
|
46
|
+
- 4be9d1f: **Pay a settlement's token pool from a Squads multisig (experimental).** `management.leaderboards.buildSolanaPayout(settlement, { network, mint, squads: { multisig, proposer, vaultIndex } })` pays from the Squads v4 multisig's vault instead of a wallet: each batch is an unsigned transaction the proposer, a member with the Initiate permission, signs to create a vault transaction holding the batch's transfers and an active proposal for it. The signers approve and execute it in Squads; Relay never approves or executes. Pass one of `payer` and `squads`. A payout gains `source` (`wallet` or `squads`) and `squads` (`multisig`, `vaultIndex`, `proposer`, `accountRent`), and each batch gains `transactionIndex` and `proposal`. While a proposal is open its winners stay pending; `confirmSolanaPayout` marks them paid once the vault transaction executes, and the new batch status `rejected` releases them when the proposal is rejected, cancelled, stale before approval or closed without executing. A proposal that was never created expires with its blockhash. `squadsProposalUrl(payout, batch)` links a batch's proposal. The CLI adds `pay-solana --squads MULTISIG --proposer ADDRESS --vault-index N`; with `--keypair` it signs and sends the proposals in order as the proposer.
|
|
47
|
+
- 9f07317: **Complete a quest from a Solana transaction the member signed (experimental).** An app's server puts `solanaActionMemo({ quest, reference? })` from `@domino-sdk/relay/server` in an SPL Memo instruction, the member signs the transaction in their wallet, and the server sends the signature with `management.attempts.submitSolanaTransaction({ quest, signature, chain? })` under `ingest` permission. Relay reads the transaction at `finalized` and runs a normal attempt for the member whose linked Solana wallet signed it, for a member-submitted `manual()` quest without input. The evaluator and a `keyed()` completion read the transaction as `ctx.action` (`{ kind, chain, signature, wallet, reference }`), null for every other attempt, and the attempt carries it as `action`. A repeated signature returns the first attempt. New refusal codes: `transaction-not-final` (409, with `Retry-After`: send the signature again), `transaction-failed`, `transaction-not-for-quest`, `wallet-not-linked` and `quest-not-transaction-completable`; signers of two members answer the existing `account-ambiguous`. The testing kit's `runQuest()` takes `action: { wallet, reference? }`. The wallets guide and the on-chain and error references describe it.
|
|
48
|
+
- 0e76fee: **Test Solana quests with real transactions.** `domino quests fixtures <signature...> --program <program>` (`--cluster devnet`, `--wallet`, `--idl <file>`, `--out <file>`) has Relay read up to 20 finalized, successful transactions through its Solana RPC and answers them as `ctx.wallet.transactions()` would, the program's instructions decoded with the IDL saving its `settings.solanaProgram()` setting stores (or the `--idl` file), ready to use as a quest test's `facts.wallet.transactions`. It calls the new experimental `management.solana.transactionFixture({ chain, program, signatures, wallet?, idl? })`, `POST /management/v1/solana/transaction-fixtures`, which needs `read` permission, stores nothing, and allows 10 calls a minute per user and 100 an hour per organization. An unknown signature is 404 `transaction-not-found`, one not finalized yet 409 `transaction-not-final`, a failed one 422 `transaction-failed`, and a transaction that does not call the program, a wallet it does not list or a program that is not on the cluster 422 `validation-failed`. `limits.transactionFixtureSignatures` is 20.
|
|
49
|
+
- 1e8f058: **Revoke a check-in station whose code leaked (experimental).** `management.quests.stations.revoke(quest, station, { reason })` and `restore(...)` (`POST /management/v1/quests/<quest>/stations/<station>/revoke` and `/restore`, permission `publish`, as pausing a quest) revoke or restore one station of a quest, recorded in the audit with the reason. Revocation is an operation of the environment, like a pause: it applies in live at once, without a promotion, and a promotion never changes it. From then on `management.attempts.submitSolanaTransaction()` refuses a transaction for the station with 409 `station-revoked`, judged when it is submitted, so one signed before the revocation is refused too; `defineStationAction` answers 409 for it before building a transaction. `management.quests.stations.list(quest)` reads each station of the quest's current release with its `status` (`open`, `closed` or `revoked`), last revocation and accepted completions. Console's quest page lists the stations, downloads each one's Solana Pay QR code as SVG or PNG for an app URL it remembers per quest and environment, and revokes or restores a station. `domino quests stations qr` draws its codes with the same helper as Console, so both print the same code, and `domino pauses list` leaves station revocations out, since `quests pause|resume` don't take them.
|
|
50
|
+
- 541fd71: **Reward holding an NFT for days.** `wallet.nfts({ collection })` is a tracker source of daily NFT holding samples that Relay writes itself, sampled the way `wallet.balances()` samples balances: once per project-calendar day, at a time members can't predict, Relay counts the NFTs of one collection in every wallet of the collection's family linked by a member who joined the project, at one pinned EVM block (on Solana, every read must reach the run's pinned slot). `collection` is that of `ctx.wallet.nfts()`, counted the same way, Metaplex Core collections included, except compressed NFTs: samples don't count a Token Metadata collection's, and a Core collection with the `BubblegumV2` plugin shows `not-configured` on the tracker. Each run first checks the collection as publication checks a `settings.nft()` value, and a wrong one shows on the tracker. Each record is one wallet, keyed by its CAIP-10 account on the collection's chain; its `data` is `WalletNftSample` (`count`, `block`). Reward holding with `automatic({ tracker, where: { path: "count", gte: 1 } })` and `streak({ days })`. In SDK 1.x it is a custom source on the reserved namespace `wallet-nfts`, with the collection in the new optional `nfts` field of custom sources (`walletNftSourceSchema`); ingestion refuses the namespace, and a project and environment can have at most 5 such trackers besides its 5 `wallet.balances()` trackers (`limit-reached`).
|
|
51
|
+
|
|
52
|
+
`management.trackers.create` and Console **New tracker** take `{ kind: "wallet-nfts", collection }` (`nftTrackerCreateSchema`, beside `managedTrackerCreateSchema`, so `ManagedTrackerSource` stays as it was), and `domino trackers create --source wallet-nfts` takes `--collection`. `management.trackers.update` changes such a tracker's title and window, never its collection. The new `nft-hold-streak` snippet rewards holding one of a collection's NFTs for 30 days in a row.
|
|
53
|
+
|
|
54
|
+
- ebbfa83: **Leaderboards and streaks on how members use your Solana program, with no indexer.** `wallet.programActivity({ program, chain?, instruction?, mint?, minAmount?, signer? })` is a tracker source Relay writes itself: every 10 minutes it reads the finalized, successful transactions of each Solana wallet linked by a member who joined the project, and records each one that calls `program` as a record keyed by `<signature>:<address>`, authored by the wallet's CAIP-10 account, with the block time as `occurredAt` and `WalletProgramActivity` as `data` (`signature`, `slot`, `timestamp`, `signer`, `instruction`, `amount`, `delta`). Only transactions the wallet signed count unless `signer: "any"`, and never before the wallet was linked. `instruction` narrows to one instruction of the program's Anchor IDL (publication refuses a name the IDL lacks), and `mint` with `minAmount` to transactions that move at least that much of a token. Mainnet and devnet. Triggers on it fire on `"new-record"` by default. In SDK 1.x it is a custom source on the reserved namespace `wallet-program-activity`, with its settings in the new optional `activity` field of custom sources (`walletProgramActivitySourceSchema`); ingestion refuses the namespace, and a project and environment can have at most 20 such trackers (`limit-reached`).
|
|
55
|
+
|
|
56
|
+
`management.trackers.create` takes `{ kind: "wallet-program-activity", chain, program, instruction, mint, minAmount, anySigner }`, and `domino trackers create --source wallet-program-activity` takes `--program`, `--chain`, `--instruction`, `--mint`, `--min-amount` and `--any-signer`. The new `program-activity` snippet adds a weekly "top users of our program" leaderboard and a 5-day streak quest.
|
|
57
|
+
|
|
58
|
+
- 29b5d46: **Reward keeping SOL delegated for days.** `wallet.stake({ chain, voteAccount })` is a tracker source of daily Solana native stake samples that Relay writes itself, sampled the way `wallet.balances()` samples balances: once per project-calendar day, at a time members can't predict, Relay reads how much SOL every Solana wallet linked by a member who joined the project delegates, every read reaching the run's pinned slot. `chain` is `"solana"` (mainnet, the default) or Solana devnet, and `voteAccount` a validator's vote account, left out to count every validator. Each wallet counts as `ctx.wallet.stake({ address })` reads that one wallet: the stake accounts it controls as staker or withdrawer. Each record is one wallet, keyed by its CAIP-10 account on the cluster; its `data` is `WalletStakeSample` (`amount` for active stake, `activating`, `deactivating`, in lamports, and `block`). Reward keeping stake with `automatic({ tracker, where: { path: "amount", gte: ... } })` and `streak({ days })`. In SDK 1.x it is a custom source on the reserved namespace `wallet-stake`, with the cluster and vote account in the new optional `stake` field of custom sources (`walletStakeSourceSchema`); ingestion refuses the namespace, and a project and environment can have at most 5 such trackers besides its other sampled trackers (`limit-reached`).
|
|
59
|
+
|
|
60
|
+
`management.trackers.create` and Console **New tracker** take `{ kind: "wallet-stake", chain, voteAccount }` (`stakeTrackerCreateSchema`, beside `managedTrackerCreateSchema`, so `ManagedTrackerSource` stays as it was), and `domino trackers create --source wallet-stake` takes `--vote-account` and `--chain`. `management.trackers.update` changes such a tracker's title and window, never its cluster or vote account. The new `validator-stake-streak` snippet rewards keeping 10 SOL delegated to your validator for 30 days in a row and ranks this month's biggest stakers by SOL delegated.
|
|
61
|
+
|
|
62
|
+
### Patch Changes
|
|
63
|
+
|
|
64
|
+
- 3a7f323: **The campaign starter has a default look worth showing.** Its greyscale theme becomes an indigo accent on cool greys in light and dark, a 1.25 type scale, two radii and two shadows, all as role tokens in `src/styles.css` (`surface`, `accent`, `accent-foreground`, `accent-ink`, `success`, `warning`, `danger`, `rounded-card`, `rounded-control`, `shadow-card`, `shadow-raised`), so a rebrand still means editing tokens rather than components. The quest card and quest page states shared by the campaign and loyalty starters are finished: a photo picker with a preview, an "In review" panel that reads as pending, a rejection that shows the reason and how to retry, a completed panel with the points earned, a short celebration that respects `prefers-reduced-motion` and the next quest, plus a loading skeleton and designed empty and error states. Buttons, links and inputs meet 44 px tap targets with a visible focus ring, and every text pair meets WCAG AA contrast in both themes. The loyalty starter keeps its neutral look and gains the same role tokens. The design reference now treats the starter's default token values, not only greyscale, as an unfinished design. The campaign starter's summary in `starters` (what the starter pickers in Console and the CLI show) no longer calls it neutral; the deprecated, frozen `starterTemplates` keeps its old copy.
|
|
65
|
+
- f36e965: `discoverSolanaWallets` reads Wallet Standard wallets through a shared helper, which Console uses to pay settlements on Solana from the browser wallet. Discovery behaves as before.
|
|
66
|
+
- 5609fc3: **`defineQuestAction`'s `build` takes the messages `@solana/kit` program clients make (experimental).** An instruction's `data`, a compiled transaction's `messageBytes` and signatures, and the wire bytes `build` can return (such as `getTransactionEncoder().encode()`'s) now accept kit's read-only byte arrays, so a message built with an instruction from a client such as `@solana-program/memo`'s `getAddMemoInstruction()` type-checks as `build`'s return value. Behavior is unchanged.
|
|
67
|
+
|
|
3
68
|
## 1.67.0
|
|
4
69
|
|
|
5
70
|
### Minor Changes
|