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,288 @@
1
+ import WebSocket from "ws";
2
+ import { PriceFeedResponse, PriceFeedResponseSchema } from "../types";
3
+ import { API_ENDPOINTS } from "../config";
4
+
5
+ /**
6
+ * Callback function type for price updates
7
+ */
8
+ export type PriceUpdateCallback = (priceData: PriceFeedResponse) => void;
9
+
10
+ /**
11
+ * Normalize feed ID: remove 0x prefix and lowercase
12
+ * Pyth WebSocket sends IDs WITHOUT 0x prefix
13
+ */
14
+ function normalizeFeedId(feedId: string): string {
15
+ let normalized = feedId.toLowerCase();
16
+ if (normalized.startsWith('0x')) {
17
+ normalized = normalized.slice(2);
18
+ }
19
+ return normalized;
20
+ }
21
+
22
+ /**
23
+ * WebSocket client for real-time price feeds from Pyth Network
24
+ */
25
+ export class FeedClient {
26
+ private url: string;
27
+ private ws?: WebSocket;
28
+ private callbacks: Map<string, PriceUpdateCallback[]> = new Map();
29
+ private onError?: (error: Error) => void;
30
+ private onClose?: () => void;
31
+ private pairFeedMap: Map<string, string> = new Map();
32
+ private reconnectAttempts: number = 0;
33
+ private maxReconnectAttempts: number = 5;
34
+ private reconnectDelay: number = 1000;
35
+
36
+ constructor(
37
+ url: string = API_ENDPOINTS.PYTH_WS,
38
+ onError?: (error: Error) => void,
39
+ onClose?: () => void
40
+ ) {
41
+ this.url = url;
42
+ this.onError = onError;
43
+ this.onClose = onClose;
44
+ }
45
+
46
+ /**
47
+ * Register a callback for a specific price feed
48
+ */
49
+ registerPriceFeedCallback(
50
+ feedId: string,
51
+ callback: PriceUpdateCallback
52
+ ): void {
53
+ const id = normalizeFeedId(feedId);
54
+ if (!this.callbacks.has(id)) {
55
+ this.callbacks.set(id, []);
56
+ }
57
+ this.callbacks.get(id)!.push(callback);
58
+
59
+ if (this.callbacks.get(id)!.length === 1 && this.isConnected()) {
60
+ this.subscribeToPriceFeeds([id]);
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Unregister a callback for a specific price feed
66
+ */
67
+ unregisterPriceFeedCallback(
68
+ feedId: string,
69
+ callback: PriceUpdateCallback
70
+ ): void {
71
+ const id = normalizeFeedId(feedId);
72
+ const cbs = this.callbacks.get(id);
73
+ if (!cbs) return;
74
+
75
+ const idx = cbs.indexOf(callback);
76
+ if (idx > -1) cbs.splice(idx, 1);
77
+
78
+ if (cbs.length === 0 && this.isConnected()) {
79
+ this.unsubscribeFromPriceFeeds([id]);
80
+ this.callbacks.delete(id);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Load pair to feed ID mappings
86
+ */
87
+ loadPairFeeds(pairFeeds: Map<string, string>): void {
88
+ this.pairFeedMap = pairFeeds;
89
+ }
90
+
91
+ /**
92
+ * Get feed ID for a trading pair
93
+ */
94
+ getFeedIdForPair(pairName: string): string | undefined {
95
+ return this.pairFeedMap.get(pairName);
96
+ }
97
+
98
+ /**
99
+ * Connect to WebSocket and listen for price updates
100
+ */
101
+ async listenForPriceUpdates(): Promise<void> {
102
+ return new Promise((resolve, reject) => {
103
+ try {
104
+ this.ws = new WebSocket(this.url);
105
+
106
+ this.ws.on("open", () => {
107
+ console.log("WebSocket connected to Pyth Network");
108
+ this.reconnectAttempts = 0;
109
+
110
+ const feedIds = Array.from(this.callbacks.keys());
111
+ if (feedIds.length > 0) {
112
+ this.subscribeToPriceFeeds(feedIds);
113
+ }
114
+
115
+ resolve();
116
+ });
117
+
118
+ this.ws.on("message", (data: WebSocket.Data) => {
119
+ try {
120
+ const message = JSON.parse(data.toString());
121
+
122
+ if (message.type !== "price_update") {
123
+ console.log("Received non-update message:", message);
124
+ }
125
+
126
+ if (message.type === "price_update") {
127
+ this.handlePriceUpdate(message);
128
+ }
129
+ } catch (error) {
130
+ console.error("Error parsing WebSocket message:", error);
131
+ }
132
+ });
133
+
134
+ this.ws.on("error", (error: Error) => {
135
+ console.error("WebSocket error:", error);
136
+ if (this.onError) {
137
+ this.onError(error);
138
+ }
139
+ reject(error);
140
+ });
141
+
142
+ this.ws.on("close", () => {
143
+ console.log("WebSocket connection closed");
144
+ if (this.onClose) {
145
+ this.onClose();
146
+ }
147
+ this.attemptReconnect();
148
+ });
149
+ } catch (error) {
150
+ reject(error);
151
+ }
152
+ });
153
+ }
154
+
155
+ /**
156
+ * Subscribe to specific price feeds
157
+ */
158
+ private subscribeToPriceFeeds(feedIds: string[]): void {
159
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
160
+
161
+ // Feed IDs should already be normalized, but ensure they are
162
+ const ids = feedIds.map(id => normalizeFeedId(id));
163
+
164
+ this.ws.send(JSON.stringify({ type: "subscribe", ids }));
165
+ console.log("Subscribed to Pyth feeds:", ids);
166
+ }
167
+
168
+ /**
169
+ * Unsubscribe from specific price feeds
170
+ */
171
+ private unsubscribeFromPriceFeeds(feedIds: string[]): void {
172
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
173
+
174
+ const ids = feedIds.map(id => normalizeFeedId(id));
175
+ this.ws.send(JSON.stringify({ type: "unsubscribe", ids }));
176
+ console.log("Unsubscribed from Pyth feeds:", ids);
177
+ }
178
+
179
+ /**
180
+ * Handle incoming price update
181
+ */
182
+ private handlePriceUpdate(message: any): void {
183
+ try {
184
+ if (message.price_feed) {
185
+ const priceUpdate = message.price_feed;
186
+
187
+ // ⭐ KEY FIX: Normalize the incoming feed ID (Pyth sends without 0x)
188
+ const feedId = normalizeFeedId(priceUpdate.id);
189
+
190
+ const priceFeed = {
191
+ id: feedId,
192
+ price: {
193
+ price: priceUpdate.price.price,
194
+ conf: priceUpdate.price.conf,
195
+ expo: priceUpdate.price.expo,
196
+ publishTime: priceUpdate.price.publish_time,
197
+ },
198
+ emaPrice: {
199
+ price: priceUpdate.ema_price.price,
200
+ conf: priceUpdate.ema_price.conf,
201
+ expo: priceUpdate.ema_price.expo,
202
+ publishTime: priceUpdate.ema_price.publish_time,
203
+ },
204
+ };
205
+
206
+ const validatedPriceFeed = PriceFeedResponseSchema.parse(priceFeed);
207
+
208
+ // Trigger callbacks
209
+ const callbacks = this.callbacks.get(validatedPriceFeed.id);
210
+ if (callbacks && callbacks.length > 0) {
211
+ console.log(`✅ Triggering ${callbacks.length} callback(s) for ${validatedPriceFeed.id}`);
212
+ callbacks.forEach((callback) => {
213
+ try {
214
+ callback(validatedPriceFeed);
215
+ } catch (error) {
216
+ console.error(
217
+ "Error in price update callback:",
218
+ error instanceof Error ? error.message : String(error)
219
+ );
220
+ }
221
+ });
222
+ } else {
223
+ console.warn(`⚠️ No callbacks registered for feed: ${validatedPriceFeed.id}`);
224
+ console.log(`Registered feeds: [${Array.from(this.callbacks.keys()).join(", ")}]`);
225
+ }
226
+ }
227
+ } catch (error) {
228
+ console.error(
229
+ "Error processing price update:",
230
+ error instanceof Error ? error.message : String(error)
231
+ );
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Attempt to reconnect to WebSocket
237
+ */
238
+ private attemptReconnect(): void {
239
+ if (this.reconnectAttempts >= this.maxReconnectAttempts) {
240
+ console.error("Max reconnection attempts reached");
241
+ return;
242
+ }
243
+
244
+ this.reconnectAttempts++;
245
+ const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
246
+
247
+ console.log(
248
+ `Attempting to reconnect in ${delay}ms (attempt ${this.reconnectAttempts})`
249
+ );
250
+
251
+ setTimeout(() => {
252
+ this.listenForPriceUpdates().catch((error) => {
253
+ console.error("Reconnection failed:", error);
254
+ });
255
+ }, delay);
256
+ }
257
+
258
+ /**
259
+ * Get latest prices via HTTP
260
+ */
261
+ async getLatestPriceUpdates(feedIds: string[]): Promise<any> {
262
+ const url = `${API_ENDPOINTS.PYTH_HTTP}?ids[]=${feedIds.join("&ids[]=")}`;
263
+
264
+ const response = await fetch(url);
265
+ if (!response.ok) {
266
+ throw new Error(`Failed to fetch prices: ${response.statusText}`);
267
+ }
268
+
269
+ return await response.json();
270
+ }
271
+
272
+ /**
273
+ * Close the WebSocket connection
274
+ */
275
+ close(): void {
276
+ if (this.ws) {
277
+ this.ws.close();
278
+ this.ws = undefined;
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Check if WebSocket is connected
284
+ */
285
+ isConnected(): boolean {
286
+ return this.ws !== undefined && this.ws.readyState === WebSocket.OPEN;
287
+ }
288
+ }
package/src/index.ts ADDED
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Avantis Trader SDK - TypeScript
3
+ *
4
+ * A comprehensive SDK for interacting with the Avantis decentralized
5
+ * leveraged trading platform.
6
+ */
7
+
8
+ // Main client
9
+ export { TraderClient } from './client';
10
+
11
+ // Signers
12
+ export { BaseSigner } from './signers/base';
13
+ export { LocalSigner } from './signers/local';
14
+ export { KMSSigner } from './signers/kms';
15
+
16
+ // Feed client
17
+ export { FeedClient, PriceUpdateCallback } from './feed/feed_client';
18
+
19
+ // RPC modules
20
+ export { PairsCache } from './rpc/pairs_cache';
21
+ export { AssetParametersRPC } from './rpc/asset_parameters';
22
+ export { CategoryParametersRPC } from './rpc/category_parameters';
23
+ export { FeeParametersRPC } from './rpc/fee_parameters';
24
+ export { TradingParametersRPC } from './rpc/trading_parameters';
25
+ export { BlendedRPC } from './rpc/blended';
26
+ export { TradeRPC } from './rpc/trade';
27
+ export { SnapshotRPC } from './rpc/snapshot';
28
+ export { TradingOperationsRPC } from './rpc/trading_operations';
29
+ export { DelegationRPC } from './rpc/delegation';
30
+ export { PairInfoQueriesRPC } from './rpc/pair_info_queries';
31
+ export { ReferralOperationsRPC } from './rpc/referral_operations';
32
+ export { MulticallRPC, type MulticallCall, type MulticallResult } from './rpc/multicall';
33
+
34
+ // Types and enums
35
+ export {
36
+ TradeInputOrderType,
37
+ MarginUpdateType,
38
+ type Spread,
39
+ type PairInfo,
40
+ type TradeInput,
41
+ type TradeResponse,
42
+ type Price,
43
+ type EmaPrice,
44
+ type PriceFeedResponse,
45
+ type OpenInterest,
46
+ type OpenInterestLimits,
47
+ type Utilization,
48
+ type Skew,
49
+ type Fee,
50
+ type Depth,
51
+ type LossProtectionInfo,
52
+ type PairData,
53
+ type Group,
54
+ type Snapshot,
55
+ type Trade,
56
+ type TradeInfo,
57
+ type OpenLimitOrder,
58
+ type ReferralTier,
59
+ type ReferralDiscount,
60
+ type ContractCallOptions,
61
+ type TransactionReceipt,
62
+ type PairsBackendReturn,
63
+ type ContractPairInfo,
64
+ type PairStruct,
65
+ type GroupStruct,
66
+ type FeeStruct,
67
+ type FeedStruct,
68
+ type BackupFeedStruct,
69
+ type LeverageStruct,
70
+ type ValuesStruct,
71
+ type PnlFeesStruct,
72
+ fromBlockchain10,
73
+ fromBlockchain6,
74
+ toBlockchain10,
75
+ toBlockchain6,
76
+ fromBlockchain12,
77
+ toBlockchain12,
78
+ fromBlockchain18,
79
+ toBlockchain18,
80
+ } from './types';
81
+
82
+ // Configuration
83
+ export {
84
+ CONTRACTS,
85
+ API_ENDPOINTS,
86
+ type ContractAddresses,
87
+ type NetworkConfig,
88
+ getContractAddress,
89
+ setContractAddresses,
90
+ } from './config';
91
+
92
+ // Utilities
93
+ export {
94
+ isTupleType,
95
+ isArrayType,
96
+ processOutputTypes,
97
+ decoder,
98
+ hexToNumber,
99
+ numberToHex,
100
+ isValidAddress,
101
+ toChecksumAddress,
102
+ sleep,
103
+ retryWithBackoff,
104
+ } from './utils';
105
+
106
+ // Crypto utilities (for advanced usage)
107
+ export {
108
+ publicKeyIntToEthAddress,
109
+ derEncodedPublicKeyToEthAddress,
110
+ getSigRS,
111
+ getSigV,
112
+ getSigRSV,
113
+ signatureToHex,
114
+ } from './crypto/spki';
@@ -0,0 +1,217 @@
1
+ import { Contract, Provider } from 'ethers';
2
+ import {
3
+ OpenInterest,
4
+ OpenInterestLimits,
5
+ Utilization,
6
+ Skew,
7
+ TradeInput,
8
+ fromBlockchain6,
9
+ fromBlockchain10,
10
+ } from '../types';
11
+ import { PairsCache } from './pairs_cache';
12
+
13
+ /**
14
+ * RPC module for retrieving asset-level parameters
15
+ */
16
+ export class AssetParametersRPC {
17
+ private provider: Provider;
18
+ private pairStorageContract: Contract;
19
+ private pairInfosContract: Contract;
20
+ private tradingStorageContract: Contract;
21
+ private pairsCache: PairsCache;
22
+
23
+ constructor(
24
+ provider: Provider,
25
+ pairStorageContract: Contract,
26
+ pairInfosContract: Contract,
27
+ pairsCache: PairsCache,
28
+ tradingStorageContract: Contract
29
+ ) {
30
+ this.provider = provider;
31
+ this.pairStorageContract = pairStorageContract;
32
+ this.pairInfosContract = pairInfosContract;
33
+ this.tradingStorageContract = tradingStorageContract;
34
+ this.pairsCache = pairsCache;
35
+ }
36
+
37
+ /**
38
+ * Get open interest limits for all pairs
39
+ * @returns Map of pair index to OI limits
40
+ */
41
+ async getOILimits(): Promise<Map<number, OpenInterestLimits>> {
42
+ const pairs = await this.pairsCache.getPairsInfo();
43
+ const limits = new Map<number, OpenInterestLimits>();
44
+
45
+ for (const [pairIndex, pairInfo] of pairs) {
46
+ limits.set(pairIndex, {
47
+ pairIndex,
48
+ maxLong: pairInfo.maxLongOiP,
49
+ maxShort: pairInfo.maxShortOiP,
50
+ });
51
+ }
52
+
53
+ return limits;
54
+ }
55
+
56
+ /**
57
+ * Get current open interest for all pairs
58
+ * @returns Map of pair index to OI
59
+ */
60
+ async getOI(): Promise<Map<number, OpenInterest>> {
61
+ const pairs = await this.pairsCache.getPairsInfo();
62
+ const oi = new Map<number, OpenInterest>();
63
+
64
+ for (const [pairIndex] of pairs) {
65
+ try {
66
+ // Use TradingStorage contract which has openInterestUSDC method
67
+ const pairOILong = await this.tradingStorageContract.openInterestUSDC(pairIndex, 0); // 0 = long
68
+ const pairOIShort = await this.tradingStorageContract.openInterestUSDC(pairIndex, 1); // 1 = short
69
+
70
+ const limits = await this.getOILimits();
71
+ const maxOI = limits.get(pairIndex)?.maxLong || 0;
72
+
73
+ oi.set(pairIndex, {
74
+ long: fromBlockchain6(pairOILong),
75
+ short: fromBlockchain6(pairOIShort),
76
+ max: maxOI,
77
+ });
78
+ } catch (error) {
79
+ console.error(`Error getting OI for pair ${pairIndex}:`, error);
80
+ // Set default values on error
81
+ oi.set(pairIndex, {
82
+ long: 0,
83
+ short: 0,
84
+ max: 0,
85
+ });
86
+ }
87
+ }
88
+
89
+ return oi;
90
+ }
91
+
92
+ /**
93
+ * Get utilization for all pairs
94
+ * @returns Map of pair index to utilization
95
+ */
96
+ async getUtilization(): Promise<Map<number, Utilization>> {
97
+ const oi = await this.getOI();
98
+ const utilization = new Map<number, Utilization>();
99
+
100
+ for (const [pairIndex, oiData] of oi) {
101
+ const utilizationLong = oiData.max > 0 ? (oiData.long / oiData.max) * 100 : 0;
102
+ const utilizationShort = oiData.max > 0 ? (oiData.short / oiData.max) * 100 : 0;
103
+
104
+ utilization.set(pairIndex, {
105
+ utilizationLong,
106
+ utilizationShort,
107
+ });
108
+ }
109
+
110
+ return utilization;
111
+ }
112
+
113
+ /**
114
+ * Get asset skew (long / total) for all pairs
115
+ * @returns Map of pair index to skew
116
+ */
117
+ async getAssetSkew(): Promise<Map<number, Skew>> {
118
+ const oi = await this.getOI();
119
+ const skew = new Map<number, Skew>();
120
+
121
+ for (const [pairIndex, oiData] of oi) {
122
+ const total = oiData.long + oiData.short;
123
+ const skewValue = total > 0 ? oiData.long / total : 0.5;
124
+
125
+ skew.set(pairIndex, { skew: skewValue });
126
+ }
127
+
128
+ return skew;
129
+ }
130
+
131
+ /**
132
+ * Get price impact spread for opening a position
133
+ * @param positionSize - Position size in USDC
134
+ * @param isLong - True for long, false for short
135
+ * @param pairIndex - Pair index
136
+ * @returns Price impact spread percentage
137
+ */
138
+ async getPriceImpactSpread(
139
+ positionSize: number,
140
+ isLong: boolean,
141
+ pairIndex: number
142
+ ): Promise<number> {
143
+ try {
144
+ const result = await this.pairInfosContract.getPriceImpactP(
145
+ pairIndex,
146
+ isLong,
147
+ BigInt(Math.floor(positionSize * 1e6))
148
+ );
149
+ return fromBlockchain10(result);
150
+ } catch (error) {
151
+ console.error('Error getting price impact spread:', error);
152
+ return 0;
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Get skew impact spread
158
+ * @param pairIndex - Pair index
159
+ * @returns Skew impact spread
160
+ */
161
+ async getSkewImpactSpread(pairIndex: number): Promise<number> {
162
+ try {
163
+ const skewMap = await this.getAssetSkew();
164
+ const skew = skewMap.get(pairIndex);
165
+
166
+ if (!skew) return 0;
167
+
168
+ // Simple skew impact calculation (can be customized)
169
+ const deviation = Math.abs(skew.skew - 0.5);
170
+ return deviation * 100; // Convert to basis points
171
+ } catch (error) {
172
+ console.error('Error getting skew impact spread:', error);
173
+ return 0;
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Get opening price impact spread for a trade
179
+ * @param tradeInput - Trade input parameters
180
+ * @returns Opening price impact spread
181
+ */
182
+ async getOpeningPriceImpactSpread(tradeInput: TradeInput): Promise<number> {
183
+ const pairIndex = await this.pairsCache.getPairIndex(tradeInput.pair);
184
+ if (pairIndex === undefined) {
185
+ throw new Error(`Pair ${tradeInput.pair} not found`);
186
+ }
187
+
188
+ const positionSize = tradeInput.collateralInTrade * tradeInput.leverage;
189
+ return await this.getPriceImpactSpread(positionSize, tradeInput.isLong, pairIndex);
190
+ }
191
+
192
+ /**
193
+ * Get one percent depth (liquidity depth)
194
+ * @returns Map of pair index to depth
195
+ */
196
+ async getOnePercentDepth(): Promise<Map<number, { above: number; below: number }>> {
197
+ const pairs = await this.pairsCache.getPairsInfo();
198
+ const depth = new Map<number, { above: number; below: number }>();
199
+
200
+ for (const [pairIndex] of pairs) {
201
+ try {
202
+ const depthAbove = await this.pairInfosContract.onePercentDepthAboveUsdc(pairIndex);
203
+ const depthBelow = await this.pairInfosContract.onePercentDepthBelowUsdc(pairIndex);
204
+
205
+ depth.set(pairIndex, {
206
+ above: fromBlockchain6(depthAbove),
207
+ below: fromBlockchain6(depthBelow),
208
+ });
209
+ } catch (error) {
210
+ // If method doesn't exist, set default values
211
+ depth.set(pairIndex, { above: 0, below: 0 });
212
+ }
213
+ }
214
+
215
+ return depth;
216
+ }
217
+ }
@@ -0,0 +1,77 @@
1
+ import { Contract, Provider } from 'ethers';
2
+ import { Utilization, Skew } from '../types';
3
+ import { AssetParametersRPC } from './asset_parameters';
4
+ import { CategoryParametersRPC } from './category_parameters';
5
+ import { PairsCache } from './pairs_cache';
6
+
7
+ /**
8
+ * RPC module for blended calculations (25% asset + 75% category)
9
+ */
10
+ export class BlendedRPC {
11
+ private assetParams: AssetParametersRPC;
12
+ private categoryParams: CategoryParametersRPC;
13
+ private pairsCache: PairsCache;
14
+
15
+ constructor(
16
+ assetParams: AssetParametersRPC,
17
+ categoryParams: CategoryParametersRPC,
18
+ pairsCache: PairsCache
19
+ ) {
20
+ this.assetParams = assetParams;
21
+ this.categoryParams = categoryParams;
22
+ this.pairsCache = pairsCache;
23
+ }
24
+
25
+ /**
26
+ * Calculate blended utilization (25% asset + 75% category)
27
+ * @returns Map of pair index to blended utilization
28
+ */
29
+ async getBlendedUtilization(): Promise<Map<number, Utilization>> {
30
+ const assetUtilization = await this.assetParams.getUtilization();
31
+ const categoryUtilization = await this.categoryParams.getUtilization();
32
+ const pairs = await this.pairsCache.getPairsInfo();
33
+
34
+ const blended = new Map<number, Utilization>();
35
+
36
+ for (const [pairIndex, pairInfo] of pairs) {
37
+ const assetUtil = assetUtilization.get(pairIndex);
38
+ const catUtil = categoryUtilization.get(pairInfo.groupIndex);
39
+
40
+ if (assetUtil && catUtil) {
41
+ blended.set(pairIndex, {
42
+ utilizationLong:
43
+ 0.25 * assetUtil.utilizationLong + 0.75 * catUtil.utilizationLong,
44
+ utilizationShort:
45
+ 0.25 * assetUtil.utilizationShort + 0.75 * catUtil.utilizationShort,
46
+ });
47
+ }
48
+ }
49
+
50
+ return blended;
51
+ }
52
+
53
+ /**
54
+ * Calculate blended skew (25% asset + 75% category)
55
+ * @returns Map of pair index to blended skew
56
+ */
57
+ async getBlendedSkew(): Promise<Map<number, Skew>> {
58
+ const assetSkew = await this.assetParams.getAssetSkew();
59
+ const categorySkew = await this.categoryParams.getCategorySkew();
60
+ const pairs = await this.pairsCache.getPairsInfo();
61
+
62
+ const blended = new Map<number, Skew>();
63
+
64
+ for (const [pairIndex, pairInfo] of pairs) {
65
+ const assetSk = assetSkew.get(pairIndex);
66
+ const catSk = categorySkew.get(pairInfo.groupIndex);
67
+
68
+ if (assetSk && catSk) {
69
+ blended.set(pairIndex, {
70
+ skew: 0.25 * assetSk.skew + 0.75 * catSk.skew,
71
+ });
72
+ }
73
+ }
74
+
75
+ return blended;
76
+ }
77
+ }