@d8x/perpetuals-sdk 0.0.21 → 0.0.23

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.
@@ -0,0 +1,390 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const d8XMath_1 = require("./d8XMath");
13
+ const nodeSDKTypes_1 = require("./nodeSDKTypes");
14
+ const process_1 = require("process");
15
+ /**
16
+ * This class handles events and stores relevant variables
17
+ * as member variables. The events change the state of the member variables:
18
+ * mktData : MarketData relevant market data with current state (e.g. index price)
19
+ * ordersInPerpetual: Map<number, OrderStruct> all open orders for the given trader
20
+ * positionInPerpetual: Map<number, MarginAccount> all open positions for the given trader
21
+ *
22
+ * TODO:
23
+ * - update functions for midprice & index & collateral prices without event
24
+ * - testing
25
+ *
26
+ * Get data:
27
+ * - getPerpetualData(perp id (string) or symbol) : PerpetualState. This is a reference!
28
+ * - getExchangeInfo() : ExchangeInfo. This is a reference!
29
+ * - getCurrentPositionRisk(perp id (string) or symbol) : MarginAccount. This is a reference!
30
+ * - getOrdersInPerpetualMap : Map<number, OrderStruct>. This is a reference!
31
+ * - getpositionInPerpetualMap : Map<number, MarginAccount>. This is a reference!
32
+ *
33
+ * Construct with a trader address and a marketData object
34
+ * Initialize to gather all the relevant data.
35
+ * Send event variables to event handler "on<EventName>" - this updates members
36
+ * - [x] onUpdateMarkPrice : emitted on proxy; updates markprice and index price data
37
+ * - [x] onUpdateUpdateFundingRate : emitted on proxy; sets funding rate
38
+ * - [x] onExecutionFailed : emitted on order book; removes an open order
39
+ * - [x] onPerpetualLimitOrderCancelled : emitted on order book; removes an open order
40
+ * - [x] onPerpetualLimitOrderCreated : emitted on order book; adds an open order to the data
41
+ * - [x] async onUpdateMarginAccount : emitted on proxy; updates position data and open interest
42
+ * - [x] onTrade : emitted on proxy; returns TradeEvent to be displayed
43
+ */
44
+ class PerpetualEventHandler {
45
+ constructor(mktData, traderAddr) {
46
+ this.mktData = mktData;
47
+ this.traderAddr = traderAddr;
48
+ this.ordersInPerpetual = new Map();
49
+ this.positionInPerpetual = new Map();
50
+ }
51
+ /**
52
+ * Call this async function to initialize the
53
+ * market data
54
+ */
55
+ initialize() {
56
+ return __awaiter(this, void 0, void 0, function* () {
57
+ this.exchangeInfo = yield this.mktData.exchangeInfo();
58
+ // loop through all pools and perpetuals and get
59
+ // open positions and open orders
60
+ for (let k = 0; k < this.exchangeInfo.pools.length; k++) {
61
+ let poolState = this.exchangeInfo.pools[k];
62
+ let poolSymbol = poolState.poolSymbol;
63
+ for (let j = 0; j < poolState.perpetuals.length; j++) {
64
+ let perpState = poolState.perpetuals[j];
65
+ let perpSymbol = perpState.baseCurrency + "-" + perpState.quoteCurrency + "-" + poolSymbol;
66
+ let orders = yield this.mktData.openOrders(this.traderAddr, perpSymbol);
67
+ let perpId = perpState.id;
68
+ this.ordersInPerpetual.set(perpId, orders);
69
+ let position = yield this.mktData.positionRisk(this.traderAddr, perpSymbol);
70
+ this.positionInPerpetual.set(perpId, position);
71
+ }
72
+ }
73
+ });
74
+ }
75
+ /**
76
+ * Get the current exchange info
77
+ * @returns exchange info
78
+ */
79
+ getExchangeInfo() {
80
+ return this.exchangeInfo;
81
+ }
82
+ /**
83
+ * getOrdersInPerpetualMap
84
+ * @returns this.ordersInPerpetual
85
+ */
86
+ getOrdersInPerpetualMap() {
87
+ return this.ordersInPerpetual;
88
+ }
89
+ /**
90
+ * getpositionInPerpetualMap
91
+ * @returns this.positionInPerpetual
92
+ */
93
+ getpositionInPerpetualMap() {
94
+ return this.positionInPerpetual;
95
+ }
96
+ /**
97
+ * Get the data for a perpetual with a given index
98
+ * @param perpetualIdOrSymbol perpetual idx as string or symbol for which we want the data
99
+ * @returns perpetual data for this idx
100
+ */
101
+ getPerpetualData(perpetualIdOrSymbol) {
102
+ var _a;
103
+ let perpId = Number(perpetualIdOrSymbol);
104
+ if (isNaN(perpId)) {
105
+ perpId = this.mktData.getPerpIdFromSymbol(perpetualIdOrSymbol);
106
+ }
107
+ //uint24 perpetualId = uint24(_iPoolId) * 100_000 + iPerpetualIndex;
108
+ let poolIdx = Math.floor(perpId / 100000);
109
+ let perpetuals = (_a = this.exchangeInfo) === null || _a === void 0 ? void 0 : _a.pools[poolIdx].perpetuals;
110
+ if (perpetuals == undefined) {
111
+ (0, process_1.emitWarning)(`exchangeInfo not found, initialize perpetualEventHandler`);
112
+ return undefined;
113
+ }
114
+ // find perpetual
115
+ let k;
116
+ for (k = 0; k < (perpetuals === null || perpetuals === void 0 ? void 0 : perpetuals.length) && perpetuals[k].id != perpId; k++)
117
+ ;
118
+ if (perpetuals[k].id != perpId) {
119
+ (0, process_1.emitWarning)(`getPerpetualData: perpetual id ${perpId} not found`);
120
+ return undefined;
121
+ }
122
+ return perpetuals[k];
123
+ }
124
+ /**
125
+ * Get the trader's current position risk (margin account data)
126
+ * @param perpetualIdOrSymbol perpetual id as string ('100003') or symbol ('BTC-USD-MATIC')
127
+ * @returns undefined if no position or margin account (='position risk')
128
+ */
129
+ getCurrentPositionRisk(perpetualIdOrSymbol) {
130
+ let perpId = Number(perpetualIdOrSymbol);
131
+ if (isNaN(perpId)) {
132
+ perpId = this.mktData.getPerpIdFromSymbol(perpetualIdOrSymbol);
133
+ }
134
+ return this.positionInPerpetual.get(perpId);
135
+ }
136
+ /**
137
+ * Update the following prices:
138
+ * - index price
139
+ * - collateral price
140
+ * - mid-price
141
+ * @param perpetualIdOrSymbol perpetual id as string ('100003') or symbol ('BTC-USD-MATIC')
142
+ */
143
+ updatePrices(perpetualIdOrSymbol) {
144
+ return __awaiter(this, void 0, void 0, function* () {
145
+ let perpId = Number(perpetualIdOrSymbol);
146
+ let symbol = perpetualIdOrSymbol;
147
+ if (!isNaN(perpId)) {
148
+ let sym = this.mktData.getSymbolFromPerpId(perpId);
149
+ if (sym == undefined) {
150
+ throw new Error(`Symbol not found for perpetual ${perpId}`);
151
+ }
152
+ symbol = sym;
153
+ }
154
+ let perpState = yield this.mktData.getPerpetualState(symbol);
155
+ let perp = this.getPerpetualData(symbol);
156
+ if (perp == undefined) {
157
+ throw new Error(`Perpetual not found: ${symbol}`);
158
+ }
159
+ perp.state = perpState.state;
160
+ perp.indexPrice = perpState.indexPrice;
161
+ perp.collToQuoteIndexPrice = perpState.collToQuoteIndexPrice;
162
+ perp.markPrice = perpState.markPrice;
163
+ perp.midPrice = perpState.midPrice;
164
+ perp.currentFundingRateBps = perpState.currentFundingRateBps;
165
+ perp.openInterestBC = perpState.openInterestBC;
166
+ perp.maxPositionBC = perpState.maxPositionBC;
167
+ perp.indexPrice = perpState.indexPrice;
168
+ perp.collToQuoteIndexPrice = perpState.collToQuoteIndexPrice;
169
+ });
170
+ }
171
+ /**
172
+ * Handle the event UpdateMarkPrice and update relevant
173
+ * data
174
+ * @param perpetualId perpetual Id
175
+ * @param fMarkPricePremium premium rate in ABDK format
176
+ * @param fSpotIndexPrice spot index price in ABDK format
177
+ * @returns void
178
+ */
179
+ onUpdateMarkPrice(perpetualId, fMarkPricePremium, fSpotIndexPrice) {
180
+ let [newMarkPrice, newIndexPrice] = PerpetualEventHandler.ConvertUpdateMarkPrice(fMarkPricePremium, fSpotIndexPrice);
181
+ let perpetual = this.getPerpetualData(perpetualId.toString());
182
+ if (perpetual == undefined) {
183
+ return;
184
+ }
185
+ perpetual.markPrice = newMarkPrice;
186
+ perpetual.indexPrice = newIndexPrice;
187
+ }
188
+ /**
189
+ * Handle the event UpdateFundingRate and update relevant
190
+ * data
191
+ * UpdateFundingRate(uint24 indexed perpetualId, int128 fFundingRate)
192
+ * @param fFundingRate funding rate in ABDK format
193
+ */
194
+ onUpdateUpdateFundingRate(perpetualId, fFundingRate) {
195
+ let newRate = (0, d8XMath_1.ABK64x64ToFloat)(fFundingRate);
196
+ let perpetual = this.getPerpetualData(perpetualId.toString());
197
+ if (perpetual == undefined) {
198
+ return;
199
+ }
200
+ perpetual.currentFundingRateBps = newRate * 1e4;
201
+ }
202
+ /**
203
+ * event ExecutionFailed(
204
+ uint24 indexed perpetualId,
205
+ address indexed trader,
206
+ bytes32 digest,
207
+ string reason
208
+ );
209
+ * @param perpetualId id of the perpetual
210
+ * @param trader address of the trader
211
+ * @param digest digest of the order/cancel order
212
+ * @param reason reason why the execution failed
213
+ */
214
+ onExecutionFailed(perpetualId, trader, digest, reason) {
215
+ if (trader != this.traderAddr) {
216
+ (0, process_1.emitWarning)(`onExecutionFailed: trader ${trader} not relevant`);
217
+ return;
218
+ }
219
+ // remove order from open orders
220
+ let orderStructs = this.ordersInPerpetual.get(perpetualId);
221
+ if (orderStructs == undefined) {
222
+ (0, process_1.emitWarning)(`onExecutionFailed: no order found for perpetual ${perpetualId}`);
223
+ return;
224
+ }
225
+ if (reason == "cancel delay required") {
226
+ // canceling failed. We don't remove the order
227
+ return;
228
+ }
229
+ PerpetualEventHandler.deleteOrder(orderStructs, digest);
230
+ }
231
+ /**
232
+ * Event emitted by perpetual proxy
233
+ * event PerpetualLimitOrderCancelled(bytes32 indexed orderHash);
234
+ * @param orderId string order id/digest
235
+ */
236
+ onPerpetualLimitOrderCancelled(orderId) {
237
+ // remove order from open orders
238
+ let perpId = PerpetualEventHandler.findOrderForId(orderId, this.ordersInPerpetual);
239
+ if (perpId == undefined) {
240
+ (0, process_1.emitWarning)(`onPerpetualLimitOrderCancelled: no order found with id ${orderId}`);
241
+ return;
242
+ }
243
+ let orderStruct = this.ordersInPerpetual.get(perpId);
244
+ PerpetualEventHandler.deleteOrder(orderStruct, orderId);
245
+ }
246
+ /**
247
+ * event PerpetualLimitOrderCreated(
248
+ * uint24 indexed perpetualId,
249
+ * address indexed trader,
250
+ * address referrerAddr,
251
+ * address brokerAddr,
252
+ * Order order,
253
+ * bytes32 digest
254
+ *)
255
+ * @param perpetualId id of the perpetual
256
+ * @param trader address of the trader
257
+ * @param referrerAddr address of the referrer
258
+ * @param brokerAddr address of the broker
259
+ * @param Order order struct
260
+ * @param digest order id
261
+ */
262
+ onPerpetualLimitOrderCreated(perpetualId, trader, referrerAddr, brokerAddr, Order, digest) {
263
+ if (trader != this.traderAddr) {
264
+ (0, process_1.emitWarning)(`onPerpetualLimitOrderCreated: trader ${trader} not relevant`);
265
+ return;
266
+ }
267
+ let order = this.mktData.smartContractOrderToOrder(Order);
268
+ let orderStruct = this.ordersInPerpetual.get(perpetualId);
269
+ if (orderStruct == undefined) {
270
+ // no order for this perpetual so far
271
+ this.ordersInPerpetual.set(perpetualId, { orders: [order], orderIds: [digest] });
272
+ }
273
+ else {
274
+ orderStruct.orderIds.push(digest);
275
+ orderStruct.orders.push(order);
276
+ }
277
+ }
278
+ /**
279
+ * This function is async -> queries the margin account
280
+ * @param perpetualId id of the perpetual
281
+ * @param trader trader address
282
+ * @param positionId position id
283
+ * @param fPositionBC position size in base currency
284
+ * @param fCashCC margin collateral in margin account
285
+ * @param fLockedInValueQC pos*average opening price
286
+ * @param fFundingPaymentCC funding payment made
287
+ * @param fOpenInterestBC open interest
288
+ */
289
+ onUpdateMarginAccount(perpetualId, trader, positionId, fPositionBC, fCashCC, fLockedInValueQC, fFundingPaymentCC, fOpenInterestBC) {
290
+ return __awaiter(this, void 0, void 0, function* () {
291
+ let perpetual = this.getPerpetualData(perpetualId.toString());
292
+ if (perpetual == undefined) {
293
+ (0, process_1.emitWarning)(`onUpdateMarginAccount: perpetual ${perpetualId} not found`);
294
+ return;
295
+ }
296
+ perpetual.openInterestBC = (0, d8XMath_1.ABK64x64ToFloat)(fOpenInterestBC);
297
+ if (trader != this.traderAddr) {
298
+ return;
299
+ }
300
+ let perpetualIdStr = perpetualId.toString();
301
+ let margin = yield this.mktData.positionRisk(this.traderAddr, perpetualIdStr);
302
+ this.positionInPerpetual.set(perpetualId, margin);
303
+ });
304
+ }
305
+ /**
306
+ *
307
+ * @param perpetualId perpetual id
308
+ * @param trader trader address
309
+ * @param positionId position id
310
+ * @param order order struct
311
+ * @param orderDigest order id
312
+ * @param newPositionSizeBC new pos size in base currency ABDK
313
+ * @param price price in ABDK format
314
+ * @returns trade event data in regular number format
315
+ */
316
+ onTrade(perpetualId, trader, positionId, order, orderDigest, newPositionSizeBC, price) {
317
+ // remove order digest from open orders
318
+ let orderStructs = this.ordersInPerpetual.get(perpetualId);
319
+ if (orderStructs == undefined) {
320
+ (0, process_1.emitWarning)(`onTrade: executed order not found ${orderDigest}`);
321
+ }
322
+ else {
323
+ PerpetualEventHandler.deleteOrder(orderStructs, orderDigest);
324
+ }
325
+ // return transformed trade info
326
+ return {
327
+ perpetualId: perpetualId,
328
+ positionId: positionId,
329
+ orderId: orderDigest,
330
+ newPositionSizeBC: (0, d8XMath_1.ABK64x64ToFloat)(newPositionSizeBC),
331
+ executionPrice: (0, d8XMath_1.ABK64x64ToFloat)(newPositionSizeBC),
332
+ };
333
+ }
334
+ /**
335
+ * static function to find the number of the OrderStruct with given orderId
336
+ * @param orderId id/digest of order
337
+ * @param orderMap mapping for perpetualId->OrderStruct
338
+ * @returns id of perpetual that contains order with id = orderId or undefined
339
+ */
340
+ static findOrderForId(orderId, orderMap) {
341
+ /*orderMapMap<number, {
342
+ orders: Order[];
343
+ orderIds: string[];*/
344
+ for (const perpId of orderMap.keys()) {
345
+ let orderStructs = orderMap.get(perpId);
346
+ if (orderStructs === null || orderStructs === void 0 ? void 0 : orderStructs.orderIds.includes(orderId)) {
347
+ return perpId;
348
+ }
349
+ }
350
+ return undefined;
351
+ }
352
+ /**
353
+ * Delete the order with given id from the class member data
354
+ * @param orderStructs array of order struct as stored for the trader and a given perpetual
355
+ * @param orderId digest/order id
356
+ * @returns void
357
+ */
358
+ static deleteOrder(orderStructs, orderId) {
359
+ // find order
360
+ let k;
361
+ for (k = 0; k < orderStructs.orderIds.length && orderStructs.orderIds[k] != orderId; k++)
362
+ ;
363
+ if (orderStructs.orderIds[k] != orderId) {
364
+ (0, process_1.emitWarning)(`deleteOrder: no order found with digest ${orderId}`);
365
+ return;
366
+ }
367
+ // delete element k on reference of orders
368
+ orderStructs.orders[k] = orderStructs.orders[orderStructs.orders.length - 1];
369
+ orderStructs.orders.pop();
370
+ orderStructs.orderIds[k] = orderStructs.orderIds[orderStructs.orderIds.length - 1];
371
+ orderStructs.orderIds.pop();
372
+ }
373
+ /**
374
+ * UpdateMarkPrice(
375
+ * uint24 indexed perpetualId,
376
+ * int128 fMarkPricePremium,
377
+ * int128 fSpotIndexPrice
378
+ * )
379
+ * @param fMarkPricePremium premium rate in ABDK format
380
+ * @param fSpotIndexPrice spot index price in ABDK format
381
+ * @returns mark price and spot index in float
382
+ */
383
+ static ConvertUpdateMarkPrice(fMarkPricePremium, fSpotIndexPrice) {
384
+ let fMarkPrice = (0, d8XMath_1.mul64x64)(fSpotIndexPrice, nodeSDKTypes_1.ONE_64x64.add(fMarkPricePremium));
385
+ let markPrice = (0, d8XMath_1.ABK64x64ToFloat)(fMarkPrice);
386
+ let indexPrice = (0, d8XMath_1.ABK64x64ToFloat)(fSpotIndexPrice);
387
+ return [markPrice, indexPrice];
388
+ }
389
+ }
390
+ exports.default = PerpetualEventHandler;
package/dist/utils.d.ts CHANGED
@@ -33,5 +33,23 @@ export declare function fromBytes4(b: Buffer): string;
33
33
  * returned by the smart contract as bytes4
