@provex/utils 1.0.16

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 (75) hide show
  1. package/README.md +287 -0
  2. package/dist/scripts/convert-svg.d.ts +1 -0
  3. package/dist/scripts/convert-svg.js +17 -0
  4. package/dist/src/chain.d.ts +14 -0
  5. package/dist/src/chain.js +18 -0
  6. package/dist/src/contracts.d.ts +30 -0
  7. package/dist/src/contracts.js +64 -0
  8. package/dist/src/conversionRates.d.ts +124 -0
  9. package/dist/src/conversionRates.js +219 -0
  10. package/dist/src/currencies.d.ts +144 -0
  11. package/dist/src/currencies.js +234 -0
  12. package/dist/src/data/countries.json +1099 -0
  13. package/dist/src/deposit.d.ts +1 -0
  14. package/dist/src/deposit.js +1 -0
  15. package/dist/src/events.d.ts +3 -0
  16. package/dist/src/events.js +16 -0
  17. package/dist/src/flags.d.ts +2 -0
  18. package/dist/src/flags.js +3 -0
  19. package/dist/src/ids.d.ts +72 -0
  20. package/dist/src/ids.js +81 -0
  21. package/dist/src/index.d.ts +16 -0
  22. package/dist/src/index.js +16 -0
  23. package/dist/src/input.d.ts +7 -0
  24. package/dist/src/input.js +35 -0
  25. package/dist/src/intent.d.ts +14 -0
  26. package/dist/src/intent.js +1 -0
  27. package/dist/src/json.d.ts +18 -0
  28. package/dist/src/json.js +44 -0
  29. package/dist/src/participants.d.ts +7 -0
  30. package/dist/src/participants.js +13 -0
  31. package/dist/src/payment.d.ts +164 -0
  32. package/dist/src/payment.js +439 -0
  33. package/dist/src/profit.d.ts +28 -0
  34. package/dist/src/profit.js +34 -0
  35. package/dist/src/sort.d.ts +36 -0
  36. package/dist/src/sort.js +39 -0
  37. package/dist/src/stats.d.ts +7 -0
  38. package/dist/src/stats.js +8 -0
  39. package/dist/src/time.d.ts +54 -0
  40. package/dist/src/time.js +106 -0
  41. package/dist/src/tokens.d.ts +3675 -0
  42. package/dist/src/tokens.js +134 -0
  43. package/dist/src/units.d.ts +17 -0
  44. package/dist/src/units.js +79 -0
  45. package/dist/src/zkp2p-types.d.ts +48 -0
  46. package/dist/src/zkp2p-types.js +1 -0
  47. package/dist/tsconfig.tsbuildinfo +1 -0
  48. package/package.json +45 -0
  49. package/scripts/convert-svg.ts +22 -0
  50. package/src/chain.ts +27 -0
  51. package/src/contracts.ts +89 -0
  52. package/src/conversionRates.ts +356 -0
  53. package/src/currencies.ts +286 -0
  54. package/src/data/countries.json +1099 -0
  55. package/src/deposit.ts +2 -0
  56. package/src/events.ts +21 -0
  57. package/src/flags/eur.svg +4 -0
  58. package/src/flags/usd.svg +25 -0
  59. package/src/flags.ts +4 -0
  60. package/src/global.d.ts +9 -0
  61. package/src/ids.ts +137 -0
  62. package/src/index.ts +16 -0
  63. package/src/input.ts +35 -0
  64. package/src/intent.ts +15 -0
  65. package/src/json.ts +42 -0
  66. package/src/participants.ts +14 -0
  67. package/src/payment.ts +573 -0
  68. package/src/profit.ts +55 -0
  69. package/src/sort.ts +49 -0
  70. package/src/stats.ts +10 -0
  71. package/src/time.ts +123 -0
  72. package/src/tokens.ts +159 -0
  73. package/src/units.ts +120 -0
  74. package/src/zkp2p-types.ts +55 -0
  75. package/tsconfig.json +18 -0
