@provex/utils 1.0.16 → 1.2.3

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.
Files changed (122) hide show
  1. package/dist/src/chain.d.ts +436 -4
  2. package/dist/src/chain.js +80 -0
  3. package/dist/src/chain.test.d.ts +1 -0
  4. package/dist/src/chain.test.js +112 -0
  5. package/dist/src/contracts.d.ts +196 -20
  6. package/dist/src/contracts.js +245 -17
  7. package/dist/src/contracts.test.d.ts +1 -0
  8. package/dist/src/contracts.test.js +243 -0
  9. package/dist/src/conversionRates.d.ts +99 -8
  10. package/dist/src/conversionRates.js +74 -4
  11. package/dist/src/conversionRates.test.d.ts +1 -0
  12. package/dist/src/conversionRates.test.js +473 -0
  13. package/dist/src/currencies.d.ts +113 -9
  14. package/dist/src/currencies.js +119 -38
  15. package/dist/src/currencies.test.d.ts +1 -0
  16. package/dist/src/currencies.test.js +343 -0
  17. package/dist/src/data/countries.json +166 -166
  18. package/dist/src/deposit.d.ts +69 -1
  19. package/dist/src/deposit.js +52 -1
  20. package/dist/src/depositKeys.d.ts +154 -0
  21. package/dist/src/depositKeys.js +104 -0
  22. package/dist/src/depositKeys.test.d.ts +1 -0
  23. package/dist/src/depositKeys.test.js +231 -0
  24. package/dist/src/endpoints.d.ts +51 -0
  25. package/dist/src/endpoints.js +70 -0
  26. package/dist/src/endpoints.test.d.ts +1 -0
  27. package/dist/src/endpoints.test.js +54 -0
  28. package/dist/src/events.d.ts +40 -2
  29. package/dist/src/events.js +86 -10
  30. package/dist/src/events.test.d.ts +1 -0
  31. package/dist/src/events.test.js +111 -0
  32. package/dist/src/fees.d.ts +136 -0
  33. package/dist/src/fees.js +178 -0
  34. package/dist/src/fees.test.d.ts +1 -0
  35. package/dist/src/fees.test.js +165 -0
  36. package/dist/src/ids.d.ts +125 -22
  37. package/dist/src/ids.js +211 -61
  38. package/dist/src/ids.test.d.ts +1 -0
  39. package/dist/src/ids.test.js +607 -0
  40. package/dist/src/index.d.ts +30 -1
  41. package/dist/src/index.js +30 -1
  42. package/dist/src/input.d.ts +23 -4
  43. package/dist/src/input.js +23 -4
  44. package/dist/src/intent.d.ts +38 -2
  45. package/dist/src/intent.js +28 -1
  46. package/dist/src/intent.test.d.ts +1 -0
  47. package/dist/src/intent.test.js +119 -0
  48. package/dist/src/json.d.ts +38 -1
  49. package/dist/src/json.js +38 -2
  50. package/dist/src/normalization.d.ts +224 -0
  51. package/dist/src/normalization.js +119 -0
  52. package/dist/src/normalization.test.d.ts +1 -0
  53. package/dist/src/normalization.test.js +284 -0
  54. package/dist/src/participants.d.ts +19 -4
  55. package/dist/src/participants.js +19 -4
  56. package/dist/src/payment.d.ts +333 -16
  57. package/dist/src/payment.js +768 -58
  58. package/dist/src/payment.test.d.ts +1 -0
  59. package/dist/src/payment.test.js +272 -0
  60. package/dist/src/peer-types.d.ts +327 -0
  61. package/dist/src/peer-types.js +169 -0
  62. package/dist/src/profit.d.ts +26 -5
  63. package/dist/src/profit.js +31 -8
  64. package/dist/src/profit.test.d.ts +1 -0
  65. package/dist/src/profit.test.js +315 -0
  66. package/dist/src/reputation.d.ts +125 -0
  67. package/dist/src/reputation.js +188 -0
  68. package/dist/src/reputation.test.d.ts +1 -0
  69. package/dist/src/reputation.test.js +156 -0
  70. package/dist/src/sort.d.ts +49 -10
  71. package/dist/src/sort.js +40 -9
  72. package/dist/src/sort.test.d.ts +1 -0
  73. package/dist/src/sort.test.js +216 -0
  74. package/dist/src/stateKey.d.ts +35 -0
  75. package/dist/src/stateKey.js +36 -0
  76. package/dist/src/stats.d.ts +55 -1
  77. package/dist/src/stats.js +46 -1
  78. package/dist/src/stats.test.d.ts +1 -0
  79. package/dist/src/stats.test.js +104 -0
  80. package/dist/src/time.d.ts +59 -9
  81. package/dist/src/time.js +71 -15
  82. package/dist/src/time.test.d.ts +1 -0
  83. package/dist/src/time.test.js +231 -0
  84. package/dist/src/tokens.d.ts +11972 -145
  85. package/dist/src/tokens.js +98 -17
  86. package/dist/src/tokens.test.d.ts +1 -0
  87. package/dist/src/tokens.test.js +193 -0
  88. package/dist/src/units.d.ts +183 -0
  89. package/dist/src/units.js +206 -6
  90. package/dist/src/units.test.d.ts +1 -0
  91. package/dist/src/units.test.js +327 -0
  92. package/dist/src/zkp2p-types.d.ts +273 -4
  93. package/dist/src/zkp2p-types.js +169 -1
  94. package/dist/tsconfig.tsbuildinfo +1 -1
  95. package/package.json +116 -26
  96. package/scripts/convert-svg.ts +0 -22
  97. package/src/chain.ts +0 -27
  98. package/src/contracts.ts +0 -89
  99. package/src/conversionRates.ts +0 -356
  100. package/src/currencies.ts +0 -286
  101. package/src/data/countries.json +0 -1099
  102. package/src/deposit.ts +0 -2
  103. package/src/events.ts +0 -21
  104. package/src/flags/eur.svg +0 -4
  105. package/src/flags/usd.svg +0 -25
  106. package/src/flags.ts +0 -4
  107. package/src/global.d.ts +0 -9
  108. package/src/ids.ts +0 -137
  109. package/src/index.ts +0 -16
  110. package/src/input.ts +0 -35
  111. package/src/intent.ts +0 -15
  112. package/src/json.ts +0 -42
  113. package/src/participants.ts +0 -14
  114. package/src/payment.ts +0 -573
  115. package/src/profit.ts +0 -55
  116. package/src/sort.ts +0 -49
  117. package/src/stats.ts +0 -10
  118. package/src/time.ts +0 -123
  119. package/src/tokens.ts +0 -159
  120. package/src/units.ts +0 -120
  121. package/src/zkp2p-types.ts +0 -55
  122. package/tsconfig.json +0 -18
