@domino-sdk/relay 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,355 @@
1
+ import {
2
+ createRelayClient
3
+ } from "./chunk-S44XSDW2.js";
4
+ import {
5
+ cosmosNetworks
6
+ } from "./chunk-7QICEKC2.js";
7
+
8
+ // src/base58.ts
9
+ var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
10
+ function base58(bytes) {
11
+ let zeros = 0;
12
+ while (zeros < bytes.length && bytes[zeros] === 0) {
13
+ zeros++;
14
+ }
15
+ const digits = [];
16
+ for (let index = zeros; index < bytes.length; index++) {
17
+ let carry = bytes[index];
18
+ for (let digit = 0; digit < digits.length; digit++) {
19
+ carry += digits[digit] * 256;
20
+ digits[digit] = carry % 58;
21
+ carry = Math.floor(carry / 58);
22
+ }
23
+ while (carry > 0) {
24
+ digits.push(carry % 58);
25
+ carry = Math.floor(carry / 58);
26
+ }
27
+ }
28
+ let result = "1".repeat(zeros);
29
+ for (let digit = digits.length - 1; digit >= 0; digit--) {
30
+ result += alphabet[digits[digit]];
31
+ }
32
+ return result;
33
+ }
34
+
35
+ // src/wallet-browser.ts
36
+ function injected() {
37
+ return typeof window === "undefined" ? void 0 : window;
38
+ }
39
+ function slug(name) {
40
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
41
+ }
42
+ function utf8Hex(message) {
43
+ let hex = "0x";
44
+ for (const byte of new TextEncoder().encode(message)) {
45
+ hex += byte.toString(16).padStart(2, "0");
46
+ }
47
+ return hex;
48
+ }
49
+ var noAccount = "The wallet didn't share an account. Unlock it and try again.";
50
+ function evmSigner(provider, info = {}) {
51
+ return {
52
+ chain: "eip155",
53
+ name: info.name,
54
+ async address() {
55
+ const accounts = await provider.request({
56
+ method: "eth_requestAccounts"
57
+ });
58
+ const account = Array.isArray(accounts) ? accounts[0] : void 0;
59
+ if (typeof account !== "string") {
60
+ throw new Error(noAccount);
61
+ }
62
+ return account;
63
+ },
64
+ async chainId() {
65
+ const id = await provider.request({ method: "eth_chainId" });
66
+ const parsed = typeof id === "string" ? Number.parseInt(id, 16) : NaN;
67
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : 1;
68
+ },
69
+ async sign(message, address) {
70
+ const signature = await provider.request({
71
+ method: "personal_sign",
72
+ params: [utf8Hex(message), address]
73
+ });
74
+ if (typeof signature !== "string") {
75
+ throw new Error("The wallet returned no signature.");
76
+ }
77
+ return { signature };
78
+ }
79
+ };
80
+ }
81
+ function solanaSigner(provider, info = {}) {
82
+ return {
83
+ chain: "solana",
84
+ name: info.name,
85
+ async address() {
86
+ const connected = await provider.connect();
87
+ const key = connected?.publicKey ?? provider.publicKey;
88
+ if (!key) {
89
+ throw new Error(noAccount);
90
+ }
91
+ return key.toBase58();
92
+ },
93
+ async sign(message) {
94
+ const signed = await provider.signMessage(
95
+ new TextEncoder().encode(message),
96
+ "utf8"
97
+ );
98
+ return {
99
+ signature: base58(
100
+ signed instanceof Uint8Array ? signed : signed.signature
101
+ )
102
+ };
103
+ }
104
+ };
105
+ }
106
+ function walletStandardSolanaSigner(wallet) {
107
+ let connected;
108
+ function feature(name) {
109
+ const value = wallet.features[name];
110
+ if (!value) {
111
+ throw new Error(`${wallet.name} doesn't support ${name}.`);
112
+ }
113
+ return value;
114
+ }
115
+ return {
116
+ chain: "solana",
117
+ name: wallet.name,
118
+ async address() {
119
+ const { accounts } = await feature("standard:connect").connect();
120
+ const available = accounts.length ? accounts : wallet.accounts;
121
+ connected = available.find(
122
+ (account) => account.chains.some((chain) => chain.startsWith("solana:"))
123
+ ) ?? available[0];
124
+ if (!connected) {
125
+ throw new Error(noAccount);
126
+ }
127
+ return connected.address;
128
+ },
129
+ async sign(message, address) {
130
+ const account = connected?.address === address ? connected : wallet.accounts.find((candidate) => candidate.address === address);
131
+ if (!account) {
132
+ throw new Error("Connect this wallet before signing.");
133
+ }
134
+ const [signed] = await feature(
135
+ "solana:signMessage"
136
+ ).signMessage({ account, message: new TextEncoder().encode(message) });
137
+ if (!signed) {
138
+ throw new Error("The wallet returned no signature.");
139
+ }
140
+ return { signature: base58(signed.signature) };
141
+ }
142
+ };
143
+ }
144
+ function cosmosSigner(provider, network, info = {}) {
145
+ const { chainId } = cosmosNetworks[network];
146
+ return {
147
+ chain: "cosmos",
148
+ network,
149
+ name: info.name,
150
+ async address() {
151
+ await provider.enable(chainId);
152
+ return (await provider.getKey(chainId)).bech32Address;
153
+ },
154
+ async sign(message, address) {
155
+ const { signature, pub_key } = await provider.signArbitrary(
156
+ chainId,
157
+ address,
158
+ message
159
+ );
160
+ if (pub_key.type !== "tendermint/PubKeySecp256k1") {
161
+ throw new Error(
162
+ "This wallet uses Ethermint keys, which Relay does not support"
163
+ );
164
+ }
165
+ return { signature, publicKey: pub_key.value };
166
+ }
167
+ };
168
+ }
169
+ async function discoverEvmWallets(timeoutMs = 300) {
170
+ const target = injected();
171
+ if (!target) {
172
+ return [];
173
+ }
174
+ const found = /* @__PURE__ */ new Map();
175
+ const announce = (event) => {
176
+ const detail = event.detail;
177
+ const info = detail?.info;
178
+ if (!info?.uuid || !detail?.provider || found.has(info.uuid)) {
179
+ return;
180
+ }
181
+ const name = info.name || "Browser wallet";
182
+ found.set(info.uuid, {
183
+ id: info.rdns || info.uuid,
184
+ name,
185
+ ...info.icon ? { icon: info.icon } : {},
186
+ signer: evmSigner(detail.provider, { name })
187
+ });
188
+ };
189
+ target.addEventListener("eip6963:announceProvider", announce);
190
+ try {
191
+ target.dispatchEvent(new Event("eip6963:requestProvider"));
192
+ await new Promise((resolve) => setTimeout(resolve, timeoutMs));
193
+ } finally {
194
+ target.removeEventListener("eip6963:announceProvider", announce);
195
+ }
196
+ if (!found.size && target.ethereum) {
197
+ return [
198
+ {
199
+ id: "injected",
200
+ name: "Browser wallet",
201
+ signer: evmSigner(target.ethereum, { name: "Browser wallet" })
202
+ }
203
+ ];
204
+ }
205
+ return [...found.values()];
206
+ }
207
+ function isSolanaInjected(value) {
208
+ const provider = value;
209
+ return typeof provider?.connect === "function" && typeof provider.signMessage === "function";
210
+ }
211
+ function discoverSolanaWallets() {
212
+ const target = injected();
213
+ if (!target) {
214
+ return [];
215
+ }
216
+ const standard = [];
217
+ const api = {
218
+ register(...wallets2) {
219
+ for (const wallet of wallets2) {
220
+ if (!standard.includes(wallet)) {
221
+ standard.push(wallet);
222
+ }
223
+ }
224
+ return () => {
225
+ };
226
+ }
227
+ };
228
+ const registered = (event) => {
229
+ const callback = event.detail;
230
+ if (typeof callback === "function") {
231
+ try {
232
+ callback(api);
233
+ } catch {
234
+ }
235
+ }
236
+ };
237
+ target.addEventListener("wallet-standard:register-wallet", registered);
238
+ try {
239
+ target.dispatchEvent(
240
+ new CustomEvent("wallet-standard:app-ready", { detail: api })
241
+ );
242
+ } finally {
243
+ target.removeEventListener("wallet-standard:register-wallet", registered);
244
+ }
245
+ const wallets = [];
246
+ const seen = /* @__PURE__ */ new Set();
247
+ function add(name, signer, icon) {
248
+ if (!wallets.some((wallet) => wallet.name === name)) {
249
+ wallets.push({ id: slug(name), name, ...icon ? { icon } : {}, signer });
250
+ }
251
+ }
252
+ for (const wallet of standard) {
253
+ if (wallet.features["standard:connect"] && wallet.features["solana:signMessage"]) {
254
+ add(wallet.name, walletStandardSolanaSigner(wallet), wallet.icon);
255
+ }
256
+ }
257
+ for (const [name, provider] of [
258
+ ["Phantom", target.phantom?.solana],
259
+ ["Solflare", target.solflare],
260
+ ["Backpack", target.backpack?.solana],
261
+ ["Solana wallet", target.solana]
262
+ ]) {
263
+ if (isSolanaInjected(provider) && !seen.has(provider)) {
264
+ seen.add(provider);
265
+ add(name, solanaSigner(provider, { name }));
266
+ }
267
+ }
268
+ return wallets;
269
+ }
270
+ function discoverCosmosWallets(network) {
271
+ const target = injected();
272
+ if (!target) {
273
+ return [];
274
+ }
275
+ const wallets = [];
276
+ for (const [name, provider] of [
277
+ ["Keplr", target.keplr],
278
+ ["Leap", target.leap],
279
+ ["Cosmostation", target.cosmostation?.providers?.keplr]
280
+ ]) {
281
+ if (typeof provider?.signArbitrary === "function") {
282
+ wallets.push({
283
+ id: `${slug(name)}:${cosmosNetworks[network].chainId}`,
284
+ name,
285
+ signer: cosmosSigner(provider, network, { name })
286
+ });
287
+ }
288
+ }
289
+ return wallets;
290
+ }
291
+
292
+ // src/browser.ts
293
+ function createBrowserRelayClient(options) {
294
+ return createRelayClient({
295
+ ...options,
296
+ submissionStore: options.submissionStore ?? browserSubmissionStore()
297
+ });
298
+ }
299
+ function browserSubmissionStore() {
300
+ let database;
301
+ function open() {
302
+ if (!globalThis.indexedDB || !globalThis.navigator?.locks) {
303
+ throw new Error(
304
+ "This browser cannot safely save submissions. Use a browser with IndexedDB and Web Locks enabled."
305
+ );
306
+ }
307
+ return database ??= new Promise((resolve, reject) => {
308
+ const request = indexedDB.open("domino-relay-submissions", 1);
309
+ request.onupgradeneeded = () => request.result.createObjectStore("submissions");
310
+ request.onerror = () => reject(new Error("Could not open submission storage"));
311
+ request.onsuccess = () => resolve(request.result);
312
+ request.onblocked = () => reject(new Error("Close other Relay tabs and try again"));
313
+ });
314
+ }
315
+ async function transact(key, value) {
316
+ const db = await open();
317
+ return new Promise((resolve, reject) => {
318
+ const transaction = db.transaction(
319
+ "submissions",
320
+ value === void 0 ? "readonly" : "readwrite",
321
+ { durability: "strict" }
322
+ );
323
+ const store = transaction.objectStore("submissions");
324
+ const request = value === void 0 ? store.get(key) : store.put(value, key);
325
+ transaction.oncomplete = () => resolve(request.result);
326
+ transaction.onerror = transaction.onabort = () => reject(
327
+ new Error(
328
+ "Could not save submission progress. Resume the saved submission when browser storage is available."
329
+ )
330
+ );
331
+ });
332
+ }
333
+ return {
334
+ read: (key) => transact(key),
335
+ write: async (key, value) => {
336
+ await transact(key, value);
337
+ },
338
+ exclusive: async (key, task) => {
339
+ await open();
340
+ return navigator.locks.request(key, task);
341
+ }
342
+ };
343
+ }
344
+
345
+ export {
346
+ evmSigner,
347
+ solanaSigner,
348
+ walletStandardSolanaSigner,
349
+ cosmosSigner,
350
+ discoverEvmWallets,
351
+ discoverSolanaWallets,
352
+ discoverCosmosWallets,
353
+ createBrowserRelayClient,
354
+ browserSubmissionStore
355
+ };