@@ -0,0 +1,219 @@
1
+ import { SUPPORTED_CURRENCIES } from './currencies.js';
2
+ import { verifierGetsKey } from './payment.js';
3
+ /**
4
+ * Process deposit currency added events into organized conversion rates
5
+ */
6
+ export function processConversionRates(options) {
7
+ const { events, sortOrder = 'asc', includeHistorical = false, chainId, } = options;
8
+ const ratesMap = new Map();
9
+ // Sort events by orderId
10
+ const sortedEvents = [...events].sort((a, b) => sortOrder === 'desc'
11
+ ? b.orderId.localeCompare(a.orderId)
12
+ : a.orderId.localeCompare(b.orderId));
13
+ for (const event of sortedEvents) {
14
+ if (!event.currentDepositConversionRateUpdated)
15
+ continue;
16
+ const providerKey = verifierGetsKey(chainId, event.verifier);
17
+ const currency = event.currency;
18
+ // Get or create provider rates map
19
+ const providerRates = ratesMap.get(providerKey) ?? new Map();
20
+ // Get or create currency rates array
21
+ const currencyRates = providerRates.get(currency) ?? [];
22
+ const rateInfo = {
23
+ currency,
24
+ depositConversionRateUpdatedId: event.currentDepositConversionRateUpdatedId,
25
+ value: event.currentDepositConversionRateUpdated.value ?? '',
26
+ verifier: event.verifier,
27
+ orderId: event.orderId,
28
+ depositId: event.depositId,
29
+ transaction: {
30
+ hash: event.transaction.hash,
31
+ block: {
32
+ timestamp: event.transaction.block.timestamp
33
+ }
34
+ },
35
+ };
36
+ if (includeHistorical) {
37
+ // Add all rates chronologically
38
+ currencyRates.push(rateInfo);
39
+ }
40
+ else {
41
+ // Only keep the latest rate (first in desc order, last in asc order)
42
+ if (currencyRates.length === 0) {
43
+ currencyRates.push(rateInfo);
44
+ }
45
+ else if (sortOrder === 'asc') {
46
+ // Replace with newer rate
47
+ currencyRates[0] = rateInfo;
48
+ }
49
+ // For desc order with includeHistorical=false, we only add the first (most recent) rate
50
+ }
51
+ providerRates.set(currency, currencyRates);
52
+ ratesMap.set(providerKey, providerRates);
53
+ }
54
+ return ratesMap;
55
+ }
56
+ /**
57
+ * Get conversion rate for a specific currency and verifier combination
58
+ */
59
+ export function getConversionRate(options) {
60
+ const { events, currency, verifier, chainId, includeHistory = false, fallbackRate } = options;
61
+ // Process rates to get organized data
62
+ const providerRates = processConversionRates({
63
+ events,
64
+ includeHistorical: includeHistory,
65
+ chainId,
66
+ });
67
+ const providerKey = verifierGetsKey(chainId, verifier);
68
+ const rates = providerRates.get(providerKey)?.get(currency);
69
+ if (rates && rates.length > 0) {
70
+ return includeHistory ? rates : rates[0];
71
+ }
72
+ // Check fallback rate if provided
73
+ if (fallbackRate &&
74
+ fallbackRate.currency === currency &&
75
+ fallbackRate.verifier === verifier) {
76
+ return {
77
+ depositConversionRateUpdatedId: '0',
78
+ currency: fallbackRate.currency,
79
+ value: fallbackRate.value,
80
+ verifier: fallbackRate.verifier,
81
+ depositId: '0',
82
+ orderId: '0', // Indicate this is a fallback/original rate
83
+ transaction: {
84
+ hash: '0',
85
+ block: {
86
+ timestamp: '0'
87
+ }
88
+ }
89
+ };
90
+ }
91
+ return null;
92
+ }
93
+ /**
94
+ * Extract unique currencies from processed provider rates
95
+ */
96
+ export function extractCurrencies(options) {
97
+ const { providerRates, providersOnly, currenciesOnly } = options;
98
+ const currencySet = new Set();
99
+ for (const [providerKey, currencyMap] of providerRates.entries()) {
100
+ // Filter by providers if specified
101
+ if (providersOnly && !providersOnly.includes(providerKey)) {
102
+ continue;
103
+ }
104
+ for (const [currency] of currencyMap.entries()) {
105
+ // Filter by currencies if specified
106
+ if (currenciesOnly && !currenciesOnly.includes(currency)) {
107
+ continue;
108
+ }
109
+ currencySet.add(currency);
110
+ }
111
+ }
112
+ return Array.from(currencySet);
113
+ }
114
+ /**
115
+ * Get the latest conversion rate across all providers for a currency
116
+ */
117
+ export function getLatestRateForCurrency(options) {
118
+ const { providerRates, currency, preferredProvider } = options;
119
+ let latestRate = null;
120
+ let latestTimestamp = BigInt(0);
121
+ // Check preferred provider first
122
+ if (preferredProvider) {
123
+ const providerRate = providerRates.get(preferredProvider)?.get(currency);
124
+ if (providerRate && providerRate.transaction.block.timestamp) {
125
+ const tsmp = BigInt(providerRate.transaction.block.timestamp);
126
+ if (tsmp > latestTimestamp) {
127
+ latestRate = providerRate;
128
+ latestTimestamp = tsmp;
129
+ }
130
+ }
131
+ }
132
+ // Check all other providers
133
+ for (const [providerKey, currencyMap] of providerRates.entries()) {
134
+ if (providerKey === preferredProvider)
135
+ continue; // Already checked
136
+ const rate = currencyMap.get(currency);
137
+ if (rate && rate.transaction.block.timestamp) {
138
+ const tsmp = BigInt(rate.transaction.block.timestamp);
139
+ if (tsmp > latestTimestamp) {
140
+ latestRate = rate;
141
+ latestTimestamp = tsmp;
142
+ }
143
+ }
144
+ }
145
+ return latestRate;
146
+ }
147
+ /**
148
+ * Get the primary conversion rate from processed provider rates
149
+ * Returns the first available rate, prioritizing by provider order
150
+ */
151
+ export function getPrimaryConversionRate(providerRates) {
152
+ for (const [, currencyMap] of providerRates) {
153
+ const usd = currencyMap.get(SUPPORTED_CURRENCIES.USD.contractId);
154
+ if (usd) {
155
+ return usd;
156
+ }
157
+ }
158
+ for (const [, currencyMap] of providerRates) {
159
+ for (const [, rate] of currencyMap.entries()) {
160
+ return rate;
161
+ }
162
+ }
163
+ return null;
164
+ }
165
+ /**
166
+ * Get primary conversion rate for a specific currency across all providers
167
+ */
168
+ export function getPrimaryRateForCurrency(providerRates, currency) {
169
+ for (const [, currencyMap] of providerRates) {
170
+ const rates = currencyMap.get(currency);
171
+ if (rates) {
172
+ return rates;
173
+ }
174
+ }
175
+ return null;
176
+ }
177
+ /**
178
+ * Get the primary verifier (payment provider) from conversion rates
179
+ */
180
+ export function getPrimaryVerifier(providerRates) {
181
+ const primaryRate = getPrimaryConversionRate(providerRates);
182
+ return primaryRate?.verifier || null;
183
+ }
184
+ /**
185
+ * Get the primary currency from conversion rates
186
+ */
187
+ export function getPrimaryCurrency(providerRates) {
188
+ const primaryRate = getPrimaryConversionRate(providerRates);
189
+ return primaryRate?.currency || null;
190
+ }
191
+ /**
192
+ * Convert token input to currency
193
+ */
194
+ export const convertTokenInputToCurrency = ({ token, amountOutInt, rate }) => {
195
+ const oneCent = 10n ** BigInt(token.decimals - 2);
196
+ const oneEther = 10n ** 18n;
197
+ const amountInInt = amountOutInt * BigInt(rate) / oneEther;
198
+ const remainder = amountInInt % oneCent;
199
+ const negativeSpace = oneCent - remainder;
200
+ const finalAmountInInt = amountInInt + (negativeSpace === oneCent ? 0n : negativeSpace);
201
+ return finalAmountInInt;
202
+ };
203
+ /**
204
+ * Convert currency amount to token amount out (inverse of convertTokenInputToCurrency)
205
+ * This accounts for the cent-rounding applied in the original function
206
+ */
207
+ export const convertCurrencyToTokenOutput = ({ token, currency, currencyAmountInt, rate, }) => {
208
+ const smallestUnit = 10n ** BigInt(token.decimals - currency.decimals);
209
+ const oneEther = 10n ** 18n;
210
+ // First, we need to find the original amount before cent-rounding was applied
211
+ // The original function rounds up to the nearest cent, so we need to find
212
+ // the maximum token amount that would result in this currency amount after rounding
213
+ // Start with the direct inverse calculation
214
+ // console.log('currencyAmountInt', currencyAmountInt, smallestUnit, converter, rate)
215
+ let tokenAmountOut = (currencyAmountInt * smallestUnit * oneEther) / rate;
216
+ const remainder = tokenAmountOut % smallestUnit;
217
+ const finalTokenAmountOut = (tokenAmountOut - remainder);
218
+ return finalTokenAmountOut;
219
+ };
@@ -0,0 +1,144 @@
1
+ import { type Hex } from 'viem';
2
+ import { type FormatRateDisplayOptions } from './units.js';
3
+ /**
4
+ * Relevant info for a currency
5
+ */
6
+ export interface CurrencyInfo {
7
+ ticker: string;
8
+ symbol: string;
9
+ name: string;
10
+ decimals: number;
11
+ contractId: Hex;
12
+ flag: string;
13
+ }
14
+ /**
15
+ * Country info from the countries.json file
16
+ */
17
+ export interface CountryInfo {
18
+ code: string;
19
+ name: string;
20
+ country: string;
21
+ countryCode: string;
22
+ flag?: string;
23
+ }
24
+ /**
25
+ * Convert a ticker to a contract ID
26
+ * @param ticker - The ticker to convert
27
+ * @returns The contract ID
28
+ */
29
+ export declare const tickerToContractId: (ticker: string) => Hex;
30
+ /**
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
37
+ */
38
+ export declare class Currency implements CurrencyInfo {
39
+ readonly ticker: string;
40
+ readonly symbol: string;
41
+ readonly name: string;
42
+ readonly decimals: number;
43
+ readonly contractId: Hex;
44
+ readonly flag: string;
45
+ constructor(code: string, symbol: string, name: string, decimals: number, flag?: string);
46
+ /**
47
+ * Generate a contract ID for the currency
48
+ * @returns The contract ID
49
+ */
50
+ private generateContractId;
51
+ /**
52
+ * Parse an amount to a bigint
53
+ * @param amount - The amount to parse
54
+ * @returns The amount as a bigint
55
+ */
56
+ parseAmount(amount: string): bigint | null;
57
+ /**
58
+ * Format an amount to a string
59
+ * @param amount - The amount to format
60
+ * @returns The amount as a string
61
+ */
62
+ formatAmount(amount: bigint | null, options?: Partial<FormatRateDisplayOptions>): string | null;
63
+ /**
64
+ * Convert to plain object for compatibility
65
+ * @returns The currency info
66
+ */
67
+ toJSON(): CurrencyInfo;
68
+ }
69
+ /**
70
+ * Generate contract ID for a currency code (standalone function for backward compatibility)
71
+ * @param currencyCode - The currency code
72
+ */
73
+ export declare function generateCurrencyId(currencyCode: string): string;
74
+ export declare const SUPPORTED_CURRENCIES: {
75
+ readonly USD: Currency;
76
+ readonly EUR: Currency;
77
+ readonly GBP: Currency;
78
+ readonly AUD: Currency;
79
+ readonly CAD: Currency;
80
+ readonly NZD: Currency;
81
+ readonly CHF: Currency;
82
+ readonly SEK: Currency;
83
+ readonly NOK: Currency;
84
+ readonly DKK: Currency;
85
+ readonly PLN: Currency;
86
+ readonly CZK: Currency;
87
+ readonly ARS: Currency;
88
+ readonly CNY: Currency;
89
+ readonly MXN: Currency;
90
+ readonly JPY: Currency;
91
+ readonly THB: Currency;
92
+ readonly RUB: Currency;
93
+ readonly AED: Currency;
94
+ readonly ZAR: Currency;
95
+ readonly HKD: Currency;
96
+ readonly SGD: Currency;
97
+ readonly SAR: Currency;
98
+ readonly ILS: Currency;
99
+ readonly IDR: Currency;
100
+ readonly MYR: Currency;
101
+ readonly PHP: Currency;
102
+ readonly VND: Currency;
103
+ readonly KRW: Currency;
104
+ readonly INR: Currency;
105
+ readonly BRL: Currency;
106
+ readonly KES: Currency;
107
+ readonly TRY: Currency;
108
+ readonly UGX: Currency;
109
+ readonly RON: Currency;
110
+ readonly HUF: Currency;
111
+ };
112
+ /** Supported currency code */
113
+ export type SupportedCurrencyCode = keyof typeof SUPPORTED_CURRENCIES;
114
+ export declare const currencyById: Map<`0x${string}`, Currency>;
115
+ /**
116
+ * Get the currency info for a currency code
117
+ * @param currencyCode - The currency code
118
+ * @returns The currency info
119
+ */
120
+ export declare function getCurrencyInfo(currencyCode: string): CurrencyInfo | null;
121
+ /**
122
+ * Get the currency info for a contract ID
123
+ * @param contractId - The contract ID
124
+ * @returns The currency info
125
+ */
126
+ export declare function getCurrencyByContractId(contractId: string): Currency | null;
127
+ /**
128
+ * Format a currency amount
129
+ * @param amount - The amount to format
130
+ * @param currencyCode - The currency code
131
+ * @returns The formatted currency amount
132
+ */
133
+ export declare function formatCurrencyAmount(amount: string | number, currencyCode: string): string;
134
+ /**
135
+ * Get all supported currencies
136
+ * @returns All supported currencies
137
+ */
138
+ export declare function getAllSupportedCurrencies(): CurrencyInfo[];
139
+ /**
140
+ * Check if a currency is supported
141
+ * @param currencyCode - The currency code
142
+ * @returns True if the currency is supported, false otherwise
143
+ */
144
+ export declare function isSupportedCurrency(currencyCode: string): boolean;
@@ -0,0 +1,234 @@
1
+ import { isHash, keccak256, toBytes } from 'viem';
2
+ import { addThousandsSeparator, formatUnits, parseUnits } from './units.js';
3
+ import countriesData from './data/countries.json' with { type: 'json' };
4
+ import { USD_FLAG_SVG, EUR_FLAG_SVG } from './flags.js';
5
+ // Convert SVG strings to data URLs
6
+ const USD_FLAG_DATA_URL = `data:image/svg+xml;base64,${btoa(USD_FLAG_SVG)}`;
7
+ const EUR_FLAG_DATA_URL = `data:image/svg+xml;base64,${btoa(EUR_FLAG_SVG)}`;
8
+ const countries = countriesData;
9
+ /**
10
+ * Convert a ticker to a contract ID
11
+ * @param ticker - The ticker to convert
12
+ * @returns The contract ID
13
+ */
14
+ export const tickerToContractId = (ticker) => {
15
+ return keccak256(toBytes(ticker.toUpperCase()));
16
+ };
17
+ /**
18
+ * Currency class that automatically generates contract ID
19
+ * @param code - The currency code
20
+ * @param symbol - The currency symbol
21
+ * @param name - The currency name
22
+ * @param decimals - The number of decimals
23
+ * @param flag - The flag image path
24
+ */
25
+ export class Currency {
26
+ ticker;
27
+ symbol;
28
+ name;
29
+ decimals;
30
+ contractId;
31
+ flag;
32
+ constructor(code, symbol, name, decimals, flag) {
33
+ this.ticker = code.toUpperCase();
34
+ this.symbol = symbol;
35
+ this.name = name;
36
+ this.decimals = decimals;
37
+ this.flag = flag || ''; // figure out a fallback flag
38
+ this.contractId = this.generateContractId();
39
+ }
40
+ /**
41
+ * Generate a contract ID for the currency
42
+ * @returns The contract ID
43
+ */
44
+ generateContractId() {
45
+ return tickerToContractId(this.ticker);
46
+ }
47
+ /**
48
+ * Parse an amount to a bigint
49
+ * @param amount - The amount to parse
50
+ * @returns The amount as a bigint
51
+ */
52
+ parseAmount(amount) {
53
+ try {
54
+ return parseUnits(amount, { decimals: this.decimals });
55
+ }
56
+ catch (error) {
57
+ console.error('Error parsing amount: %o', amount, error);
58
+ return null;
59
+ }
60
+ }
61
+ /**
62
+ * Format an amount to a string
63
+ * @param amount - The amount to format
64
+ * @returns The amount as a string
65
+ */
66
+ formatAmount(amount, options = {}) {
67
+ try {
68
+ if (amount === null) {
69
+ return null;
70
+ }
71
+ return formatUnits(amount, {
72
+ decimals: this.decimals,
73
+ truncateDecimals: 2,
74
+ padDecimals: 2,
75
+ ...options,
76
+ });
77
+ }
78
+ catch (err) {
79
+ console.error('Error formatting amount: %o', amount, err);
80
+ return null;
81
+ }
82
+ }
83
+ /**
84
+ * Convert to plain object for compatibility
85
+ * @returns The currency info
86
+ */
87
+ toJSON() {
88
+ return {
89
+ ticker: this.ticker,
90
+ symbol: this.symbol,
91
+ name: this.name,
92
+ decimals: this.decimals,
93
+ contractId: this.contractId,
94
+ flag: this.flag,
95
+ };
96
+ }
97
+ }
98
+ /**
99
+ * Generate contract ID for a currency code (standalone function for backward compatibility)
100
+ * @param currencyCode - The currency code
101
+ */
102
+ export function generateCurrencyId(currencyCode) {
103
+ const bytes = toBytes(currencyCode.toUpperCase());
104
+ return keccak256(bytes);
105
+ }
106
+ /**
107
+ * Get the flag for a country
108
+ * @param countryCode - The country code
109
+ * @returns The flag image path
110
+ */
111
+ const flagFromCountries = (countryCode) => {
112
+ const country = countries.find((country) => country.code === countryCode);
113
+ return country?.flag || '';
114
+ };
115
+ // Supported fiat currencies with local flag files for major ones
116
+ export const SUPPORTED_CURRENCIES = {
117
+ USD: new Currency('USD', '$', 'US Dollar', 2, USD_FLAG_DATA_URL),
118
+ EUR: new Currency('EUR', '€', 'Euro', 2, EUR_FLAG_DATA_URL),
119
+ GBP: new Currency('GBP', '£', 'British Pound', 2, flagFromCountries('GBP')),
120
+ AUD: new Currency('AUD', 'A$', 'Australian Dollar', 2, flagFromCountries('AUD')),
121
+ CAD: new Currency('CAD', 'C$', 'Canadian Dollar', 2, flagFromCountries('CAD')),
122
+ NZD: new Currency('NZD', 'NZ$', 'New Zealand Dollar', 2, flagFromCountries('NZD')),
123
+ CHF: new Currency('CHF', 'CHF', 'Swiss Franc', 2, flagFromCountries('CHF')),
124
+ SEK: new Currency('SEK', 'kr', 'Swedish Krona', 2, flagFromCountries('SEK')),
125
+ NOK: new Currency('NOK', 'kr', 'Norwegian Krone', 2, flagFromCountries('NOK')),
126
+ DKK: new Currency('DKK', 'kr', 'Danish Krone', 2, flagFromCountries('DKK')),
127
+ PLN: new Currency('PLN', 'zł', 'Polish Złoty', 2, flagFromCountries('PLN')),
128
+ CZK: new Currency('CZK', 'Kč', 'Czech Koruna', 2, flagFromCountries('CZK')),
129
+ ARS: new Currency('ARS', 'ARS', 'Argentine Peso', 2, flagFromCountries('ARS')),
130
+ CNY: new Currency('CNY', '¥', 'Chinese Yuan', 2, flagFromCountries('CNY')),
131
+ MXN: new Currency('MXN', 'MX$', 'Mexican Peso', 2, flagFromCountries('MXN')),
132
+ JPY: new Currency('JPY', '¥', 'Japanese Yen', 0, flagFromCountries('JPY')),
133
+ THB: new Currency('THB', '฿', 'Thai Baht', 2, flagFromCountries('THB')),
134
+ RUB: new Currency('RUB', '₽', 'Russian Ruble', 2, flagFromCountries('RUB')),
135
+ AED: new Currency('AED', 'AED', 'United Arab Emirates Dirham', 2, flagFromCountries('AED')),
136
+ ZAR: new Currency('ZAR', 'R', 'South African Rand', 2, flagFromCountries('ZAR')),
137
+ HKD: new Currency('HKD', 'HK$', 'Hong Kong Dollar', 2, flagFromCountries('HKD')),
138
+ SGD: new Currency('SGD', 'S$', 'Singapore Dollar', 2, flagFromCountries('SGD')),
139
+ SAR: new Currency('SAR', 'SAR', 'Saudi Riyal', 2, flagFromCountries('SAR')),
140
+ ILS: new Currency('ILS', '₪', 'Israeli New Shekel', 2, flagFromCountries('ILS')),
141
+ IDR: new Currency('IDR', 'Rp', 'Indonesian Rupiah', 2, flagFromCountries('IDR')),
142
+ MYR: new Currency('MYR', 'RM', 'Malaysian Ringgit', 2, flagFromCountries('MYR')),
143
+ PHP: new Currency('PHP', '₱', 'Philippine Peso', 2, flagFromCountries('PHP')),
144
+ VND: new Currency('VND', '₫', 'Vietnamese Dong', 0, flagFromCountries('VND')),
145
+ KRW: new Currency('KRW', '₩', 'South Korean Won', 0, flagFromCountries('KRW')),
146
+ INR: new Currency('INR', '₹', 'Indian Rupee', 2, flagFromCountries('INR')),
147
+ BRL: new Currency('BRL', 'R$', 'Brazilian Real', 2, flagFromCountries('BRL')),
148
+ KES: new Currency('KES', 'KES', 'Kenyan Shilling', 2, flagFromCountries('KES')),
149
+ TRY: new Currency('TRY', '₺', 'Turkish Lira', 2, flagFromCountries('TRY')),
150
+ UGX: new Currency('UGX', 'USh', 'Ugandan Shilling', 0, flagFromCountries('UGX')),
151
+ RON: new Currency('RON', 'RON', 'Romanian Leu', 2, flagFromCountries('RON')),
152
+ HUF: new Currency('HUF', 'Ft', 'Hungarian Forint', 2, flagFromCountries('HUF')),
153
+ };
154
+ /** Entries for the supported currencies */
155
+ const entries = Object.entries(SUPPORTED_CURRENCIES).sort(([a], [b]) => a.localeCompare(b));
156
+ entries.forEach(([, currency]) => {
157
+ // console.log(currency.ticker, currency.contractId)
158
+ if (!currency.flag) {
159
+ console.error(`missing flag for ${currency.ticker}`);
160
+ }
161
+ });
162
+ export const currencyById = new Map(entries.map(([, currency]) => {
163
+ return [currency.contractId, currency];
164
+ }));
165
+ /**
166
+ * Get the currency info for a currency code
167
+ * @param currencyCode - The currency code
168
+ * @returns The currency info
169
+ */
170
+ export function getCurrencyInfo(currencyCode) {
171
+ const currency = SUPPORTED_CURRENCIES[currencyCode.toUpperCase()];
172
+ return currency ? currency.toJSON() : null;
173
+ }
174
+ /**
175
+ * Get the currency info for a contract ID
176
+ * @param contractId - The contract ID
177
+ * @returns The currency info
178
+ */
179
+ export function getCurrencyByContractId(contractId) {
180
+ if (!isHash(contractId)) {
181
+ throw new Error('Invalid contract ID');
182
+ }
183
+ const currency = currencyById.get(contractId);
184
+ return currency ? currency : null;
185
+ }
186
+ /**
187
+ * Format currency amount safely without Number() constructor, preserving decimal places
188
+ * @param value - The value to format
189
+ * @param decimals - The number of decimals
190
+ * @returns The formatted currency amount
191
+ */
192
+ function formatCurrencyToFixedDecimals(value, decimals) {
193
+ const stringValue = value.toString();
194
+ const [integerPart, fractionalPart = ''] = stringValue.split('.');
195
+ if (decimals === 0) {
196
+ return integerPart;
197
+ }
198
+ // Pad or truncate fractional part to desired decimals
199
+ const paddedFractional = fractionalPart.padEnd(decimals, '0').slice(0, decimals);
200
+ // For currency formatting, always preserve trailing zeros
201
+ return `${integerPart}.${paddedFractional}`;
202
+ }
203
+ /**
204
+ * Format a currency amount
205
+ * @param amount - The amount to format
206
+ * @param currencyCode - The currency code
207
+ * @returns The formatted currency amount
208
+ */
209
+ export function formatCurrencyAmount(amount, currencyCode) {
210
+ const currencyInfo = getCurrencyInfo(currencyCode);
211
+ if (!currencyInfo) {
212
+ return `${amount} ${currencyCode}`;
213
+ }
214
+ // Use currency-specific formatting that preserves decimal places
215
+ const formatted = formatCurrencyToFixedDecimals(amount, currencyInfo.decimals);
216
+ // Add thousands separators for better readability
217
+ const formattedWithSeparators = addThousandsSeparator(formatted);
218
+ return `${currencyInfo.symbol}${formattedWithSeparators}`;
219
+ }
220
+ /**
221
+ * Get all supported currencies
222
+ * @returns All supported currencies
223
+ */
224
+ export function getAllSupportedCurrencies() {
225
+ return entries.map(([, currency]) => currency.toJSON());
226
+ }
227
+ /**
228
+ * Check if a currency is supported
229
+ * @param currencyCode - The currency code
230
+ * @returns True if the currency is supported, false otherwise
231
+ */
232
+ export function isSupportedCurrency(currencyCode) {
233
+ return currencyCode.toUpperCase() in SUPPORTED_CURRENCIES;
234
+ }