@@ -0,0 +1,473 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { base } from 'viem/chains';
4
+ import { processConversionRates, getConversionRate, extractCurrencies, getLatestRateForCurrency, getPrimaryConversionRate, getPrimaryRateForCurrency, getPrimaryVerifier, getPrimaryCurrency, convertTokenInputToCurrency, convertCurrencyToTokenOutput, } from './conversionRates.js';
5
+ import { SUPPORTED_CURRENCIES } from './currencies.js';
6
+ import { v2BaseContracts } from './contracts.js';
7
+ describe('conversionRates', () => {
8
+ // Test data
9
+ const USD_CONTRACT_ID = SUPPORTED_CURRENCIES.USD.contractId;
10
+ const EUR_CONTRACT_ID = SUPPORTED_CURRENCIES.EUR.contractId;
11
+ // Use actual v2 verifier addresses from contracts
12
+ const VENMO_VERIFIER = v2BaseContracts.venmoReclaimVerifier[0];
13
+ const ZELLE_VERIFIER = v2BaseContracts.zelleBaseVerifier[0];
14
+ const createMockEvent = (overrides = {}) => ({
15
+ depositConversionRateUpdatedId: 'rate-1',
16
+ currency: USD_CONTRACT_ID,
17
+ verifier: VENMO_VERIFIER,
18
+ value: '1000000000000000000', // 1.0
19
+ orderId: '0x0001',
20
+ depositId: 'deposit-1',
21
+ transaction: {
22
+ hash: '0xhash1',
23
+ block: { timestamp: '1700000000' },
24
+ },
25
+ currentDepositConversionRateUpdatedId: 'rate-1',
26
+ currentDepositConversionRateUpdated: {
27
+ value: '1000000000000000000',
28
+ },
29
+ ...overrides,
30
+ });
31
+ const MOCK_TOKEN = {
32
+ address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
33
+ decimals: 6,
34
+ symbol: 'USDC',
35
+ name: 'USD Coin',
36
+ chainId: base.id,
37
+ };
38
+ describe('processConversionRates', () => {
39
+ it('should process empty events array', () => {
40
+ const result = processConversionRates({
41
+ chainId: base.id,
42
+ events: [],
43
+ });
44
+ assert.equal(result.size, 0);
45
+ });
46
+ it('should process single event', () => {
47
+ const events = [createMockEvent()];
48
+ const result = processConversionRates({
49
+ chainId: base.id,
50
+ events,
51
+ });
52
+ assert.equal(result.size, 1);
53
+ const venmoRates = result.get('venmo');
54
+ assert.ok(venmoRates, 'Should have venmo rates');
55
+ const usdRates = venmoRates.get(USD_CONTRACT_ID);
56
+ assert.ok(usdRates, 'Should have USD rates');
57
+ assert.equal(usdRates.length, 1);
58
+ });
59
+ it('should skip events without currentDepositConversionRateUpdated', () => {
60
+ const events = [
61
+ createMockEvent({ currentDepositConversionRateUpdated: undefined }),
62
+ ];
63
+ const result = processConversionRates({
64
+ chainId: base.id,
65
+ events,
66
+ });
67
+ assert.equal(result.size, 0);
68
+ });
69
+ it('should sort events by orderId in ascending order by default', () => {
70
+ const events = [
71
+ createMockEvent({ orderId: '0x0002' }),
72
+ createMockEvent({ orderId: '0x0001' }),
73
+ createMockEvent({ orderId: '0x0003' }),
74
+ ];
75
+ const result = processConversionRates({
76
+ chainId: base.id,
77
+ events,
78
+ includeHistorical: true,
79
+ });
80
+ const venmoRates = result.get('venmo')?.get(USD_CONTRACT_ID);
81
+ assert.ok(venmoRates);
82
+ assert.equal(venmoRates[0].orderId, '0x0001');
83
+ assert.equal(venmoRates[1].orderId, '0x0002');
84
+ assert.equal(venmoRates[2].orderId, '0x0003');
85
+ });
86
+ it('should sort events in descending order when specified', () => {
87
+ const events = [
88
+ createMockEvent({ orderId: '0x0002' }),
89
+ createMockEvent({ orderId: '0x0001' }),
90
+ createMockEvent({ orderId: '0x0003' }),
91
+ ];
92
+ const result = processConversionRates({
93
+ chainId: base.id,
94
+ events,
95
+ sortOrder: 'desc',
96
+ includeHistorical: true,
97
+ });
98
+ const venmoRates = result.get('venmo')?.get(USD_CONTRACT_ID);
99
+ assert.ok(venmoRates);
100
+ assert.equal(venmoRates[0].orderId, '0x0003');
101
+ });
102
+ it('should keep only latest rate when includeHistorical is false', () => {
103
+ const events = [
104
+ createMockEvent({
105
+ orderId: '0x0001',
106
+ currentDepositConversionRateUpdated: { value: '1000000000000000000' },
107
+ }),
108
+ createMockEvent({
109
+ orderId: '0x0002',
110
+ currentDepositConversionRateUpdated: { value: '1050000000000000000' },
111
+ }),
112
+ ];
113
+ const result = processConversionRates({
114
+ chainId: base.id,
115
+ events,
116
+ includeHistorical: false,
117
+ });
118
+ const venmoRates = result.get('venmo')?.get(USD_CONTRACT_ID);
119
+ assert.ok(venmoRates);
120
+ assert.equal(venmoRates.length, 1);
121
+ // Should have the latest rate (sorted asc, so last one replaces)
122
+ assert.equal(venmoRates[0].value, '1050000000000000000');
123
+ });
124
+ it('should group rates by provider', () => {
125
+ const events = [
126
+ createMockEvent({ verifier: VENMO_VERIFIER }),
127
+ createMockEvent({ verifier: ZELLE_VERIFIER }),
128
+ ];
129
+ const result = processConversionRates({
130
+ chainId: base.id,
131
+ events,
132
+ });
133
+ assert.ok(result.get('venmo'), 'Should have venmo rates');
134
+ assert.ok(result.get('zelle'), 'Should have zelle rates');
135
+ });
136
+ it('should group rates by currency', () => {
137
+ const events = [
138
+ createMockEvent({ currency: USD_CONTRACT_ID }),
139
+ createMockEvent({ currency: EUR_CONTRACT_ID }),
140
+ ];
141
+ const result = processConversionRates({
142
+ chainId: base.id,
143
+ events,
144
+ });
145
+ const venmoRates = result.get('venmo');
146
+ assert.ok(venmoRates);
147
+ assert.ok(venmoRates.get(USD_CONTRACT_ID), 'Should have USD rates');
148
+ assert.ok(venmoRates.get(EUR_CONTRACT_ID), 'Should have EUR rates');
149
+ });
150
+ });
151
+ describe('getConversionRate', () => {
152
+ it('should return null for empty events', () => {
153
+ const result = getConversionRate({
154
+ events: [],
155
+ currency: USD_CONTRACT_ID,
156
+ verifier: VENMO_VERIFIER,
157
+ chainId: base.id,
158
+ });
159
+ assert.equal(result, null);
160
+ });
161
+ it('should return rate for matching currency and verifier', () => {
162
+ const events = [createMockEvent()];
163
+ const result = getConversionRate({
164
+ events,
165
+ currency: USD_CONTRACT_ID,
166
+ verifier: VENMO_VERIFIER,
167
+ chainId: base.id,
168
+ });
169
+ assert.ok(result, 'Should return a rate');
170
+ assert.ok(!Array.isArray(result), 'Should return single rate');
171
+ assert.equal(result.value, '1000000000000000000');
172
+ });
173
+ it('should return null for non-matching currency', () => {
174
+ const events = [createMockEvent({ currency: USD_CONTRACT_ID })];
175
+ const result = getConversionRate({
176
+ events,
177
+ currency: EUR_CONTRACT_ID,
178
+ verifier: VENMO_VERIFIER,
179
+ chainId: base.id,
180
+ });
181
+ assert.equal(result, null);
182
+ });
183
+ it('should return array when includeHistory is true', () => {
184
+ const events = [
185
+ createMockEvent({ orderId: '0x0001' }),
186
+ createMockEvent({ orderId: '0x0002' }),
187
+ ];
188
+ const result = getConversionRate({
189
+ events,
190
+ currency: USD_CONTRACT_ID,
191
+ verifier: VENMO_VERIFIER,
192
+ chainId: base.id,
193
+ includeHistory: true,
194
+ });
195
+ assert.ok(Array.isArray(result), 'Should return array');
196
+ assert.equal(result.length, 2);
197
+ });
198
+ it('should use fallback rate when no matching rate found', () => {
199
+ const fallbackRate = {
200
+ currency: USD_CONTRACT_ID,
201
+ value: '999000000000000000',
202
+ verifier: VENMO_VERIFIER,
203
+ };
204
+ const result = getConversionRate({
205
+ events: [],
206
+ currency: USD_CONTRACT_ID,
207
+ verifier: VENMO_VERIFIER,
208
+ chainId: base.id,
209
+ fallbackRate,
210
+ });
211
+ assert.ok(result, 'Should return fallback rate');
212
+ assert.ok(!Array.isArray(result));
213
+ assert.equal(result.value, '999000000000000000');
214
+ });
215
+ it('should not use fallback rate when currency does not match', () => {
216
+ const fallbackRate = {
217
+ currency: EUR_CONTRACT_ID,
218
+ value: '999000000000000000',
219
+ verifier: VENMO_VERIFIER,
220
+ };
221
+ const result = getConversionRate({
222
+ events: [],
223
+ currency: USD_CONTRACT_ID,
224
+ verifier: VENMO_VERIFIER,
225
+ chainId: base.id,
226
+ fallbackRate,
227
+ });
228
+ assert.equal(result, null);
229
+ });
230
+ });
231
+ describe('extractCurrencies', () => {
232
+ it('should extract currencies from provider rates', () => {
233
+ const providerRates = new Map([
234
+ [
235
+ 'venmo',
236
+ new Map([
237
+ [USD_CONTRACT_ID, [createMockEvent()]],
238
+ [EUR_CONTRACT_ID, [createMockEvent({ currency: EUR_CONTRACT_ID })]],
239
+ ]),
240
+ ],
241
+ ]);
242
+ const currencies = extractCurrencies({ providerRates });
243
+ assert.equal(currencies.length, 2);
244
+ assert.ok(currencies.includes(USD_CONTRACT_ID));
245
+ assert.ok(currencies.includes(EUR_CONTRACT_ID));
246
+ });
247
+ it('should filter by providers when specified', () => {
248
+ const providerRates = new Map([
249
+ ['venmo', new Map([[USD_CONTRACT_ID, [createMockEvent()]]])],
250
+ ['zelle', new Map([[EUR_CONTRACT_ID, [createMockEvent({ currency: EUR_CONTRACT_ID })]]])],
251
+ ]);
252
+ const currencies = extractCurrencies({
253
+ providerRates,
254
+ providersOnly: ['venmo'],
255
+ });
256
+ assert.equal(currencies.length, 1);
257
+ assert.ok(currencies.includes(USD_CONTRACT_ID));
258
+ });
259
+ it('should filter by currencies when specified', () => {
260
+ const providerRates = new Map([
261
+ [
262
+ 'venmo',
263
+ new Map([
264
+ [USD_CONTRACT_ID, [createMockEvent()]],
265
+ [EUR_CONTRACT_ID, [createMockEvent({ currency: EUR_CONTRACT_ID })]],
266
+ ]),
267
+ ],
268
+ ]);
269
+ const currencies = extractCurrencies({
270
+ providerRates,
271
+ currenciesOnly: [USD_CONTRACT_ID],
272
+ });
273
+ assert.equal(currencies.length, 1);
274
+ assert.ok(currencies.includes(USD_CONTRACT_ID));
275
+ });
276
+ it('should return empty array for empty provider rates', () => {
277
+ const currencies = extractCurrencies({ providerRates: new Map() });
278
+ assert.equal(currencies.length, 0);
279
+ });
280
+ });
281
+ describe('getLatestRateForCurrency', () => {
282
+ it('should return null for empty provider rates', () => {
283
+ const result = getLatestRateForCurrency({
284
+ providerRates: new Map(),
285
+ currency: USD_CONTRACT_ID,
286
+ });
287
+ assert.equal(result, null);
288
+ });
289
+ it('should return latest rate by timestamp', () => {
290
+ const oldRate = createMockEvent({
291
+ transaction: { hash: '0x1', block: { timestamp: '1700000000' } },
292
+ });
293
+ const newRate = createMockEvent({
294
+ transaction: { hash: '0x2', block: { timestamp: '1700001000' } },
295
+ verifier: ZELLE_VERIFIER,
296
+ });
297
+ const providerRates = new Map([
298
+ ['venmo', new Map([[USD_CONTRACT_ID, oldRate]])],
299
+ ['zelle', new Map([[USD_CONTRACT_ID, newRate]])],
300
+ ]);
301
+ const result = getLatestRateForCurrency({
302
+ providerRates,
303
+ currency: USD_CONTRACT_ID,
304
+ });
305
+ assert.ok(result);
306
+ assert.equal(result.transaction.block.timestamp, '1700001000');
307
+ });
308
+ it('should prefer specified provider when timestamps match', () => {
309
+ const rate = createMockEvent({
310
+ transaction: { hash: '0x1', block: { timestamp: '1700000000' } },
311
+ });
312
+ const providerRates = new Map([
313
+ ['venmo', new Map([[USD_CONTRACT_ID, rate]])],
314
+ ['zelle', new Map([[USD_CONTRACT_ID, { ...rate, verifier: ZELLE_VERIFIER }]])],
315
+ ]);
316
+ const result = getLatestRateForCurrency({
317
+ providerRates,
318
+ currency: USD_CONTRACT_ID,
319
+ preferredProvider: 'venmo',
320
+ });
321
+ assert.ok(result);
322
+ assert.equal(result.verifier, VENMO_VERIFIER);
323
+ });
324
+ });
325
+ describe('getPrimaryConversionRate', () => {
326
+ it('should return null for empty provider rates', () => {
327
+ const result = getPrimaryConversionRate(new Map());
328
+ assert.equal(result, null);
329
+ });
330
+ it('should prioritize USD over other currencies', () => {
331
+ const usdRate = createMockEvent({ currency: USD_CONTRACT_ID });
332
+ const eurRate = createMockEvent({ currency: EUR_CONTRACT_ID });
333
+ const providerRates = new Map([
334
+ ['venmo', new Map([[EUR_CONTRACT_ID, eurRate]])],
335
+ ['zelle', new Map([[USD_CONTRACT_ID, usdRate]])],
336
+ ]);
337
+ const result = getPrimaryConversionRate(providerRates);
338
+ assert.ok(result);
339
+ assert.equal(result.currency, USD_CONTRACT_ID);
340
+ });
341
+ it('should return first available rate if no USD', () => {
342
+ const eurRate = createMockEvent({ currency: EUR_CONTRACT_ID });
343
+ const providerRates = new Map([
344
+ ['venmo', new Map([[EUR_CONTRACT_ID, eurRate]])],
345
+ ]);
346
+ const result = getPrimaryConversionRate(providerRates);
347
+ assert.ok(result);
348
+ assert.equal(result.currency, EUR_CONTRACT_ID);
349
+ });
350
+ });
351
+ describe('getPrimaryRateForCurrency', () => {
352
+ it('should return rate for specified currency', () => {
353
+ const eurRate = createMockEvent({ currency: EUR_CONTRACT_ID });
354
+ const providerRates = new Map([
355
+ ['venmo', new Map([[EUR_CONTRACT_ID, eurRate]])],
356
+ ]);
357
+ const result = getPrimaryRateForCurrency(providerRates, EUR_CONTRACT_ID);
358
+ assert.ok(result);
359
+ assert.equal(result.currency, EUR_CONTRACT_ID);
360
+ });
361
+ it('should return null if currency not found', () => {
362
+ const usdRate = createMockEvent({ currency: USD_CONTRACT_ID });
363
+ const providerRates = new Map([
364
+ ['venmo', new Map([[USD_CONTRACT_ID, usdRate]])],
365
+ ]);
366
+ const result = getPrimaryRateForCurrency(providerRates, EUR_CONTRACT_ID);
367
+ assert.equal(result, null);
368
+ });
369
+ });
370
+ describe('getPrimaryVerifier', () => {
371
+ it('should return verifier from primary rate', () => {
372
+ const rate = createMockEvent({ verifier: VENMO_VERIFIER });
373
+ const providerRates = new Map([
374
+ ['venmo', new Map([[USD_CONTRACT_ID, rate]])],
375
+ ]);
376
+ const result = getPrimaryVerifier(providerRates);
377
+ assert.equal(result, VENMO_VERIFIER);
378
+ });
379
+ it('should return null for empty rates', () => {
380
+ const result = getPrimaryVerifier(new Map());
381
+ assert.equal(result, null);
382
+ });
383
+ });
384
+ describe('getPrimaryCurrency', () => {
385
+ it('should return currency from primary rate', () => {
386
+ const rate = createMockEvent({ currency: USD_CONTRACT_ID });
387
+ const providerRates = new Map([
388
+ ['venmo', new Map([[USD_CONTRACT_ID, rate]])],
389
+ ]);
390
+ const result = getPrimaryCurrency(providerRates);
391
+ assert.equal(result, USD_CONTRACT_ID);
392
+ });
393
+ it('should return null for empty rates', () => {
394
+ const result = getPrimaryCurrency(new Map());
395
+ assert.equal(result, null);
396
+ });
397
+ });
398
+ describe('convertTokenInputToCurrency', () => {
399
+ it('should convert token amount at 1:1 rate', () => {
400
+ const rate = 10n ** 18n; // 1.0 rate
401
+ const amountOutInt = 100000000n; // 100 USDC
402
+ const result = convertTokenInputToCurrency({
403
+ token: MOCK_TOKEN,
404
+ amountOutInt,
405
+ rate,
406
+ });
407
+ // At 1:1, 100 tokens should give ~100 fiat units
408
+ // Result is rounded up to nearest cent (10^4 for USDC with 6 decimals)
409
+ assert.equal(result, 100000000n);
410
+ });
411
+ it('should convert token amount at 1.05 rate', () => {
412
+ const rate = 1050000000000000000n; // 1.05 rate
413
+ const amountOutInt = 100000000n; // 100 USDC
414
+ const result = convertTokenInputToCurrency({
415
+ token: MOCK_TOKEN,
416
+ amountOutInt,
417
+ rate,
418
+ });
419
+ // 100 * 1.05 = 105, rounded up to nearest cent
420
+ assert.equal(result, 105000000n);
421
+ });
422
+ it('should round up to nearest cent', () => {
423
+ const rate = 10n ** 18n;
424
+ const amountOutInt = 100000001n; // 100.000001 USDC
425
+ const result = convertTokenInputToCurrency({
426
+ token: MOCK_TOKEN,
427
+ amountOutInt,
428
+ rate,
429
+ });
430
+ // Should round up from 100.000001 to 100.01 (nearest cent in 6 decimals)
431
+ assert.equal(result, 100010000n);
432
+ });
433
+ });
434
+ describe('convertCurrencyToTokenOutput', () => {
435
+ it('should convert fiat to tokens at 1:1 rate', () => {
436
+ const rate = 10n ** 18n;
437
+ const currencyAmountInt = 10000n; // $100.00 in cents
438
+ const result = convertCurrencyToTokenOutput({
439
+ token: MOCK_TOKEN,
440
+ currency: SUPPORTED_CURRENCIES.USD,
441
+ currencyAmountInt,
442
+ rate,
443
+ });
444
+ // At 1:1, $100 should give 100 USDC = 100_000000
445
+ assert.equal(result, 100000000n);
446
+ });
447
+ it('should convert fiat to tokens at 1.05 rate', () => {
448
+ const rate = 1050000000000000000n; // 1.05 rate
449
+ const currencyAmountInt = 10500n; // $105.00 in cents
450
+ const result = convertCurrencyToTokenOutput({
451
+ token: MOCK_TOKEN,
452
+ currency: SUPPORTED_CURRENCIES.USD,
453
+ currencyAmountInt,
454
+ rate,
455
+ });
456
+ // $105 at 1.05 rate should give 100 USDC
457
+ assert.equal(result, 100000000n);
458
+ });
459
+ it('should floor to smallest token unit', () => {
460
+ const rate = 10n ** 18n;
461
+ const currencyAmountInt = 10001n; // $100.01
462
+ const result = convertCurrencyToTokenOutput({
463
+ token: MOCK_TOKEN,
464
+ currency: SUPPORTED_CURRENCIES.USD,
465
+ currencyAmountInt,
466
+ rate,
467
+ });
468
+ // $100.01 at 1:1 = 100.01 USDC = 100_010000 base units
469
+ // Floored to smallest unit (10^4 for 6-2 decimal difference)
470
+ assert.equal(result, 100010000n);
471
+ });
472
+ });
473
+ });
@@ -1,15 +1,38 @@
1
+ /**
2
+ * @fileoverview Fiat currency definitions and utilities for the Provex protocol.
3
+ *
4
+ * This module defines supported fiat currencies with their symbols, decimals,
5
+ * and contract IDs (keccak256 hashes used for on-chain identification).
6
+ *
7
+ * @remarks
8
+ * - Currency contract IDs are keccak256(currencyCode) used in smart contracts
9
+ * - Most currencies use 2 decimal places, but JPY/KRW/VND/UGX use 0
10
+ * - The Currency class provides parsing/formatting utilities for amounts
11
+ *
12
+ * @see {@link payment} for payment provider currency support
13
+ * @see {@link conversionRates} for exchange rate calculations
14
+ */
1
15
  import { type Hex } from 'viem';
