@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/zip321.js ADDED
@@ -0,0 +1,354 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Zip321Generator = exports.Zip321Parser = exports.Zip321ParsingError = void 0;
7
+ exports.parsePaymentUri = parsePaymentUri;
8
+ exports.createPaymentUri = createPaymentUri;
9
+ const bech32_1 = require("bech32");
10
+ const bs58check_1 = __importDefault(require("bs58check"));
11
+ const unified_address_1 = require("./unified_address");
12
+ class Zip321ParsingError extends Error {
13
+ constructor(message) {
14
+ super(message);
15
+ this.name = 'Zip321ParsingError';
16
+ }
17
+ }
18
+ exports.Zip321ParsingError = Zip321ParsingError;
19
+ class Zip321Parser {
20
+ /**
21
+ * Parses a zcash: URI into a list of Payment objects.
22
+ * @param uri The zcash URI string.
23
+ * @param options Parsing options.
24
+ * @returns An array of Payment objects.
25
+ * @throws Zip321ParsingError if the URI is invalid.
26
+ */
27
+ static parse(uri, options = {}) {
28
+ if (!uri.startsWith('zcash:')) {
29
+ throw new Zip321ParsingError('Invalid scheme: must start with zcash:');
30
+ }
31
+ const parts = uri.substring(6).split('?');
32
+ const path = parts[0];
33
+ const query = parts[1];
34
+ if (parts.length > 2) {
35
+ throw new Zip321ParsingError('Invalid URI: multiple question marks');
36
+ }
37
+ const params = new URLSearchParams(query || '');
38
+ const payments = new Map();
39
+ // Helper to get or create payment at index
40
+ const getPayment = (index) => {
41
+ if (!payments.has(index)) {
42
+ payments.set(index, { address: '' });
43
+ }
44
+ return payments.get(index);
45
+ };
46
+ // 1. Handle address in path
47
+ if (path && path.length > 0) {
48
+ if (!this.isValidAddress(path)) {
49
+ throw new Zip321ParsingError(`Invalid address in path: ${path}`);
50
+ }
51
+ const p = getPayment(0);
52
+ p.address = path;
53
+ }
54
+ // 2. Parse query parameters
55
+ params.forEach((value, key) => {
56
+ const match = key.match(/^([a-zA-Z][a-zA-Z0-9+-]*)(\.(\d+))?$/);
57
+ if (!match) {
58
+ if (key.startsWith('req-')) {
59
+ throw new Zip321ParsingError(`Unknown required parameter: ${key}`);
60
+ }
61
+ return; // Ignore unknown non-req params
62
+ }
63
+ const paramName = match[1];
64
+ const indexStr = match[3];
65
+ let index = 0;
66
+ if (indexStr) {
67
+ if (indexStr.startsWith('0')) {
68
+ throw new Zip321ParsingError(`Invalid index (leading zero): ${indexStr}`);
69
+ }
70
+ index = parseInt(indexStr, 10);
71
+ if (index > 9999) {
72
+ throw new Zip321ParsingError(`Index out of range: ${indexStr}`);
73
+ }
74
+ }
75
+ else {
76
+ index = 0;
77
+ }
78
+ if (index === 0 && paramName === 'address' && path.length > 0) {
79
+ throw new Zip321ParsingError('Duplicate address: defined in path and query');
80
+ }
81
+ const payment = getPayment(index);
82
+ switch (paramName) {
83
+ case 'address':
84
+ if (payment.address)
85
+ throw new Zip321ParsingError(`Duplicate address for index ${index}`);
86
+ if (!this.isValidAddress(value))
87
+ throw new Zip321ParsingError(`Invalid address: ${value}`);
88
+ payment.address = value;
89
+ break;
90
+ case 'amount':
91
+ if (payment.amount !== undefined)
92
+ throw new Zip321ParsingError(`Duplicate amount for index ${index}`);
93
+ if (payment.assetId)
94
+ throw new Zip321ParsingError(`Amount and req-asset cannot both be present for index ${index}`);
95
+ payment.amount = this.parseAmount(value);
96
+ break;
97
+ case 'req-asset':
98
+ if (payment.assetId)
99
+ throw new Zip321ParsingError(`Duplicate req-asset for index ${index}`);
100
+ if (payment.amount !== undefined)
101
+ throw new Zip321ParsingError(`Amount and req-asset cannot both be present for index ${index}`);
102
+ const assetData = this.parseAsset(value);
103
+ payment.assetId = assetData.assetId;
104
+ payment.amount = assetData.amount;
105
+ break;
106
+ case 'memo':
107
+ if (payment.memo)
108
+ throw new Zip321ParsingError(`Duplicate memo for index ${index}`);
109
+ if (!this.isValidBase64Url(value))
110
+ throw new Zip321ParsingError(`Invalid Base64URL memo: ${value}`);
111
+ payment.memo = value;
112
+ break;
113
+ case 'label':
114
+ if (payment.label)
115
+ throw new Zip321ParsingError(`Duplicate label for index ${index}`);
116
+ payment.label = value;
117
+ break;
118
+ case 'message':
119
+ if (payment.message)
120
+ throw new Zip321ParsingError(`Duplicate message for index ${index}`);
121
+ payment.message = value;
122
+ break;
123
+ default:
124
+ if (paramName.startsWith('req-')) {
125
+ throw new Zip321ParsingError(`Unknown required parameter: ${paramName}`);
126
+ }
127
+ if (!payment.otherParams)
128
+ payment.otherParams = {};
129
+ payment.otherParams[paramName] = value;
130
+ break;
131
+ }
132
+ });
133
+ const result = [];
134
+ const sortedIndices = Array.from(payments.keys()).sort((a, b) => a - b);
135
+ for (const idx of sortedIndices) {
136
+ const p = payments.get(idx);
137
+ if (!p.address) {
138
+ throw new Zip321ParsingError(`Missing address for index ${idx}`);
139
+ }
140
+ if (this.isTransparent(p.address) && p.memo) {
141
+ throw new Zip321ParsingError(`Memo not allowed for transparent address at index ${idx}`);
142
+ }
143
+ if ((this.isTransparent(p.address) || this.isSapling(p.address)) && p.assetId) {
144
+ throw new Zip321ParsingError(`Custom assets not allowed for transparent/Sapling address at index ${idx}`);
145
+ }
146
+ result.push(p);
147
+ }
148
+ if (result.length === 0) {
149
+ throw new Zip321ParsingError('No payments found in URI');
150
+ }
151
+ // Orchard conversion logic
152
+ if (options.useOrchard) {
153
+ for (const p of result) {
154
+ if (this.isUnified(p.address)) {
155
+ try {
156
+ const ua = (0, unified_address_1.parseUnifiedAddress)(p.address);
157
+ if (ua.orchard) {
158
+ p.address = ua.orchard;
159
+ }
160
+ }
161
+ catch (e) {
162
+ console.warn('Failed to extract Orchard receiver from UA:', e);
163
+ }
164
+ }
165
+ }
166
+ }
167
+ return result;
168
+ }
169
+ static isValidAddress(address) {
170
+ return this.isTransparent(address) || this.isSapling(address) || this.isUnified(address);
171
+ }
172
+ static isTransparent(address) {
173
+ try {
174
+ bs58check_1.default.decode(address);
175
+ return address.startsWith('t');
176
+ }
177
+ catch {
178
+ return false;
179
+ }
180
+ }
181
+ static isSapling(address) {
182
+ try {
183
+ const decoded = bech32_1.bech32.decode(address);
184
+ return decoded.prefix === 'zs' || decoded.prefix === 'ztestsapling';
185
+ }
186
+ catch {
187
+ return false;
188
+ }
189
+ }
190
+ static isUnified(address) {
191
+ try {
192
+ const decoded = bech32_1.bech32m.decode(address, 1000);
193
+ return decoded.prefix === 'u' || decoded.prefix === 'utest';
194
+ }
195
+ catch (e) {
196
+ return false;
197
+ }
198
+ }
199
+ static parseAmount(value) {
200
+ if (!/^\d+(\.\d{1,8})?$/.test(value)) {
201
+ throw new Zip321ParsingError(`Invalid amount format: ${value}`);
202
+ }
203
+ const parts = value.split('.');
204
+ let whole = BigInt(parts[0]);
205
+ let fraction = parts[1] || '';
206
+ fraction = fraction.padEnd(8, '0');
207
+ const zatoshis = whole * 100000000n + BigInt(fraction);
208
+ if (zatoshis > 2100000000000000n) {
209
+ throw new Zip321ParsingError(`Amount exceeds max supply: ${value}`);
210
+ }
211
+ return zatoshis;
212
+ }
213
+ static parseAsset(base64) {
214
+ if (!this.isValidBase64Url(base64)) {
215
+ throw new Zip321ParsingError(`Invalid Base64URL asset: ${base64}`);
216
+ }
217
+ const b64 = base64.replace(/-/g, '+').replace(/_/g, '/');
218
+ const pad = b64.length % 4;
219
+ const padded = pad ? b64 + '='.repeat(4 - pad) : b64;
220
+ let buffer;
221
+ try {
222
+ const binString = atob(padded);
223
+ buffer = new Uint8Array(binString.length);
224
+ for (let i = 0; i < binString.length; i++) {
225
+ buffer[i] = binString.charCodeAt(i);
226
+ }
227
+ }
228
+ catch (e) {
229
+ throw new Zip321ParsingError(`Failed to decode asset base64: ${e}`);
230
+ }
231
+ if (buffer[0] !== 0x00) {
232
+ throw new Zip321ParsingError(`Unsupported asset version: ${buffer[0]}`);
233
+ }
234
+ const amountBytes = buffer.slice(1, 9);
235
+ let amount = 0n;
236
+ for (let i = 0; i < 8; i++) {
237
+ amount += BigInt(amountBytes[i]) << BigInt(i * 8);
238
+ }
239
+ const assetIdBytes = buffer.slice(9);
240
+ const assetId = Array.from(assetIdBytes).map(b => b.toString(16).padStart(2, '0')).join('');
241
+ return { assetId, amount };
242
+ }
243
+ static isValidBase64Url(str) {
244
+ return /^[A-Za-z0-9\-_]*$/.test(str);
245
+ }
246
+ }
247
+ exports.Zip321Parser = Zip321Parser;
248
+ class Zip321Generator {
249
+ /**
250
+ * Creates a ZIP 321 compliant URI from a list of payments.
251
+ * @param payments List of Payment objects.
252
+ * @param options Generation options.
253
+ * @returns The formatted zcash: URI.
254
+ * @throws Error if payments list is empty or invalid.
255
+ */
256
+ static create(payments, options = {}) {
257
+ if (!payments || payments.length === 0) {
258
+ throw new Error('Payments list cannot be empty');
259
+ }
260
+ // Orchard conversion logic
261
+ const processedPayments = payments.map(p => {
262
+ if (options.useOrchard && Zip321Parser['isUnified'](p.address)) {
263
+ try {
264
+ const ua = (0, unified_address_1.parseUnifiedAddress)(p.address);
265
+ if (ua.orchard) {
266
+ return { ...p, address: ua.orchard };
267
+ }
268
+ }
269
+ catch (e) {
270
+ console.warn('Failed to extract Orchard receiver from UA:', e);
271
+ }
272
+ }
273
+ return p;
274
+ });
275
+ processedPayments.forEach((p, i) => {
276
+ if (!p.address)
277
+ throw new Error(`Payment at index ${i} missing address`);
278
+ });
279
+ const parts = [];
280
+ const primary = processedPayments[0];
281
+ let uri = `zcash:${primary.address}`;
282
+ const addParam = (key, value, index) => {
283
+ if (value === undefined)
284
+ return;
285
+ const paramKey = index !== undefined && index > 0 ? `${key}.${index}` : key;
286
+ parts.push(`${paramKey}=${value}`);
287
+ };
288
+ if (primary.assetId) {
289
+ addParam('req-asset', this.formatAsset(primary.assetId, primary.amount || 0n));
290
+ }
291
+ else if (primary.amount !== undefined) {
292
+ addParam('amount', this.formatAmount(primary.amount));
293
+ }
294
+ addParam('memo', primary.memo);
295
+ addParam('label', primary.label);
296
+ addParam('message', primary.message);
297
+ if (primary.otherParams) {
298
+ Object.entries(primary.otherParams).forEach(([k, v]) => addParam(k, v));
299
+ }
300
+ for (let i = 1; i < processedPayments.length; i++) {
301
+ const p = processedPayments[i];
302
+ addParam('address', p.address, i);
303
+ if (p.assetId) {
304
+ addParam('req-asset', this.formatAsset(p.assetId, p.amount || 0n), i);
305
+ }
306
+ else if (p.amount !== undefined) {
307
+ addParam('amount', this.formatAmount(p.amount), i);
308
+ }
309
+ addParam('memo', p.memo, i);
310
+ addParam('label', p.label, i);
311
+ addParam('message', p.message, i);
312
+ if (p.otherParams) {
313
+ Object.entries(p.otherParams).forEach(([k, v]) => addParam(k, v, i));
314
+ }
315
+ }
316
+ if (parts.length > 0) {
317
+ uri += `?${parts.join('&')}`;
318
+ }
319
+ return uri;
320
+ }
321
+ static formatAmount(zatoshis) {
322
+ const whole = zatoshis / 100000000n;
323
+ const frac = zatoshis % 100000000n;
324
+ if (frac === 0n) {
325
+ return whole.toString();
326
+ }
327
+ let fracStr = frac.toString().padStart(8, '0');
328
+ fracStr = fracStr.replace(/0+$/, '');
329
+ return `${whole}.${fracStr}`;
330
+ }
331
+ static formatAsset(assetId, amount) {
332
+ const buffer = new Uint8Array(1 + 8 + 32);
333
+ buffer[0] = 0x00;
334
+ for (let i = 0; i < 8; i++) {
335
+ buffer[1 + i] = Number((amount >> BigInt(i * 8)) & 0xffn);
336
+ }
337
+ for (let i = 0; i < 32; i++) {
338
+ buffer[9 + i] = parseInt(assetId.substr(i * 2, 2), 16);
339
+ }
340
+ let binary = '';
341
+ for (let i = 0; i < buffer.length; i++) {
342
+ binary += String.fromCharCode(buffer[i]);
343
+ }
344
+ const b64 = btoa(binary);
345
+ return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
346
+ }
347
+ }
348
+ exports.Zip321Generator = Zip321Generator;
349
+ function parsePaymentUri(uri, options = {}) {
350
+ return Zip321Parser.parse(uri, options);
351
+ }
352
+ function createPaymentUri(payments, options = {}) {
353
+ return Zip321Generator.create(payments, options);
354
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const zip321_1 = require("./zip321");
5
+ (0, vitest_1.describe)('Zip321Parser', () => {
6
+ (0, vitest_1.it)('parses a simple single payment URI', () => {
7
+ const uri = 'zcash:ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez?amount=1&memo=VGhpcyBpcyBhIHNpbXBsZSBtZW1vLg';
8
+ const payments = zip321_1.Zip321Parser.parse(uri);
9
+ (0, vitest_1.expect)(payments).toHaveLength(1);
10
+ (0, vitest_1.expect)(payments[0].address).toBe('ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez');
11
+ (0, vitest_1.expect)(payments[0].amount).toBe(100000000n);
12
+ (0, vitest_1.expect)(payments[0].memo).toBe('VGhpcyBpcyBhIHNpbXBsZSBtZW1vLg');
13
+ });
14
+ (0, vitest_1.it)('parses a multi-payment URI', () => {
15
+ const uri = 'zcash:?address=tmEZhbWHTpdKMw5it8YDspUXSMGQyFwovpU&amount=123.456&address.1=ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez&amount.1=0.001&memo.1=VGhpcyBpcyBhIHNpbXBsZSBtZW1vLg';
16
+ const payments = zip321_1.Zip321Parser.parse(uri);
17
+ (0, vitest_1.expect)(payments).toHaveLength(2);
18
+ // Index 0 (implicit)
19
+ (0, vitest_1.expect)(payments[0].address).toBe('tmEZhbWHTpdKMw5it8YDspUXSMGQyFwovpU');
20
+ (0, vitest_1.expect)(payments[0].amount).toBe(12345600000n);
21
+ // Index 1
22
+ (0, vitest_1.expect)(payments[1].address).toBe('ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez');
23
+ (0, vitest_1.expect)(payments[1].amount).toBe(100000n);
24
+ (0, vitest_1.expect)(payments[1].memo).toBe('VGhpcyBpcyBhIHNpbXBsZSBtZW1vLg');
25
+ });
26
+ (0, vitest_1.it)('parses URI with address in path', () => {
27
+ const uri = 'zcash:ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez?amount=1';
28
+ const payments = zip321_1.Zip321Parser.parse(uri);
29
+ (0, vitest_1.expect)(payments).toHaveLength(1);
30
+ (0, vitest_1.expect)(payments[0].address).toBe('ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez');
31
+ (0, vitest_1.expect)(payments[0].amount).toBe(100000000n);
32
+ });
33
+ (0, vitest_1.it)('throws on invalid scheme', () => {
34
+ (0, vitest_1.expect)(() => zip321_1.Zip321Parser.parse('http:ztestsapling...')).toThrow('Invalid scheme');
35
+ });
36
+ (0, vitest_1.it)('throws on duplicate address', () => {
37
+ (0, vitest_1.expect)(() => zip321_1.Zip321Parser.parse('zcash:ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez?address=ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez')).toThrow('Duplicate address');
38
+ });
39
+ (0, vitest_1.it)('throws on missing address for index', () => {
40
+ (0, vitest_1.expect)(() => zip321_1.Zip321Parser.parse('zcash:?amount=1')).toThrow('Missing address');
41
+ });
42
+ (0, vitest_1.it)('throws on invalid amount format', () => {
43
+ (0, vitest_1.expect)(() => zip321_1.Zip321Parser.parse('zcash:ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez?amount=1.2.3')).toThrow('Invalid amount');
44
+ });
45
+ (0, vitest_1.it)('throws on memo for transparent address', () => {
46
+ const uri = 'zcash:tmEZhbWHTpdKMw5it8YDspUXSMGQyFwovpU?amount=1&memo=VGhpcyBpcyBhIHNpbXBsZSBtZW1vLg';
47
+ (0, vitest_1.expect)(() => zip321_1.Zip321Parser.parse(uri)).toThrow('Memo not allowed for transparent address');
48
+ });
49
+ });
50
+ (0, vitest_1.describe)('Zip321Generator', () => {
51
+ (0, vitest_1.it)('creates a simple single payment URI', () => {
52
+ const payments = [{
53
+ address: 'ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez',
54
+ amount: 100000000n,
55
+ memo: 'VGhpcyBpcyBhIHNpbXBsZSBtZW1vLg'
56
+ }];
57
+ const uri = zip321_1.Zip321Generator.create(payments);
58
+ (0, vitest_1.expect)(uri).toContain('zcash:ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez');
59
+ (0, vitest_1.expect)(uri).toContain('amount=1');
60
+ (0, vitest_1.expect)(uri).toContain('memo=VGhpcyBpcyBhIHNpbXBsZSBtZW1vLg');
61
+ });
62
+ (0, vitest_1.it)('creates a multi-payment URI', () => {
63
+ const payments = [
64
+ {
65
+ address: 'tmEZhbWHTpdKMw5it8YDspUXSMGQyFwovpU',
66
+ amount: 12345600000n
67
+ },
68
+ {
69
+ address: 'ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez',
70
+ amount: 100000n,
71
+ memo: 'VGhpcyBpcyBhIHNpbXBsZSBtZW1vLg'
72
+ }
73
+ ];
74
+ const uri = zip321_1.Zip321Generator.create(payments);
75
+ (0, vitest_1.expect)(uri).toContain('zcash:tmEZhbWHTpdKMw5it8YDspUXSMGQyFwovpU');
76
+ (0, vitest_1.expect)(uri).toContain('amount=123.456');
77
+ (0, vitest_1.expect)(uri).toContain('address.1=ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez');
78
+ (0, vitest_1.expect)(uri).toContain('amount.1=0.001');
79
+ (0, vitest_1.expect)(uri).toContain('memo.1=VGhpcyBpcyBhIHNpbXBsZSBtZW1vLg');
80
+ });
81
+ (0, vitest_1.it)('round-trip test', () => {
82
+ const original = [
83
+ {
84
+ address: 'ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez',
85
+ amount: 50000000n,
86
+ memo: 'VGhpcyBpcyBhIHNpbXBsZSBtZW1vLg'
87
+ },
88
+ {
89
+ address: 'tmEZhbWHTpdKMw5it8YDspUXSMGQyFwovpU',
90
+ amount: 100000000n
91
+ }
92
+ ];
93
+ const uri = zip321_1.Zip321Generator.create(original);
94
+ const parsed = zip321_1.Zip321Parser.parse(uri);
95
+ (0, vitest_1.expect)(parsed).toHaveLength(2);
96
+ (0, vitest_1.expect)(parsed[0].address).toBe(original[0].address);
97
+ (0, vitest_1.expect)(parsed[0].amount).toBe(original[0].amount);
98
+ (0, vitest_1.expect)(parsed[0].memo).toBe(original[0].memo);
99
+ (0, vitest_1.expect)(parsed[1].address).toBe(original[1].address);
100
+ (0, vitest_1.expect)(parsed[1].amount).toBe(original[1].amount);
101
+ });
102
+ (0, vitest_1.it)('parses the user reported unified address', () => {
103
+ const ua = 'u1cen6dqc7qdwm24m0wkr3ph59vlrzksqx0e28j0yv8xhfvdv68k7yexafyrwja3qe20naaaa75q33sujupsmsc7t3vw34q83dl2xa70ud8z0p28z83fuc2gry6q23aame7xjwyp4n2vvuu7audwrcmu0gwqn6crmf6pky75pa000w6c4zlhv2gfdw8tveezxaj73atp5hj7w82fycwny';
104
+ const uri = `zcash:${ua}?amount=1&label=Payment%20Request`;
105
+ const parsed = zip321_1.Zip321Parser.parse(uri);
106
+ (0, vitest_1.expect)(parsed).toHaveLength(1);
107
+ (0, vitest_1.expect)(parsed[0].address).toBe(ua);
108
+ });
109
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const zip321_1 = require("./zip321");
5
+ (0, vitest_1.describe)('ZIP 321 Orchard Conversion', () => {
6
+ const unifiedAddress = 'u1cen6dqc7qdwm24m0wkr3ph59vlrzksqx0e28j0yv8xhfvdv68k7yexafyrwja3qe20naaaa75q33sujupsmsc7t3vw34q83dl2xa70ud8z0p28z83fuc2gry6q23aame7xjwyp4n2vvuu7audwrcmu0gwqn6crmf6pky75pa000w6c4zlhv2gfdw8tveezxaj73atp5hj7w82fycwny';
7
+ const orchardAddress = 'u1kv5mzj7pjc4syjepu72dc0ejns8eyfvwlgafnf5v7yke5mkme7d8jc05qkf656ud3kcr69822lxywjvtllg3sv09u2ur3uxdq5ez8v5q';
8
+ (0, vitest_1.it)('should use Unified Address by default in createPaymentUri', () => {
9
+ const uri = (0, zip321_1.createPaymentUri)([{ address: unifiedAddress, amount: 1000n }]);
10
+ (0, vitest_1.expect)(uri).toContain(unifiedAddress);
11
+ (0, vitest_1.expect)(uri).not.toContain(orchardAddress);
12
+ });
13
+ (0, vitest_1.it)('should convert to Orchard address in createPaymentUri when useOrchard is true', () => {
14
+ const uri = (0, zip321_1.createPaymentUri)([{ address: unifiedAddress, amount: 1000n }], { useOrchard: true });
15
+ (0, vitest_1.expect)(uri).toContain(orchardAddress);
16
+ (0, vitest_1.expect)(uri).not.toContain(unifiedAddress);
17
+ });
18
+ (0, vitest_1.it)('should use Unified Address by default in parsePaymentUri', () => {
19
+ const uri = `zcash:${unifiedAddress}?amount=0.00001`;
20
+ const payments = (0, zip321_1.parsePaymentUri)(uri);
21
+ (0, vitest_1.expect)(payments[0].address).toBe(unifiedAddress);
22
+ });
23
+ (0, vitest_1.it)('should convert to Orchard address in parsePaymentUri when useOrchard is true', () => {
24
+ const uri = `zcash:${unifiedAddress}?amount=0.00001`;
25
+ const payments = (0, zip321_1.parsePaymentUri)(uri, { useOrchard: true });
26
+ (0, vitest_1.expect)(payments[0].address).toBe(orchardAddress);
27
+ });
28
+ (0, vitest_1.it)('should handle multiple payments with conversion', () => {
29
+ const uri = `zcash:${unifiedAddress}?amount=1&address.1=${unifiedAddress}&amount.1=2`;
30
+ const payments = (0, zip321_1.parsePaymentUri)(uri, { useOrchard: true });
31
+ (0, vitest_1.expect)(payments[0].address).toBe(orchardAddress);
32
+ (0, vitest_1.expect)(payments[1].address).toBe(orchardAddress);
33
+ });
34
+ });
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@zucchinifi/dapp-sdk",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript SDK for Zucchini Wallet",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format cjs,esm --dts",
21
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
22
+ "lint": "tsc --noEmit",
23
+ "test": "vitest run"
24
+ },
25
+ "keywords": [
26
+ "zcash",
27
+ "wallet",
28
+ "sdk",
29
+ "zucchini"
30
+ ],
31
+ "author": "Zucchini Team",
32
+ "license": "MIT",
33
+ "devDependencies": {
34
+ "@types/qrcode": "^1.5.6",
35
+ "tsup": "^8.0.0",
36
+ "typescript": "^5.0.0",
37
+ "vitest": "^2.1.4"
38
+ },
39
+ "dependencies": {
40
+ "@noble/hashes": "^2.0.1",
41
+ "bech32": "^2.0.0",
42
+ "bigint-buffer": "^1.1.5",
43
+ "blakejs": "^1.2.1",
44
+ "bs58check": "^4.0.0",
45
+ "qrcode": "^1.5.4"
46
+ }
47
+ }