34
34
  */
35
35
  export declare function fromBytes4HexString(s: string): string;
36
+ /**
37
+ *
38
+ * @param {string} s string representing a hex-number ("0x...")
39
+ * @param {Object} mapping list of symbol and clean symbol pairs, e.g. [{symbol: "MATIC", cleanSymbol: "MATC"}, ...]
40
+ * @returns {string} user friendly currency symbol, e.g. "MATIC"
41
+ */
42
+ export declare function contractSymbolToSymbol(s: string, mapping: Array<{
43
+ [key: string]: string;
44
+ }>): string | undefined;
45
+ /**
46
+ * Converts symbol or symbol combination into long format
47
+ * @param {string} s symbol, e.g., USDC-MATC-USDC, MATC, USDC, ...
48
+ * @param {Object} mapping list of symbol and clean symbol pairs, e.g. [{symbol: "MATIC", cleanSymbol: "MATC"}, ...]
49
+ * @returns {string} long format e.g. MATIC. if not found the element is ""
50
+ */
51
+ export declare function symbol4BToLongSymbol(s: string, mapping: Array<{
52
+ [key: string]: string;
53
+ }>): string;
36
54
  export declare function combineFlags(f1: BigNumber, f2: BigNumber): BigNumber;
37
55
  export declare function containsFlag(f1: BigNumber, f2: BigNumber): boolean;
