@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.
- package/README.md +287 -0
- package/dist/scripts/convert-svg.d.ts +1 -0
- package/dist/scripts/convert-svg.js +17 -0
- package/dist/src/chain.d.ts +14 -0
- package/dist/src/chain.js +18 -0
- package/dist/src/contracts.d.ts +30 -0
- package/dist/src/contracts.js +64 -0
- package/dist/src/conversionRates.d.ts +124 -0
- package/dist/src/conversionRates.js +219 -0
- package/dist/src/currencies.d.ts +144 -0
- package/dist/src/currencies.js +234 -0
- package/dist/src/data/countries.json +1099 -0
- package/dist/src/deposit.d.ts +1 -0
- package/dist/src/deposit.js +1 -0
- package/dist/src/events.d.ts +3 -0
- package/dist/src/events.js +16 -0
- package/dist/src/flags.d.ts +2 -0
- package/dist/src/flags.js +3 -0
- package/dist/src/ids.d.ts +72 -0
- package/dist/src/ids.js +81 -0
- package/dist/src/index.d.ts +16 -0
- package/dist/src/index.js +16 -0
- package/dist/src/input.d.ts +7 -0
- package/dist/src/input.js +35 -0
- package/dist/src/intent.d.ts +14 -0
- package/dist/src/intent.js +1 -0
- package/dist/src/json.d.ts +18 -0
- package/dist/src/json.js +44 -0
- package/dist/src/participants.d.ts +7 -0
- package/dist/src/participants.js +13 -0
- package/dist/src/payment.d.ts +164 -0
- package/dist/src/payment.js +439 -0
- package/dist/src/profit.d.ts +28 -0
- package/dist/src/profit.js +34 -0
- package/dist/src/sort.d.ts +36 -0
- package/dist/src/sort.js +39 -0
- package/dist/src/stats.d.ts +7 -0
- package/dist/src/stats.js +8 -0
- package/dist/src/time.d.ts +54 -0
- package/dist/src/time.js +106 -0
- package/dist/src/tokens.d.ts +3675 -0
- package/dist/src/tokens.js +134 -0
- package/dist/src/units.d.ts +17 -0
- package/dist/src/units.js +79 -0
- package/dist/src/zkp2p-types.d.ts +48 -0
- package/dist/src/zkp2p-types.js +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +45 -0
- package/scripts/convert-svg.ts +22 -0
- package/src/chain.ts +27 -0
- package/src/contracts.ts +89 -0
- package/src/conversionRates.ts +356 -0
- package/src/currencies.ts +286 -0
- package/src/data/countries.json +1099 -0
- package/src/deposit.ts +2 -0
- package/src/events.ts +21 -0
- package/src/flags/eur.svg +4 -0
- package/src/flags/usd.svg +25 -0
- package/src/flags.ts +4 -0
- package/src/global.d.ts +9 -0
- package/src/ids.ts +137 -0
- package/src/index.ts +16 -0
- package/src/input.ts +35 -0
- package/src/intent.ts +15 -0
- package/src/json.ts +42 -0
- package/src/participants.ts +14 -0
- package/src/payment.ts +573 -0
- package/src/profit.ts +55 -0
- package/src/sort.ts +49 -0
- package/src/stats.ts +10 -0
- package/src/time.ts +123 -0
- package/src/tokens.ts +159 -0
- package/src/units.ts +120 -0
- package/src/zkp2p-types.ts +55 -0
- 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
|
+
}
|