@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/src/time.ts ADDED
@@ -0,0 +1,123 @@
1
+ /** Interval in milliseconds */
2
+ export const intervalIn = {
3
+ yearInDays: 365,
4
+ quarterInMonths: 3,
5
+ monthInDays: 30,
6
+ hourInDays: 24,
7
+ daysInWeek: 7,
8
+ minuteInHours: 60,
9
+ secondInMinutes: 60,
10
+ millisecondInSecond: 1000,
11
+ } as const
12
+ const second = intervalIn.millisecondInSecond
13
+ const minute = intervalIn.secondInMinutes * second
14
+ const hour = intervalIn.minuteInHours * minute
15
+ const day = intervalIn.hourInDays * hour
16
+ const month = intervalIn.monthInDays * day
17
+ const year = intervalIn.yearInDays * month
18
+
19
+ /** Intervals in milliseconds */
20
+ export const interval = {
21
+ second,
22
+ minute,
23
+ hour,
24
+ day,
25
+ month,
26
+ year,
27
+ } as const
28
+
29
+ /** Default display intervals */
30
+ export const defaultDisplayInterval = {
31
+ hour: 7 * 24,
32
+ day: 6 * 30,
33
+ month: 60,
34
+ } as const
35
+
36
+ /**
37
+ * Format a timestamp
38
+ * @param timestamp - The timestamp to format
39
+ * @param param1.anchor - The anchor date
40
+ * @param param1.showSeconds - Whether to show seconds
41
+ * @returns The formatted timestamp
42
+ */
43
+ export const formatTimestamp = (timestamp: number | Date | string, {
44
+ anchor = new Date(),
45
+ showSeconds = false,
46
+ }: {
47
+ anchor?: Date,
48
+ showSeconds?: boolean,
49
+ } = {}) => {
50
+ const date = new Date(timestamp)
51
+ const diffMs = anchor.getTime() - date.getTime()
52
+ const diffMins = Math.floor(diffMs / interval.minute)
53
+ const diffHours = Math.floor(diffMs / interval.hour)
54
+ const diffDays = Math.floor(diffMs / interval.day)
55
+ const diffMonths = Math.floor(diffDays / interval.month)
56
+ if (diffMonths > 0) {
57
+ if (diffMonths < intervalIn.quarterInMonths) {
58
+ return `${diffMonths}mo ${diffDays % intervalIn.monthInDays}d ago`
59
+ }
60
+ return `${diffMonths}mo ago`
61
+ }
62
+ if (diffMins < 1) {
63
+ if (showSeconds) {
64
+ if (diffMs < 10 * interval.second) {
65
+ return 'Just now'
66
+ }
67
+ return `${Math.floor(diffMs / interval.second)}s ago`
68
+ }
69
+ return 'Just now'
70
+ }
71
+ if (diffMins < intervalIn.secondInMinutes) {
72
+ return `${diffMins}m ago`
73
+ } else if (diffHours < intervalIn.hourInDays) {
74
+ if (diffHours < 6) {
75
+ return `${diffHours}h ${diffMins % intervalIn.minuteInHours}m ago`
76
+ }
77
+ return `${diffHours}h ago`
78
+ } else {
79
+ if (diffDays < intervalIn.daysInWeek) {
80
+ return `${diffDays}d ${diffHours % intervalIn.hourInDays}h ago`
81
+ }
82
+ return `${diffDays}d ago`
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Format a duration
88
+ * @param param0.start - The start date
89
+ * @param param0.until - The end date
90
+ * @returns The formatted duration
91
+ */
92
+ export const formatDuration = ({
93
+ start = new Date(),
94
+ until = new Date(),
95
+ }: {
96
+ start?: Date,
97
+ until?: Date,
98
+ } = {}) => {
99
+ const diffMs = until.getTime() - start.getTime()
100
+ const diffMins = Math.floor(diffMs / interval.minute)
101
+ const diffHours = Math.floor(diffMs / interval.hour)
102
+ const diffDays = Math.floor(diffMs / interval.day)
103
+ const diffMonths = Math.floor(diffDays / intervalIn.monthInDays)
104
+ if (diffMonths > 0) {
105
+ return `${diffMonths}mo ${diffDays % intervalIn.monthInDays}d`
106
+ }
107
+ if (diffDays > 0) {
108
+ return `${diffDays}d ${diffHours % intervalIn.hourInDays}h`
109
+ }
110
+ if (diffHours > 0) {
111
+ return `${diffHours}h ${diffMins % intervalIn.minuteInHours}m`
112
+ }
113
+ return `${diffMins % intervalIn.minuteInHours}m`
114
+ }
115
+
116
+ /**
117
+ * Sleep for a given number of milliseconds
118
+ * @param ms - The number of milliseconds to sleep
119
+ * @returns A promise that resolves after the given number of milliseconds
120
+ */
121
+ export const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
122
+
123
+ export type TimeResolutionKey = "hour" | "day" | "month"
package/src/tokens.ts ADDED
@@ -0,0 +1,159 @@
1
+ import {
2
+ createPublicClient, getAddress, http, type Chain
3
+ } from 'viem'
4
+ import { base, pulsechain } from 'viem/chains'
5
+ import { chains, type ChainId } from './chain.js'
6
+
7
+ /**
8
+ * Complete token information including address, symbol, name, and decimals.
9
+ * This is the root interface for all token-related operations.
10
+ */
11
+ export interface TokenInfo {
12
+ address: string
13
+ decimals: number
14
+ symbol: string
15
+ name: string
16
+ chainId: ChainId
17
+ mainnetAddress?: string
18
+ }
19
+
20
+ const BASE_USDC = {
21
+ address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
22
+ decimals: 6,
23
+ symbol: 'USDC',
24
+ name: 'USD Coin',
25
+ chainId: base.id,
26
+ } as TokenInfo
27
+
28
+ export const PULSECHAIN_USDC = {
29
+ address: '0x15D38573d2feeb82e7ad5187aB8c1D52810B1f07',
30
+ decimals: 6,
31
+ symbol: 'USDC',
32
+ name: 'USD Coin from Ethereum',
33
+ chainId: pulsechain.id,
34
+ mainnetAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
35
+ } as TokenInfo
36
+
37
+ /** All tokens info */
38
+ export const allTokensInfo = {
39
+ [base.id]: {
40
+ // Base Network Tokens
41
+ USDC: BASE_USDC,
42
+ // USDT: {
43
+ // address: '0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2',
44
+ // decimals: 6,
45
+ // symbol: 'USDT',
46
+ // name: 'Tether USD',
47
+ // chainId: base.id,
48
+ // } as TokenInfo,
49
+ // ETH: {
50
+ // address: '0x4200000000000000000000000000000000000006',
51
+ // decimals: 18,
52
+ // symbol: 'WETH',
53
+ // name: 'Wrapped Ether',
54
+ // chainId: base.id,
55
+ // } as TokenInfo,
56
+ },
57
+
58
+ // [mainnet.id]: {
59
+ // // Ethereum Mainnet Tokens
60
+ // USDC: {
61
+ // address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
62
+ // decimals: 6,
63
+ // symbol: 'USDC',
64
+ // name: 'USD Coin',
65
+ // chainId: mainnet.id,
66
+ // } as TokenInfo,
67
+ // USDT: {
68
+ // address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
69
+ // decimals: 6,
70
+ // symbol: 'USDT',
71
+ // name: 'Tether USD',
72
+ // chainId: mainnet.id,
73
+ // } as TokenInfo,
74
+ // WETH: {
75
+ // address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
76
+ // decimals: 18,
77
+ // symbol: 'WETH',
78
+ // name: 'Wrapped Ether',
79
+ // chainId: mainnet.id,
80
+ // } as TokenInfo,
81
+ // ETH: {
82
+ // address: zeroAddress,
83
+ // decimals: 18,
84
+ // symbol: 'ETH',
85
+ // name: 'Ether',
86
+ // chainId: mainnet.id,
87
+ // } as TokenInfo,
88
+ // DAI: {
89
+ // address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
90
+ // decimals: 18,
91
+ // symbol: 'DAI',
92
+ // name: 'Dai Stablecoin',
93
+ // chainId: mainnet.id,
94
+ // } as TokenInfo,
95
+ // },
96
+ [pulsechain.id]: {
97
+ USDC: PULSECHAIN_USDC,
98
+ },
99
+ // USDT: {
100
+ // address: '0x0Cb6F5a34ad42ec934882A05265A7d5F59b51A2f',
101
+ // decimals: 6,
102
+ // symbol: 'USDT',
103
+ // name: 'Tether USD from Ethereum',
104
+ // chainId: pulsechain.id,
105
+ // mainnetAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
106
+ // } as TokenInfo,
107
+ // ETH: {
108
+ // address: '0x02DcdD04e3F455D838cd1249292C58f3B79e3C3C',
109
+ // decimals: 18,
110
+ // symbol: 'WETH',
111
+ // name: 'Wrapped Ether from Ethereum',
112
+ // chainId: pulsechain.id,
113
+ // mainnetAddress: '0x0000000000000000000000000000000000000000',
114
+ // } as TokenInfo,
115
+ } as Record<ChainId, Record<string, TokenInfo>>
116
+
117
+ export const getDefaultToken = (chainId: ChainId | null) => {
118
+ return allTokensInfo[chainId ?? pulsechain.id]?.USDC ?? null
119
+ }
120
+
121
+ export const getDefaultTokens = (chainIds: ChainId[]) => {
122
+ return chainIds.length ? chainIds.map((chainId) => getDefaultToken(chainId)) : chains.map((chain) => getDefaultToken(chain.id))
123
+ }
124
+
125
+ /**
126
+ * Get tokens by chain ID
127
+ * @param chainId - The chain ID
128
+ * @returns Array of tokens for that chain
129
+ */
130
+ export const getTokensByChainId = (chainId: ChainId): TokenInfo[] => {
131
+ return Object.values(allTokensInfo[chainId] ?? {})
132
+ }
133
+
134
+ /**
135
+ * Get a token by address
136
+ * @param address - The address of the token
137
+ * @returns The token info
138
+ */
139
+ export const tokenByAddress = (chainId: ChainId, address: string | undefined): TokenInfo | null => {
140
+ return !address ? null : Object.values(allTokensInfo[chainId] ?? {}).find(token => getAddress(token.address) === getAddress(address)) ?? null
141
+ }
142
+
143
+ /**
144
+ * Create a public client for reading from contracts
145
+ * @param chain - The chain
146
+ * @returns The public client
147
+ */
148
+ export const publicClient = (chain: Chain) => createPublicClient({
149
+ chain,
150
+ transport: http()
151
+ })
152
+
153
+ // export const pulseChainTokenToMainnet = (pulsechainToken: TokenInfo) => {
154
+ // const tokens = allTokensInfo[mainnet.id]
155
+ // const token = Object.values(tokens).find((t) => (
156
+ // getAddress(t.address) === getAddress(pulsechainToken.mainnetAddress!)
157
+ // ))
158
+ // return token!
159
+ // }
package/src/units.ts ADDED
@@ -0,0 +1,120 @@
1
+ import { formatUnits as viemFormatUnits, parseUnits as viemParseUnits } from 'viem'
2
+ import type { TokenInfo } from './tokens.js'
3
+
4
+ export const oneEther = 10n ** 18n
5
+
6
+ // Add thousands separators to a number string
7
+ export function addThousandsSeparator(numberString: string, delimiter: string = ','): string {
8
+ let [integerPart, fractionalPart] = numberString.split('.')
9
+
10
+ integerPart = integerPart || '0'
11
+ fractionalPart = fractionalPart || ''
12
+
13
+ // Add thousands separators to integer part
14
+ const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, delimiter)
15
+
16
+ // Return with fractional part if it exists
17
+ return fractionalPart ? `${formattedInteger}.${fractionalPart}` : formattedInteger
18
+ }
19
+
20
+ // // Format currency amounts with exactly 2 decimal places and thousands separators
21
+ // export function formatCurrencyDisplay(amount: bigint, decimals: number): string {
22
+ // const formatted = formatUnits(amount, decimals)
23
+ // const [integerPart, fractionalPart = ''] = formatted.split('.')
24
+
25
+ // // Ensure exactly 2 decimal places for currency
26
+ // const paddedFractional = fractionalPart.padEnd(2, '0').substring(0, 2)
27
+ // const numberString = `${integerPart}.${paddedFractional}`
28
+
29
+ // // Add thousands separators for better readability
30
+ // return addThousandsSeparator(numberString)
31
+ // }
32
+
33
+ // Options for formatRateDisplay function
34
+ export interface FormatRateDisplayOptions {
35
+ decimals: number
36
+ truncateDecimals?: number
37
+ padDecimals?: number
38
+ cutoff?: bigint
39
+ delimiter?: string
40
+ roundUp?: boolean
41
+ }
42
+
43
+ export const parseUnits = (amount: string, options: Pick<FormatRateDisplayOptions, 'decimals' | 'delimiter'>): bigint => {
44
+ return viemParseUnits(amount.split(options.delimiter ?? ',').join(''), options.decimals)
45
+ }
46
+
47
+ // Format exchange rates with natural precision, minimum 2 decimal places and thousands separators
48
+ export function formatUnits(amount: bigint, options: FormatRateDisplayOptions): string {
49
+ const { decimals, truncateDecimals, padDecimals = 0, delimiter = ',', cutoff, roundUp = false } = options
50
+ if (cutoff && amount < cutoff) {
51
+ return `<${formatUnits(cutoff, { ...options, truncateDecimals: 0, cutoff: undefined })}`
52
+ }
53
+ if (roundUp && truncateDecimals !== undefined) {
54
+ // const minimumDecimal = decimals - truncateDecimals
55
+ const minimumDecimal = BigInt(decimals - truncateDecimals)
56
+ const minimalMod = amount % (10n ** minimumDecimal)
57
+ const halfMinimalDecimal = (10n ** minimumDecimal) / 2n
58
+ if (minimalMod >= halfMinimalDecimal) {
59
+ amount = amount + (10n ** minimumDecimal) - minimalMod
60
+ }
61
+ }
62
+ const formatted = viemFormatUnits(amount, decimals)
63
+
64
+
65
+ const [integerPart, fractionalPart = ''] = formatted.split('.')
66
+
67
+ // If truncateDecimals is specified, limit decimal places
68
+ let finalFractionalPart = fractionalPart
69
+ if (truncateDecimals !== undefined) {
70
+ finalFractionalPart = fractionalPart.slice(0, truncateDecimals)
71
+ }
72
+
73
+ // Always pad to at least 2 decimal places (or padDecimals)
74
+ if (finalFractionalPart.length < padDecimals) {
75
+ finalFractionalPart = finalFractionalPart.padEnd(padDecimals, '0')
76
+ }
77
+
78
+ // If there are no decimals, still show .00
79
+ const numberString = `${integerPart}.${finalFractionalPart}`
80
+ return addThousandsSeparator(numberString, delimiter)
81
+ }
82
+
83
+ export function formatTokenAmountForDisplay(amount: bigint, options: FormatRateDisplayOptions): string {
84
+ return formatUnits(amount, { truncateDecimals: 2, ...options })
85
+ }
86
+
87
+ // Safe integer parsing without using Number()
88
+ export function parseIntegerSafe(value: string): number | null {
89
+ // Use parseInt instead of Number() for integer parsing
90
+ const parsed = parseInt(value, 10)
91
+ return isNaN(parsed) ? null : parsed
92
+ }
93
+
94
+ // // Get display title for limits (shows original user limits on hover)
95
+ // export function getLimitsDisplayTitle(deposit: { formattedMinAmount: string; formattedMaxAmount: string }): string {
96
+ // return `User set: ${deposit.formattedMinAmount} - ${deposit.formattedMaxAmount}`
97
+ // }
98
+
99
+ // Convert blockchain timestamp (seconds) to Date object
100
+ export function timestampToDate(timestamp: string | number | bigint | undefined | null): Date | null {
101
+ if (!timestamp) return null
102
+
103
+ // Convert seconds to milliseconds for Date constructor
104
+ const timestampMs = Number(timestamp) * 1000
105
+ return new Date(timestampMs)
106
+ }
107
+
108
+ // export function formatTokenAmountForDisplay(amount: bigint, tokenInfo: TokenInfo): string {
109
+ // return formatUnits(amount, { decimals: tokenInfo.decimals })
110
+ // }
111
+
112
+ export const formatAmount = (token: TokenInfo, amount: bigint | null, options: Partial<FormatRateDisplayOptions> = {}) => {
113
+ if (!amount) return null
114
+ return formatUnits(amount, {
115
+ decimals: token.decimals,
116
+ truncateDecimals: Math.min(token.decimals - 4, 8),
117
+ padDecimals: 2,
118
+ ...options,
119
+ })
120
+ }
@@ -0,0 +1,55 @@
1
+ import type { Hex } from "viem"
2
+
3
+ import type { ProviderKey, ValidationDepositData } from "./payment.js"
4
+
5
+ export type ValidationOptions = {
6
+ providerKey: ProviderKey
7
+ telegramHandle: string
8
+ providerId: string
9
+ }
10
+
11
+ export type Zkp2pResponse<T> = {
12
+ success: boolean
13
+ message: string
14
+ statusCode: number
15
+ responseObject: T
16
+ }
17
+
18
+ export type CreateResponse = {
19
+ id: number
20
+ processorName: string
21
+ depositData: string
22
+ hashedOnchainId: Hex
23
+ createdAt: string
24
+ }
25
+
26
+ export type PayeeDetails = {
27
+ id: number
28
+ processorName: string
29
+ createdAt: string
30
+ hashedOnchainId: Hex
31
+ depositData: ValidationDepositData
32
+ }
33
+
34
+ export type IntentValidationOptions = {
35
+ processorName: ProviderKey
36
+ depositId: string
37
+ tokenAmount: string
38
+ payeeDetails: Hex
39
+ toAddress: Hex
40
+ fiatCurrencyCode: Hex
41
+ chainId: string
42
+ }
43
+
44
+ export type IntentValidation = {
45
+ signedIntent: Hex
46
+ intentData: {
47
+ depositId: string
48
+ tokenAmount: string
49
+ recipientAddress: Hex
50
+ verifierAddress: Hex
51
+ currencyCodeHash: Hex
52
+ gatingServiceSignature: Hex
53
+ },
54
+ depositData: ValidationDepositData
55
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "useDefineForClassFields": true,
5
+ "module": "NodeNext",
6
+ "composite": true,
7
+ "allowJs": true,
8
+ "outDir": "./dist",
9
+ "strict": true,
10
+ "isolatedModules": true,
11
+ "skipLibCheck": true,
12
+ "moduleDetection": "force",
13
+ "esModuleInterop": true,
14
+ "resolveJsonModule": true,
15
+ "moduleResolution": "nodenext"
16
+ },
17
+ "include": ["src/**/*.ts", "src/**/*.json", "src/**/*.svg", "scripts/**/*.ts"]
18
+ }