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.
- package/.claude/settings.local.json +11 -0
- package/CONTRACT_METHOD_FIXES.md +189 -0
- package/INTEGRATION_SUMMARY.md +219 -0
- package/OPTIMIZATION_GUIDE.md +238 -0
- package/README.md +384 -0
- package/SNAPSHOT_FIX_SUMMARY.md +161 -0
- package/SNAPSHOT_OPTIMIZATION_SUMMARY.md +199 -0
- package/dist/abis/Referral.d.ts +36 -0
- package/dist/abis/Referral.js +4 -0
- package/dist/abis/Trading.d.ts +57 -0
- package/dist/abis/Trading.js +742 -0
- package/dist/abis/erc20.d.ts +51 -0
- package/dist/abis/erc20.js +4 -0
- package/dist/abis/index.d.ts +8 -0
- package/dist/abis/index.js +24 -0
- package/dist/abis/multicall.d.ts +85 -0
- package/dist/abis/multicall.js +4 -0
- package/dist/abis/pairInfos.d.ts +77 -0
- package/dist/abis/pairInfos.js +4 -0
- package/dist/abis/pairStorage.d.ts +124 -0
- package/dist/abis/pairStorage.js +4 -0
- package/dist/abis/priceAggregator.d.ts +77 -0
- package/dist/abis/priceAggregator.js +4 -0
- package/dist/abis/tardingStorage.d.ts +97 -0
- package/dist/abis/tardingStorage.js +1295 -0
- package/dist/abis.d.ts +623 -0
- package/dist/abis.js +49 -0
- package/dist/client.d.ts +118 -0
- package/dist/client.js +224 -0
- package/dist/config.d.ts +43 -0
- package/dist/config.js +42 -0
- package/dist/crypto/spki.d.ts +55 -0
- package/dist/crypto/spki.js +160 -0
- package/dist/feed/feed_client.d.ts +68 -0
- package/dist/feed/feed_client.js +239 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +87 -0
- package/dist/rpc/asset_parameters.d.ts +62 -0
- package/dist/rpc/asset_parameters.js +169 -0
- package/dist/rpc/blended.d.ts +23 -0
- package/dist/rpc/blended.js +55 -0
- package/dist/rpc/category_parameters.d.ts +34 -0
- package/dist/rpc/category_parameters.js +105 -0
- package/dist/rpc/delegation.d.ts +81 -0
- package/dist/rpc/delegation.js +180 -0
- package/dist/rpc/fee_parameters.d.ts +46 -0
- package/dist/rpc/fee_parameters.js +113 -0
- package/dist/rpc/multicall.d.ts +83 -0
- package/dist/rpc/multicall.js +117 -0
- package/dist/rpc/pair_info_queries.d.ts +101 -0
- package/dist/rpc/pair_info_queries.js +161 -0
- package/dist/rpc/pairs_cache.d.ts +62 -0
- package/dist/rpc/pairs_cache.js +240 -0
- package/dist/rpc/referral_operations.d.ts +67 -0
- package/dist/rpc/referral_operations.js +143 -0
- package/dist/rpc/snapshot.d.ts +49 -0
- package/dist/rpc/snapshot.js +162 -0
- package/dist/rpc/trade.d.ts +84 -0
- package/dist/rpc/trade.js +249 -0
- package/dist/rpc/trading_operations.d.ts +103 -0
- package/dist/rpc/trading_operations.js +295 -0
- package/dist/rpc/trading_parameters.d.ts +49 -0
- package/dist/rpc/trading_parameters.js +94 -0
- package/dist/signers/base.d.ts +24 -0
- package/dist/signers/base.js +10 -0
- package/dist/signers/kms.d.ts +47 -0
- package/dist/signers/kms.js +172 -0
- package/dist/signers/local.d.ts +43 -0
- package/dist/signers/local.js +64 -0
- package/dist/types.d.ts +1419 -0
- package/dist/types.js +245 -0
- package/dist/utils.d.ts +52 -0
- package/dist/utils.js +134 -0
- package/examples/advanced-queries.ts +181 -0
- package/examples/basic-usage.ts +78 -0
- package/examples/delegation-and-referrals.ts +130 -0
- package/examples/get-pyth-ids.ts +61 -0
- package/examples/kms-signer.ts +31 -0
- package/examples/optimized-snapshot.ts +153 -0
- package/examples/price-feed-with-sdk-ids.ts +97 -0
- package/examples/price-feed.ts +36 -0
- package/examples/trading-operations.ts +149 -0
- package/package.json +41 -0
- package/src/abis/Referral.ts +3 -0
- package/src/abis/Trading.ts +741 -0
- package/src/abis/erc20.ts +3 -0
- package/src/abis/index.ts +8 -0
- package/src/abis/multicall.ts +3 -0
- package/src/abis/pairInfos.ts +3 -0
- package/src/abis/pairStorage.ts +3 -0
- package/src/abis/priceAggregator.ts +3 -0
- package/src/abis/tardingStorage.ts +1294 -0
- package/src/abis.ts +56 -0
- package/src/client.ts +373 -0
- package/src/config.ts +62 -0
- package/src/crypto/spki.ts +197 -0
- package/src/feed/feed_client.ts +288 -0
- package/src/index.ts +114 -0
- package/src/rpc/asset_parameters.ts +217 -0
- package/src/rpc/blended.ts +77 -0
- package/src/rpc/category_parameters.ts +128 -0
- package/src/rpc/delegation.ts +225 -0
- package/src/rpc/fee_parameters.ts +150 -0
- package/src/rpc/multicall.ts +164 -0
- package/src/rpc/pair_info_queries.ts +208 -0
- package/src/rpc/pairs_cache.ts +268 -0
- package/src/rpc/referral_operations.ts +164 -0
- package/src/rpc/snapshot.ts +210 -0
- package/src/rpc/trade.ts +306 -0
- package/src/rpc/trading_operations.ts +378 -0
- package/src/rpc/trading_parameters.ts +127 -0
- package/src/signers/base.ts +27 -0
- package/src/signers/kms.ts +212 -0
- package/src/signers/local.ts +70 -0
- package/src/types.ts +410 -0
- package/src/utils.ts +155 -0
- 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
|
+
}
|