perps-sdk-ts 1.0.1

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 (117) hide show
  1. package/.claude/settings.local.json +11 -0
  2. package/CONTRACT_METHOD_FIXES.md +189 -0
  3. package/INTEGRATION_SUMMARY.md +219 -0
  4. package/OPTIMIZATION_GUIDE.md +238 -0
  5. package/README.md +384 -0
  6. package/SNAPSHOT_FIX_SUMMARY.md +161 -0
  7. package/SNAPSHOT_OPTIMIZATION_SUMMARY.md +199 -0
  8. package/dist/abis/Referral.d.ts +36 -0
  9. package/dist/abis/Referral.js +4 -0
  10. package/dist/abis/Trading.d.ts +57 -0
  11. package/dist/abis/Trading.js +742 -0
  12. package/dist/abis/erc20.d.ts +51 -0
  13. package/dist/abis/erc20.js +4 -0
  14. package/dist/abis/index.d.ts +8 -0
  15. package/dist/abis/index.js +24 -0
  16. package/dist/abis/multicall.d.ts +85 -0
  17. package/dist/abis/multicall.js +4 -0
  18. package/dist/abis/pairInfos.d.ts +77 -0
  19. package/dist/abis/pairInfos.js +4 -0
  20. package/dist/abis/pairStorage.d.ts +124 -0
  21. package/dist/abis/pairStorage.js +4 -0
  22. package/dist/abis/priceAggregator.d.ts +77 -0
  23. package/dist/abis/priceAggregator.js +4 -0
  24. package/dist/abis/tardingStorage.d.ts +97 -0
  25. package/dist/abis/tardingStorage.js +1295 -0
  26. package/dist/abis.d.ts +623 -0
  27. package/dist/abis.js +49 -0
  28. package/dist/client.d.ts +118 -0
  29. package/dist/client.js +224 -0
  30. package/dist/config.d.ts +43 -0
  31. package/dist/config.js +42 -0
  32. package/dist/crypto/spki.d.ts +55 -0
  33. package/dist/crypto/spki.js +160 -0
  34. package/dist/feed/feed_client.d.ts +68 -0
  35. package/dist/feed/feed_client.js +239 -0
  36. package/dist/index.d.ts +28 -0
  37. package/dist/index.js +87 -0
  38. package/dist/rpc/asset_parameters.d.ts +62 -0
  39. package/dist/rpc/asset_parameters.js +169 -0
  40. package/dist/rpc/blended.d.ts +23 -0
  41. package/dist/rpc/blended.js +55 -0
  42. package/dist/rpc/category_parameters.d.ts +34 -0
  43. package/dist/rpc/category_parameters.js +105 -0
  44. package/dist/rpc/delegation.d.ts +81 -0
  45. package/dist/rpc/delegation.js +180 -0
  46. package/dist/rpc/fee_parameters.d.ts +46 -0
  47. package/dist/rpc/fee_parameters.js +113 -0
  48. package/dist/rpc/multicall.d.ts +83 -0
  49. package/dist/rpc/multicall.js +117 -0
  50. package/dist/rpc/pair_info_queries.d.ts +101 -0
  51. package/dist/rpc/pair_info_queries.js +161 -0
  52. package/dist/rpc/pairs_cache.d.ts +62 -0
  53. package/dist/rpc/pairs_cache.js +240 -0
  54. package/dist/rpc/referral_operations.d.ts +67 -0
  55. package/dist/rpc/referral_operations.js +143 -0
  56. package/dist/rpc/snapshot.d.ts +49 -0
  57. package/dist/rpc/snapshot.js +162 -0
  58. package/dist/rpc/trade.d.ts +84 -0
  59. package/dist/rpc/trade.js +249 -0
  60. package/dist/rpc/trading_operations.d.ts +103 -0
  61. package/dist/rpc/trading_operations.js +295 -0
  62. package/dist/rpc/trading_parameters.d.ts +49 -0
  63. package/dist/rpc/trading_parameters.js +94 -0
  64. package/dist/signers/base.d.ts +24 -0
  65. package/dist/signers/base.js +10 -0
  66. package/dist/signers/kms.d.ts +47 -0
  67. package/dist/signers/kms.js +172 -0
  68. package/dist/signers/local.d.ts +43 -0
  69. package/dist/signers/local.js +64 -0
  70. package/dist/types.d.ts +1419 -0
  71. package/dist/types.js +245 -0
  72. package/dist/utils.d.ts +52 -0
  73. package/dist/utils.js +134 -0
  74. package/examples/advanced-queries.ts +181 -0
  75. package/examples/basic-usage.ts +78 -0
  76. package/examples/delegation-and-referrals.ts +130 -0
  77. package/examples/get-pyth-ids.ts +61 -0
  78. package/examples/kms-signer.ts +31 -0
  79. package/examples/optimized-snapshot.ts +153 -0
  80. package/examples/price-feed-with-sdk-ids.ts +97 -0
  81. package/examples/price-feed.ts +36 -0
  82. package/examples/trading-operations.ts +149 -0
  83. package/package.json +41 -0
  84. package/src/abis/Referral.ts +3 -0
  85. package/src/abis/Trading.ts +741 -0
  86. package/src/abis/erc20.ts +3 -0
  87. package/src/abis/index.ts +8 -0
  88. package/src/abis/multicall.ts +3 -0
  89. package/src/abis/pairInfos.ts +3 -0
  90. package/src/abis/pairStorage.ts +3 -0
  91. package/src/abis/priceAggregator.ts +3 -0
  92. package/src/abis/tardingStorage.ts +1294 -0
  93. package/src/abis.ts +56 -0
  94. package/src/client.ts +373 -0
  95. package/src/config.ts +62 -0
  96. package/src/crypto/spki.ts +197 -0
  97. package/src/feed/feed_client.ts +288 -0
  98. package/src/index.ts +114 -0
  99. package/src/rpc/asset_parameters.ts +217 -0
  100. package/src/rpc/blended.ts +77 -0
  101. package/src/rpc/category_parameters.ts +128 -0
  102. package/src/rpc/delegation.ts +225 -0
  103. package/src/rpc/fee_parameters.ts +150 -0
  104. package/src/rpc/multicall.ts +164 -0
  105. package/src/rpc/pair_info_queries.ts +208 -0
  106. package/src/rpc/pairs_cache.ts +268 -0
  107. package/src/rpc/referral_operations.ts +164 -0
  108. package/src/rpc/snapshot.ts +210 -0
  109. package/src/rpc/trade.ts +306 -0
  110. package/src/rpc/trading_operations.ts +378 -0
  111. package/src/rpc/trading_parameters.ts +127 -0
  112. package/src/signers/base.ts +27 -0
  113. package/src/signers/kms.ts +212 -0
  114. package/src/signers/local.ts +70 -0
  115. package/src/types.ts +410 -0
  116. package/src/utils.ts +155 -0
  117. package/tsconfig.json +18 -0