package/dist/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.containsFlag = exports.combineFlags = exports.fromBytes4HexString = exports.fromBytes4 = exports.toBytes4 = exports.to4Chars = void 0;
3
+ exports.containsFlag = exports.combineFlags = exports.symbol4BToLongSymbol = exports.contractSymbolToSymbol = exports.fromBytes4HexString = exports.fromBytes4 = exports.toBytes4 = exports.to4Chars = void 0;
4
4
  const ethers_1 = require("ethers");
5
5
  /**
6
6
  * @module utils
@@ -76,6 +76,42 @@ function fromBytes4HexString(s) {
76
76
  return res;
77
77
  }
78
78
  exports.fromBytes4HexString = fromBytes4HexString;
79
+ /**
80
+ *
81
+ * @param {string} s string representing a hex-number ("0x...")
82
+ * @param {Object} mapping list of symbol and clean symbol pairs, e.g. [{symbol: "MATIC", cleanSymbol: "MATC"}, ...]
83
+ * @returns {string} user friendly currency symbol, e.g. "MATIC"
84
+ */
85
+ function contractSymbolToSymbol(s, mapping) {
86
+ s = fromBytes4HexString(s);
87
+ for (let pair of mapping) {
88
+ if (pair.cleanSymbol == s) {
89
+ return pair.symbol;
90
+ }
91
+ }
92
+ }
93
+ exports.contractSymbolToSymbol = contractSymbolToSymbol;
94
+ /**
95
+ * Converts symbol or symbol combination into long format
96
+ * @param {string} s symbol, e.g., USDC-MATC-USDC, MATC, USDC, ...
97
+ * @param {Object} mapping list of symbol and clean symbol pairs, e.g. [{symbol: "MATIC", cleanSymbol: "MATC"}, ...]
98
+ * @returns {string} long format e.g. MATIC. if not found the element is ""
99
+ */
100
+ function symbol4BToLongSymbol(s, mapping) {
101
+ let ccy = s.split("-");
102
+ let longCCY = "";
103
+ for (let k = 0; k < ccy.length; k++) {
104
+ let sym = ccy[k];
105
+ for (let pair of mapping) {
106
+ if (pair.cleanSymbol == sym) {
107
+ longCCY = longCCY + "-" + pair.symbol;
108
+ break;
109
+ }
110
+ }
111
+ }
112
+ return longCCY.substring(1);
113
+ }
114
+ exports.symbol4BToLongSymbol = symbol4BToLongSymbol;
79
115
  function combineFlags(f1, f2) {
80
116
  return ethers_1.BigNumber.from(parseInt(f1.toString()) | parseInt(f2.toString()));
81
117
  }