2
16
  import { type FormatRateDisplayOptions } from './units.js';
3
17
  /**
4
- * Relevant info for a currency
18
+ * Currency information interface for display and calculations.
19
+ * Used for both the Currency class instances and serialized JSON representations.
5
20
  */
6
21
  export interface CurrencyInfo {
22
+ /** ISO 4217 currency code (e.g., 'USD', 'EUR') */
7
23
  ticker: string;
24
+ /** Currency symbol for display (e.g., '$', '€', '£') */
8
25
  symbol: string;
26
+ /** Full currency name (e.g., 'US Dollar') */
9
27
  name: string;
28
+ /** Number of decimal places (2 for most, 0 for JPY/KRW) */
10
29
  decimals: number;
30
+ /** keccak256 hash of the currency code, used in smart contracts */
11
31
  contractId: Hex;
32
+ /** Flag image URL or data URL for display */
12
33
  flag: string;
34
+ /** Zoom level for flag display (1 for square flags USD/EUR, 1.5 for rectangular) */
35
+ zoom: number;
13
36
  }
14
37
  /**
15
38
  * Country info from the countries.json file
@@ -28,12 +51,20 @@ export interface CountryInfo {
28
51
  */
29
52
  export declare const tickerToContractId: (ticker: string) => Hex;
