@zucchinifi/dapp-sdk 0.1.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/dist/index.mjs ADDED
@@ -0,0 +1,727 @@
1
+ // src/utils.ts
2
+ function toZats(amount) {
3
+ if (typeof amount === "number") {
4
+ amount = amount.toString();
5
+ }
6
+ amount = amount.replace(/,/g, "");
7
+ const parts = amount.split(".");
8
+ let whole = parts[0];
9
+ let fraction = parts[1] || "";
10
+ if (fraction.length > 8) {
11
+ throw new Error("Amount has more than 8 decimal places");
12
+ }
13
+ while (fraction.length < 8) {
14
+ fraction += "0";
15
+ }
16
+ const total = whole + fraction;
17
+ return BigInt(total);
18
+ }
19
+ function fromZats(amount) {
20
+ const zats = BigInt(amount).toString();
21
+ const padded = zats.padStart(9, "0");
22
+ const whole = padded.slice(0, -8);
23
+ const fraction = padded.slice(-8);
24
+ const cleanFraction = fraction.replace(/0+$/, "");
25
+ if (cleanFraction === "") {
26
+ return whole;
27
+ }
28
+ return `${whole}.${cleanFraction}`;
29
+ }
30
+ function shortenAddress(address, chars = 4) {
31
+ if (!address) return "";
32
+ if (address.length <= chars * 2 + 3) return address;
33
+ return `${address.slice(0, chars + 3)}...${address.slice(-chars)}`;
34
+ }
35
+
36
+ // src/zip321.ts
37
+ import { bech32 as bech322, bech32m as bech32m2 } from "bech32";
38
+ import bs58check2 from "bs58check";
39
+
40
+ // src/unified_address.ts
41
+ import { bech32m, bech32 } from "bech32";
42
+ import bs58check from "bs58check";
43
+
44
+ // src/f4jumble.ts
45
+ import { blake2bInit, blake2bUpdate, blake2bFinal } from "blakejs";
46
+ function get_h_pers(i) {
47
+ const p = new Uint8Array(16);
48
+ p.set([85, 65, 95, 70, 52, 74, 117, 109, 98, 108, 101, 95, 72, i, 0, 0]);
49
+ return p;
50
+ }
51
+ function get_g_pers(i, j) {
52
+ const p = new Uint8Array(16);
53
+ p.set([85, 65, 95, 70, 52, 74, 117, 109, 98, 108, 101, 95, 71, i, j & 255, j >> 8 & 255]);
54
+ return p;
55
+ }
56
+ function xor(target, source) {
57
+ const len = Math.min(target.length, source.length);
58
+ for (let i = 0; i < len; i++) {
59
+ target[i] ^= source[i];
60
+ }
61
+ }
62
+ var State = class {
63
+ constructor(message) {
64
+ const leftLen = Math.min(64, Math.floor(message.length / 2));
65
+ this.left = message.subarray(0, leftLen);
66
+ this.right = message.subarray(leftLen);
67
+ }
68
+ h_round(i) {
69
+ const ctx = blake2bInit(this.left.length, null, null, get_h_pers(i));
70
+ blake2bUpdate(ctx, this.right);
71
+ const hash = blake2bFinal(ctx);
72
+ xor(this.left, hash);
73
+ }
74
+ g_round(i) {
75
+ const outBytes = 64;
76
+ const numChunks = Math.ceil(this.right.length / outBytes);
77
+ for (let j = 0; j < numChunks; j++) {
78
+ const ctx = blake2bInit(outBytes, null, null, get_g_pers(i, j));
79
+ blake2bUpdate(ctx, this.left);
80
+ const hash = blake2bFinal(ctx);
81
+ const offset = j * outBytes;
82
+ const targetChunk = this.right.subarray(offset, offset + outBytes);
83
+ xor(targetChunk, hash);
84
+ }
85
+ }
86
+ apply_f4jumble() {
87
+ this.g_round(0);
88
+ this.h_round(0);
89
+ this.g_round(1);
90
+ this.h_round(1);
91
+ }
92
+ apply_f4jumble_inv() {
93
+ this.h_round(1);
94
+ this.g_round(1);
95
+ this.h_round(0);
96
+ this.g_round(0);
97
+ }
98
+ };
99
+ function f4jumble(message) {
100
+ const msgCopy = new Uint8Array(message);
101
+ const state = new State(msgCopy);
102
+ state.apply_f4jumble();
103
+ return msgCopy;
104
+ }
105
+ function f4jumble_inv(message) {
106
+ const msgCopy = new Uint8Array(message);
107
+ const state = new State(msgCopy);
108
+ state.apply_f4jumble_inv();
109
+ return msgCopy;
110
+ }
111
+
112
+ // src/unified_address.ts
113
+ var TYPE_P2PKH = 0;
114
+ var TYPE_P2SH = 1;
115
+ var TYPE_SAPLING = 2;
116
+ var TYPE_ORCHARD = 3;
117
+ function parseUnifiedAddress(unifiedAddress) {
118
+ if (!unifiedAddress.startsWith("u")) {
119
+ throw new Error("Not a Unified Address (must start with 'u')");
120
+ }
121
+ const { prefix: hrp, words: decodedWords } = bech32m.decode(unifiedAddress, 1e3);
122
+ if (hrp !== "u" && hrp !== "utest") {
123
+ throw new Error(`Invalid prefix: ${hrp} `);
124
+ }
125
+ const unjumbled = f4jumble_inv(new Uint8Array(bech32m.fromWords(decodedWords)));
126
+ if (!unjumbled) throw new Error("F4Jumble failed");
127
+ const padding = new Uint8Array(16);
128
+ const hrpBytes = new TextEncoder().encode(hrp);
129
+ padding.set(hrpBytes);
130
+ const suffix = unjumbled.slice(-16);
131
+ let isValidPadding = true;
132
+ for (let i = 0; i < 16; i++) {
133
+ if (suffix[i] !== padding[i]) {
134
+ isValidPadding = false;
135
+ break;
136
+ }
137
+ }
138
+ if (!isValidPadding) {
139
+ throw new Error("Invalid padding");
140
+ }
141
+ const data = unjumbled.slice(0, -16);
142
+ const receivers = [];
143
+ let offset = 0;
144
+ let lastTypeCode = -1;
145
+ while (offset < data.length) {
146
+ const { value: typeCode, bytes: typeCodeLength } = readCompactSize(data, offset);
147
+ offset += typeCodeLength;
148
+ const { value: length, bytes: lengthLength } = readCompactSize(data, offset);
149
+ offset += lengthLength;
150
+ if (offset + length > data.length) {
151
+ throw new Error("Invalid receiver length");
152
+ }
153
+ const receiverData = data.slice(offset, offset + length);
154
+ offset += length;
155
+ if (typeCode < lastTypeCode) {
156
+ throw new Error("Receivers not sorted by type code");
157
+ }
158
+ lastTypeCode = typeCode;
159
+ switch (typeCode) {
160
+ case TYPE_P2PKH:
161
+ if (length !== 20) throw new Error("Invalid P2PKH length");
162
+ const p2pkhPrefix = hrp === "u" ? [28, 184] : [29, 37];
163
+ receivers.push({ type: "p2pkh", typeCode, data: receiverData, address: bs58check.encode(Buffer.concat([Buffer.from(p2pkhPrefix), Buffer.from(receiverData)])) });
164
+ break;
165
+ case TYPE_P2SH:
166
+ if (length !== 20) throw new Error("Invalid P2SH length");
167
+ const p2shPrefix = hrp === "u" ? [28, 189] : [28, 186];
168
+ receivers.push({ type: "p2sh", typeCode, data: receiverData, address: bs58check.encode(Buffer.concat([Buffer.from(p2shPrefix), Buffer.from(receiverData)])) });
169
+ break;
170
+ case TYPE_SAPLING:
171
+ if (length !== 43) throw new Error("Invalid Sapling length");
172
+ const saplingHrp = hrp === "u" ? "zs" : "ztestsapling";
173
+ receivers.push({ type: "sapling", typeCode, data: receiverData, address: bech32.encode(saplingHrp, bech32.toWords(receiverData), 1e3) });
174
+ break;
175
+ case TYPE_ORCHARD:
176
+ if (length !== 43) throw new Error("Invalid Orchard length");
177
+ const tlv = new Uint8Array(1 + 1 + receiverData.length);
178
+ tlv[0] = 3;
179
+ tlv[1] = receiverData.length;
180
+ tlv.set(receiverData, 2);
181
+ const payloadWithPadding = new Uint8Array(tlv.length + 16);
182
+ payloadWithPadding.set(tlv);
183
+ payloadWithPadding.set(padding, tlv.length);
184
+ const jumbledOrchard = f4jumble(payloadWithPadding);
185
+ const orchardWords = bech32m.toWords(jumbledOrchard);
186
+ const orchardAddr = bech32m.encode(hrp, orchardWords, 1e3);
187
+ receivers.push({ type: "orchard", typeCode, data: receiverData, address: orchardAddr });
188
+ break;
189
+ default:
190
+ receivers.push({ type: "unknown", typeCode, data: receiverData, address: "" });
191
+ }
192
+ }
193
+ const result = {
194
+ unknown: []
195
+ };
196
+ for (const receiver of receivers) {
197
+ switch (receiver.type) {
198
+ case "p2pkh":
199
+ case "p2sh":
200
+ result.transparent = receiver.address;
201
+ break;
202
+ case "sapling":
203
+ result.sapling = receiver.address;
204
+ break;
205
+ case "orchard":
206
+ result.orchard = receiver.address;
207
+ break;
208
+ case "unknown":
209
+ result.unknown?.push({ typeCode: receiver.typeCode, data: receiver.data });
210
+ break;
211
+ }
212
+ }
213
+ return result;
214
+ }
215
+ function readCompactSize(data, offset) {
216
+ const first = data[offset];
217
+ if (first < 253) {
218
+ return { value: first, bytes: 1 };
219
+ } else if (first === 253) {
220
+ const val = data[offset + 1] | data[offset + 2] << 8;
221
+ return { value: val, bytes: 3 };
222
+ } else if (first === 254) {
223
+ const val = data[offset + 1] | data[offset + 2] << 8 | data[offset + 3] << 16 | data[offset + 4] << 24;
224
+ return { value: val, bytes: 5 };
225
+ } else {
226
+ throw new Error("CompactSize 8 bytes not supported");
227
+ }
228
+ }
229
+
230
+ // src/zip321.ts
231
+ var Zip321ParsingError = class extends Error {
232
+ constructor(message) {
233
+ super(message);
234
+ this.name = "Zip321ParsingError";
235
+ }
236
+ };
237
+ var Zip321Parser = class {
238
+ /**
239
+ * Parses a zcash: URI into a list of Payment objects.
240
+ * @param uri The zcash URI string.
241
+ * @param options Parsing options.
242
+ * @returns An array of Payment objects.
243
+ * @throws Zip321ParsingError if the URI is invalid.
244
+ */
245
+ static parse(uri, options = {}) {
246
+ if (!uri.startsWith("zcash:")) {
247
+ throw new Zip321ParsingError("Invalid scheme: must start with zcash:");
248
+ }
249
+ const parts = uri.substring(6).split("?");
250
+ const path = parts[0];
251
+ const query = parts[1];
252
+ if (parts.length > 2) {
253
+ throw new Zip321ParsingError("Invalid URI: multiple question marks");
254
+ }
255
+ const params = new URLSearchParams(query || "");
256
+ const payments = /* @__PURE__ */ new Map();
257
+ const getPayment = (index) => {
258
+ if (!payments.has(index)) {
259
+ payments.set(index, { address: "" });
260
+ }
261
+ return payments.get(index);
262
+ };
263
+ if (path && path.length > 0) {
264
+ if (!this.isValidAddress(path)) {
265
+ throw new Zip321ParsingError(`Invalid address in path: ${path}`);
266
+ }
267
+ const p = getPayment(0);
268
+ p.address = path;
269
+ }
270
+ params.forEach((value, key) => {
271
+ const match = key.match(/^([a-zA-Z][a-zA-Z0-9+-]*)(\.(\d+))?$/);
272
+ if (!match) {
273
+ if (key.startsWith("req-")) {
274
+ throw new Zip321ParsingError(`Unknown required parameter: ${key}`);
275
+ }
276
+ return;
277
+ }
278
+ const paramName = match[1];
279
+ const indexStr = match[3];
280
+ let index = 0;
281
+ if (indexStr) {
282
+ if (indexStr.startsWith("0")) {
283
+ throw new Zip321ParsingError(`Invalid index (leading zero): ${indexStr}`);
284
+ }
285
+ index = parseInt(indexStr, 10);
286
+ if (index > 9999) {
287
+ throw new Zip321ParsingError(`Index out of range: ${indexStr}`);
288
+ }
289
+ } else {
290
+ index = 0;
291
+ }
292
+ if (index === 0 && paramName === "address" && path.length > 0) {
293
+ throw new Zip321ParsingError("Duplicate address: defined in path and query");
294
+ }
295
+ const payment = getPayment(index);
296
+ switch (paramName) {
297
+ case "address":
298
+ if (payment.address) throw new Zip321ParsingError(`Duplicate address for index ${index}`);
299
+ if (!this.isValidAddress(value)) throw new Zip321ParsingError(`Invalid address: ${value}`);
300
+ payment.address = value;
301
+ break;
302
+ case "amount":
303
+ if (payment.amount !== void 0) throw new Zip321ParsingError(`Duplicate amount for index ${index}`);
304
+ if (payment.assetId) throw new Zip321ParsingError(`Amount and req-asset cannot both be present for index ${index}`);
305
+ payment.amount = this.parseAmount(value);
306
+ break;
307
+ case "req-asset":
308
+ if (payment.assetId) throw new Zip321ParsingError(`Duplicate req-asset for index ${index}`);
309
+ if (payment.amount !== void 0) throw new Zip321ParsingError(`Amount and req-asset cannot both be present for index ${index}`);
310
+ const assetData = this.parseAsset(value);
311
+ payment.assetId = assetData.assetId;
312
+ payment.amount = assetData.amount;
313
+ break;
314
+ case "memo":
315
+ if (payment.memo) throw new Zip321ParsingError(`Duplicate memo for index ${index}`);
316
+ if (!this.isValidBase64Url(value)) throw new Zip321ParsingError(`Invalid Base64URL memo: ${value}`);
317
+ payment.memo = value;
318
+ break;
319
+ case "label":
320
+ if (payment.label) throw new Zip321ParsingError(`Duplicate label for index ${index}`);
321
+ payment.label = value;
322
+ break;
323
+ case "message":
324
+ if (payment.message) throw new Zip321ParsingError(`Duplicate message for index ${index}`);
325
+ payment.message = value;
326
+ break;
327
+ default:
328
+ if (paramName.startsWith("req-")) {
329
+ throw new Zip321ParsingError(`Unknown required parameter: ${paramName}`);
330
+ }
331
+ if (!payment.otherParams) payment.otherParams = {};
332
+ payment.otherParams[paramName] = value;
333
+ break;
334
+ }
335
+ });
336
+ const result = [];
337
+ const sortedIndices = Array.from(payments.keys()).sort((a, b) => a - b);
338
+ for (const idx of sortedIndices) {
339
+ const p = payments.get(idx);
340
+ if (!p.address) {
341
+ throw new Zip321ParsingError(`Missing address for index ${idx}`);
342
+ }
343
+ if (this.isTransparent(p.address) && p.memo) {
344
+ throw new Zip321ParsingError(`Memo not allowed for transparent address at index ${idx}`);
345
+ }
346
+ if ((this.isTransparent(p.address) || this.isSapling(p.address)) && p.assetId) {
347
+ throw new Zip321ParsingError(`Custom assets not allowed for transparent/Sapling address at index ${idx}`);
348
+ }
349
+ result.push(p);
350
+ }
351
+ if (result.length === 0) {
352
+ throw new Zip321ParsingError("No payments found in URI");
353
+ }
354
+ if (options.useOrchard) {
355
+ for (const p of result) {
356
+ if (this.isUnified(p.address)) {
357
+ try {
358
+ const ua = parseUnifiedAddress(p.address);
359
+ if (ua.orchard) {
360
+ p.address = ua.orchard;
361
+ }
362
+ } catch (e) {
363
+ console.warn("Failed to extract Orchard receiver from UA:", e);
364
+ }
365
+ }
366
+ }
367
+ }
368
+ return result;
369
+ }
370
+ static isValidAddress(address) {
371
+ return this.isTransparent(address) || this.isSapling(address) || this.isUnified(address);
372
+ }
373
+ static isTransparent(address) {
374
+ try {
375
+ bs58check2.decode(address);
376
+ return address.startsWith("t");
377
+ } catch {
378
+ return false;
379
+ }
380
+ }
381
+ static isSapling(address) {
382
+ try {
383
+ const decoded = bech322.decode(address);
384
+ return decoded.prefix === "zs" || decoded.prefix === "ztestsapling";
385
+ } catch {
386
+ return false;
387
+ }
388
+ }
389
+ static isUnified(address) {
390
+ try {
391
+ const decoded = bech32m2.decode(address, 1e3);
392
+ return decoded.prefix === "u" || decoded.prefix === "utest";
393
+ } catch (e) {
394
+ return false;
395
+ }
396
+ }
397
+ static parseAmount(value) {
398
+ if (!/^\d+(\.\d{1,8})?$/.test(value)) {
399
+ throw new Zip321ParsingError(`Invalid amount format: ${value}`);
400
+ }
401
+ const parts = value.split(".");
402
+ let whole = BigInt(parts[0]);
403
+ let fraction = parts[1] || "";
404
+ fraction = fraction.padEnd(8, "0");
405
+ const zatoshis = whole * 100000000n + BigInt(fraction);
406
+ if (zatoshis > 2100000000000000n) {
407
+ throw new Zip321ParsingError(`Amount exceeds max supply: ${value}`);
408
+ }
409
+ return zatoshis;
410
+ }
411
+ static parseAsset(base64) {
412
+ if (!this.isValidBase64Url(base64)) {
413
+ throw new Zip321ParsingError(`Invalid Base64URL asset: ${base64}`);
414
+ }
415
+ const b64 = base64.replace(/-/g, "+").replace(/_/g, "/");
416
+ const pad = b64.length % 4;
417
+ const padded = pad ? b64 + "=".repeat(4 - pad) : b64;
418
+ let buffer;
419
+ try {
420
+ const binString = atob(padded);
421
+ buffer = new Uint8Array(binString.length);
422
+ for (let i = 0; i < binString.length; i++) {
423
+ buffer[i] = binString.charCodeAt(i);
424
+ }
425
+ } catch (e) {
426
+ throw new Zip321ParsingError(`Failed to decode asset base64: ${e}`);
427
+ }
428
+ if (buffer[0] !== 0) {
429
+ throw new Zip321ParsingError(`Unsupported asset version: ${buffer[0]}`);
430
+ }
431
+ const amountBytes = buffer.slice(1, 9);
432
+ let amount = 0n;
433
+ for (let i = 0; i < 8; i++) {
434
+ amount += BigInt(amountBytes[i]) << BigInt(i * 8);
435
+ }
436
+ const assetIdBytes = buffer.slice(9);
437
+ const assetId = Array.from(assetIdBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
438
+ return { assetId, amount };
439
+ }
440
+ static isValidBase64Url(str) {
441
+ return /^[A-Za-z0-9\-_]*$/.test(str);
442
+ }
443
+ };
444
+ var Zip321Generator = class {
445
+ /**
446
+ * Creates a ZIP 321 compliant URI from a list of payments.
447
+ * @param payments List of Payment objects.
448
+ * @param options Generation options.
449
+ * @returns The formatted zcash: URI.
450
+ * @throws Error if payments list is empty or invalid.
451
+ */
452
+ static create(payments, options = {}) {
453
+ if (!payments || payments.length === 0) {
454
+ throw new Error("Payments list cannot be empty");
455
+ }
456
+ const processedPayments = payments.map((p) => {
457
+ if (options.useOrchard && Zip321Parser["isUnified"](p.address)) {
458
+ try {
459
+ const ua = parseUnifiedAddress(p.address);
460
+ if (ua.orchard) {
461
+ return { ...p, address: ua.orchard };
462
+ }
463
+ } catch (e) {
464
+ console.warn("Failed to extract Orchard receiver from UA:", e);
465
+ }
466
+ }
467
+ return p;
468
+ });
469
+ processedPayments.forEach((p, i) => {
470
+ if (!p.address) throw new Error(`Payment at index ${i} missing address`);
471
+ });
472
+ const parts = [];
473
+ const primary = processedPayments[0];
474
+ let uri = `zcash:${primary.address}`;
475
+ const addParam = (key, value, index) => {
476
+ if (value === void 0) return;
477
+ const paramKey = index !== void 0 && index > 0 ? `${key}.${index}` : key;
478
+ parts.push(`${paramKey}=${value}`);
479
+ };
480
+ if (primary.assetId) {
481
+ addParam("req-asset", this.formatAsset(primary.assetId, primary.amount || 0n));
482
+ } else if (primary.amount !== void 0) {
483
+ addParam("amount", this.formatAmount(primary.amount));
484
+ }
485
+ addParam("memo", primary.memo);
486
+ addParam("label", primary.label);
487
+ addParam("message", primary.message);
488
+ if (primary.otherParams) {
489
+ Object.entries(primary.otherParams).forEach(([k, v]) => addParam(k, v));
490
+ }
491
+ for (let i = 1; i < processedPayments.length; i++) {
492
+ const p = processedPayments[i];
493
+ addParam("address", p.address, i);
494
+ if (p.assetId) {
495
+ addParam("req-asset", this.formatAsset(p.assetId, p.amount || 0n), i);
496
+ } else if (p.amount !== void 0) {
497
+ addParam("amount", this.formatAmount(p.amount), i);
498
+ }
499
+ addParam("memo", p.memo, i);
500
+ addParam("label", p.label, i);
501
+ addParam("message", p.message, i);
502
+ if (p.otherParams) {
503
+ Object.entries(p.otherParams).forEach(([k, v]) => addParam(k, v, i));
504
+ }
505
+ }
506
+ if (parts.length > 0) {
507
+ uri += `?${parts.join("&")}`;
508
+ }
509
+ return uri;
510
+ }
511
+ static formatAmount(zatoshis) {
512
+ const whole = zatoshis / 100000000n;
513
+ const frac = zatoshis % 100000000n;
514
+ if (frac === 0n) {
515
+ return whole.toString();
516
+ }
517
+ let fracStr = frac.toString().padStart(8, "0");
518
+ fracStr = fracStr.replace(/0+$/, "");
519
+ return `${whole}.${fracStr}`;
520
+ }
521
+ static formatAsset(assetId, amount) {
522
+ const buffer = new Uint8Array(1 + 8 + 32);
523
+ buffer[0] = 0;
524
+ for (let i = 0; i < 8; i++) {
525
+ buffer[1 + i] = Number(amount >> BigInt(i * 8) & 0xffn);
526
+ }
527
+ for (let i = 0; i < 32; i++) {
528
+ buffer[9 + i] = parseInt(assetId.substr(i * 2, 2), 16);
529
+ }
530
+ let binary = "";
531
+ for (let i = 0; i < buffer.length; i++) {
532
+ binary += String.fromCharCode(buffer[i]);
533
+ }
534
+ const b64 = btoa(binary);
535
+ return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
536
+ }
537
+ };
538
+ function parsePaymentUri(uri, options = {}) {
539
+ return Zip321Parser.parse(uri, options);
540
+ }
541
+ function createPaymentUri(payments, options = {}) {
542
+ return Zip321Generator.create(payments, options);
543
+ }
544
+
545
+ // src/qrcode.ts
546
+ import QRCode from "qrcode";
547
+ var DEFAULT_LOGO = "data:image/png;base64,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";
548
+ async function generateQRCode(text, options = {}) {
549
+ const {
550
+ width = 300,
551
+ margin = 2,
552
+ color = { dark: "#000000", light: "#ffffff" },
553
+ logoUrl = DEFAULT_LOGO,
554
+ logoWidth = 60,
555
+ logoHeight = 60
556
+ } = options;
557
+ try {
558
+ const qrDataUrl = await QRCode.toDataURL(text, {
559
+ width,
560
+ margin,
561
+ color,
562
+ errorCorrectionLevel: "H"
563
+ // High error correction for logo overlay
564
+ });
565
+ if (!logoUrl) {
566
+ return qrDataUrl;
567
+ }
568
+ if (typeof document === "undefined") {
569
+ console.warn("Logo overlay requires browser environment (Canvas API). Returning plain QR code.");
570
+ return qrDataUrl;
571
+ }
572
+ return new Promise((resolve, reject) => {
573
+ const canvas = document.createElement("canvas");
574
+ canvas.width = width;
575
+ canvas.height = width;
576
+ const ctx = canvas.getContext("2d");
577
+ if (!ctx) {
578
+ reject(new Error("Failed to get canvas context"));
579
+ return;
580
+ }
581
+ const qrImage = new Image();
582
+ qrImage.onload = () => {
583
+ ctx.drawImage(qrImage, 0, 0);
584
+ const logo = new Image();
585
+ logo.crossOrigin = "Anonymous";
586
+ logo.onload = () => {
587
+ const x = (width - logoWidth) / 2;
588
+ const y = (width - logoHeight) / 2;
589
+ ctx.drawImage(logo, x, y, logoWidth, logoHeight);
590
+ resolve(canvas.toDataURL("image/png"));
591
+ };
592
+ logo.onerror = (err) => {
593
+ console.warn("Failed to load logo image", err);
594
+ resolve(qrDataUrl);
595
+ };
596
+ logo.src = logoUrl;
597
+ };
598
+ qrImage.onerror = (err) => reject(err);
599
+ qrImage.src = qrDataUrl;
600
+ });
601
+ } catch (error) {
602
+ throw new Error(`Failed to generate QR code: ${error}`);
603
+ }
604
+ }
605
+
606
+ // src/index.ts
607
+ var ZucchiniSDK = class {
608
+ /**
609
+ * Check if the Zucchini extension is installed and the provider is injected.
610
+ */
611
+ get isInstalled() {
612
+ return typeof window !== "undefined" && !!window.zucchini;
613
+ }
614
+ /**
615
+ * Get the provider instance.
616
+ * @throws Error if Zucchini is not installed.
617
+ */
618
+ get provider() {
619
+ if (!this.isInstalled) {
620
+ throw new Error("Zucchini Wallet is not installed");
621
+ }
622
+ return window.zucchini;
623
+ }
624
+ /**
625
+ * Connect to the wallet.
626
+ * Requests permission from the user to access their accounts.
627
+ * @param permissions List of permissions to request.
628
+ */
629
+ async connect(permissions = []) {
630
+ return this.request({ method: "connect", params: { permissions } });
631
+ }
632
+ /**
633
+ * Send a transaction.
634
+ * Requires 'send_transaction' permission.
635
+ */
636
+ async sendTransaction(args) {
637
+ return this.request({
638
+ method: "sendTransaction",
639
+ params: args
640
+ });
641
+ }
642
+ async getBalance() {
643
+ return this.request({
644
+ method: "getBalance"
645
+ });
646
+ }
647
+ async getAddresses() {
648
+ return this.request({
649
+ method: "getAddresses"
650
+ });
651
+ }
652
+ async getNetwork() {
653
+ return this.request({
654
+ method: "getNetwork"
655
+ });
656
+ }
657
+ /**
658
+ * Send a generic request to the wallet.
659
+ */
660
+ async request(args) {
661
+ return this.provider.request(args);
662
+ }
663
+ on(event, handler) {
664
+ this.provider.on(event, handler);
665
+ }
666
+ off(event, handler) {
667
+ this.provider.off(event, handler);
668
+ }
669
+ async disconnect() {
670
+ return this.request({ method: "disconnect" });
671
+ }
672
+ async isConnected() {
673
+ try {
674
+ return await this.request({ method: "isConnected" });
675
+ } catch (e) {
676
+ return false;
677
+ }
678
+ }
679
+ async getUniversalViewingKey() {
680
+ return this.request({ method: "getUniversalViewingKey" });
681
+ }
682
+ async getAllViewingKeys() {
683
+ return this.request({ method: "getAllViewingKeys" });
684
+ }
685
+ async getWalletStatus() {
686
+ return this.request({ method: "getWalletStatus" });
687
+ }
688
+ async getTokens() {
689
+ const API_BASE_URL = "https://api.zucchinifi.xyz";
690
+ const res = await fetch(`${API_BASE_URL}/tokens`);
691
+ if (!res.ok) throw new Error("Failed to fetch tokens");
692
+ return res.json();
693
+ }
694
+ async getSwapQuote(args) {
695
+ const API_BASE_URL = "https://api.zucchinifi.xyz";
696
+ const { from, to, amount, recipient, refundAddress } = args;
697
+ const params = new URLSearchParams({
698
+ from,
699
+ to,
700
+ amount,
701
+ recipient: recipient || "0x0000000000000000000000000000000000000000",
702
+ refundAddress: refundAddress || "0x0000000000000000000000000000000000000000",
703
+ dry: "true"
704
+ });
705
+ const res = await fetch(`${API_BASE_URL}/swap/quote?${params.toString()}`);
706
+ const data = await res.json();
707
+ if (data.error) {
708
+ throw new Error(data.error);
709
+ }
710
+ return data.quote;
711
+ }
712
+ };
713
+ var zucchini = new ZucchiniSDK();
714
+ export {
715
+ Zip321Generator,
716
+ Zip321Parser,
717
+ Zip321ParsingError,
718
+ ZucchiniSDK,
719
+ createPaymentUri,
720
+ fromZats,
721
+ generateQRCode,
722
+ parsePaymentUri,
723
+ parseUnifiedAddress,
724
+ shortenAddress,
725
+ toZats,
726
+ zucchini
727
+ };