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,128 @@
1
+ import { Contract, Provider } from 'ethers';
2
+ import { OpenInterest, Utilization, Skew, fromBlockchain6 } from '../types';
3
+ import { PairsCache } from './pairs_cache';
4
+
5
+ /**
6
+ * RPC module for retrieving category-level parameters
7
+ */
8
+ export class CategoryParametersRPC {
9
+ private provider: Provider;
10
+ private pairStorageContract: Contract;
11
+ private pairsCache: PairsCache;
12
+
13
+ constructor(
14
+ provider: Provider,
15
+ pairStorageContract: Contract,
16
+ pairsCache: PairsCache
17
+ ) {
18
+ this.provider = provider;
19
+ this.pairStorageContract = pairStorageContract;
20
+ this.pairsCache = pairsCache;
21
+ }
22
+
23
+ /**
24
+ * Get open interest limits per category
25
+ * Note: Group OI limits are calculated from pair backend data
26
+ * @returns Map of group index to OI limits
27
+ */
28
+ async getOILimits(): Promise<Map<number, OpenInterest>> {
29
+ const groupIndexes = await this.pairsCache.getGroupIndexes();
30
+ const limits = new Map<number, OpenInterest>();
31
+
32
+ // Get limits from pair backend data which includes group info
33
+ for (const groupIndex of groupIndexes) {
34
+ try {
35
+ // Get first pair in this group to get group max OI
36
+ const pairsInGroup = await this.pairsCache.getPairsInGroup(groupIndex);
37
+ if (pairsInGroup.length > 0) {
38
+ const backendData = await this.pairsCache.getPairBackend(pairsInGroup[0]);
39
+ const maxOI = Number(backendData.group.maxOpenInterestP) / 1e10; // Convert from 10 decimals to number
40
+
41
+ limits.set(groupIndex, {
42
+ long: maxOI,
43
+ short: maxOI,
44
+ max: maxOI,
45
+ });
46
+ }
47
+ } catch (error) {
48
+ console.error(`Error getting OI limits for group ${groupIndex}:`, error);
49
+ }
50
+ }
51
+
52
+ return limits;
53
+ }
54
+
55
+ /**
56
+ * Get current open interest per category
57
+ * Note: Calculated by summing pair OIs in each group
58
+ * @returns Map of group index to OI
59
+ */
60
+ async getOI(): Promise<Map<number, OpenInterest>> {
61
+ const groupIndexes = await this.pairsCache.getGroupIndexes();
62
+ const oi = new Map<number, OpenInterest>();
63
+ const limits = await this.getOILimits();
64
+
65
+ for (const groupIndex of groupIndexes) {
66
+ try {
67
+ // Sum up OI from all pairs in this group
68
+ const pairsInGroup = await this.pairsCache.getPairsInGroup(groupIndex);
69
+ let totalLongOI = 0;
70
+ let totalShortOI = 0;
71
+
72
+ // This would require asset params to get individual pair OI
73
+ // For now, return default values
74
+ // TODO: Properly calculate by summing pair OIs
75
+ const maxOI = limits.get(groupIndex)?.max || 0;
76
+
77
+ oi.set(groupIndex, {
78
+ long: totalLongOI,
79
+ short: totalShortOI,
80
+ max: maxOI,
81
+ });
82
+ } catch (error) {
83
+ console.error(`Error getting OI for group ${groupIndex}:`, error);
84
+ }
85
+ }
86
+
87
+ return oi;
88
+ }
89
+
90
+ /**
91
+ * Get utilization per category
92
+ * @returns Map of group index to utilization
93
+ */
94
+ async getUtilization(): Promise<Map<number, Utilization>> {
95
+ const oi = await this.getOI();
96
+ const utilization = new Map<number, Utilization>();
97
+
98
+ for (const [groupIndex, oiData] of oi) {
99
+ const utilizationLong = oiData.max > 0 ? (oiData.long / oiData.max) * 100 : 0;
100
+ const utilizationShort = oiData.max > 0 ? (oiData.short / oiData.max) * 100 : 0;
101
+
102
+ utilization.set(groupIndex, {
103
+ utilizationLong,
104
+ utilizationShort,
105
+ });
106
+ }
107
+
108
+ return utilization;
109
+ }
110
+
111
+ /**
112
+ * Get category skew (long / total)
113
+ * @returns Map of group index to skew
114
+ */
115
+ async getCategorySkew(): Promise<Map<number, Skew>> {
116
+ const oi = await this.getOI();
117
+ const skew = new Map<number, Skew>();
118
+
119
+ for (const [groupIndex, oiData] of oi) {
120
+ const total = oiData.long + oiData.short;
121
+ const skewValue = total > 0 ? oiData.long / total : 0.5;
122
+
123
+ skew.set(groupIndex, { skew: skewValue });
124
+ }
125
+
126
+ return skew;
127
+ }
128
+ }
@@ -0,0 +1,225 @@
1
+ import { Contract, TransactionReceipt, TransactionRequest } from 'ethers';
2
+ import { BaseSigner } from '../signers/base';
3
+
4
+ /**
5
+ * Delegation RPC
6
+ * Handles delegation functionality for the Trading contract.
7
+ * Allows one address to execute trades on behalf of another.
8
+ */
9
+ export class DelegationRPC {
10
+ constructor(
11
+ private tradingContract: Contract,
12
+ private signer?: BaseSigner
13
+ ) {}
14
+
15
+ /**
16
+ * Set a delegate wallet that can perform trade actions on behalf of caller
17
+ * @param delegate - Address to authorize as delegate (use 0x0 to revoke)
18
+ * @returns Transaction receipt
19
+ */
20
+ async setDelegate(delegate: string): Promise<TransactionReceipt | null> {
21
+ if (!this.signer) {
22
+ throw new Error('Signer required for delegation operations');
23
+ }
24
+
25
+ const tx: TransactionRequest = {
26
+ to: await this.tradingContract.getAddress(),
27
+ data: this.tradingContract.interface.encodeFunctionData('setDelegate', [delegate]),
28
+ };
29
+
30
+ return await this.signAndSend(tx);
31
+ }
32
+
33
+ /**
34
+ * Remove the current delegate for the caller
35
+ * @returns Transaction receipt
36
+ */
37
+ async removeDelegate(): Promise<TransactionReceipt | null> {
38
+ if (!this.signer) {
39
+ throw new Error('Signer required for delegation operations');
40
+ }
41
+
42
+ const tx: TransactionRequest = {
43
+ to: await this.tradingContract.getAddress(),
44
+ data: this.tradingContract.interface.encodeFunctionData('removeDelegate'),
45
+ };
46
+
47
+ return await this.signAndSend(tx);
48
+ }
49
+
50
+ /**
51
+ * Get the delegate wallet for a given owner
52
+ * @param owner - Owner address to check
53
+ * @returns Delegate address (0x0 if none set)
54
+ */
55
+ async getDelegateFor(owner: string): Promise<string> {
56
+ return await this.tradingContract.delegations(owner);
57
+ }
58
+
59
+ /**
60
+ * Execute a delegated action on behalf of a trader
61
+ * @param trader - Address of the trader to act on behalf of
62
+ * @param callData - ABI-encoded function data for the action
63
+ * @param value - ETH value to send (for execution fees)
64
+ * @returns Transaction receipt
65
+ */
66
+ async delegatedAction(
67
+ trader: string,
68
+ callData: string,
69
+ value: bigint = 0n
70
+ ): Promise<TransactionReceipt | null> {
71
+ if (!this.signer) {
72
+ throw new Error('Signer required for delegation operations');
73
+ }
74
+
75
+ const tx: TransactionRequest = {
76
+ to: await this.tradingContract.getAddress(),
77
+ data: this.tradingContract.interface.encodeFunctionData('delegatedAction', [
78
+ trader,
79
+ callData,
80
+ ]),
81
+ value,
82
+ };
83
+
84
+ return await this.signAndSend(tx);
85
+ }
86
+
87
+ /**
88
+ * Helper: Create call data for openTrade to be used with delegatedAction
89
+ * @param tradeStruct - Trade struct with blockchain values
90
+ * @param orderType - Order type (0-3)
91
+ * @param slippageP - Slippage percentage in blockchain units
92
+ * @returns Encoded call data
93
+ */
94
+ encodeOpenTrade(
95
+ tradeStruct: any,
96
+ orderType: number,
97
+ slippageP: bigint
98
+ ): string {
99
+ return this.tradingContract.interface.encodeFunctionData('openTrade', [
100
+ tradeStruct,
101
+ orderType,
102
+ slippageP,
103
+ ]);
104
+ }
105
+
106
+ /**
107
+ * Helper: Create call data for closeTradeMarket to be used with delegatedAction
108
+ * @param pairIndex - Trading pair index
109
+ * @param index - Trade index
110
+ * @param amount - Amount to close in blockchain units
111
+ * @returns Encoded call data
112
+ */
113
+ encodeCloseTradeMarket(
114
+ pairIndex: number,
115
+ index: number,
116
+ amount: bigint
117
+ ): string {
118
+ return this.tradingContract.interface.encodeFunctionData('closeTradeMarket', [
119
+ pairIndex,
120
+ index,
121
+ amount,
122
+ ]);
123
+ }
124
+
125
+ /**
126
+ * Helper: Create call data for updateTpAndSl to be used with delegatedAction
127
+ * @param pairIndex - Trading pair index
128
+ * @param index - Trade index
129
+ * @param newSl - New stop loss in blockchain units
130
+ * @param newTp - New take profit in blockchain units
131
+ * @param priceUpdateData - Price update data
132
+ * @returns Encoded call data
133
+ */
134
+ encodeUpdateTpAndSl(
135
+ pairIndex: number,
136
+ index: number,
137
+ newSl: bigint,
138
+ newTp: bigint,
139
+ priceUpdateData: string[]
140
+ ): string {
141
+ return this.tradingContract.interface.encodeFunctionData('updateTpAndSl', [
142
+ pairIndex,
143
+ index,
144
+ newSl,
145
+ newTp,
146
+ priceUpdateData,
147
+ ]);
148
+ }
149
+
150
+ /**
151
+ * Helper: Create call data for updateMargin to be used with delegatedAction
152
+ * @param pairIndex - Trading pair index
153
+ * @param index - Trade index
154
+ * @param updateType - 0 for DEPOSIT, 1 for WITHDRAW
155
+ * @param amount - Amount in blockchain units
156
+ * @param priceUpdateData - Price update data
157
+ * @returns Encoded call data
158
+ */
159
+ encodeUpdateMargin(
160
+ pairIndex: number,
161
+ index: number,
162
+ updateType: number,
163
+ amount: bigint,
164
+ priceUpdateData: string[]
165
+ ): string {
166
+ return this.tradingContract.interface.encodeFunctionData('updateMargin', [
167
+ pairIndex,
168
+ index,
169
+ updateType,
170
+ amount,
171
+ priceUpdateData,
172
+ ]);
173
+ }
174
+
175
+ /**
176
+ * Helper method to sign and send transactions
177
+ */
178
+ private async signAndSend(tx: TransactionRequest): Promise<TransactionReceipt | null> {
179
+ if (!this.signer) {
180
+ throw new Error('Signer not set');
181
+ }
182
+
183
+ const address = await this.signer.getAddress();
184
+ tx.from = address;
185
+
186
+ const provider = this.tradingContract.runner?.provider;
187
+ if (!provider) {
188
+ throw new Error('Provider not available');
189
+ }
190
+
191
+ if (!tx.chainId) {
192
+ const network = await provider.getNetwork();
193
+ tx.chainId = network.chainId;
194
+ }
195
+
196
+ if (tx.nonce === undefined) {
197
+ tx.nonce = await provider.getTransactionCount(address);
198
+ }
199
+
200
+ if (!tx.gasLimit) {
201
+ tx.gasLimit = await provider.estimateGas(tx);
202
+ }
203
+
204
+ if (!tx.maxFeePerGas && !tx.gasPrice) {
205
+ const feeData = await provider.getFeeData();
206
+ if (feeData.maxFeePerGas) {
207
+ tx.maxFeePerGas = feeData.maxFeePerGas;
208
+ tx.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas || feeData.maxFeePerGas;
209
+ } else {
210
+ tx.gasPrice = feeData.gasPrice || undefined;
211
+ }
212
+ }
213
+
214
+ const signedTx = await this.signer.signTransaction(tx);
215
+ const txResponse = await provider.broadcastTransaction(signedTx);
216
+ return await txResponse.wait();
217
+ }
218
+
219
+ /**
220
+ * Set signer for transactions
221
+ */
222
+ setSigner(signer: BaseSigner): void {
223
+ this.signer = signer;
224
+ }
225
+ }
@@ -0,0 +1,150 @@
1
+ import { Contract, Provider } from 'ethers';
2
+ import { Fee, TradeInput, fromBlockchain10, toBlockchain6 } from '../types';
3
+ import { PairsCache } from './pairs_cache';
4
+
5
+ /**
6
+ * RPC module for fee calculations
7
+ */
8
+ export class FeeParametersRPC {
9
+ private provider: Provider;
10
+ private pairInfosContract: Contract;
11
+ private referralContract?: Contract;
12
+ private pairsCache: PairsCache;
13
+
14
+ constructor(
15
+ provider: Provider,
16
+ pairInfosContract: Contract,
17
+ pairsCache: PairsCache,
18
+ referralContract?: Contract
19
+ ) {
20
+ this.provider = provider;
21
+ this.pairInfosContract = pairInfosContract;
22
+ this.pairsCache = pairsCache;
23
+ this.referralContract = referralContract;
24
+ }
25
+
26
+ /**
27
+ * Get margin fee for all pairs (in basis points)
28
+ * @returns Map of pair index to fee
29
+ */
30
+ async getMarginFee(): Promise<Map<number, Fee>> {
31
+ const pairs = await this.pairsCache.getPairsInfo();
32
+ const fees = new Map<number, Fee>();
33
+
34
+ for (const [pairIndex] of pairs) {
35
+ try {
36
+ const feeP = await this.pairInfosContract.getPairMarginFeeP(pairIndex);
37
+ fees.set(pairIndex, {
38
+ feeP: fromBlockchain10(feeP),
39
+ });
40
+ } catch (error) {
41
+ console.error(`Error getting margin fee for pair ${pairIndex}:`, error);
42
+ fees.set(pairIndex, { feeP: 0 });
43
+ }
44
+ }
45
+
46
+ return fees;
47
+ }
48
+
49
+ /**
50
+ * Get constant spread parameter for each pair
51
+ * @returns Map of pair index to constant spread
52
+ */
53
+ async constantSpreadParameter(): Promise<Map<number, number>> {
54
+ const pairs = await this.pairsCache.getPairsInfo();
55
+ const spreads = new Map<number, number>();
56
+
57
+ for (const [pairIndex, pairInfo] of pairs) {
58
+ // Use the min spread as the constant spread
59
+ spreads.set(pairIndex, pairInfo.spread.min);
60
+ }
61
+
62
+ return spreads;
63
+ }
64
+
65
+ /**
66
+ * Get opening fee for a position
67
+ * @param positionSize - Position size in USDC
68
+ * @param isLong - True for long, false for short
69
+ * @param pairIndex - Pair index
70
+ * @returns Opening fee in USDC
71
+ */
72
+ async getOpeningFee(
73
+ positionSize: number,
74
+ isLong: boolean,
75
+ pairIndex: number
76
+ ): Promise<number> {
77
+ try {
78
+ const feeUsdc = await this.pairInfosContract.getOpenFeeUsdc(
79
+ pairIndex,
80
+ toBlockchain6(positionSize),
81
+ isLong
82
+ );
83
+ return fromBlockchain10(feeUsdc);
84
+ } catch (error) {
85
+ console.error('Error getting opening fee:', error);
86
+ return 0;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Get opening fee for a new trade with referral
92
+ * @param tradeInput - Trade input parameters
93
+ * @param referrer - Referrer address (optional)
94
+ * @returns Opening fee in USDC
95
+ */
96
+ async getNewTradeOpeningFee(
97
+ tradeInput: TradeInput,
98
+ referrer?: string
99
+ ): Promise<number> {
100
+ const pairIndex = await this.pairsCache.getPairIndex(tradeInput.pair);
101
+ if (pairIndex === undefined) {
102
+ throw new Error(`Pair ${tradeInput.pair} not found`);
103
+ }
104
+
105
+ const positionSize = tradeInput.collateralInTrade * tradeInput.leverage;
106
+ let fee = await this.getOpeningFee(positionSize, tradeInput.isLong, pairIndex);
107
+
108
+ // Apply referral discount if applicable
109
+ if (referrer && this.referralContract) {
110
+ try {
111
+ const rebate = await this.getTradeReferralRebate(
112
+ tradeInput.referrer || referrer,
113
+ referrer,
114
+ fee
115
+ );
116
+ fee -= rebate;
117
+ } catch (error) {
118
+ console.error('Error getting referral rebate:', error);
119
+ }
120
+ }
121
+
122
+ return fee;
123
+ }
124
+
125
+ /**
126
+ * Get trade referral rebate
127
+ * @param trader - Trader address
128
+ * @param referrer - Referrer address
129
+ * @param openingFee - Opening fee amount
130
+ * @returns Rebate amount
131
+ */
132
+ async getTradeReferralRebate(
133
+ trader: string,
134
+ referrer: string,
135
+ openingFee: number
136
+ ): Promise<number> {
137
+ if (!this.referralContract) {
138
+ return 0;
139
+ }
140
+
141
+ try {
142
+ const rebateP = await this.referralContract.getReferralRebateP(trader, referrer);
143
+ const rebatePercentage = fromBlockchain10(rebateP);
144
+ return openingFee * (rebatePercentage / 100);
145
+ } catch (error) {
146
+ console.error('Error getting referral rebate:', error);
147
+ return 0;
148
+ }
149
+ }
150
+ }
@@ -0,0 +1,164 @@
1
+ import { Contract } from 'ethers';
2
+
3
+ /**
4
+ * Call structure for multicall
5
+ */
6
+ export interface MulticallCall {
7
+ target: string;
8
+ callData: string;
9
+ }
10
+
11
+ /**
12
+ * Result structure from multicall
13
+ */
14
+ export interface MulticallResult {
15
+ blockNumber: number;
16
+ returnData: string[];
17
+ }
18
+
19
+ /**
20
+ * Multicall RPC
21
+ * Allows batching multiple contract view calls into a single request
22
+ */
23
+ export class MulticallRPC {
24
+ constructor(private multicallContract: Contract) {}
25
+
26
+ /**
27
+ * Execute multiple calls in a single transaction
28
+ * @param calls - Array of calls to execute
29
+ * @returns Block number and return data from each call
30
+ */
31
+ async aggregate(calls: MulticallCall[]): Promise<MulticallResult> {
32
+ const result = await this.multicallContract.aggregate(calls);
33
+
34
+ return {
35
+ blockNumber: Number(result.blockNumber || result[0]),
36
+ returnData: Array.isArray(result.returnData) ? result.returnData : result[1],
37
+ };
38
+ }
39
+
40
+ /**
41
+ * Helper: Create call data for a contract function
42
+ * @param contract - Contract instance
43
+ * @param functionName - Function name
44
+ * @param args - Function arguments
45
+ * @returns Call data object
46
+ */
47
+ createCall(contract: Contract, functionName: string, args: any[] = []): MulticallCall {
48
+ return {
49
+ target: contract.target as string,
50
+ callData: contract.interface.encodeFunctionData(functionName, args),
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Helper: Decode return data from a multicall result
56
+ * @param contract - Contract instance used for decoding
57
+ * @param functionName - Function name that was called
58
+ * @param returnData - Raw return data from multicall
59
+ * @returns Decoded result
60
+ */
61
+ decodeResult(contract: Contract, functionName: string, returnData: string): any {
62
+ return contract.interface.decodeFunctionResult(functionName, returnData);
63
+ }
64
+
65
+ /**
66
+ * Execute multicall and decode results
67
+ * @param calls - Array of call definitions with contract and function info
68
+ * @returns Decoded results
69
+ */
70
+ async aggregateAndDecode(
71
+ calls: Array<{
72
+ contract: Contract;
73
+ functionName: string;
74
+ args?: any[];
75
+ }>
76
+ ): Promise<{
77
+ blockNumber: number;
78
+ results: any[];
79
+ }> {
80
+ // Create multicall calls
81
+ const multicallCalls = calls.map((call) =>
82
+ this.createCall(call.contract, call.functionName, call.args || [])
83
+ );
84
+
85
+ // Execute multicall
86
+ const { blockNumber, returnData } = await this.aggregate(multicallCalls);
87
+
88
+ // Decode results
89
+ const results = returnData.map((data, index) => {
90
+ const call = calls[index];
91
+ return this.decodeResult(call.contract, call.functionName, data);
92
+ });
93
+
94
+ return {
95
+ blockNumber,
96
+ results,
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Batch read multiple open trades
102
+ * @param tradingStorageContract - TradingStorage contract instance
103
+ * @param trader - Trader address
104
+ * @param pairIndex - Pair index
105
+ * @param indices - Array of trade indices
106
+ * @returns Array of decoded trade data
107
+ */
108
+ async batchGetOpenTrades(
109
+ tradingStorageContract: Contract,
110
+ trader: string,
111
+ pairIndex: number,
112
+ indices: number[]
113
+ ): Promise<any[]> {
114
+ const calls = indices.map((index) => ({
115
+ contract: tradingStorageContract,
116
+ functionName: 'openTrades',
117
+ args: [trader, pairIndex, index],
118
+ }));
119
+
120
+ const { results } = await this.aggregateAndDecode(calls);
121
+ return results;
122
+ }
123
+
124
+ /**
125
+ * Batch read multiple trade infos
126
+ * @param tradingStorageContract - TradingStorage contract instance
127
+ * @param trader - Trader address
128
+ * @param pairIndex - Pair index
129
+ * @param indices - Array of trade indices
130
+ * @returns Array of decoded trade info data
131
+ */
132
+ async batchGetOpenTradesInfo(
133
+ tradingStorageContract: Contract,
134
+ trader: string,
135
+ pairIndex: number,
136
+ indices: number[]
137
+ ): Promise<any[]> {
138
+ const calls = indices.map((index) => ({
139
+ contract: tradingStorageContract,
140
+ functionName: 'openTradesInfo',
141
+ args: [trader, pairIndex, index],
142
+ }));
143
+
144
+ const { results } = await this.aggregateAndDecode(calls);
145
+ return results;
146
+ }
147
+
148
+ /**
149
+ * Batch read pair information
150
+ * @param pairStorageContract - PairStorage contract instance
151
+ * @param pairIndices - Array of pair indices
152
+ * @returns Array of decoded pair data
153
+ */
154
+ async batchGetPairs(pairStorageContract: Contract, pairIndices: number[]): Promise<any[]> {
155
+ const calls = pairIndices.map((pairIndex) => ({
156
+ contract: pairStorageContract,
157
+ functionName: 'pairs',
158
+ args: [pairIndex],
159
+ }));
160
+
161
+ const { results } = await this.aggregateAndDecode(calls);
162
+ return results;
163
+ }
164
+ }