30
53
  /**
31
- * Currency class that automatically generates contract ID
32
- * @param code - The currency code
33
- * @param symbol - The currency symbol
34
- * @param name - The currency name
35
- * @param decimals - The number of decimals
36
- * @param flag - The flag image path
54
+ * Currency class representing a fiat currency with parsing and formatting utilities.
55
+ *
56
+ * Each currency has:
57
+ * - A unique ticker (ISO 4217 code)
58
+ * - A display symbol
59
+ * - A contract ID (keccak256 hash for on-chain use)
60
+ * - Decimal precision for amount formatting
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * const usd = SUPPORTED_CURRENCIES.USD
65
+ * const amount = usd.parseAmount('100.50') // 10050n (2 decimals)
66
+ * const display = usd.formatAmount(10050n) // '100.50'
67
+ * ```
37
68
  */
38
69
  export declare class Currency implements CurrencyInfo {
39
70
  readonly ticker: string;
@@ -42,7 +73,18 @@ export declare class Currency implements CurrencyInfo {
42
73
  readonly decimals: number;
43
74
  readonly contractId: Hex;
44
75
  readonly flag: string;
45
- constructor(code: string, symbol: string, name: string, decimals: number, flag?: string);
76
+ readonly zoom: number;
77
+ /**
78
+ * Create a new Currency instance.
79
+ *
80
+ * @param code - ISO 4217 currency code (converted to uppercase)
81
+ * @param symbol - Display symbol (e.g., '$')
82
+ * @param name - Full currency name
83
+ * @param decimals - Number of decimal places
84
+ * @param flag - Optional flag image URL
85
+ * @param zoom - Optional zoom level for flag display (default: 1.5)
86
+ */
87
+ constructor(code: string, symbol: string, name: string, decimals: number, flag?: string, zoom?: number);
46
88
  /**
47
89
  * Generate a contract ID for the currency
48
90
  * @returns The contract ID
@@ -71,6 +113,30 @@ export declare class Currency implements CurrencyInfo {
71
113
  * @param currencyCode - The currency code
72
114
  */
73
115
  export declare function generateCurrencyId(currencyCode: string): string;
116
+ /**
117
+ * Registry of all supported fiat currencies.
118
+ *
119
+ * Each currency is a Currency instance with:
120
+ * - Correct decimal precision (0 for JPY/KRW/VND/UGX, 2 for others)
121
+ * - Unique contract ID (keccak256 hash)
122
+ * - Flag image for UI display
123
+ *
124
+ * @remarks
125
+ * Payment providers support different subsets of currencies.
126
+ * Use `getCurrenciesForProvider()` in payment.ts to get supported currencies
127
+ * for a specific payment method.
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * // Get USD currency info
132
+ * const usd = SUPPORTED_CURRENCIES.USD
133
+ * console.log(usd.symbol) // '$'
134
+ * console.log(usd.contractId) // '0x...' (keccak256('USD'))
135
+ *
136
+ * // Parse user input
137
+ * const amount = usd.parseAmount('100.50')
138
+ * ```
139
+ */
74
140
  export declare const SUPPORTED_CURRENCIES: {
75
141
  readonly USD: Currency;
76
142
  readonly EUR: Currency;
@@ -109,9 +175,31 @@ export declare const SUPPORTED_CURRENCIES: {
109
175
  readonly RON: Currency;
110
176
  readonly HUF: Currency;
111
177
  };
112
- /** Supported currency code */
178
+ /**
179
+ * Union type of all supported currency codes.
180
+ * Use for type-safe currency code parameters.
181
+ */
113
182
  export type SupportedCurrencyCode = keyof typeof SUPPORTED_CURRENCIES;
183
+ /**
184
+ * Map from contract ID (keccak256 hash) to Currency instance.
185
+ * Used for O(1) lookup of currency info from on-chain data.
186
+ *
187
+ * @example
188
+ * ```ts
189
+ * const contractId = '0x...' // from blockchain event
190
+ * const currency = currencyById.get(contractId)
191
+ * if (currency) {
192
+ * console.log(currency.ticker) // 'USD'
193
+ * }
194
+ * ```
195
+ */
114
196
  export declare const currencyById: Map<`0x${string}`, Currency>;
197
+ /**
198
+ * Get the currency name/ticker by contract ID
199
+ * @param contractId - The contract ID (keccak256 hash)
200
+ * @returns The currency ticker or the original contractId if not found
201
+ */
202
+ export declare function getCurrencyNameByContractId(contractId: string): string;
115
203
  /**
116
204
  * Get the currency info for a currency code
117
205
  * @param currencyCode - The currency code
@@ -142,3 +230,19 @@ export declare function getAllSupportedCurrencies(): CurrencyInfo[];
142
230
  * @returns True if the currency is supported, false otherwise
143
231
  */
144
232
  export declare function isSupportedCurrency(currencyCode: string): boolean;
233
+ /**
234
+ * Format the default conversion rate based on currency decimals.
235
+ * Returns "1" for zero-decimal currencies (JPY, KRW, VND, UGX),
236
+ * or "1.00" for standard 2-decimal currencies (USD, EUR, etc.).
237
+ *
238
+ * @param currency - The currency to format the default rate for
239
+ * @returns Formatted default rate string matching the currency's decimal precision
240
+ *
241
+ * @example
242
+ * ```ts
243
+ * formatDefaultRate(SUPPORTED_CURRENCIES.JPY) // "1" (0 decimals)
244
+ * formatDefaultRate(SUPPORTED_CURRENCIES.USD) // "1.00" (2 decimals)
245
+ * formatDefaultRate(SUPPORTED_CURRENCIES.EUR) // "1.00" (2 decimals)
246
+ * ```
247
+ */
248
+ export declare function formatDefaultRate(currency: Currency | CurrencyInfo): string;