@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
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@provex/utils",
3
+ "version": "1.0.16",
4
+ "main": "dist/src/index.js",
5
+ "types": "dist/src/index.d.ts",
6
+ "type": "module",
7
+ "access": "public",
8
+ "exports": {
9
+ ".": "./dist/src/index.js",
10
+ "./contracts": "./dist/src/contracts.js",
11
+ "./currencies": "./dist/src/currencies.js",
12
+ "./units": "./dist/src/units.js",
13
+ "./payment": "./dist/src/payment.js",
14
+ "./sort": "./dist/src/sort.js",
15
+ "./time": "./dist/src/time.js",
16
+ "./tokens": "./dist/src/tokens.js",
17
+ "./intent": "./dist/src/intent.js",
18
+ "./conversionRates": "./dist/src/conversionRates.js",
19
+ "./json": "./dist/src/json.js",
20
+ "./profit": "./dist/src/profit.js",
21
+ "./chain": "./dist/src/chain.js",
22
+ "./participants": "./dist/src/participants.js",
23
+ "./input": "./dist/src/input.js",
24
+ "./stats": "./dist/src/stats.js",
25
+ "./ids": "./dist/src/ids.js",
26
+ "./events": "./dist/src/events.js",
27
+ "./zkp2p-types": "./dist/src/zkp2p-types.js",
28
+ "./deposit": "./dist/src/deposit.js"
29
+ },
30
+ "scripts": {
31
+ "build": "tsx scripts/convert-svg && tsc && cp -r src/data dist/src/"
32
+ },
33
+ "keywords": [],
34
+ "author": "",
35
+ "license": "ISC",
36
+ "description": "",
37
+ "dependencies": {
38
+ "tsx": "^4.20.3",
39
+ "typescript": "^5.9.2",
40
+ "viem": "^2.38.0"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^22.0.0"
44
+ }
45
+ }
@@ -0,0 +1,22 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ const __dirname = import.meta.dirname;
5
+
6
+ // Read SVG files and convert to TypeScript constants
7
+ const svgDir = path.join(__dirname, '../src/flags');
8
+ const outputFile = path.join(__dirname, '../src/flags.ts');
9
+
10
+ const svgFiles = ['usd.svg', 'eur.svg'];
11
+ let output = '// Auto-generated from SVG files\n\n';
12
+
13
+ svgFiles.forEach(filename => {
14
+ const svgPath = path.join(svgDir, filename);
15
+ const svgContent = fs.readFileSync(svgPath, 'utf8');
16
+ const cleanSvg = svgContent.replace(/[\r\n]/g, ' ').replace(/\s+/g, ' ').trim();
17
+ const constName = filename.replace('.svg', '').toUpperCase() + '_FLAG_SVG';
18
+ output += `export const ${constName} = '${cleanSvg}';\n`;
19
+ });
20
+
21
+ fs.writeFileSync(outputFile, output);
22
+ console.log('Generated flags.ts from SVG files');
package/src/chain.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { base, pulsechain, type Chain } from 'viem/chains'
2
+
3
+ export const chains = [pulsechain, base] as [Chain, ...Chain[]]
4
+
5
+ export const chainIds = chains.map((chain) => chain.id)
6
+
7
+ export type ChainId = (typeof chainIds)[number]
8
+
9
+ /**
10
+ * Get the explorer URL for a given chain, type, and hash
11
+ * @param chain - The chain to get the explorer URL for
12
+ * @param type - The type of explorer to use (tx or address)
13
+ * @param hash - The hash to get the explorer URL for
14
+ * @returns The explorer URL
15
+ */
16
+ export function getExplorerUrl(chain: Chain, type: 'tx' | 'address', hash: string) {
17
+ return `${chain.blockExplorers?.default.url}/${type}/${hash}`
18
+ }
19
+
20
+ export const chainIdToChain = new Map<ChainId, Chain>(
21
+ chains.map((chain) => [chain.id, chain] as const)
22
+ )
23
+
24
+ export const chainIdToTargetBlockTime = new Map<number, number>([
25
+ [369, 10_000],
26
+ [8453, 2_000],
27
+ ])
@@ -0,0 +1,89 @@
1
+ import { getAddress, type Hex } from 'viem'
2
+ import { base, pulsechain } from 'viem/chains'
3
+ import type { ChainId } from './chain.js'
4
+
5
+ /**
6
+ * Contracts for the app
7
+ */
8
+ export type Contracts = {
9
+ gatingService: Hex
10
+ usdc: Hex
11
+ escrow: Hex[]
12
+ venmoReclaimVerifier: Hex[]
13
+ revolutReclaimVerifier: Hex[]
14
+ cashappReclaimVerifier: Hex[]
15
+ wiseReclaimVerifier: Hex[]
16
+ mercadopagoReclaimVerifier: Hex[]
17
+ zelleBaseVerifier: Hex[]
18
+ zelleBoAReclaimVerifier: Hex[]
19
+ zelleChaseReclaimVerifier: Hex[]
20
+ zelleCitiReclaimVerifier: Hex[]
21
+ paypalReclaimVerifier: Hex[]
22
+ monzoReclaimVerifier: Hex[]
23
+ }
24
+
25
+ export const reclaimSigners = {
26
+ zkp2pWitnessSigner: '0x0636c417755E3ae25C6c166D181c0607F4C572A3',
27
+ reclaimWitnessSigner: '0x244897572368eadf65bfbc5aec98d8e5443a9072',
28
+ } as const
29
+
30
+ export const baseContracts = {
31
+ gatingService: getAddress('0x396D31055Db28C0C6f36e8b36f18FE7227248a97'),
32
+ usdc: getAddress('0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'),
33
+ escrow: [getAddress('0xCA38607D85E8F6294Dc10728669605E6664C2D70')],
34
+ venmoReclaimVerifier: [getAddress('0x9a733B55a875D0DB4915c6B36350b24F8AB99dF5')],
35
+ revolutReclaimVerifier: [getAddress('0xAA5A1B62B01781E789C900d616300717CD9A41aB')],
36
+ cashappReclaimVerifier: [getAddress('0x76D33A33068D86016B806dF02376dDBb23Dd3703')],
37
+ wiseReclaimVerifier: [getAddress('0xFF0149799631D7A5bdE2e7eA9b306c42b3d9a9ca')],
38
+ mercadopagoReclaimVerifier: [
39
+ // getAddress('0x00d003c73eab8feaec04bab976235915fe7641e3'),
40
+ getAddress('0xf2AC5be14F32Cbe6A613CFF8931d95460D6c33A3'),
41
+ ],
42
+ zelleBaseVerifier: [
43
+ // getAddress('0x1783f040783c0827fb64d128ece548d9b3613ad5'),
44
+ getAddress('0x431a078A5029146aAB239c768A615CD484519aF7'),
45
+ ],
46
+ zelleChaseReclaimVerifier: [getAddress('0xBcD7C6BBcA5869fBefe3E322263EE1090221D7A9')],
47
+ zelleBoAReclaimVerifier: [getAddress('0x809c7A20A2dbBB3210fC73Dd7C01Cd7Fa70C195F')],
48
+ zelleCitiReclaimVerifier: [getAddress('0x3128badc46dbe2e37bdd2d64f33fb3b3a639570e')],
49
+ paypalReclaimVerifier: [getAddress('0x03d17E9371C858072E171276979f6B44571C5DeA')],
50
+ monzoReclaimVerifier: [getAddress('0x0dE46433bD251027f73eD8f28E01eF05DA36a2E0')],
51
+ } as Contracts
52
+
53
+ export const pulsechainContracts = {
54
+ gatingService: getAddress('0x4Bd6364FCC4D8A537aBfee33B16E959794fDf9Cf'),
55
+ usdc: getAddress('0x15D38573d2feeb82e7ad5187aB8c1D52810B1f07'),
56
+ escrow: [getAddress('0x2932E08BFD212Ef0732257bDd6463BF55E9b97d7')],
57
+ cashappReclaimVerifier: [getAddress('0x9224433D6AD9a5A774ca74Ca33c01747FA710A9d')],
58
+ mercadopagoReclaimVerifier: [getAddress('0xc4D0cf4DF12e329f9c951d1D9BB815da6138850A')],
59
+ monzoReclaimVerifier: [getAddress('0xBde2c79c1897459e66C0fdF6764EED0c6839C878')],
60
+ nullifierRegistry: [getAddress('0x471D3c6C9E32CE2556d0117aC161Aa82AEac17d8')],
61
+ paypalReclaimVerifier: [getAddress('0x1DdBfd39a141C5A2329d849CADa6a83404E0Ce4F')],
62
+ revolutReclaimVerifier: [getAddress('0x8234AA463C8B94778f58eA0bFEfcBAC3683341A9')],
63
+ venmoReclaimVerifier: [getAddress('0x0Dd524dA03f610480aF3F433B411b6016cFBDeab')],
64
+ wiseReclaimVerifier: [getAddress('0x7B72d7CfAfDfD397E2460c933e84D16dc121845a')],
65
+ zelleBaseVerifier: [getAddress('0x539566771f9c38c6038aafc1946bf11af9cdb201')],
66
+ zelleBoAReclaimVerifier: [getAddress('0x506a746b8228F9f9d4522105b973Dc7223A64EAC')],
67
+ zelleChaseReclaimVerifier: [getAddress('0xB0aA0a58959465bBCf0eCbD62A84B743A5435c99')],
68
+ zelleCitiReclaimVerifier: [getAddress('0x5624E479924eBFb25B51F36e3FDaE605dADb448b')],
69
+ } as Contracts
70
+
71
+ export const contractsByChainId = new Map<ChainId, Contracts>([
72
+ [base.id, baseContracts],
73
+ [pulsechain.id, pulsechainContracts],
74
+ ])
75
+
76
+ export const getContracts = (chainId: ChainId) => {
77
+ return contractsByChainId.get(chainId) ?? null
78
+ }
79
+
80
+ export const getLatestContract = (chainId: ChainId, key: keyof Contracts) => {
81
+ const contracts = getContracts(chainId)
82
+ if (!contracts) return null
83
+ const target = contracts[key]
84
+ if (!target) return null
85
+ if (Array.isArray(target)) {
86
+ return target[target.length - 1] ?? null
87
+ }
88
+ return target
89
+ }
@@ -0,0 +1,356 @@
1
+ import type { Hex } from 'viem'
2
+ import { Currency, SUPPORTED_CURRENCIES } from './currencies.js'
3
+ import { verifierGetsKey, type ProviderKey } from './payment.js'
4
+ import type { SortDirection } from './sort.js'
5
+ import type { TokenInfo } from './tokens.js'
6
+ import type { ChainId } from './chain.js'
7
+
8
+ /**
9
+ * Conversion rate info
10
+ */
11
+ export interface ConversionRateInfo {
12
+ depositConversionRateUpdatedId: string
13
+ currency: string
14
+ verifier: string
15
+ value: string
16
+ orderId: string
17
+ depositId: string
18
+ transaction: {
19
+ hash: string
20
+ block: {
21
+ timestamp: string
22
+ }
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Conversion rate info with current deposit conversion rate updated
28
+ */
29
+ export interface ConversionRateInfoWithCurrent extends ConversionRateInfo {
30
+ currentDepositConversionRateUpdatedId: string
31
+ currentDepositConversionRateUpdated: {
32
+ value: string
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Provider rates
38
+ */
39
+ export type ProviderRates<RateInfo> = Map<ProviderKey, Map<Hex, RateInfo>>
40
+
41
+ /**
42
+ * Process conversion rates options
43
+ */
44
+ interface ProcessConversionRatesOptions {
45
+ chainId: ChainId
46
+ events: ConversionRateInfoWithCurrent[]
47
+ sortOrder?: SortDirection // Default: 'desc' (most recent first)
48
+ includeHistorical?: boolean // Default: false (only latest rates)
49
+ }
50
+
51
+ /**
52
+ * Get conversion rate options
53
+ */
54
+ interface GetConversionRateOptions {
55
+ events: ConversionRateInfoWithCurrent[]
56
+ currency: Hex
57
+ verifier: string
58
+ chainId: ChainId
59
+ includeHistory?: boolean // Default: false (only latest rate)
60
+ fallbackRate?: { currency: string; value: string; verifier: string } | null
61
+ }
62
+
63
+ /**
64
+ * Extract currencies options
65
+ */
66
+ interface ExtractCurrenciesOptions {
67
+ providerRates: ProviderRates<ConversionRateInfo[]>
68
+ providersOnly?: ProviderKey[] // Filter to specific providers
69
+ currenciesOnly?: string[] // Filter to specific currencies
70
+ }
71
+
72
+ /**
73
+ * Process deposit currency added events into organized conversion rates
74
+ */
75
+ export function processConversionRates(options: ProcessConversionRatesOptions): ProviderRates<ConversionRateInfo[]> {
76
+ const {
77
+ events,
78
+ sortOrder = 'asc',
79
+ includeHistorical = false,
80
+ chainId,
81
+ } = options
82
+
83
+ const ratesMap = new Map<ProviderKey, Map<Hex, ConversionRateInfo[]>>()
84
+
85
+ // Sort events by orderId
86
+ const sortedEvents = [...events].sort((a, b) =>
87
+ sortOrder === 'desc'
88
+ ? b.orderId.localeCompare(a.orderId)
89
+ : a.orderId.localeCompare(b.orderId)
90
+ )
91
+
92
+ for (const event of sortedEvents) {
93
+ if (!event.currentDepositConversionRateUpdated) continue
94
+
95
+ const providerKey = verifierGetsKey(chainId, event.verifier)
96
+ const currency = event.currency as Hex
97
+
98
+ // Get or create provider rates map
99
+ const providerRates = ratesMap.get(providerKey) ?? new Map<Hex, ConversionRateInfo[]>()
100
+
101
+ // Get or create currency rates array
102
+ const currencyRates = providerRates.get(currency) ?? []
103
+
104
+ const rateInfo: ConversionRateInfo = {
105
+ currency,
106
+ depositConversionRateUpdatedId: event.currentDepositConversionRateUpdatedId,
107
+ value: event.currentDepositConversionRateUpdated.value ?? '',
108
+ verifier: event.verifier,
109
+ orderId: event.orderId,
110
+ depositId: event.depositId,
111
+ transaction: {
112
+ hash: event.transaction!.hash!,
113
+ block: {
114
+ timestamp: event.transaction!.block!.timestamp
115
+ }
116
+ },
117
+ }
118
+
119
+ if (includeHistorical) {
120
+ // Add all rates chronologically
121
+ currencyRates.push(rateInfo)
122
+ } else {
123
+ // Only keep the latest rate (first in desc order, last in asc order)
124
+ if (currencyRates.length === 0) {
125
+ currencyRates.push(rateInfo)
126
+ } else if (sortOrder === 'asc') {
127
+ // Replace with newer rate
128
+ currencyRates[0] = rateInfo
129
+ }
130
+ // For desc order with includeHistorical=false, we only add the first (most recent) rate
131
+ }
132
+
133
+ providerRates.set(currency, currencyRates)
134
+ ratesMap.set(providerKey, providerRates)
135
+ }
136
+
137
+ return ratesMap
138
+ }
139
+
140
+ /**
141
+ * Get conversion rate for a specific currency and verifier combination
142
+ */
143
+ export function getConversionRate(options: GetConversionRateOptions): ConversionRateInfo | ConversionRateInfo[] | null {
144
+ const {
145
+ events,
146
+ currency,
147
+ verifier,
148
+ chainId,
149
+ includeHistory = false,
150
+ fallbackRate
151
+ } = options
152
+
153
+ // Process rates to get organized data
154
+ const providerRates = processConversionRates({
155
+ events,
156
+ includeHistorical: includeHistory,
157
+ chainId,
158
+ })
159
+
160
+ const providerKey = verifierGetsKey(chainId, verifier)
161
+ const rates = providerRates.get(providerKey)?.get(currency)
162
+
163
+ if (rates && rates.length > 0) {
164
+ return includeHistory ? rates : rates[0]
165
+ }
166
+
167
+ // Check fallback rate if provided
168
+ if (fallbackRate &&
169
+ fallbackRate.currency === currency &&
170
+ fallbackRate.verifier === verifier) {
171
+ return {
172
+ depositConversionRateUpdatedId: '0',
173
+ currency: fallbackRate.currency,
174
+ value: fallbackRate.value,
175
+ verifier: fallbackRate.verifier,
176
+ depositId: '0',
177
+ orderId: '0', // Indicate this is a fallback/original rate
178
+ transaction: {
179
+ hash: '0',
180
+ block: {
181
+ timestamp: '0'
182
+ }
183
+ }
184
+ }
185
+ }
186
+
187
+ return null
188
+ }
189
+
190
+ /**
191
+ * Extract unique currencies from processed provider rates
192
+ */
193
+ export function extractCurrencies(options: ExtractCurrenciesOptions): string[] {
194
+ const { providerRates, providersOnly, currenciesOnly } = options
195
+
196
+ const currencySet = new Set<string>()
197
+
198
+ for (const [providerKey, currencyMap] of providerRates.entries()) {
199
+ // Filter by providers if specified
200
+ if (providersOnly && !providersOnly.includes(providerKey)) {
201
+ continue
202
+ }
203
+
204
+ for (const [currency] of currencyMap.entries()) {
205
+ // Filter by currencies if specified
206
+ if (currenciesOnly && !currenciesOnly.includes(currency)) {
207
+ continue
208
+ }
209
+
210
+ currencySet.add(currency)
211
+ }
212
+ }
213
+
214
+ return Array.from(currencySet)
215
+ }
216
+
217
+ /**
218
+ * Get the latest conversion rate across all providers for a currency
219
+ */
220
+ export function getLatestRateForCurrency(options: {
221
+ providerRates: ProviderRates<ConversionRateInfo>
222
+ currency: Hex
223
+ preferredProvider?: ProviderKey
224
+ }): ConversionRateInfo | null {
225
+ const { providerRates, currency, preferredProvider } = options
226
+
227
+ let latestRate: ConversionRateInfo | null = null
228
+ let latestTimestamp = BigInt(0)
229
+
230
+ // Check preferred provider first
231
+ if (preferredProvider) {
232
+ const providerRate = providerRates.get(preferredProvider)?.get(currency)
233
+ if (providerRate && providerRate.transaction.block.timestamp) {
234
+ const tsmp = BigInt(providerRate.transaction.block.timestamp)
235
+ if (tsmp > latestTimestamp) {
236
+ latestRate = providerRate
237
+ latestTimestamp = tsmp
238
+ }
239
+ }
240
+ }
241
+
242
+ // Check all other providers
243
+ for (const [providerKey, currencyMap] of providerRates.entries()) {
244
+ if (providerKey === preferredProvider) continue // Already checked
245
+
246
+ const rate = currencyMap.get(currency)
247
+ if (rate && rate.transaction.block.timestamp) {
248
+ const tsmp = BigInt(rate.transaction.block.timestamp)
249
+ if (tsmp > latestTimestamp) {
250
+ latestRate = rate
251
+ latestTimestamp = tsmp
252
+ }
253
+ }
254
+ }
255
+
256
+ return latestRate
257
+ }
258
+
259
+ /**
260
+ * Get the primary conversion rate from processed provider rates
261
+ * Returns the first available rate, prioritizing by provider order
262
+ */
263
+ export function getPrimaryConversionRate(providerRates: ProviderRates<ConversionRateInfo>): ConversionRateInfo | null {
264
+ for (const [, currencyMap] of providerRates) {
265
+ const usd = currencyMap.get(SUPPORTED_CURRENCIES.USD.contractId)
266
+ if (usd) {
267
+ return usd
268
+ }
269
+ }
270
+ for (const [, currencyMap] of providerRates) {
271
+ for (const [, rate] of currencyMap.entries()) {
272
+ return rate
273
+ }
274
+ }
275
+ return null
276
+ }
277
+
278
+ /**
279
+ * Get primary conversion rate for a specific currency across all providers
280
+ */
281
+ export function getPrimaryRateForCurrency(
282
+ providerRates: ProviderRates<ConversionRateInfo>,
283
+ currency: Hex
284
+ ): ConversionRateInfo | null {
285
+ for (const [, currencyMap] of providerRates) {
286
+ const rates = currencyMap.get(currency)
287
+ if (rates) {
288
+ return rates
289
+ }
290
+ }
291
+ return null
292
+ }
293
+
294
+ /**
295
+ * Get the primary verifier (payment provider) from conversion rates
296
+ */
297
+ export function getPrimaryVerifier(providerRates: ProviderRates<ConversionRateInfo>): string | null {
298
+ const primaryRate = getPrimaryConversionRate(providerRates)
299
+ return primaryRate?.verifier || null
300
+ }
301
+
302
+ /**
303
+ * Get the primary currency from conversion rates
304
+ */
305
+ export function getPrimaryCurrency(providerRates: ProviderRates<ConversionRateInfo>): string | null {
306
+ const primaryRate = getPrimaryConversionRate(providerRates)
307
+ return primaryRate?.currency || null
308
+ }
309
+
310
+ /**
311
+ * Convert token input to currency
312
+ */
313
+ export const convertTokenInputToCurrency = ({ token, amountOutInt, rate }: {
314
+ token: TokenInfo
315
+ amountOutInt: bigint
316
+ rate: bigint
317
+ }): bigint => {
318
+ const oneCent = 10n ** BigInt(token.decimals - 2)
319
+ const oneEther = 10n ** 18n
320
+ const amountInInt = amountOutInt * BigInt(rate) / oneEther
321
+ const remainder = amountInInt % oneCent
322
+ const negativeSpace = oneCent - remainder
323
+ const finalAmountInInt = amountInInt + (negativeSpace === oneCent ? 0n : negativeSpace)
324
+ return finalAmountInInt
325
+ }
326
+
327
+ /**
328
+ * Convert currency amount to token amount out (inverse of convertTokenInputToCurrency)
329
+ * This accounts for the cent-rounding applied in the original function
330
+ */
331
+ export const convertCurrencyToTokenOutput = ({
332
+ token,
333
+ currency,
334
+ currencyAmountInt,
335
+ rate,
336
+ }: {
337
+ token: TokenInfo
338
+ currency: Currency
339
+ currencyAmountInt: bigint
340
+ rate: bigint
341
+ }): bigint => {
342
+ const smallestUnit = 10n ** BigInt(token.decimals - currency.decimals)
343
+ const oneEther = 10n ** 18n
344
+
345
+ // First, we need to find the original amount before cent-rounding was applied
346
+ // The original function rounds up to the nearest cent, so we need to find
347
+ // the maximum token amount that would result in this currency amount after rounding
348
+
349
+ // Start with the direct inverse calculation
350
+ // console.log('currencyAmountInt', currencyAmountInt, smallestUnit, converter, rate)
351
+ let tokenAmountOut = (currencyAmountInt * smallestUnit * oneEther) / rate
352
+
353
+ const remainder = tokenAmountOut % smallestUnit
354
+ const finalTokenAmountOut = (tokenAmountOut - remainder)
355
+ return finalTokenAmountOut
356
+ }