@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/dist/solana.js
ADDED
|
@@ -0,0 +1,699 @@
|
|
|
1
|
+
import {
|
|
2
|
+
maxCompletionReceipts,
|
|
3
|
+
walletLinkedEventDataSchema
|
|
4
|
+
} from "./chunk-WNEQEDMU.js";
|
|
5
|
+
import "./chunk-ODJGTFPY.js";
|
|
6
|
+
import "./chunk-DIXYVGXO.js";
|
|
7
|
+
import "./chunk-CZ23KRVK.js";
|
|
8
|
+
import "./chunk-MWMCQLCG.js";
|
|
9
|
+
import "./chunk-ME2KUAVJ.js";
|
|
10
|
+
import "./chunk-PO2ZDY27.js";
|
|
11
|
+
import "./chunk-2A3BJ262.js";
|
|
12
|
+
import {
|
|
13
|
+
paginate,
|
|
14
|
+
solanaDevnet,
|
|
15
|
+
solanaMainnet,
|
|
16
|
+
solanaMemoProgram
|
|
17
|
+
} from "./chunk-CQVKOD3B.js";
|
|
18
|
+
import "./chunk-VNEJH4NT.js";
|
|
19
|
+
|
|
20
|
+
// src/solana.ts
|
|
21
|
+
import {
|
|
22
|
+
address as address3,
|
|
23
|
+
generateKeyPairSigner as generateKeyPairSigner2
|
|
24
|
+
} from "@solana/kit";
|
|
25
|
+
import {
|
|
26
|
+
TOKEN_2022_PROGRAM_ADDRESS,
|
|
27
|
+
extension,
|
|
28
|
+
fetchMint,
|
|
29
|
+
findAssociatedTokenPda,
|
|
30
|
+
getCreateMintInstructionPlan,
|
|
31
|
+
getMintToATAInstructionPlan
|
|
32
|
+
} from "@solana-program/token-2022";
|
|
33
|
+
|
|
34
|
+
// src/solana-badge-transactions.ts
|
|
35
|
+
import {
|
|
36
|
+
appendTransactionMessageInstructions,
|
|
37
|
+
createTransactionMessage,
|
|
38
|
+
flattenInstructionPlan,
|
|
39
|
+
getBase64EncodedWireTransaction,
|
|
40
|
+
getSignatureFromTransaction,
|
|
41
|
+
pipe,
|
|
42
|
+
setTransactionMessageFeePayerSigner,
|
|
43
|
+
setTransactionMessageLifetimeUsingBlockhash,
|
|
44
|
+
signTransactionMessageWithSigners,
|
|
45
|
+
address
|
|
46
|
+
} from "@solana/kit";
|
|
47
|
+
var memoInstruction = (memo) => ({
|
|
48
|
+
programAddress: address(solanaMemoProgram),
|
|
49
|
+
data: new TextEncoder().encode(memo)
|
|
50
|
+
});
|
|
51
|
+
var instructions = (plan) => flattenInstructionPlan(plan).map((step) => {
|
|
52
|
+
if (step.kind !== "single") {
|
|
53
|
+
throw new Error("Expected a plan of single instructions");
|
|
54
|
+
}
|
|
55
|
+
return step.instruction;
|
|
56
|
+
});
|
|
57
|
+
async function sign(rpc, feePayer, steps) {
|
|
58
|
+
const { value: blockhash } = await rpc.getLatestBlockhash({ commitment: "confirmed" }).send();
|
|
59
|
+
const transaction = await signTransactionMessageWithSigners(
|
|
60
|
+
pipe(
|
|
61
|
+
createTransactionMessage({ version: 0 }),
|
|
62
|
+
(message) => setTransactionMessageFeePayerSigner(feePayer, message),
|
|
63
|
+
(message) => setTransactionMessageLifetimeUsingBlockhash(blockhash, message),
|
|
64
|
+
(message) => appendTransactionMessageInstructions(steps, message)
|
|
65
|
+
)
|
|
66
|
+
);
|
|
67
|
+
return {
|
|
68
|
+
wire: getBase64EncodedWireTransaction(transaction),
|
|
69
|
+
signature: getSignatureFromTransaction(transaction),
|
|
70
|
+
lastValidBlockHeight: blockhash.lastValidBlockHeight
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
async function sendAndConfirm(rpc, transaction) {
|
|
74
|
+
try {
|
|
75
|
+
await rpc.sendTransaction(transaction.wire, {
|
|
76
|
+
encoding: "base64",
|
|
77
|
+
preflightCommitment: "confirmed"
|
|
78
|
+
}).send();
|
|
79
|
+
} catch (error) {
|
|
80
|
+
const {
|
|
81
|
+
value: [status]
|
|
82
|
+
} = await rpc.getSignatureStatuses([transaction.signature]).send();
|
|
83
|
+
if (!status) {
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
await confirmed(rpc, transaction.signature, transaction.lastValidBlockHeight);
|
|
88
|
+
return transaction.signature;
|
|
89
|
+
}
|
|
90
|
+
async function confirmed(rpc, signature, lastValidBlockHeight) {
|
|
91
|
+
for (; ; ) {
|
|
92
|
+
const {
|
|
93
|
+
value: [status]
|
|
94
|
+
} = await rpc.getSignatureStatuses([signature]).send();
|
|
95
|
+
if (status?.err) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`Transaction ${signature} failed: ${JSON.stringify(status.err, (_, value) => typeof value === "bigint" ? String(value) : value)}`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (status?.confirmationStatus === "confirmed" || status?.confirmationStatus === "finalized") {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (await rpc.getBlockHeight({ commitment: "confirmed" }).send() > lastValidBlockHeight) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`Transaction ${signature} was not confirmed before its blockhash expired`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/solana-compressed-badges.ts
|
|
113
|
+
import {
|
|
114
|
+
AccountRole,
|
|
115
|
+
addEncoderSizePrefix,
|
|
116
|
+
getAddressDecoder,
|
|
117
|
+
getAddressEncoder,
|
|
118
|
+
getArrayEncoder,
|
|
119
|
+
getBase58Encoder,
|
|
120
|
+
getBooleanEncoder,
|
|
121
|
+
getBytesEncoder,
|
|
122
|
+
fixEncoderSize,
|
|
123
|
+
getNullableEncoder,
|
|
124
|
+
getProgramDerivedAddress,
|
|
125
|
+
getStructEncoder,
|
|
126
|
+
getU16Encoder,
|
|
127
|
+
getU32Encoder,
|
|
128
|
+
getU64Decoder,
|
|
129
|
+
getU64Encoder,
|
|
130
|
+
getU8Encoder,
|
|
131
|
+
getUtf8Encoder,
|
|
132
|
+
address as address2,
|
|
133
|
+
decimalFixedPointToString,
|
|
134
|
+
fetchEncodedAccount,
|
|
135
|
+
generateKeyPairSigner,
|
|
136
|
+
lamports as toLamports,
|
|
137
|
+
lamportsToSol
|
|
138
|
+
} from "@solana/kit";
|
|
139
|
+
var bubblegumProgram = /* @__PURE__ */ (() => address2("BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY"))();
|
|
140
|
+
var coreProgram = /* @__PURE__ */ (() => address2("CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d"))();
|
|
141
|
+
var compressionProgram = /* @__PURE__ */ (() => address2(
|
|
142
|
+
"mcmt6YrQEMKw8Mw43FmpRLmf7BqRnFMKmAcbxE3xkAW"
|
|
143
|
+
))();
|
|
144
|
+
var noopProgram = /* @__PURE__ */ (() => address2("mnoopTCrg4p8ry25e4bcWA9XZjbNjMTfgYVGGEdRsf3"))();
|
|
145
|
+
var systemProgram = /* @__PURE__ */ (() => address2("11111111111111111111111111111111"))();
|
|
146
|
+
var coreCpiSigner = /* @__PURE__ */ (() => address2("CbNY3JiXdXNE9tPNEk1aRZVEkWdj2v7kfJLNQwZZgpXk"))();
|
|
147
|
+
var string = () => addEncoderSizePrefix(getUtf8Encoder(), getU32Encoder());
|
|
148
|
+
var treeVersion2 = 1;
|
|
149
|
+
var coreCollectionKey = 5;
|
|
150
|
+
var discriminator = () => fixEncoderSize(getBytesEncoder(), 8);
|
|
151
|
+
var readonly = (account) => ({
|
|
152
|
+
address: account,
|
|
153
|
+
role: AccountRole.READONLY
|
|
154
|
+
});
|
|
155
|
+
var writable = (account) => ({
|
|
156
|
+
address: account,
|
|
157
|
+
role: AccountRole.WRITABLE
|
|
158
|
+
});
|
|
159
|
+
var signing = (signer, role) => ({
|
|
160
|
+
address: signer.address,
|
|
161
|
+
role,
|
|
162
|
+
signer
|
|
163
|
+
});
|
|
164
|
+
var treeConfigAddress = async (tree) => (await getProgramDerivedAddress({
|
|
165
|
+
programAddress: bubblegumProgram,
|
|
166
|
+
seeds: [getAddressEncoder().encode(tree)]
|
|
167
|
+
}))[0];
|
|
168
|
+
var mintV2Data = /* @__PURE__ */ (() => getStructEncoder([
|
|
169
|
+
["discriminator", discriminator()],
|
|
170
|
+
["name", string()],
|
|
171
|
+
["symbol", string()],
|
|
172
|
+
["uri", string()],
|
|
173
|
+
["sellerFeeBasisPoints", getU16Encoder()],
|
|
174
|
+
["primarySaleHappened", getBooleanEncoder()],
|
|
175
|
+
["isMutable", getBooleanEncoder()],
|
|
176
|
+
["tokenStandard", getNullableEncoder(getU8Encoder())],
|
|
177
|
+
["creators", getArrayEncoder(getU8Encoder())],
|
|
178
|
+
["collection", getNullableEncoder(getAddressEncoder())],
|
|
179
|
+
["assetData", getNullableEncoder(getU8Encoder())],
|
|
180
|
+
["assetDataSchema", getNullableEncoder(getU8Encoder())]
|
|
181
|
+
]))();
|
|
182
|
+
async function mintV2(badge, authority, owner) {
|
|
183
|
+
const tree = address2(badge.tree);
|
|
184
|
+
const collection = address2(badge.collection);
|
|
185
|
+
return {
|
|
186
|
+
programAddress: bubblegumProgram,
|
|
187
|
+
accounts: [
|
|
188
|
+
writable(await treeConfigAddress(tree)),
|
|
189
|
+
signing(authority, AccountRole.WRITABLE_SIGNER),
|
|
190
|
+
signing(authority, AccountRole.READONLY_SIGNER),
|
|
191
|
+
signing(authority, AccountRole.READONLY_SIGNER),
|
|
192
|
+
readonly(owner),
|
|
193
|
+
readonly(owner),
|
|
194
|
+
writable(tree),
|
|
195
|
+
writable(collection),
|
|
196
|
+
readonly(coreCpiSigner),
|
|
197
|
+
readonly(noopProgram),
|
|
198
|
+
readonly(compressionProgram),
|
|
199
|
+
readonly(coreProgram),
|
|
200
|
+
readonly(systemProgram)
|
|
201
|
+
],
|
|
202
|
+
data: mintV2Data.encode({
|
|
203
|
+
discriminator: Uint8Array.of(120, 121, 23, 146, 173, 110, 199, 205),
|
|
204
|
+
name: badge.metadata.name,
|
|
205
|
+
symbol: badge.metadata.symbol ?? "",
|
|
206
|
+
uri: badge.metadata.uri,
|
|
207
|
+
// No royalties: badges are not sold.
|
|
208
|
+
sellerFeeBasisPoints: 0,
|
|
209
|
+
primarySaleHappened: false,
|
|
210
|
+
isMutable: true,
|
|
211
|
+
// TokenStandard::NonFungible, the one Bubblegum accepts.
|
|
212
|
+
tokenStandard: 0,
|
|
213
|
+
creators: [],
|
|
214
|
+
collection,
|
|
215
|
+
assetData: null,
|
|
216
|
+
assetDataSchema: null
|
|
217
|
+
})
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
async function checkCompressedBadge(rpc, badge, authority) {
|
|
221
|
+
const tree = address2(badge.tree);
|
|
222
|
+
const collection = address2(badge.collection);
|
|
223
|
+
const read = (account) => fetchEncodedAccount(rpc, account, { commitment: "confirmed" });
|
|
224
|
+
const [config, core] = await Promise.all([
|
|
225
|
+
read(await treeConfigAddress(tree)),
|
|
226
|
+
read(collection)
|
|
227
|
+
]);
|
|
228
|
+
if (!config.exists || config.programAddress !== bubblegumProgram || config.data.length < 91 || config.data[90] !== treeVersion2) {
|
|
229
|
+
throw new Error(`${tree} is not a Bubblegum v2 tree`);
|
|
230
|
+
}
|
|
231
|
+
if (getAddressDecoder().decode(config.data, 8) !== authority && getAddressDecoder().decode(config.data, 40) !== authority && config.data[88] !== 1) {
|
|
232
|
+
throw new Error(
|
|
233
|
+
`${authority} is neither the creator nor the delegate of ${tree}`
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
const capacity = getU64Decoder().decode(config.data, 72);
|
|
237
|
+
if (getU64Decoder().decode(config.data, 80) >= capacity) {
|
|
238
|
+
throw new Error(`${tree} is full: it holds ${capacity} badges`);
|
|
239
|
+
}
|
|
240
|
+
if (!core.exists || core.programAddress !== coreProgram || core.data[0] !== coreCollectionKey) {
|
|
241
|
+
throw new Error(`${collection} is not a Core collection`);
|
|
242
|
+
}
|
|
243
|
+
if (getAddressDecoder().decode(core.data, 1) !== authority) {
|
|
244
|
+
throw new Error(`${authority} is not the update authority of ${collection}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function compressedBadgeMinter(input) {
|
|
248
|
+
return {
|
|
249
|
+
async instructions(owner) {
|
|
250
|
+
await checkCompressedBadge(
|
|
251
|
+
input.rpc,
|
|
252
|
+
input.badge,
|
|
253
|
+
input.authority.address
|
|
254
|
+
);
|
|
255
|
+
return [await mintV2(input.badge, input.authority, owner)];
|
|
256
|
+
},
|
|
257
|
+
async asset(signature) {
|
|
258
|
+
for (let read = 1; ; read++) {
|
|
259
|
+
const asset = await mintedAsset(input.rpc, signature);
|
|
260
|
+
if (asset !== null || read === 5) {
|
|
261
|
+
return asset;
|
|
262
|
+
}
|
|
263
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
async function mintedAsset(rpc, signature) {
|
|
269
|
+
const transaction = await rpc.getTransaction(signature, {
|
|
270
|
+
commitment: "confirmed",
|
|
271
|
+
encoding: "json",
|
|
272
|
+
maxSupportedTransactionVersion: 0
|
|
273
|
+
}).send();
|
|
274
|
+
if (!transaction) {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
const keys = transaction.transaction.message.accountKeys;
|
|
278
|
+
const outer = transaction.transaction.message.instructions.findIndex(
|
|
279
|
+
(instruction) => keys[instruction.programIdIndex] === bubblegumProgram
|
|
280
|
+
);
|
|
281
|
+
const logged = transaction.meta?.innerInstructions?.find((group) => group.index === outer)?.instructions.map((instruction) => ({
|
|
282
|
+
program: keys[instruction.programIdIndex],
|
|
283
|
+
data: getBase58Encoder().encode(instruction.data)
|
|
284
|
+
})).find(
|
|
285
|
+
({ program, data }) => program === noopProgram && data.length >= 41 && data[0] === 1 && data[1] === 0 && data[6] === 1 && data[7] === 1 && data[8] === 1
|
|
286
|
+
);
|
|
287
|
+
if (!logged) {
|
|
288
|
+
throw new Error(`Transaction ${signature} minted no compressed NFT`);
|
|
289
|
+
}
|
|
290
|
+
return getAddressDecoder().decode(logged.data, 9);
|
|
291
|
+
}
|
|
292
|
+
async function createSolanaBadgeTree(input) {
|
|
293
|
+
const { authority, rpc, maxDepth, maxBufferSize } = input;
|
|
294
|
+
const tree = input.tree ?? await generateKeyPairSigner();
|
|
295
|
+
const collection = input.collectionKey ?? await generateKeyPairSigner();
|
|
296
|
+
const soulbound = !input.collection.transferable;
|
|
297
|
+
const treeBytes = merkleTreeSize(maxDepth, maxBufferSize);
|
|
298
|
+
const collectionBytes = coreCollectionSize(input.collection, soulbound);
|
|
299
|
+
const rents = await Promise.all(
|
|
300
|
+
[treeBytes, treeConfigSize, collectionBytes].map(
|
|
301
|
+
(size) => rpc.getMinimumBalanceForRentExemption(BigInt(size)).send()
|
|
302
|
+
)
|
|
303
|
+
);
|
|
304
|
+
const lamports = toLamports(rents.reduce((sum, rent2) => sum + rent2, 0n));
|
|
305
|
+
const rent = {
|
|
306
|
+
lamports,
|
|
307
|
+
sol: decimalFixedPointToString(lamportsToSol(lamports)),
|
|
308
|
+
capacity: 2 ** maxDepth,
|
|
309
|
+
treeBytes
|
|
310
|
+
};
|
|
311
|
+
const confirm = input.confirmRent ?? ((cost) => {
|
|
312
|
+
console.info(
|
|
313
|
+
`Creating a badge tree for ${cost.capacity} badges and its collection costs ${cost.sol} SOL in rent (${cost.lamports} lamports)`
|
|
314
|
+
);
|
|
315
|
+
return true;
|
|
316
|
+
});
|
|
317
|
+
if (!await confirm(rent)) {
|
|
318
|
+
throw new Error("Badge tree creation was not confirmed; nothing was sent");
|
|
319
|
+
}
|
|
320
|
+
const signature = await sendAndConfirm(
|
|
321
|
+
rpc,
|
|
322
|
+
await sign(rpc, authority, [
|
|
323
|
+
createAccount(authority, tree, rents[0], treeBytes, compressionProgram),
|
|
324
|
+
await createTreeV2(authority, tree.address, maxDepth, maxBufferSize),
|
|
325
|
+
createCollectionV2(authority, collection, input.collection, soulbound)
|
|
326
|
+
])
|
|
327
|
+
);
|
|
328
|
+
return { tree: tree.address, collection: collection.address, signature };
|
|
329
|
+
}
|
|
330
|
+
var merkleTreeSize = (maxDepth, maxBufferSize) => 1 + (1 + 4 + 4 + 32 + 8 + 1 + 5) + 8 * 3 + maxBufferSize * (32 + 32 * maxDepth + 8) + (32 * maxDepth + 32 + 8);
|
|
331
|
+
var treeConfigSize = 8 + 32 + 32 + 8 + 8 + 1 + 1 + 1 + 5;
|
|
332
|
+
var coreCollectionSize = (collection, soulbound) => {
|
|
333
|
+
const utf8 = (text) => new TextEncoder().encode(text).length;
|
|
334
|
+
const base = 1 + 32 + 4 + utf8(collection.name) + 4 + utf8(collection.uri) + 8;
|
|
335
|
+
let plugins = 1;
|
|
336
|
+
let records = 1 + 33 + 8;
|
|
337
|
+
if (soulbound) {
|
|
338
|
+
plugins += 1 + 1;
|
|
339
|
+
records += 1 + 1 + 8;
|
|
340
|
+
}
|
|
341
|
+
return base + (1 + 8) + plugins + (1 + 4 + records + 4);
|
|
342
|
+
};
|
|
343
|
+
function createAccount(payer, account, lamports, space, owner) {
|
|
344
|
+
return {
|
|
345
|
+
programAddress: systemProgram,
|
|
346
|
+
accounts: [
|
|
347
|
+
signing(payer, AccountRole.WRITABLE_SIGNER),
|
|
348
|
+
signing(account, AccountRole.WRITABLE_SIGNER)
|
|
349
|
+
],
|
|
350
|
+
data: getStructEncoder([
|
|
351
|
+
["instruction", getU32Encoder()],
|
|
352
|
+
["lamports", getU64Encoder()],
|
|
353
|
+
["space", getU64Encoder()],
|
|
354
|
+
["owner", getAddressEncoder()]
|
|
355
|
+
]).encode({ instruction: 0, lamports, space, owner })
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
async function createTreeV2(authority, tree, maxDepth, maxBufferSize) {
|
|
359
|
+
return {
|
|
360
|
+
programAddress: bubblegumProgram,
|
|
361
|
+
accounts: [
|
|
362
|
+
writable(await treeConfigAddress(tree)),
|
|
363
|
+
writable(tree),
|
|
364
|
+
signing(authority, AccountRole.WRITABLE_SIGNER),
|
|
365
|
+
signing(authority, AccountRole.READONLY_SIGNER),
|
|
366
|
+
readonly(noopProgram),
|
|
367
|
+
readonly(compressionProgram),
|
|
368
|
+
readonly(systemProgram)
|
|
369
|
+
],
|
|
370
|
+
data: getStructEncoder([
|
|
371
|
+
["discriminator", discriminator()],
|
|
372
|
+
["maxDepth", getU32Encoder()],
|
|
373
|
+
["maxBufferSize", getU32Encoder()],
|
|
374
|
+
["public", getNullableEncoder(getBooleanEncoder())]
|
|
375
|
+
]).encode({
|
|
376
|
+
discriminator: Uint8Array.of(55, 99, 95, 215, 142, 203, 227, 205),
|
|
377
|
+
maxDepth,
|
|
378
|
+
maxBufferSize,
|
|
379
|
+
public: false
|
|
380
|
+
})
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
var corePlugin = { permanentFreezeDelegate: 5, bubblegumV2: 15 };
|
|
384
|
+
var corePluginAuthority = { none: 0 };
|
|
385
|
+
function createCollectionV2(authority, collection, details, soulbound) {
|
|
386
|
+
const plugins = [
|
|
387
|
+
Uint8Array.of(corePlugin.bubblegumV2, 0),
|
|
388
|
+
...soulbound ? [
|
|
389
|
+
Uint8Array.of(
|
|
390
|
+
corePlugin.permanentFreezeDelegate,
|
|
391
|
+
1,
|
|
392
|
+
// frozen
|
|
393
|
+
1,
|
|
394
|
+
// Some(
|
|
395
|
+
corePluginAuthority.none
|
|
396
|
+
)
|
|
397
|
+
] : []
|
|
398
|
+
];
|
|
399
|
+
return {
|
|
400
|
+
programAddress: coreProgram,
|
|
401
|
+
accounts: [
|
|
402
|
+
signing(collection, AccountRole.WRITABLE_SIGNER),
|
|
403
|
+
readonly(authority.address),
|
|
404
|
+
signing(authority, AccountRole.WRITABLE_SIGNER),
|
|
405
|
+
readonly(systemProgram)
|
|
406
|
+
],
|
|
407
|
+
data: getStructEncoder([
|
|
408
|
+
["instruction", getU8Encoder()],
|
|
409
|
+
["name", string()],
|
|
410
|
+
["uri", string()],
|
|
411
|
+
["plugins", getNullableEncoder(getArrayEncoder(getBytesEncoder()))],
|
|
412
|
+
[
|
|
413
|
+
"externalPluginAdapters",
|
|
414
|
+
getNullableEncoder(getArrayEncoder(getU8Encoder()))
|
|
415
|
+
]
|
|
416
|
+
]).encode({
|
|
417
|
+
instruction: 21,
|
|
418
|
+
name: details.name,
|
|
419
|
+
uri: details.uri,
|
|
420
|
+
plugins,
|
|
421
|
+
externalPluginAdapters: []
|
|
422
|
+
})
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// src/solana.ts
|
|
427
|
+
var solanaBadgeReceiptKey = "badge";
|
|
428
|
+
async function awardSolanaBadge(input) {
|
|
429
|
+
const completion = questCompletion(input.event);
|
|
430
|
+
if (!completion || input.quests && !input.quests.includes(completion.quest)) {
|
|
431
|
+
return { status: "ignored" };
|
|
432
|
+
}
|
|
433
|
+
return awardCompletion(input, completion, badgeLookups(input));
|
|
434
|
+
}
|
|
435
|
+
async function awardOwedSolanaBadges(input) {
|
|
436
|
+
const { event, management } = input;
|
|
437
|
+
const linked = event.type === "wallet.linked" ? walletLinkedEventDataSchema.safeParse(event.data) : void 0;
|
|
438
|
+
if (!linked?.success || linked.data.chain !== "solana") {
|
|
439
|
+
return { status: "ignored" };
|
|
440
|
+
}
|
|
441
|
+
const { member } = linked.data;
|
|
442
|
+
const lookups = badgeLookups(input);
|
|
443
|
+
const awards = [];
|
|
444
|
+
for (const quest of input.quests ?? [void 0]) {
|
|
445
|
+
for await (const completion of paginate(
|
|
446
|
+
(cursor) => management.completions.list({ member, quest, cursor, limit: 100 })
|
|
447
|
+
)) {
|
|
448
|
+
if (completion.source?.kind !== "quest") {
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
awards.push({
|
|
452
|
+
completion: completion.id,
|
|
453
|
+
quest: completion.quest,
|
|
454
|
+
award: await awardCompletion(
|
|
455
|
+
input,
|
|
456
|
+
{ id: completion.id, quest: completion.quest, member },
|
|
457
|
+
lookups
|
|
458
|
+
)
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return { status: "awarded", awards };
|
|
463
|
+
}
|
|
464
|
+
function badgeLookups(input) {
|
|
465
|
+
const once = (read) => {
|
|
466
|
+
let value;
|
|
467
|
+
return () => value ??= read();
|
|
468
|
+
};
|
|
469
|
+
const wallets = /* @__PURE__ */ new Map();
|
|
470
|
+
return {
|
|
471
|
+
wallet(member) {
|
|
472
|
+
if (!wallets.has(member)) {
|
|
473
|
+
wallets.set(
|
|
474
|
+
member,
|
|
475
|
+
once(() => provenSolanaWallet(input.management, member))
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
return wallets.get(member)();
|
|
479
|
+
},
|
|
480
|
+
minter: input.badge ? compressedBadgeMinter({ ...input, badge: input.badge }) : tokenBadgeMinter(input, address3(input.mint))
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
function tokenBadgeMinter(input, mint) {
|
|
484
|
+
let decimals;
|
|
485
|
+
return {
|
|
486
|
+
async instructions(owner) {
|
|
487
|
+
decimals ??= badgeDecimals(input.rpc, mint, input.authority.address);
|
|
488
|
+
const [token] = await findAssociatedTokenPda({
|
|
489
|
+
owner,
|
|
490
|
+
mint,
|
|
491
|
+
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
|
|
492
|
+
});
|
|
493
|
+
return instructions(
|
|
494
|
+
getMintToATAInstructionPlan({
|
|
495
|
+
payer: input.authority,
|
|
496
|
+
ata: token,
|
|
497
|
+
owner,
|
|
498
|
+
mint,
|
|
499
|
+
mintAuthority: input.authority,
|
|
500
|
+
amount: 1n,
|
|
501
|
+
decimals: await decimals
|
|
502
|
+
})
|
|
503
|
+
);
|
|
504
|
+
},
|
|
505
|
+
asset: async () => mint
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
async function awardCompletion(input, completion, lookups) {
|
|
509
|
+
const { management, rpc, authority } = input;
|
|
510
|
+
let receipts = await management.completions.receipts(completion.id);
|
|
511
|
+
const existing = receipts.find(
|
|
512
|
+
(receipt) => receipt.key === solanaBadgeReceiptKey
|
|
513
|
+
);
|
|
514
|
+
if (existing) {
|
|
515
|
+
return { status: "already-minted", receipt: existing };
|
|
516
|
+
}
|
|
517
|
+
const record = async (attempt2) => {
|
|
518
|
+
const asset = await lookups.minter.asset(attempt2.signature);
|
|
519
|
+
if (asset === null) {
|
|
520
|
+
throw new SolanaBadgePendingError(attempt2.signature);
|
|
521
|
+
}
|
|
522
|
+
return management.completions.recordReceipt(completion.id, {
|
|
523
|
+
key: solanaBadgeReceiptKey,
|
|
524
|
+
network: attempt2.network,
|
|
525
|
+
signature: attempt2.signature,
|
|
526
|
+
asset,
|
|
527
|
+
...attempt2.recipient === null ? {} : { recipient: attempt2.recipient },
|
|
528
|
+
...input.label === void 0 ? {} : { label: input.label }
|
|
529
|
+
});
|
|
530
|
+
};
|
|
531
|
+
const settled = async () => {
|
|
532
|
+
const attempt2 = latestAttempt(receipts);
|
|
533
|
+
const outcome = attempt2 && await attemptOutcome(rpc, attempt2);
|
|
534
|
+
if (outcome === "landed") {
|
|
535
|
+
return { status: "minted", receipt: await record(attempt2) };
|
|
536
|
+
}
|
|
537
|
+
if (outcome === "pending") {
|
|
538
|
+
throw new SolanaBadgePendingError(attempt2.signature);
|
|
539
|
+
}
|
|
540
|
+
return attempt2 ? attemptNumber(attempt2.key) + 1 : 1;
|
|
541
|
+
};
|
|
542
|
+
const next = await settled();
|
|
543
|
+
if (typeof next !== "number") {
|
|
544
|
+
return next;
|
|
545
|
+
}
|
|
546
|
+
if (receipts.length >= maxCompletionReceipts - 1) {
|
|
547
|
+
throw new Error(
|
|
548
|
+
`Completion ${completion.id} holds ${receipts.length} receipts, leaving no room for another badge attempt and the badge`
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
const wallet = await lookups.wallet(completion.member);
|
|
552
|
+
if (!wallet) {
|
|
553
|
+
return { status: input.whenNoWallet === "defer" ? "deferred" : "no-wallet" };
|
|
554
|
+
}
|
|
555
|
+
const owner = address3(wallet);
|
|
556
|
+
const transaction = await sign(rpc, authority, [
|
|
557
|
+
...await lookups.minter.instructions(owner),
|
|
558
|
+
memoInstruction(solanaBadgeMemo(completion.id))
|
|
559
|
+
]);
|
|
560
|
+
let attempt;
|
|
561
|
+
try {
|
|
562
|
+
attempt = await management.completions.recordReceipt(completion.id, {
|
|
563
|
+
key: `${attemptPrefix}${next}`,
|
|
564
|
+
network: input.network,
|
|
565
|
+
signature: transaction.signature,
|
|
566
|
+
// A compressed badge's asset ID is known once its mint lands.
|
|
567
|
+
...input.mint === void 0 ? {} : { asset: input.mint },
|
|
568
|
+
recipient: owner,
|
|
569
|
+
label: `Badge mint, valid through block height ${transaction.lastValidBlockHeight}`
|
|
570
|
+
});
|
|
571
|
+
} catch (error) {
|
|
572
|
+
if (error.code !== "receipt-conflict") {
|
|
573
|
+
throw error;
|
|
574
|
+
}
|
|
575
|
+
receipts = await management.completions.receipts(completion.id);
|
|
576
|
+
const answer = await settled();
|
|
577
|
+
if (typeof answer !== "number") {
|
|
578
|
+
return answer;
|
|
579
|
+
}
|
|
580
|
+
throw error;
|
|
581
|
+
}
|
|
582
|
+
await sendAndConfirm(rpc, transaction);
|
|
583
|
+
return { status: "minted", receipt: await record(attempt) };
|
|
584
|
+
}
|
|
585
|
+
var SolanaBadgePendingError = class extends Error {
|
|
586
|
+
constructor(signature) {
|
|
587
|
+
super(
|
|
588
|
+
`The badge transaction ${signature} for this completion is still confirming; try again later`
|
|
589
|
+
);
|
|
590
|
+
this.signature = signature;
|
|
591
|
+
this.name = "SolanaBadgePendingError";
|
|
592
|
+
}
|
|
593
|
+
signature;
|
|
594
|
+
code = "badge-pending";
|
|
595
|
+
};
|
|
596
|
+
async function createSolanaBadgeMint(input) {
|
|
597
|
+
const { authority, rpc } = input;
|
|
598
|
+
const mint = input.mint ?? await generateKeyPairSigner2();
|
|
599
|
+
const plan = await getCreateMintInstructionPlan(
|
|
600
|
+
{
|
|
601
|
+
getMinimumBalance: async (space) => rpc.getMinimumBalanceForRentExemption(BigInt(space)).send()
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
payer: authority,
|
|
605
|
+
newMint: mint,
|
|
606
|
+
decimals: 0,
|
|
607
|
+
mintAuthority: authority,
|
|
608
|
+
extensions: [
|
|
609
|
+
extension("NonTransferable", {}),
|
|
610
|
+
extension("MetadataPointer", {
|
|
611
|
+
authority: authority.address,
|
|
612
|
+
metadataAddress: mint.address
|
|
613
|
+
}),
|
|
614
|
+
extension("TokenMetadata", {
|
|
615
|
+
updateAuthority: authority.address,
|
|
616
|
+
mint: mint.address,
|
|
617
|
+
name: input.name,
|
|
618
|
+
symbol: input.symbol,
|
|
619
|
+
uri: input.uri,
|
|
620
|
+
additionalMetadata: /* @__PURE__ */ new Map()
|
|
621
|
+
})
|
|
622
|
+
]
|
|
623
|
+
}
|
|
624
|
+
);
|
|
625
|
+
const signature = await sendAndConfirm(
|
|
626
|
+
rpc,
|
|
627
|
+
await sign(rpc, authority, instructions(plan))
|
|
628
|
+
);
|
|
629
|
+
return { mint: mint.address, signature };
|
|
630
|
+
}
|
|
631
|
+
var solanaBadgeMemo = (completion) => `domino-badge:${completion}`;
|
|
632
|
+
function questCompletion(event) {
|
|
633
|
+
const { data } = event;
|
|
634
|
+
const source = data.source;
|
|
635
|
+
const fromQuest = source === void 0 ? typeof data.attempt === "string" : source?.kind === "quest";
|
|
636
|
+
if (event.type !== "completion.created" || !fromQuest || typeof data.completion !== "string" || typeof data.quest !== "string" || typeof data.member !== "string") {
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
639
|
+
return { id: data.completion, quest: data.quest, member: data.member };
|
|
640
|
+
}
|
|
641
|
+
async function provenSolanaWallet(management, member) {
|
|
642
|
+
let newest;
|
|
643
|
+
for await (const identity of paginate(
|
|
644
|
+
(cursor) => management.members.identityStates.list(member, { cursor, limit: 100 })
|
|
645
|
+
)) {
|
|
646
|
+
if (identity.kind !== "wallet" || identity.chain !== "solana" || !identity.linked || identity.source === "import") {
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
const candidate = {
|
|
650
|
+
address: identity.address,
|
|
651
|
+
linkedAt: identity.linkedAt ?? -1,
|
|
652
|
+
revision: identity.revision
|
|
653
|
+
};
|
|
654
|
+
if (!newest || candidate.linkedAt > newest.linkedAt || candidate.linkedAt === newest.linkedAt && candidate.revision > newest.revision) {
|
|
655
|
+
newest = candidate;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return newest?.address;
|
|
659
|
+
}
|
|
660
|
+
async function badgeDecimals(rpc, mint, authority) {
|
|
661
|
+
const account = await fetchMint(rpc, mint, { commitment: "confirmed" });
|
|
662
|
+
if (account.programAddress !== TOKEN_2022_PROGRAM_ADDRESS) {
|
|
663
|
+
throw new Error(`${mint} is not a Token-2022 mint`);
|
|
664
|
+
}
|
|
665
|
+
const mintAuthority = account.data.mintAuthority;
|
|
666
|
+
if (mintAuthority.__option === "None" || mintAuthority.value !== authority) {
|
|
667
|
+
throw new Error(`${authority} is not the mint authority of ${mint}`);
|
|
668
|
+
}
|
|
669
|
+
return account.data.decimals;
|
|
670
|
+
}
|
|
671
|
+
var attemptPrefix = "badge-attempt-";
|
|
672
|
+
var attemptNumber = (key) => Number(key.slice(attemptPrefix.length));
|
|
673
|
+
function latestAttempt(receipts) {
|
|
674
|
+
return receipts.filter((receipt) => /^badge-attempt-[1-9][0-9]*$/.test(receipt.key)).sort((a, b) => attemptNumber(b.key) - attemptNumber(a.key))[0];
|
|
675
|
+
}
|
|
676
|
+
async function attemptOutcome(rpc, attempt) {
|
|
677
|
+
const lastValid = attempt.label?.match(/block height (\d+)$/)?.[1];
|
|
678
|
+
const height = await rpc.getBlockHeight({ commitment: "finalized" }).send();
|
|
679
|
+
const {
|
|
680
|
+
value: [status]
|
|
681
|
+
} = await rpc.getSignatureStatuses([attempt.signature], {
|
|
682
|
+
searchTransactionHistory: true
|
|
683
|
+
}).send();
|
|
684
|
+
if (status?.confirmationStatus === "confirmed" || status?.confirmationStatus === "finalized") {
|
|
685
|
+
return status.err ? "failed" : "landed";
|
|
686
|
+
}
|
|
687
|
+
return lastValid === void 0 || height > BigInt(lastValid) ? "expired" : "pending";
|
|
688
|
+
}
|
|
689
|
+
export {
|
|
690
|
+
SolanaBadgePendingError,
|
|
691
|
+
awardOwedSolanaBadges,
|
|
692
|
+
awardSolanaBadge,
|
|
693
|
+
createSolanaBadgeMint,
|
|
694
|
+
createSolanaBadgeTree,
|
|
695
|
+
solanaBadgeMemo,
|
|
696
|
+
solanaBadgeReceiptKey,
|
|
697
|
+
solanaDevnet,
|
|
698
|
+
solanaMainnet
|
|
699
|
+
};
|
|
@@ -68,4 +68,5 @@ declare const streakProgressSchema: z.ZodObject<{
|
|
|
68
68
|
}, z.core.$strip>;
|
|
69
69
|
type StreakProgress = z.infer<typeof streakProgressSchema>;
|
|
70
70
|
|
|
71
|
-
export {
|
|
71
|
+
export { quizContentSchema as b, quizQuestionSchema as c, quizQuestionsSchema as d, streakProgressSchema as e, quizChoiceSchema as q, streakPolicySchema as s };
|
|
72
|
+
export type { QuizContent as Q, StreakProgress as S, StreakPolicy as a };
|