@@ -15,7 +15,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const ethers_1 = require("ethers");
16
16
  const perpetualDataHandler_1 = __importDefault(require("./perpetualDataHandler"));
17
17
  const nodeSDKTypes_1 = require("./nodeSDKTypes");
18
- const utils_1 = require("./utils");
19
18
  const d8XMath_1 = require("./d8XMath");
20
19
  /**
21
20
  * This is a parent class for the classes that require
@@ -73,9 +72,7 @@ class WriteAccessHandler extends perpetualDataHandler_1.default {
73
72
  //extract margin-currency name
74
73
  let symbolarr = symbol.split("-");
75
74
  symbol = symbol.length == 3 ? symbolarr[2] : symbolarr[0];
76
- //transform into bytes4 currencies (without the space): "BTC", "USD", "MATC"
77
- symbol = (0, utils_1.to4Chars)(symbol);
78
- symbol = symbol.replace(/\0/g, "");
75
+ //note: symbol is in long format
79
76
  let marginTokenAddr = this.symbolToTokenAddrMap.get(symbol);
80
77
  if (marginTokenAddr == undefined || this.signer == null) {
81
78
  throw Error("No margin token or signer defined, call createProxyInstance");
package/package.json CHANGED
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "name": "@d8x/perpetuals-sdk",
29
29
  "description": "Node TypeScript SDK for D8X Perpetual Futures",
30
- "version": "0.0.21",
30
+ "version": "0.0.23",
31
31
  "main": "./dist/index.js",
32
32
  "types": "./dist/index.d.ts",
33
33
  "directories": {
@@ -190,13 +190,13 @@ export default class AccountTrade extends WriteAccessHandler {
190
190
  * @returns Exchange fee, in decimals (i.e. 0.1% is 0.001).
191
191
  */
