@provex/utils 1.0.16 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/chain.d.ts +436 -4
- package/dist/src/chain.js +80 -0
- package/dist/src/chain.test.d.ts +1 -0
- package/dist/src/chain.test.js +112 -0
- package/dist/src/contracts.d.ts +196 -20
- package/dist/src/contracts.js +245 -17
- package/dist/src/contracts.test.d.ts +1 -0
- package/dist/src/contracts.test.js +243 -0
- package/dist/src/conversionRates.d.ts +99 -8
- package/dist/src/conversionRates.js +74 -4
- package/dist/src/conversionRates.test.d.ts +1 -0
- package/dist/src/conversionRates.test.js +473 -0
- package/dist/src/currencies.d.ts +113 -9
- package/dist/src/currencies.js +119 -38
- package/dist/src/currencies.test.d.ts +1 -0
- package/dist/src/currencies.test.js +343 -0
- package/dist/src/data/countries.json +166 -166
- package/dist/src/deposit.d.ts +69 -1
- package/dist/src/deposit.js +52 -1
- package/dist/src/depositKeys.d.ts +154 -0
- package/dist/src/depositKeys.js +104 -0
- package/dist/src/depositKeys.test.d.ts +1 -0
- package/dist/src/depositKeys.test.js +231 -0
- package/dist/src/endpoints.d.ts +51 -0
- package/dist/src/endpoints.js +70 -0
- package/dist/src/endpoints.test.d.ts +1 -0
- package/dist/src/endpoints.test.js +54 -0
- package/dist/src/events.d.ts +40 -2
- package/dist/src/events.js +86 -10
- package/dist/src/events.test.d.ts +1 -0
- package/dist/src/events.test.js +111 -0
- package/dist/src/fees.d.ts +136 -0
- package/dist/src/fees.js +178 -0
- package/dist/src/fees.test.d.ts +1 -0
- package/dist/src/fees.test.js +165 -0
- package/dist/src/ids.d.ts +125 -22
- package/dist/src/ids.js +211 -61
- package/dist/src/ids.test.d.ts +1 -0
- package/dist/src/ids.test.js +607 -0
- package/dist/src/index.d.ts +30 -1
- package/dist/src/index.js +30 -1
- package/dist/src/input.d.ts +23 -4
- package/dist/src/input.js +23 -4
- package/dist/src/intent.d.ts +38 -2
- package/dist/src/intent.js +28 -1
- package/dist/src/intent.test.d.ts +1 -0
- package/dist/src/intent.test.js +119 -0
- package/dist/src/json.d.ts +38 -1
- package/dist/src/json.js +38 -2
- package/dist/src/normalization.d.ts +224 -0
- package/dist/src/normalization.js +119 -0
- package/dist/src/normalization.test.d.ts +1 -0
- package/dist/src/normalization.test.js +284 -0
- package/dist/src/participants.d.ts +19 -4
- package/dist/src/participants.js +19 -4
- package/dist/src/payment.d.ts +333 -16
- package/dist/src/payment.js +768 -58
- package/dist/src/payment.test.d.ts +1 -0
- package/dist/src/payment.test.js +272 -0
- package/dist/src/peer-types.d.ts +327 -0
- package/dist/src/peer-types.js +169 -0
- package/dist/src/profit.d.ts +26 -5
- package/dist/src/profit.js +31 -8
- package/dist/src/profit.test.d.ts +1 -0
- package/dist/src/profit.test.js +315 -0
- package/dist/src/reputation.d.ts +125 -0
- package/dist/src/reputation.js +188 -0
- package/dist/src/reputation.test.d.ts +1 -0
- package/dist/src/reputation.test.js +156 -0
- package/dist/src/sort.d.ts +49 -10
- package/dist/src/sort.js +40 -9
- package/dist/src/sort.test.d.ts +1 -0
- package/dist/src/sort.test.js +216 -0
- package/dist/src/stateKey.d.ts +35 -0
- package/dist/src/stateKey.js +36 -0
- package/dist/src/stats.d.ts +55 -1
- package/dist/src/stats.js +46 -1
- package/dist/src/stats.test.d.ts +1 -0
- package/dist/src/stats.test.js +104 -0
- package/dist/src/time.d.ts +59 -9
- package/dist/src/time.js +71 -15
- package/dist/src/time.test.d.ts +1 -0
- package/dist/src/time.test.js +231 -0
- package/dist/src/tokens.d.ts +11972 -145
- package/dist/src/tokens.js +98 -17
- package/dist/src/tokens.test.d.ts +1 -0
- package/dist/src/tokens.test.js +193 -0
- package/dist/src/units.d.ts +183 -0
- package/dist/src/units.js +206 -6
- package/dist/src/units.test.d.ts +1 -0
- package/dist/src/units.test.js +327 -0
- package/dist/src/zkp2p-types.d.ts +273 -4
- package/dist/src/zkp2p-types.js +169 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +116 -26
- package/scripts/convert-svg.ts +0 -22
- package/src/chain.ts +0 -27
- package/src/contracts.ts +0 -89
- package/src/conversionRates.ts +0 -356
- package/src/currencies.ts +0 -286
- package/src/data/countries.json +0 -1099
- package/src/deposit.ts +0 -2
- package/src/events.ts +0 -21
- package/src/flags/eur.svg +0 -4
- package/src/flags/usd.svg +0 -25
- package/src/flags.ts +0 -4
- package/src/global.d.ts +0 -9
- package/src/ids.ts +0 -137
- package/src/index.ts +0 -16
- package/src/input.ts +0 -35
- package/src/intent.ts +0 -15
- package/src/json.ts +0 -42
- package/src/participants.ts +0 -14
- package/src/payment.ts +0 -573
- package/src/profit.ts +0 -55
- package/src/sort.ts +0 -49
- package/src/stats.ts +0 -10
- package/src/time.ts +0 -123
- package/src/tokens.ts +0 -159
- package/src/units.ts +0 -120
- package/src/zkp2p-types.ts +0 -55
- package/tsconfig.json +0 -18
package/dist/src/fees.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fee calculation utilities for Provex protocol.
|
|
3
|
+
*
|
|
4
|
+
* Fee Precision Formats:
|
|
5
|
+
* - Basis points (bps): 10000 = 100%, 100 = 1%, 1 = 0.01%
|
|
6
|
+
* - 18 decimals: 1e18 = 100% (like Solidity's ether units)
|
|
7
|
+
*
|
|
8
|
+
* The protocol uses basis points for most fee calculations.
|
|
9
|
+
* Some contracts (Pulsechain V3) use 18-decimal precision which must
|
|
10
|
+
* be normalized to basis points before use.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // Calculate 2% fee on 100 USDC
|
|
14
|
+
* const fee = calculateFeeAmount({ amount: 100_000000n, feeBps: 200n })
|
|
15
|
+
* // fee = 2_000000n (2 USDC)
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Maximum basis points value representing 100%.
|
|
19
|
+
* Used as the denominator in fee calculations.
|
|
20
|
+
*/
|
|
21
|
+
export const BPS_DENOMINATOR = 10000n;
|
|
22
|
+
/**
|
|
23
|
+
* 18-decimal representation of 100%.
|
|
24
|
+
* Used to detect and normalize 18-decimal fee values.
|
|
25
|
+
*/
|
|
26
|
+
export const EIGHTEEN_DECIMAL_100_PERCENT = 10n ** 18n;
|
|
27
|
+
/**
|
|
28
|
+
* Threshold to distinguish basis points from 18-decimal values.
|
|
29
|
+
* If a raw fee value exceeds this, it's likely using 18-decimal precision.
|
|
30
|
+
*/
|
|
31
|
+
const PRECISION_DETECTION_THRESHOLD = 1000000n;
|
|
32
|
+
/**
|
|
33
|
+
* Calculate the fee amount for a given transaction.
|
|
34
|
+
*
|
|
35
|
+
* @param params.amount - Transaction amount in smallest unit (e.g., 6 decimals for USDC)
|
|
36
|
+
* @param params.feeBps - Fee in basis points (200 = 2%)
|
|
37
|
+
* @returns Fee amount in the same units as the input amount
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* // 2% fee on 100 USDC (6 decimals)
|
|
41
|
+
* calculateFeeAmount({ amount: 100_000000n, feeBps: 200n })
|
|
42
|
+
* // Returns: 2_000000n (2 USDC)
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // 0.5% fee on 1000 USDC
|
|
46
|
+
* calculateFeeAmount({ amount: 1000_000000n, feeBps: 50n })
|
|
47
|
+
* // Returns: 5_000000n (5 USDC)
|
|
48
|
+
*/
|
|
49
|
+
export function calculateFeeAmount(params) {
|
|
50
|
+
const { amount, feeBps } = params;
|
|
51
|
+
return (amount * feeBps) / BPS_DENOMINATOR;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Calculate the net amount after deducting fees.
|
|
55
|
+
*
|
|
56
|
+
* @param params.grossAmount - Original amount before fees
|
|
57
|
+
* @param params.feeBps - Fee in basis points (200 = 2%)
|
|
58
|
+
* @returns Amount remaining after fee deduction
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* // Net amount after 2% fee on 100 USDC
|
|
62
|
+
* calculateNetAmount({ grossAmount: 100_000000n, feeBps: 200n })
|
|
63
|
+
* // Returns: 98_000000n (98 USDC)
|
|
64
|
+
*/
|
|
65
|
+
export function calculateNetAmount(params) {
|
|
66
|
+
const { grossAmount, feeBps } = params;
|
|
67
|
+
const feeAmount = calculateFeeAmount({ amount: grossAmount, feeBps });
|
|
68
|
+
return grossAmount - feeAmount;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Format a basis points value as a human-readable percentage string.
|
|
72
|
+
*
|
|
73
|
+
* @param feeBps - Fee in basis points
|
|
74
|
+
* @returns Formatted percentage string (e.g., "2%", "0.50%")
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* formatFeeBps(200n) // "2%"
|
|
78
|
+
* formatFeeBps(50n) // "0.50%"
|
|
79
|
+
* formatFeeBps(0n) // "0%"
|
|
80
|
+
* formatFeeBps(10000n) // "100%"
|
|
81
|
+
* formatFeeBps(1n) // "0.01%"
|
|
82
|
+
*/
|
|
83
|
+
export function formatFeeBps(feeBps) {
|
|
84
|
+
// Convert bps to percentage: bps / 100
|
|
85
|
+
// We use number for display since percentages won't exceed Number.MAX_SAFE_INTEGER
|
|
86
|
+
const percentage = Number(feeBps) / 100;
|
|
87
|
+
// Format with appropriate decimal places
|
|
88
|
+
if (percentage === 0) {
|
|
89
|
+
return '0%';
|
|
90
|
+
}
|
|
91
|
+
// If it's a whole number, show without decimals
|
|
92
|
+
if (Number.isInteger(percentage)) {
|
|
93
|
+
return `${percentage}%`;
|
|
94
|
+
}
|
|
95
|
+
// Otherwise show 2 decimal places, removing trailing zeros
|
|
96
|
+
const formatted = percentage.toFixed(2).replace(/\.?0+$/, '');
|
|
97
|
+
return `${formatted}%`;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Detect the precision of a raw fee value and normalize to basis points.
|
|
101
|
+
*
|
|
102
|
+
* Contracts may use different precisions:
|
|
103
|
+
* - Basis points: 10000 = 100% (most common)
|
|
104
|
+
* - 18 decimals: 1e18 = 100% (like ether, used by some V3 contracts)
|
|
105
|
+
*
|
|
106
|
+
* Heuristic: If value > 1,000,000, assume 18-decimal precision.
|
|
107
|
+
*
|
|
108
|
+
* @param rawFee - Raw fee value from contract
|
|
109
|
+
* @returns Normalized fee information with bps value and detected precision
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* // Basis points input
|
|
113
|
+
* normalizeFeePrecision(200n)
|
|
114
|
+
* // Returns: { percentage: "2%", precision: "bps", normalizedBps: 200n }
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* // 18-decimal input (2%)
|
|
118
|
+
* normalizeFeePrecision(20000000000000000n)
|
|
119
|
+
* // Returns: { percentage: "2%", precision: "18decimals", normalizedBps: 200n }
|
|
120
|
+
*/
|
|
121
|
+
export function normalizeFeePrecision(rawFee) {
|
|
122
|
+
// Detect 18-decimal precision by checking if value is very large
|
|
123
|
+
if (rawFee > PRECISION_DETECTION_THRESHOLD) {
|
|
124
|
+
// 18-decimal precision: 1e18 = 100%
|
|
125
|
+
// Convert to percentage for display
|
|
126
|
+
const percentage = Number(rawFee) / Number(EIGHTEEN_DECIMAL_100_PERCENT) * 100;
|
|
127
|
+
// Normalize to basis points: (rawFee * 10000) / 1e18
|
|
128
|
+
const normalizedBps = (rawFee * BPS_DENOMINATOR) / EIGHTEEN_DECIMAL_100_PERCENT;
|
|
129
|
+
return {
|
|
130
|
+
percentage: formatFromNumber(percentage),
|
|
131
|
+
precision: '18decimals',
|
|
132
|
+
normalizedBps,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// Already in basis points
|
|
136
|
+
return {
|
|
137
|
+
percentage: formatFeeBps(rawFee),
|
|
138
|
+
precision: 'bps',
|
|
139
|
+
normalizedBps: rawFee,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Helper to format a number as a percentage string.
|
|
144
|
+
* @internal
|
|
145
|
+
*/
|
|
146
|
+
function formatFromNumber(percentage) {
|
|
147
|
+
if (percentage === 0) {
|
|
148
|
+
return '0%';
|
|
149
|
+
}
|
|
150
|
+
if (Number.isInteger(percentage)) {
|
|
151
|
+
return `${percentage}%`;
|
|
152
|
+
}
|
|
153
|
+
const formatted = percentage.toFixed(2).replace(/\.?0+$/, '');
|
|
154
|
+
return `${formatted}%`;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Calculate total fees from multiple fee sources.
|
|
158
|
+
*
|
|
159
|
+
* @param fees - Array of fee amounts (all in same precision)
|
|
160
|
+
* @returns Total fee amount
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* // Protocol fee + referrer fee
|
|
164
|
+
* calculateTotalFees([2_000000n, 500000n])
|
|
165
|
+
* // Returns: 2_500000n
|
|
166
|
+
*/
|
|
167
|
+
export function calculateTotalFees(fees) {
|
|
168
|
+
return fees.reduce((total, fee) => total + fee, 0n);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Check if a fee value represents a zero/no fee.
|
|
172
|
+
*
|
|
173
|
+
* @param feeBps - Fee in basis points
|
|
174
|
+
* @returns true if fee is zero
|
|
175
|
+
*/
|
|
176
|
+
export function isZeroFee(feeBps) {
|
|
177
|
+
return feeBps === 0n;
|
|
178
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { BPS_DENOMINATOR, EIGHTEEN_DECIMAL_100_PERCENT, calculateFeeAmount, calculateNetAmount, formatFeeBps, normalizeFeePrecision, calculateTotalFees, isZeroFee, } from './fees.js';
|
|
4
|
+
describe('BPS_DENOMINATOR', () => {
|
|
5
|
+
it('should equal 10000', () => {
|
|
6
|
+
assert.equal(BPS_DENOMINATOR, 10000n);
|
|
7
|
+
});
|
|
8
|
+
});
|
|
9
|
+
describe('EIGHTEEN_DECIMAL_100_PERCENT', () => {
|
|
10
|
+
it('should equal 10^18', () => {
|
|
11
|
+
assert.equal(EIGHTEEN_DECIMAL_100_PERCENT, 10n ** 18n);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
describe('calculateFeeAmount', () => {
|
|
15
|
+
it('should calculate 2% fee correctly', () => {
|
|
16
|
+
// 2% = 200 bps, 100 USDC (6 decimals) = 100_000000
|
|
17
|
+
const result = calculateFeeAmount({ amount: 100000000n, feeBps: 200n });
|
|
18
|
+
assert.equal(result, 2000000n); // 2 USDC
|
|
19
|
+
});
|
|
20
|
+
it('should calculate 0.5% fee correctly', () => {
|
|
21
|
+
// 0.5% = 50 bps
|
|
22
|
+
const result = calculateFeeAmount({ amount: 1000000000n, feeBps: 50n });
|
|
23
|
+
assert.equal(result, 5000000n); // 5 USDC
|
|
24
|
+
});
|
|
25
|
+
it('should return 0 for 0% fee', () => {
|
|
26
|
+
const result = calculateFeeAmount({ amount: 100000000n, feeBps: 0n });
|
|
27
|
+
assert.equal(result, 0n);
|
|
28
|
+
});
|
|
29
|
+
it('should handle 100% fee', () => {
|
|
30
|
+
const result = calculateFeeAmount({ amount: 100000000n, feeBps: 10000n });
|
|
31
|
+
assert.equal(result, 100000000n);
|
|
32
|
+
});
|
|
33
|
+
it('should handle 0 amount', () => {
|
|
34
|
+
const result = calculateFeeAmount({ amount: 0n, feeBps: 200n });
|
|
35
|
+
assert.equal(result, 0n);
|
|
36
|
+
});
|
|
37
|
+
it('should handle small amounts with integer division', () => {
|
|
38
|
+
// 1 USDC with 2% fee = 0.02 USDC, but integer division truncates
|
|
39
|
+
const result = calculateFeeAmount({ amount: 1000000n, feeBps: 200n });
|
|
40
|
+
assert.equal(result, 20000n); // 0.02 USDC
|
|
41
|
+
});
|
|
42
|
+
it('should handle very small amounts that round to zero', () => {
|
|
43
|
+
// 0.0001 USDC with 0.01% fee
|
|
44
|
+
const result = calculateFeeAmount({ amount: 100n, feeBps: 1n });
|
|
45
|
+
assert.equal(result, 0n); // Rounds down to 0
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe('calculateNetAmount', () => {
|
|
49
|
+
it('should calculate net amount after 2% fee', () => {
|
|
50
|
+
const result = calculateNetAmount({ grossAmount: 100000000n, feeBps: 200n });
|
|
51
|
+
assert.equal(result, 98000000n); // 98 USDC
|
|
52
|
+
});
|
|
53
|
+
it('should return full amount for 0% fee', () => {
|
|
54
|
+
const result = calculateNetAmount({ grossAmount: 100000000n, feeBps: 0n });
|
|
55
|
+
assert.equal(result, 100000000n);
|
|
56
|
+
});
|
|
57
|
+
it('should return 0 for 100% fee', () => {
|
|
58
|
+
const result = calculateNetAmount({ grossAmount: 100000000n, feeBps: 10000n });
|
|
59
|
+
assert.equal(result, 0n);
|
|
60
|
+
});
|
|
61
|
+
it('should handle 0.5% fee', () => {
|
|
62
|
+
const result = calculateNetAmount({ grossAmount: 1000000000n, feeBps: 50n });
|
|
63
|
+
assert.equal(result, 995000000n); // 995 USDC
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('formatFeeBps', () => {
|
|
67
|
+
it('should format 0% correctly', () => {
|
|
68
|
+
assert.equal(formatFeeBps(0n), '0%');
|
|
69
|
+
});
|
|
70
|
+
it('should format whole percentages without decimals', () => {
|
|
71
|
+
assert.equal(formatFeeBps(100n), '1%');
|
|
72
|
+
assert.equal(formatFeeBps(200n), '2%');
|
|
73
|
+
assert.equal(formatFeeBps(500n), '5%');
|
|
74
|
+
assert.equal(formatFeeBps(1000n), '10%');
|
|
75
|
+
assert.equal(formatFeeBps(10000n), '100%');
|
|
76
|
+
});
|
|
77
|
+
it('should format fractional percentages with decimals', () => {
|
|
78
|
+
assert.equal(formatFeeBps(50n), '0.5%');
|
|
79
|
+
assert.equal(formatFeeBps(1n), '0.01%');
|
|
80
|
+
assert.equal(formatFeeBps(25n), '0.25%');
|
|
81
|
+
assert.equal(formatFeeBps(150n), '1.5%');
|
|
82
|
+
});
|
|
83
|
+
it('should remove trailing zeros', () => {
|
|
84
|
+
assert.equal(formatFeeBps(250n), '2.5%');
|
|
85
|
+
assert.equal(formatFeeBps(300n), '3%');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe('normalizeFeePrecision', () => {
|
|
89
|
+
describe('basis points input', () => {
|
|
90
|
+
it('should detect and normalize 0 bps', () => {
|
|
91
|
+
const result = normalizeFeePrecision(0n);
|
|
92
|
+
assert.equal(result.precision, 'bps');
|
|
93
|
+
assert.equal(result.normalizedBps, 0n);
|
|
94
|
+
assert.equal(result.percentage, '0%');
|
|
95
|
+
});
|
|
96
|
+
it('should detect and normalize 200 bps (2%)', () => {
|
|
97
|
+
const result = normalizeFeePrecision(200n);
|
|
98
|
+
assert.equal(result.precision, 'bps');
|
|
99
|
+
assert.equal(result.normalizedBps, 200n);
|
|
100
|
+
assert.equal(result.percentage, '2%');
|
|
101
|
+
});
|
|
102
|
+
it('should detect and normalize small bps values', () => {
|
|
103
|
+
const result = normalizeFeePrecision(50n);
|
|
104
|
+
assert.equal(result.precision, 'bps');
|
|
105
|
+
assert.equal(result.normalizedBps, 50n);
|
|
106
|
+
assert.equal(result.percentage, '0.5%');
|
|
107
|
+
});
|
|
108
|
+
it('should detect and normalize 10000 bps (100%)', () => {
|
|
109
|
+
const result = normalizeFeePrecision(10000n);
|
|
110
|
+
assert.equal(result.precision, 'bps');
|
|
111
|
+
assert.equal(result.normalizedBps, 10000n);
|
|
112
|
+
assert.equal(result.percentage, '100%');
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
describe('18-decimal input', () => {
|
|
116
|
+
it('should detect and normalize 18-decimal 2%', () => {
|
|
117
|
+
// 2% in 18 decimals = 0.02 * 1e18 = 2e16
|
|
118
|
+
const rawFee = 20000000000000000n;
|
|
119
|
+
const result = normalizeFeePrecision(rawFee);
|
|
120
|
+
assert.equal(result.precision, '18decimals');
|
|
121
|
+
assert.equal(result.normalizedBps, 200n);
|
|
122
|
+
assert.equal(result.percentage, '2%');
|
|
123
|
+
});
|
|
124
|
+
it('should detect and normalize 18-decimal 0.5%', () => {
|
|
125
|
+
// 0.5% in 18 decimals = 0.005 * 1e18 = 5e15
|
|
126
|
+
const rawFee = 5000000000000000n;
|
|
127
|
+
const result = normalizeFeePrecision(rawFee);
|
|
128
|
+
assert.equal(result.precision, '18decimals');
|
|
129
|
+
assert.equal(result.normalizedBps, 50n);
|
|
130
|
+
assert.equal(result.percentage, '0.5%');
|
|
131
|
+
});
|
|
132
|
+
it('should detect and normalize 18-decimal 100%', () => {
|
|
133
|
+
// 100% in 18 decimals = 1e18
|
|
134
|
+
const rawFee = 1000000000000000000n;
|
|
135
|
+
const result = normalizeFeePrecision(rawFee);
|
|
136
|
+
assert.equal(result.precision, '18decimals');
|
|
137
|
+
assert.equal(result.normalizedBps, 10000n);
|
|
138
|
+
assert.equal(result.percentage, '100%');
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
describe('calculateTotalFees', () => {
|
|
143
|
+
it('should sum multiple fees', () => {
|
|
144
|
+
const result = calculateTotalFees([2000000n, 500000n, 100000n]);
|
|
145
|
+
assert.equal(result, 2600000n);
|
|
146
|
+
});
|
|
147
|
+
it('should return 0 for empty array', () => {
|
|
148
|
+
const result = calculateTotalFees([]);
|
|
149
|
+
assert.equal(result, 0n);
|
|
150
|
+
});
|
|
151
|
+
it('should return single fee for array with one element', () => {
|
|
152
|
+
const result = calculateTotalFees([5000000n]);
|
|
153
|
+
assert.equal(result, 5000000n);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
describe('isZeroFee', () => {
|
|
157
|
+
it('should return true for 0 bps', () => {
|
|
158
|
+
assert.equal(isZeroFee(0n), true);
|
|
159
|
+
});
|
|
160
|
+
it('should return false for non-zero bps', () => {
|
|
161
|
+
assert.equal(isZeroFee(1n), false);
|
|
162
|
+
assert.equal(isZeroFee(100n), false);
|
|
163
|
+
assert.equal(isZeroFee(200n), false);
|
|
164
|
+
});
|
|
165
|
+
});
|
package/dist/src/ids.d.ts
CHANGED
|
@@ -1,40 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview ID generation and parsing utilities for the indexer.
|
|
3
|
+
*
|
|
4
|
+
* The indexer uses composite IDs (orderIds) to uniquely identify and order
|
|
5
|
+
* events across multiple chains. Each ID type encodes different information:
|
|
6
|
+
*
|
|
7
|
+
* - **deposit**: [escrow (20)][localId (8)][chainId (4)] = 32 bytes
|
|
8
|
+
* - **block**: [timestamp (8)][chainId (4)] = 12 bytes
|
|
9
|
+
* - **transaction**: [timestamp (8)][txIndex (4)][chainId (4)] = 16 bytes
|
|
10
|
+
* - **log**: [timestamp (8)][txIndex (4)][logIndex (4)][chainId (4)] = 20 bytes
|
|
11
|
+
* - **order**: [timestamp (8)][txIndex (4)][eventType (1)][logIndex (4)][chainId (4)] = 21 bytes
|
|
12
|
+
* - **stat**: [timestamp (8)][hash (20)][chainId (4)] = 32 bytes
|
|
13
|
+
*
|
|
14
|
+
* @remarks
|
|
15
|
+
* The orderId structure ensures:
|
|
16
|
+
* - Global ordering across chains by timestamp
|
|
17
|
+
* - Deterministic ordering within same block
|
|
18
|
+
* - Parseability to extract original components
|
|
19
|
+
*
|
|
20
|
+
* @see {@link events} for event type definitions
|
|
21
|
+
* @see {@link depositKeys} for deposit compound keys
|
|
22
|
+
*/
|
|
1
23
|
import { type Hex } from "viem";
|
|
2
24
|
import { type EventType } from "./events.js";
|
|
3
25
|
import { type TimeResolutionKey } from "./time.js";
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
depositId: Hex;
|
|
8
|
-
};
|
|
9
|
-
export type DepositCurrencyInputs = DepositVerifierInputs & {
|
|
10
|
-
currency: Hex;
|
|
11
|
-
};
|
|
12
|
-
export type ConversionRateUpdatedInputs = DepositCurrencyInputs & {
|
|
13
|
-
changeId: number;
|
|
14
|
-
};
|
|
26
|
+
import type { StatEvent } from "./stats.js";
|
|
27
|
+
import { type ChainId } from "./chain.js";
|
|
28
|
+
/** Input parameters for provider hash generation */
|
|
15
29
|
export type ProviderHashInputs = {
|
|
16
30
|
chainId: bigint | number;
|
|
17
31
|
hash: Hex;
|
|
18
32
|
};
|
|
33
|
+
/** Input parameters for on-chain hash generation */
|
|
19
34
|
export type OnChainHashInputs = {
|
|
20
35
|
chainId: bigint | number;
|
|
21
36
|
hash: Hex;
|
|
22
37
|
};
|
|
38
|
+
/** Input parameters for on-chain address hashing */
|
|
23
39
|
export type OnChainAddressInputs = {
|
|
24
40
|
chainId: bigint | number;
|
|
25
41
|
address: Hex;
|
|
26
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* Generate a chain-specific hash by combining chainId and an existing hash.
|
|
45
|
+
* Creates a unique identifier that includes chain context.
|
|
46
|
+
*/
|
|
27
47
|
export declare const onChainHash: ({ chainId, hash }: OnChainHashInputs) => `0x${string}`;
|
|
28
48
|
export declare const onChainAddress: ({ chainId, address }: OnChainAddressInputs) => `0x${string}`;
|
|
49
|
+
/**
|
|
50
|
+
* ID generator functions for various entity types.
|
|
51
|
+
* Each function creates a unique, deterministic identifier.
|
|
52
|
+
*/
|
|
29
53
|
export declare const ids: {
|
|
54
|
+
/** Block ID: keccak256(chainId || blockHash) */
|
|
30
55
|
block: ({ chainId, hash }: OnChainHashInputs) => `0x${string}`;
|
|
56
|
+
/** Transaction ID: keccak256(chainId || txHash) */
|
|
31
57
|
transaction: ({ chainId, hash }: OnChainHashInputs) => `0x${string}`;
|
|
32
|
-
|
|
58
|
+
/** Provider hash ID: keccak256(chainId || providerHash) */
|
|
33
59
|
providerHash: ({ chainId, hash }: OnChainHashInputs) => `0x${string}`;
|
|
60
|
+
/** Verifier ID: keccak256(chainId || verifierAddress) */
|
|
34
61
|
verifier: ({ chainId, address }: OnChainAddressInputs) => `0x${string}`;
|
|
35
|
-
depositVerifierAdded: ({ depositId, verifier }: DepositVerifierInputs) => `0x${string}`;
|
|
36
|
-
depositCurrencyAdded: ({ depositId, currency, verifier }: DepositCurrencyInputs) => `0x${string}`;
|
|
37
|
-
depositConversionRateUpdated: ({ currency, verifier, depositId, changeId }: ConversionRateUpdatedInputs) => `0x${string}`;
|
|
38
62
|
};
|
|
39
63
|
export type OrderIdInputs = {
|
|
40
64
|
timestamp: bigint;
|
|
@@ -51,22 +75,101 @@ export type OrderIdOrderInputs = OrderIdLogInputs & {
|
|
|
51
75
|
};
|
|
52
76
|
export type DepositIdInputs = {
|
|
53
77
|
chainId: bigint | number;
|
|
78
|
+
escrow: Hex;
|
|
54
79
|
depositId: bigint | number;
|
|
55
80
|
};
|
|
56
81
|
export type LogStatsIdInputs = {
|
|
57
82
|
timestamp: bigint;
|
|
58
83
|
type: TimeResolutionKey;
|
|
59
|
-
|
|
84
|
+
event: StatEvent;
|
|
60
85
|
currency: Hex | null;
|
|
61
86
|
token: Hex;
|
|
62
87
|
verifier: Hex;
|
|
63
88
|
chainId: bigint | number;
|
|
89
|
+
version: 'v2' | 'v3';
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Chunk a hex string into parts of specified byte sizes
|
|
93
|
+
* @param input - The hex string to chunk (with 0x prefix)
|
|
94
|
+
* @param byteSizes - Array of byte sizes for each chunk
|
|
95
|
+
* @returns Array of hex strings, one for each chunk
|
|
96
|
+
*/
|
|
97
|
+
export declare function chunkHex<T extends readonly number[]>(input: Hex, byteSizes: T): {
|
|
98
|
+
[K in keyof T]: Hex;
|
|
64
99
|
};
|
|
100
|
+
/**
|
|
101
|
+
* OrderId serialization and parsing utilities.
|
|
102
|
+
*
|
|
103
|
+
* Each orderId type has a `serialize` method to create the ID from components,
|
|
104
|
+
* and a `parse` method to extract components from an existing ID.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* // Create a deposit orderId
|
|
109
|
+
* const id = orderId.deposit.serialize({ chainId: 8453n, escrow, depositId: 42n })
|
|
110
|
+
*
|
|
111
|
+
* // Parse it back
|
|
112
|
+
* const { chainId, escrow, depositId } = orderId.deposit.parse(id)
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
65
115
|
export declare const orderId: {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Deposit orderId: uniquely identifies a deposit across all chains and escrows.
|
|
118
|
+
* Format: [escrow (20)][localId (8)][chainId (4)] = 32 bytes
|
|
119
|
+
*/
|
|
120
|
+
deposit: {
|
|
121
|
+
serialize: ({ chainId, escrow, depositId }: DepositIdInputs) => `0x${string}`;
|
|
122
|
+
parse: (id: Hex) => {
|
|
123
|
+
escrow: Hex;
|
|
124
|
+
depositId: bigint;
|
|
125
|
+
chainId: ChainId;
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
block: {
|
|
129
|
+
serialize: ({ timestamp, chainId }: OrderIdInputs) => `0x${string}`;
|
|
130
|
+
parse: (id: Hex) => {
|
|
131
|
+
timestamp: bigint;
|
|
132
|
+
chainId: ChainId;
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
transaction: {
|
|
136
|
+
serialize: ({ timestamp, chainId, transactionIndex }: OrderIdTransactionInputs) => `0x${string}`;
|
|
137
|
+
parse: (id: Hex) => {
|
|
138
|
+
timestamp: bigint;
|
|
139
|
+
transactionIndex: number;
|
|
140
|
+
chainId: ChainId;
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
log: {
|
|
144
|
+
serialize: ({ timestamp, chainId, transactionIndex, logIndex }: OrderIdLogInputs) => `0x${string}`;
|
|
145
|
+
parse: (id: Hex) => {
|
|
146
|
+
timestamp: bigint;
|
|
147
|
+
transactionIndex: number;
|
|
148
|
+
logIndex: number;
|
|
149
|
+
chainId: ChainId;
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
order: {
|
|
153
|
+
serialize: ({ timestamp, chainId, transactionIndex, logIndex, name }: OrderIdOrderInputs) => `0x${string}`;
|
|
154
|
+
parse: (id: Hex) => {
|
|
155
|
+
timestamp: bigint;
|
|
156
|
+
transactionIndex: number;
|
|
157
|
+
eventTypeId: number;
|
|
158
|
+
name?: EventType;
|
|
159
|
+
logIndex: number;
|
|
160
|
+
chainId: ChainId;
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
stat: {
|
|
164
|
+
serialize: ({ timestamp, type, event, currency, verifier, token, chainId, version }: LogStatsIdInputs) => `0x${string}`;
|
|
165
|
+
/**
|
|
166
|
+
* Parse a stat orderId to extract timestamp and chainId.
|
|
167
|
+
* Note: The hash component cannot be reversed to get type/event/currency/verifier/token.
|
|
168
|
+
* Format: [timestamp (8)][hash (20)][chainId (4)] = 32 bytes
|
|
169
|
+
*/
|
|
170
|
+
parse: (id: Hex) => {
|
|
171
|
+
timestamp: bigint;
|
|
172
|
+
chainId: ChainId;
|
|
173
|
+
};
|
|
174
|
+
};
|
|
72
175
|
};
|