@@ -0,0 +1,208 @@
1
+ import { Contract } from 'ethers';
2
+ import { Trade, fromBlockchain6, fromBlockchain10, fromBlockchain12, toBlockchain6, toBlockchain10 } from '../types';
3
+
4
+ /**
5
+ * PairInfo Queries RPC
6
+ * Handles querying pair-related information like price impact, skew, fees, and loss protection
7
+ */
8
+ export class PairInfoQueriesRPC {
9
+ constructor(
10
+ private pairInfosContract: Contract,
11
+ private priceAggregatorContract: Contract
12
+ ) {}
13
+
14
+ /**
15
+ * Get loss protection tier for a trade
16
+ * @param trade - Trade information
17
+ * @param isPnl - Whether calculating for PnL
18
+ * @returns Loss protection tier
19
+ */
20
+ async getLossProtectionTier(trade: Trade, isPnl: boolean = false): Promise<number> {
21
+ // Convert trade to blockchain format
22
+ const tradeStruct = {
23
+ trader: trade.trader,
24
+ pairIndex: trade.pairIndex,
25
+ index: trade.index,
26
+ initialPosToken: toBlockchain6(trade.initialPosToken),
27
+ positionSizeUSDC: toBlockchain6(trade.positionSizeUSDC),
28
+ openPrice: toBlockchain10(trade.openPrice),
29
+ buy: trade.buy,
30
+ leverage: toBlockchain10(trade.leverage),
31
+ tp: toBlockchain10(trade.tp),
32
+ sl: toBlockchain10(trade.sl),
33
+ timestamp: trade.timestamp,
34
+ };
35
+
36
+ const tier = await this.pairInfosContract.lossProtectionTier(tradeStruct, isPnl);
37
+ return Number(tier);
38
+ }
39
+
40
+ /**
41
+ * Get price impact spread for a position
42
+ * @param pairIndex - Trading pair index
43
+ * @param isLong - True for long position
44
+ * @param positionSizeUsdc - Position size in USDC
45
+ * @param isOpen - Whether opening or closing
46
+ * @returns Price impact spread percentage
47
+ */
48
+ async getPriceImpactSpread(
49
+ pairIndex: number,
50
+ isLong: boolean,
51
+ positionSizeUsdc: number,
52
+ isOpen: boolean
53
+ ): Promise<number> {
54
+ const positionSize = toBlockchain6(positionSizeUsdc);
55
+ const spread = await this.pairInfosContract.getPriceImpactSpread(
56
+ pairIndex,
57
+ isLong,
58
+ positionSize,
59
+ isOpen
60
+ );
61
+ return fromBlockchain10(spread);
62
+ }
63
+
64
+ /**
65
+ * Get skew impact spread for a position
66
+ * @param pairIndex - Trading pair index
67
+ * @param isLong - True for long position
68
+ * @param positionSizeUsdc - Position size in USDC
69
+ * @param isOpen - Whether opening or closing
70
+ * @returns Skew impact spread percentage
71
+ */
72
+ async getSkewImpactSpread(
73
+ pairIndex: number,
74
+ isLong: boolean,
75
+ positionSizeUsdc: number,
76
+ isOpen: boolean
77
+ ): Promise<number> {
78
+ const positionSize = toBlockchain6(positionSizeUsdc);
79
+ const spread = await this.pairInfosContract.getSkewImpactSpread(
80
+ pairIndex,
81
+ isLong,
82
+ positionSize,
83
+ isOpen
84
+ );
85
+ return fromBlockchain10(spread);
86
+ }
87
+
88
+ /**
89
+ * Get price impact percentage for a position
90
+ * @param pairIndex - Trading pair index
91
+ * @param isLong - True for long position
92
+ * @param positionSizeUsdc - Position size in USDC
93
+ * @returns Price impact percentage
94
+ */
95
+ async getPriceImpactP(
96
+ pairIndex: number,
97
+ isLong: boolean,
98
+ positionSizeUsdc: number
99
+ ): Promise<number> {
100
+ const positionSize = toBlockchain6(positionSizeUsdc);
101
+ const impact = await this.pairInfosContract.getPriceImpactP(pairIndex, isLong, positionSize);
102
+ return fromBlockchain10(impact);
103
+ }
104
+
105
+ /**
106
+ * Get opening fee in USDC for a position
107
+ * @param pairIndex - Trading pair index
108
+ * @param positionSizeUsdc - Position size in USDC
109
+ * @param isLong - True for long position
110
+ * @returns Opening fee in USDC
111
+ */
112
+ async getOpenFeeUsdc(
113
+ pairIndex: number,
114
+ positionSizeUsdc: number,
115
+ isLong: boolean
116
+ ): Promise<number> {
117
+ const positionSize = toBlockchain6(positionSizeUsdc);
118
+ const fee = await this.pairInfosContract.getOpenFeeUsdc(pairIndex, positionSize, isLong);
119
+ return fromBlockchain6(fee);
120
+ }
121
+
122
+ /**
123
+ * Get opening fee percentage for a position (from PriceAggregator)
124
+ * @param pairIndex - Trading pair index
125
+ * @param positionSizeUsdc - Position size in USDC
126
+ * @param isLong - True for long position
127
+ * @returns Opening fee percentage (12 decimals)
128
+ */
129
+ async getOpenFeeP(
130
+ pairIndex: number,
131
+ positionSizeUsdc: number,
132
+ isLong: boolean
133
+ ): Promise<number> {
134
+ const positionSize = toBlockchain6(positionSizeUsdc);
135
+ const fee = await this.priceAggregatorContract.openFeeP(pairIndex, positionSize, isLong);
136
+ return fromBlockchain12(fee);
137
+ }
138
+
139
+ /**
140
+ * Get pair margin fee percentage
141
+ * @param pairIndex - Trading pair index
142
+ * @returns Margin fee percentage
143
+ */
144
+ async getPairMarginFeeP(pairIndex: number): Promise<number> {
145
+ const fee = await this.pairInfosContract.getPairMarginFeeP(pairIndex);
146
+ return fromBlockchain10(fee);
147
+ }
148
+
149
+ /**
150
+ * Get loss protection tier for a pair and position size
151
+ * @param pairIndex - Trading pair index
152
+ * @param positionSizeUsdc - Position size in USDC
153
+ * @returns Loss protection tier
154
+ */
155
+ async getLossProtectionTierForSize(
156
+ pairIndex: number,
157
+ positionSizeUsdc: number
158
+ ): Promise<number> {
159
+ const positionSize = toBlockchain6(positionSizeUsdc);
160
+ const tier = await this.pairInfosContract.getLossProtectionTier(pairIndex, positionSize);
161
+ return Number(tier);
162
+ }
163
+
164
+ /**
165
+ * Get loss protection percentage for a pair and tier
166
+ * @param pairIndex - Trading pair index
167
+ * @param tier - Loss protection tier
168
+ * @returns Loss protection percentage
169
+ */
170
+ async getLossProtectionP(pairIndex: number, tier: number): Promise<number> {
171
+ const protection = await this.pairInfosContract.getLossProtectionP(pairIndex, tier);
172
+ return fromBlockchain10(protection);
173
+ }
174
+
175
+ /**
176
+ * Get 1% depth above (for longs) in USDC
177
+ * @param pairIndex - Trading pair index
178
+ * @returns Depth in USDC
179
+ */
180
+ async getOnePercentDepthAboveUsdc(pairIndex: number): Promise<number> {
181
+ const depth = await this.pairInfosContract.onePercentDepthAboveUsdc(pairIndex);
182
+ return fromBlockchain6(depth);
183
+ }
184
+
185
+ /**
186
+ * Get 1% depth below (for shorts) in USDC
187
+ * @param pairIndex - Trading pair index
188
+ * @returns Depth in USDC
189
+ */
190
+ async getOnePercentDepthBelowUsdc(pairIndex: number): Promise<number> {
191
+ const depth = await this.pairInfosContract.onePercentDepthBelowUsdc(pairIndex);
192
+ return fromBlockchain6(depth);
193
+ }
194
+
195
+ /**
196
+ * Get both depth values for a pair
197
+ * @param pairIndex - Trading pair index
198
+ * @returns Object with depth above and below
199
+ */
200
+ async getDepth(pairIndex: number): Promise<{ above: number; below: number }> {
201
+ const [above, below] = await Promise.all([
202
+ this.getOnePercentDepthAboveUsdc(pairIndex),
203
+ this.getOnePercentDepthBelowUsdc(pairIndex),
204
+ ]);
205
+
206
+ return { above, below };
207
+ }
208
+ }
@@ -0,0 +1,268 @@
1
+ import { Contract, Provider } from 'ethers';
2
+ import { ContractPairInfo, PairInfo, PairInfoSchema, PairsBackendReturn, fromBlockchain10, fromBlockchain6 } from '../types';
3
+ import { API_ENDPOINTS } from '../config';
4
+
5
+ /**
6
+ * RPC module for caching and managing trading pair information
7
+ */
8
+ export class PairsCache {
9
+ private provider: Provider;
10
+ private pairStorageContract: Contract;
11
+ private pairsCache?: Map<number, PairInfo>;
12
+ private pairNameToIndexMap?: Map<string, number>;
13
+
14
+ constructor(provider: Provider, pairStorageContract: Contract) {
15
+ this.provider = provider;
16
+ this.pairStorageContract = pairStorageContract;
17
+ }
18
+
19
+ /**
20
+ * Get all trading pairs from blockchain (with caching)
21
+ * @param forceRefresh - Force refresh from blockchain
22
+ * @returns Map of pair index to PairInfo
23
+ */
24
+ async getPairsInfo(forceRefresh: boolean = false): Promise<Map<number, PairInfo>> {
25
+ if (this.pairsCache && !forceRefresh) {
26
+ return this.pairsCache;
27
+ }
28
+
29
+ const pairs = new Map<number, PairInfo>();
30
+ const pairNameToIndex = new Map<string, number>();
31
+
32
+ try {
33
+ // Get pairs count
34
+ const pairsCount = await this.pairStorageContract.pairsCount();
35
+ const count = Number(pairsCount);
36
+
37
+ // Fetch all pairs
38
+ for (let i = 0; i < count; i++) {
39
+ const pairData = await this.getOtherPairInfoFromIndex(i);
40
+ const pair = await this.getPairInfoNameFromIndex(i)
41
+ const pairInfo: PairInfo = {
42
+ from: pair.from,
43
+ to: pair.to,
44
+ spread: {
45
+ min: fromBlockchain10(pairData.spreadP ),
46
+ max: fromBlockchain10(pairData.spreadP ),
47
+ },
48
+ groupIndex: Number(pairData.groupIndex),
49
+ feeIndex: Number(pairData.feeIndex),
50
+ maxLeverage: fromBlockchain10(pairData.leverages.maxLeverage),
51
+ maxShortOiP: fromBlockchain10( pairData.values.maxShortOiP),
52
+ maxLongOiP: fromBlockchain10( pairData.values.maxLongOiP)
53
+ };
54
+
55
+ pairs.set(i, pairInfo);
56
+ pairNameToIndex.set(`${pairInfo.from}/${pairInfo.to}`, i);
57
+ }
58
+
59
+ this.pairsCache = pairs;
60
+ this.pairNameToIndexMap = pairNameToIndex;
61
+
62
+ return pairs;
63
+ } catch (error) {
64
+ console.error('Error fetching pairs info from blockchain:', error);
65
+ throw error;
66
+ }
67
+ }
68
+
69
+ async getPairBackend(index: number): Promise<PairsBackendReturn> {
70
+ const [pair, group, fee] = await this.pairStorageContract.pairsBackend(index);
71
+
72
+ const mapped: PairsBackendReturn = {
73
+ pair: {
74
+ feed: {
75
+ maxOpenDeviationP: pair.feed.maxOpenDeviationP,
76
+ maxCloseDeviationP: pair.feed.maxCloseDeviationP,
77
+ feedId: pair.feed.feedId,
78
+ },
79
+ backupFeed: {
80
+ maxDeviationP: pair.backupFeed.maxDeviationP,
81
+ feedId: pair.backupFeed.feedId,
82
+ },
83
+ spreadP: pair.spreadP,
84
+ pnlSpreadP: pair.pnlSpreadP,
85
+ leverages: {
86
+ minLeverage: pair.leverages.minLeverage,
87
+ maxLeverage: pair.leverages.maxLeverage,
88
+ pnlMinLeverage: pair.leverages.pnlMinLeverage,
89
+ pnlMaxLeverage: pair.leverages.pnlMaxLeverage,
90
+ },
91
+ priceImpactMultiplier: pair.priceImpactMultiplier,
92
+ skewImpactMultiplier: pair.skewImpactMultiplier,
93
+ groupIndex: pair.groupIndex,
94
+ feeIndex: pair.feeIndex,
95
+ values: {
96
+ maxGainP: pair[9].maxGainP,
97
+ maxSlP: pair[9].maxSlP,
98
+ maxLongOiP: pair[9].maxLongOiP,
99
+ maxShortOiP: pair[9].maxShortOiP,
100
+ groupOpenInterestPercentageP: pair[9].groupOpenInterestPercentageP,
101
+ maxWalletOIP: pair[9].maxWalletOIP,
102
+ isUSDCAligned: pair[9].isUSDCAligned,
103
+ },
104
+ },
105
+
106
+ group: {
107
+ name: group.name,
108
+ maxOpenInterestP: group.maxOpenInterestP,
109
+ isSpreadDynamic: group.isSpreadDynamic,
110
+ },
111
+
112
+ fee: {
113
+ openFeeP: fee.openFeeP,
114
+ closeFeeP: fee.closeFeeP,
115
+ limitOrderFeeP: fee.limitOrderFeeP,
116
+ minLevPosUSDC: fee.minLevPosUSDC,
117
+ pnlFees: {
118
+ numTiers: fee.pnlFees.numTiers,
119
+ tierP: [...fee.pnlFees.tierP],
120
+ feesP: [...fee.pnlFees.feesP],
121
+ },
122
+ },
123
+ };
124
+
125
+ return mapped;
126
+ }
127
+
128
+ getPairInfoNameFromIndex = async (index:number) :Promise<{from:string, to:string}>=>{
129
+ const pairData = await this.pairStorageContract.getPairData(index)
130
+ return {
131
+ from: pairData.from,
132
+ to: pairData.to
133
+ }
134
+ }
135
+
136
+ getOtherPairInfoFromIndex = async (index: number): Promise<ContractPairInfo> => {
137
+ const pairData = await this.pairStorageContract.pairs(index);
138
+ const values = pairData[9]
139
+ const result: ContractPairInfo = {
140
+ feed: {
141
+ maxOpenDeviationP: pairData.feed.maxOpenDeviationP,
142
+ maxCloseDeviationP: pairData.feed.maxCloseDeviationP,
143
+ feedId: pairData.feed.feedId,
144
+ },
145
+ backupFeed: {
146
+ maxDeviationP: pairData.backupFeed.maxDeviationP,
147
+ feedId: pairData.backupFeed.feedId,
148
+ },
149
+ spreadP: pairData.spreadP,
150
+ pnlSpreadP: pairData.pnlSpreadP,
151
+ leverages: {
152
+ minLeverage: pairData.leverages.minLeverage,
153
+ maxLeverage: pairData.leverages.maxLeverage,
154
+ pnlMinLeverage: pairData.leverages.pnlMinLeverage,
155
+ pnlMaxLeverage: pairData.leverages.pnlMaxLeverage,
156
+ },
157
+ priceImpactMultiplier: pairData.priceImpactMultiplier,
158
+ skewImpactMultiplier: pairData.skewImpactMultiplier,
159
+ groupIndex: pairData.groupIndex,
160
+ feeIndex: pairData.feeIndex,
161
+ values: {
162
+ maxGainP: values.maxGainP,
163
+ maxSlP: values.maxSlP,
164
+ maxLongOiP: values.maxLongOiP,
165
+ maxShortOiP: values.maxShortOiP,
166
+ groupOpenInterestPercentageP: values.groupOpenInterestPercentageP,
167
+ maxWalletOIP: values.maxWalletOIP,
168
+ isUSDCAligned: values.isUSDCAligned,
169
+ },
170
+ };
171
+
172
+ return result;
173
+ };
174
+
175
+
176
+ /**
177
+ * Get pair information from socket API
178
+ * @returns Pair information from API
179
+ */
180
+ async getPairInfoFromSocket(): Promise<any> {
181
+ try {
182
+ const response = await fetch(API_ENDPOINTS.SOCKET_API);
183
+ if (!response.ok) {
184
+ throw new Error(`Failed to fetch from socket API: ${response.statusText}`);
185
+ }
186
+ return await response.json();
187
+ } catch (error) {
188
+ console.error('Error fetching pair info from socket API:', error);
189
+ throw error;
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Get pair index from pair name
195
+ * @param pairName - Pair name (e.g., "BTC/USD")
196
+ * @returns Pair index or undefined if not found
197
+ */
198
+ async getPairIndex(pairName: string): Promise<number | undefined> {
199
+ if (!this.pairNameToIndexMap) {
200
+ await this.getPairsInfo();
201
+ }
202
+ return this.pairNameToIndexMap?.get(pairName);
203
+ }
204
+
205
+ /**
206
+ * Get pair name from index
207
+ * @param pairIndex - Pair index
208
+ * @returns Pair name or undefined if not found
209
+ */
210
+ async getPairName(pairIndex: number): Promise<string | undefined> {
211
+ if (!this.pairsCache) {
212
+ await this.getPairsInfo();
213
+ }
214
+ const pair = this.pairsCache?.get(pairIndex);
215
+ return pair ? `${pair.from}/${pair.to}` : undefined;
216
+ }
217
+
218
+ /**
219
+ * Get all unique group indexes
220
+ * @returns Array of group indexes
221
+ */
222
+ async getGroupIndexes(): Promise<number[]> {
223
+ const pairs = await this.getPairsInfo();
224
+ const groupIndexes = new Set<number>();
225
+
226
+ pairs.forEach((pair) => {
227
+ groupIndexes.add(pair.groupIndex);
228
+ });
229
+
230
+ return Array.from(groupIndexes).sort((a, b) => a - b);
231
+ }
232
+
233
+ /**
234
+ * Get all pairs in a specific group
235
+ * @param groupIndex - Group index
236
+ * @returns Array of pair indexes in the group
237
+ */
238
+ async getPairsInGroup(groupIndex: number): Promise<number[]> {
239
+ const pairs = await this.getPairsInfo();
240
+ const pairsInGroup: number[] = [];
241
+
242
+ pairs.forEach((pair, index) => {
243
+ if (pair.groupIndex === groupIndex) {
244
+ pairsInGroup.push(index);
245
+ }
246
+ });
247
+
248
+ return pairsInGroup;
249
+ }
250
+
251
+ /**
252
+ * Get pair info by index
253
+ * @param pairIndex - Pair index
254
+ * @returns PairInfo or undefined
255
+ */
256
+ async getPairByIndex(pairIndex: number): Promise<PairInfo | undefined> {
257
+ const pairs = await this.getPairsInfo();
258
+ return pairs.get(pairIndex);
259
+ }
260
+
261
+ /**
262
+ * Clear the cache
263
+ */
264
+ clearCache(): void {
265
+ this.pairsCache = undefined;
266
+ this.pairNameToIndexMap = undefined;
267
+ }
268
+ }
@@ -0,0 +1,164 @@
1
+ import { Contract, TransactionReceipt, TransactionRequest } from 'ethers';
2
+ import { ethers } from 'ethers';
3
+ import { ReferralTier, ReferralDiscount } from '../types';
4
+ import { BaseSigner } from '../signers/base';
5
+
6
+ /**
7
+ * Referral Operations RPC
8
+ * Handles referral program functionality including codes, tiers, and discounts
9
+ */
10
+ export class ReferralOperationsRPC {
11
+ constructor(
12
+ private referralContract: Contract,
13
+ private signer?: BaseSigner
14
+ ) {}
15
+
16
+ /**
17
+ * Get referral information for a trader
18
+ * @param account - Trader address
19
+ * @returns Referral code and referrer address
20
+ */
21
+ async getTraderReferralInfo(account: string): Promise<{ code: string; referrer: string }> {
22
+ const [code, referrer] = await this.referralContract.getTraderReferralInfo(account);
23
+
24
+ return {
25
+ code: ethers.decodeBytes32String(code),
26
+ referrer,
27
+ };
28
+ }
29
+
30
+ /**
31
+ * Get referrer tier
32
+ * @param account - Referrer address
33
+ * @returns Tier ID
34
+ */
35
+ async getReferrerTier(account: string): Promise<number> {
36
+ const tier = await this.referralContract.referrerTiers(account);
37
+ return Number(tier);
38
+ }
39
+
40
+ /**
41
+ * Get tier information
42
+ * @param tierId - Tier ID
43
+ * @returns Tier information with fee discount and rebate percentages
44
+ */
45
+ async getTierInfo(tierId: number): Promise<ReferralTier> {
46
+ const tier = await this.referralContract.tiers(tierId);
47
+
48
+ return {
49
+ feeDiscountPct: Number(tier.feeDiscountPct),
50
+ refRebatePct: Number(tier.refRebatePct),
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Calculate referral discount for a trader and fee
56
+ * @param account - Trader address
57
+ * @param fee - Original fee amount
58
+ * @returns Discount information
59
+ */
60
+ async getTraderReferralDiscount(account: string, fee: number): Promise<ReferralDiscount> {
61
+ const result = await this.referralContract.traderReferralDiscount(account, fee);
62
+
63
+ return {
64
+ traderDiscount: Number(result.traderDiscount),
65
+ referrer: result.referrer,
66
+ rebateShare: Number(result.rebateShare),
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Set referral code for the caller
72
+ * @param code - Referral code (max 32 characters)
73
+ * @returns Transaction receipt
74
+ */
75
+ async setReferralCode(code: string): Promise<TransactionReceipt | null> {
76
+ if (!this.signer) {
77
+ throw new Error('Signer required for setting referral code');
78
+ }
79
+
80
+ // Convert string to bytes32
81
+ const codeBytes32 = ethers.encodeBytes32String(code);
82
+
83
+ const tx: TransactionRequest = {
84
+ to: await this.referralContract.getAddress(),
85
+ data: this.referralContract.interface.encodeFunctionData('setTraderReferralCodeByUser', [
86
+ codeBytes32,
87
+ ]),
88
+ };
89
+
90
+ return await this.signAndSend(tx);
91
+ }
92
+
93
+ /**
94
+ * Check if an account has a referral code set
95
+ * @param account - Trader address
96
+ * @returns True if referral code is set
97
+ */
98
+ async hasReferralCode(account: string): Promise<boolean> {
99
+ const { code } = await this.getTraderReferralInfo(account);
100
+ return code !== '' && code !== '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00';
101
+ }
102
+
103
+ /**
104
+ * Get effective fee after referral discount
105
+ * @param account - Trader address
106
+ * @param baseFee - Base fee amount
107
+ * @returns Effective fee after discount
108
+ */
109
+ async getEffectiveFee(account: string, baseFee: number): Promise<number> {
110
+ const discount = await this.getTraderReferralDiscount(account, baseFee);
111
+ return baseFee - discount.traderDiscount;
112
+ }
113
+
114
+ /**
115
+ * Helper method to sign and send transactions
116
+ */
117
+ private async signAndSend(tx: TransactionRequest): Promise<TransactionReceipt | null> {
118
+ if (!this.signer) {
119
+ throw new Error('Signer not set');
120
+ }
121
+
122
+ const address = await this.signer.getAddress();
123
+ tx.from = address;
124
+
125
+ const provider = this.referralContract.runner?.provider;
126
+ if (!provider) {
127
+ throw new Error('Provider not available');
128
+ }
129
+
130
+ if (!tx.chainId) {
131
+ const network = await provider.getNetwork();
132
+ tx.chainId = network.chainId;
133
+ }
134
+
135
+ if (tx.nonce === undefined) {
136
+ tx.nonce = await provider.getTransactionCount(address);
137
+ }
138
+
139
+ if (!tx.gasLimit) {
140
+ tx.gasLimit = await provider.estimateGas(tx);
141
+ }
142
+
143
+ if (!tx.maxFeePerGas && !tx.gasPrice) {
144
+ const feeData = await provider.getFeeData();
145
+ if (feeData.maxFeePerGas) {
146
+ tx.maxFeePerGas = feeData.maxFeePerGas;
147
+ tx.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas || feeData.maxFeePerGas;
148
+ } else {
149
+ tx.gasPrice = feeData.gasPrice || undefined;
150
+ }
151
+ }
152
+
153
+ const signedTx = await this.signer.signTransaction(tx);
154
+ const txResponse = await provider.broadcastTransaction(signedTx);
155
+ return await txResponse.wait();
156
+ }
157
+
158
+ /**
159
+ * Set signer for transactions
160
+ */
161
+ setSigner(signer: BaseSigner): void {
162
+ this.signer = signer;
163
+ }
164
+ }