192
192
  public async queryExchangeFee(poolSymbolName: string, brokerAddr?: string): Promise<number> {
193
- if (this.proxyContract == null || this.signer == null) {
193
+ if (this.proxyContract == null) {
194
194
  throw Error("no proxy contract or wallet initialized. Use createProxyInstance().");
195
195
  }
196
196
  if (typeof brokerAddr == "undefined") {
197
197
  brokerAddr = ZERO_ADDRESS;
198
198
  }
199
- let poolId = WriteAccessHandler._getPoolIdFromSymbol(poolSymbolName, this.poolStaticInfos);
199
+ let poolId = PerpetualDataHandler._getPoolIdFromSymbol(poolSymbolName, this.poolStaticInfos);
200
200
  let feeTbps = await this.proxyContract.queryExchangeFee(poolId, this.traderAddr, brokerAddr);
201
201
  return feeTbps / 100_000;
202
202
  }
package/src/d8XMath.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { assert } from "console";
1
2
  import { BigNumber } from "ethers";
2
3
  import { DECIMALS, ONE_64x64 } from "./nodeSDKTypes";
3
4
 
@@ -177,3 +178,87 @@ export function calculateLiquidationPriceCollateralQuote(
177
178
  let denominator = maintenance_margin_rate * Math.abs(position) - position;
178
179
  return numerator / denominator;
179
180
  }
181
+
182
+ /**
183
+ *
184
+ * @param targetLeverage Leverage of the resulting position. It must be positive unless the resulting position is closed.
185
+ * @param currentPosition Current position size, in base currency, signed.
186
+ * @param currentLockedInValue Current locked in value, average entry price times position size, in quote currency.
187
+ * @param tradeAmount Trade amount, in base currency, signed.
188
+ * @param markPrice Mark price, positive.
189
+ * @param indexPriceS2 Index price, positive.
190
+ * @param indexPriceS3 Collateral index price, positive.
191
+ * @param tradePrice Expected price to trade tradeAmount.
192
+ * @param feeRate
193
+ * @returns
194
+ */
195
+ export function getMarginRequiredForLeveragedTrade(
196
+ targetLeverage: number | undefined,
197
+ currentPosition: number,
198
+ currentLockedInValue: number,
199
+ tradeAmount: number,
200
+ markPrice: number,
201
+ indexPriceS2: number,
202
+ indexPriceS3: number,
203
+ tradePrice: number,
204
+ feeRate: number
205
+ ): number {
206
+ // we solve for margin in:
207
+ // |new position| * Sm / leverage + fee rate * |trade amount| * S2 = margin * S3 + current position * Sm - L + trade amount * (Sm - trade price)
208
+ // --> M S3 = |P'|Sm/L + FeeQC - PnL + (P'-P)(Price - Sm) = pos value / leverage + fees + price impact - pnl
209
+ let isClosing =
210
+ currentPosition != 0 && currentPosition * tradeAmount < 0 && currentPosition * (currentPosition + tradeAmount) >= 0;
211
+ let feesCC = (feeRate * Math.abs(tradeAmount) * indexPriceS2) / indexPriceS3;
212
+ let collRequired = feesCC;
213
+
214
+ if (!isClosing) {
215
+ if (targetLeverage == undefined || targetLeverage <= 0) {
216
+ throw Error("opening trades must have positive leverage");
217
+ }
218
+ // unrealized pnl (could be + or -) - price impact premium (+)
219
+ let pnlQC = currentPosition * markPrice - currentLockedInValue - tradeAmount * (tradePrice - markPrice);
220
+ collRequired +=
221
+ Math.max(0, (Math.abs(currentPosition + tradeAmount) * markPrice) / targetLeverage - pnlQC) / indexPriceS3;
222
+ }
223
+ return collRequired;
224
+ }
225
+
226
+ export function getMaxSignedPositionSize(
227
+ marginCollateral: number,
228
+ currentPosition: number,
229
+ currentLockedInValue: number,
230
+ direction: number,
231
+ limitPrice: number,
232
+ initialMarginRate: number,
233
+ feeRate: number,
234
+ markPrice: number,
235
+ indexPriceS2: number,
236
+ indexPriceS3: number
237
+ ): number {
238
+ // we solve for new position in:
239
+ // |new position| * Sm / leverage + fee rate * |trade amount| * S2 = margin * S3 + current position * Sm - L + trade amount * (Sm - entry price)
240
+ // |trade amount| = (new position - current position) * direction
241
+ let availableCash = marginCollateral * indexPriceS3 + currentPosition * markPrice - currentLockedInValue;
242
+ let effectiveMarginRate =
243
+ markPrice * initialMarginRate + feeRate * indexPriceS2 + direction * (limitPrice - markPrice);
244
+
245
+ return availableCash / effectiveMarginRate;
246
+ }
247
+
248
+ export function getNewPositionLeverage(
249
+ tradeAmount: number,
250
+ marginCollateral: number,
251
+ currentPosition: number,
252
+ currentLockedInValue: number,
253
+ indexPriceS2: number,
254
+ indexPriceS3: number,
255
+ markPrice: number,
256
+ limitPrice: number,
257
+ feeRate: number
258
+ ): number {
259
+ let newPosition = tradeAmount + currentPosition;
260
+ let pnlQC = currentPosition * markPrice - currentLockedInValue + tradeAmount * (markPrice - limitPrice);
261
+ return (
262
+ (Math.abs(newPosition) * indexPriceS2) / (marginCollateral * indexPriceS3 + pnlQC - feeRate * Math.abs(tradeAmount))
263
+ );
264
+ }
@@ -149,12 +149,4 @@ export default class LiquidityProviderTool extends WriteAccessHandler {
149
149
  });
150
150
  return tx;
151
151
  }
152
-
153
- /*
154
- TODO:
155
- - add liquidity
156
- addLiquidity(uint8 _poolId, int128 _fTokenAmount)
157
- - remove liquidity
158
- function removeLiquidity(uint8 _poolId, int128 _fShareAmount) external override nonReentrant
159
- */
160
152
  }