@d8x/perpetuals-sdk 0.1.4 → 0.1.5

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.
@@ -3012,12 +3012,12 @@
3012
3012
  "type": "int128"
3013
3013
  },
3014
3014
  {
3015
- "internalType": "int128",
3016
- "name": "fTradeAmountBC",
3017
- "type": "int128"
3015
+ "internalType": "bool",
3016
+ "name": "_isBuy",
3017
+ "type": "bool"
3018
3018
  }
3019
3019
  ],
3020
- "name": "getMaxSignedTradeSizeForPos",
3020
+ "name": "getMaxSignedOpenTradeSizeForPos",
3021
3021
  "outputs": [
3022
3022
  {
3023
3023
  "internalType": "int128",
@@ -4834,7 +4834,13 @@
4834
4834
  }
4835
4835
  ],
4836
4836
  "name": "tradeViaOrderBook",
4837
- "outputs": [],
4837
+ "outputs": [
4838
+ {
4839
+ "internalType": "bool",
4840
+ "name": "",
4841
+ "type": "bool"
4842
+ }
4843
+ ],
4838
4844
  "stateMutability": "nonpayable",
4839
4845
  "type": "function"
4840
4846
  },
@@ -23,12 +23,12 @@
23
23
  },
24
24
  {
25
25
  "name": "central-park",
26
- "version": 4.0,
26
+ "version": 5.0,
27
27
  "proxyAddr": "0x4F1C7EE9E881Cc735f39Ca36c649ba9E930B4E4e",
28
28
  "nodeURL": "https://polygon-mumbai.blockpi.network/v1/rpc/public",
29
29
  "priceFeedConfigNetwork": "testnet",
30
30
  "proxyABILocation": "../abi/central-park/IPerpetualManager.json",
31
- "limitOrderBookFactoryAddr": "0xeC71488Ca228506C93CDaEd4C00Ef65ee7E6A4F1",
31
+ "limitOrderBookFactoryAddr": "0xdb43AdBb66925B7529b400C31879e499a72f6fad",
32
32
  "limitOrderBookFactoryABILocation": "../abi/central-park/LimitOrderBookFactory.json",
33
33
  "limitOrderBookABILocation": "../abi/central-park/LimitOrderBook.json",
34
34
  "symbolListLocation": "../config/symbolList.json"
@@ -59,7 +59,7 @@ export default class LiquidatorTool extends WriteAccessHandler {
59
59
  * If not, the position can be liquidated.
60
60
  * @param {string} symbol Symbol of the form ETH-USD-MATIC.
61
61
  * @param {string} traderAddr Address of the trader whose position you want to assess.
62
- * @param {[number,number]} indexPrices optional, index price S2/S3 for which we test
62
+ * @param {number[]} indexPrices optional, index price S2/S3 for which we test
63
63
  * @example
64
64
  * import { LiquidatorTool, PerpetualDataHandler } from '@d8x/perpetuals-sdk';
65
65
  * async function main() {
@@ -13,6 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const writeAccessHandler_1 = __importDefault(require("./writeAccessHandler"));
16
+ const d8XMath_1 = require("./d8XMath");
16
17
  /**
17
18
  * Functions to liquidate traders. This class requires a private key
18
19
  * and executes smart-contract interactions that require gas-payments.
@@ -92,7 +93,7 @@ class LiquidatorTool extends writeAccessHandler_1.default {
92
93
  * If not, the position can be liquidated.
93
94
  * @param {string} symbol Symbol of the form ETH-USD-MATIC.
94
95
  * @param {string} traderAddr Address of the trader whose position you want to assess.
95
- * @param {[number,number]} indexPrices optional, index price S2/S3 for which we test
96
+ * @param {number[]} indexPrices optional, index price S2/S3 for which we test
96
97
  * @example
97
98
  * import { LiquidatorTool, PerpetualDataHandler } from '@d8x/perpetuals-sdk';
98
99
  * async function main() {
@@ -124,12 +125,23 @@ class LiquidatorTool extends writeAccessHandler_1.default {
124
125
  let obj = yield this.priceFeedGetter.fetchPricesForPerpetual(symbol);
125
126
  indexPrices = [obj.idxPrices[0], obj.idxPrices[1]];
126
127
  }
127
- let traderState = yield this.proxyContract.getTraderState(perpID, traderAddr, indexPrices);
128
+ let traderState = yield this.proxyContract.getTraderState(perpID, traderAddr, indexPrices.map((x) => (0, d8XMath_1.floatToABK64x64)(x)));
128
129
  if (traderState[idx_notional] == 0) {
129
130
  // trader does not have open position
130
- return false;
131
+ return true;
131
132
  }
132
- return yield this.proxyContract.isTraderMaintenanceMarginSafe(perpID, traderAddr);
133
+ // calculate margin from traderstate
134
+ const idx_maintenanceMgnRate = 10;
135
+ const idx_marginAccountPositionBC = 4;
136
+ const idx_collateralToQuoteConversion = 9;
137
+ const idx_marginBalance = 0;
138
+ const maintMgnRate = (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_maintenanceMgnRate]);
139
+ const pos = (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_marginAccountPositionBC]);
140
+ const marginbalance = (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_marginBalance]);
141
+ const coll2quote = (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_collateralToQuoteConversion]);
142
+ const base2collateral = indexPrices[0] / coll2quote;
143
+ const threshold = Math.abs(pos * base2collateral * maintMgnRate);
144
+ return marginbalance >= threshold;
133
145
  });
134
146
  }
135
147
  /**
@@ -282,6 +282,14 @@ export default class MarketData extends PerpetualDataHandler {
282
282
  * @ignore
283
283
  */
284
284
  static orderIdsOfTrader(traderAddr: string, orderBookContract: ethers.Contract): Promise<string[]>;
285
+ /**
286
+ * Query the available margin conditional on the given (or current) index prices
287
+ * Result is in collateral currency
288
+ * @param traderAddr address of the trader
289
+ * @param symbol perpetual symbol of the form BTC-USD-MATIC
290
+ * @param indexPrices optional index prices, will otherwise fetch from REST API
291
+ * @returns available margin in collateral currency
292
+ */
285
293
  getAvailableMargin(traderAddr: string, symbol: string, indexPrices?: [number, number]): Promise<number>;
286
294
  /**
287
295
  * Calculate a type of exchange loyality score based on trader volume
@@ -249,9 +249,10 @@ class MarketData extends perpetualDataHandler_1.default {
249
249
  let exchangeFeeTbps = yield this.proxyContract.queryExchangeFee(poolId, traderAddr, (_b = order.brokerAddr) !== null && _b !== void 0 ? _b : nodeSDKTypes_1.ZERO_ADDRESS);
250
250
  let exchangeFeeCC = (Math.abs(tradeAmountBC) * exchangeFeeTbps * 1e-5 * S2) / S3;
251
251
  let brokerFeeCC = (Math.abs(tradeAmountBC) * ((_c = order.brokerFeeTbps) !== null && _c !== void 0 ? _c : 0) * 1e-5 * S2) / S3;
252
+ let referralFeeCC = this.symbolToPerpStaticInfo.get(account.symbol).referralRebate;
252
253
  // Trade type:
253
254
  let isClose = newPositionBC == 0 || newPositionBC * tradeAmountBC < 0;
254
- let isOpen = newPositionBC != 0 && (currentPositionBC == 0 || newPositionBC * currentPositionBC > 0); // regular open, no flip
255
+ let isOpen = newPositionBC != 0 && (currentPositionBC == 0 || tradeAmountBC * currentPositionBC > 0); // regular open, no flip
255
256
  let isFlip = Math.abs(newPositionBC) > Math.abs(currentPositionBC) && !isOpen; // flip position sign, not fully closed
256
257
  let keepPositionLvgOnClose = ((_d = order.keepPositionLvg) !== null && _d !== void 0 ? _d : false) && !isOpen;
257
258
  // Contract: _doMarginCollateralActions
@@ -272,10 +273,10 @@ class MarketData extends perpetualDataHandler_1.default {
272
273
  targetLvg = isFlip || isOpen ? (_e = order.leverage) !== null && _e !== void 0 ? _e : 1 / initialMarginRate : 0;
273
274
  let [b0, pos0] = isOpen ? [0, 0] : [account.collateralCC, currentPositionBC];
274
275
  traderDepositCC = (0, d8XMath_1.getDepositAmountForLvgTrade)(b0, pos0, tradeAmountBC, targetLvg, tradePrice, S3, Sm);
275
- console.log("deposit for trget lvg:", traderDepositCC);
276
276
  // fees are paid from wallet in this case
277
277
  // referral rebate??
278
- traderDepositCC += exchangeFeeCC + brokerFeeCC;
278
+ console.log("deposit for trget lvg:", traderDepositCC);
279
+ traderDepositCC += exchangeFeeCC + brokerFeeCC + referralFeeCC;
279
280
  }
280
281
  // Contract: _executeTrade
281
282
  let deltaCashCC = (-tradeAmountBC * (tradePrice - S2)) / S3;
@@ -286,7 +287,7 @@ class MarketData extends perpetualDataHandler_1.default {
286
287
  deltaCashCC += pnl / S3;
287
288
  }
288
289
  // funding and fees
289
- deltaCashCC = deltaCashCC + account.unrealizedFundingCollateralCCY - exchangeFeeCC - brokerFeeCC;
290
+ deltaCashCC = deltaCashCC + account.unrealizedFundingCollateralCCY - exchangeFeeCC - brokerFeeCC - referralFeeCC;
290
291
  // New cash, locked-in, entry price & leverage after trade
291
292
  let newLockedInValueQC = currentLockedInQC + deltaLockedQC;
292
293
  let newMarginCashCC = currentMarginCashCC + deltaCashCC + traderDepositCC;
@@ -411,6 +412,9 @@ class MarketData extends perpetualDataHandler_1.default {
411
412
  else {
412
413
  S2Liq = (0, d8XMath_1.calculateLiquidationPriceCollateralQuote)(lockedInQC, signedPositionBC, marginCashCC, tau);
413
414
  }
415
+ // floor at 0
416
+ S2Liq = S2Liq < 0 ? 0 : S2Liq;
417
+ S3Liq = S3Liq && S3Liq < 0 ? 0 : S3Liq;
414
418
  return [S2Liq, S3Liq, tau];
415
419
  }
416
420
  /**
@@ -595,6 +599,7 @@ class MarketData extends perpetualDataHandler_1.default {
595
599
  S2Symbol: perpInfo.S2Symbol,
596
600
  S3Symbol: perpInfo.S3Symbol,
597
601
  lotSizeBC: perpInfo.lotSizeBC,
602
+ referralRebate: perpInfo.referralRebate,
598
603
  priceIds: perpInfo.priceIds,
599
604
  };
600
605
  return res;
@@ -665,6 +670,14 @@ class MarketData extends perpetualDataHandler_1.default {
665
670
  return digests;
666
671
  });
667
672
  }
673
+ /**
674
+ * Query the available margin conditional on the given (or current) index prices
675
+ * Result is in collateral currency
676
+ * @param traderAddr address of the trader
677
+ * @param symbol perpetual symbol of the form BTC-USD-MATIC
678
+ * @param indexPrices optional index prices, will otherwise fetch from REST API
679
+ * @returns available margin in collateral currency
680
+ */
668
681
  getAvailableMargin(traderAddr, symbol, indexPrices) {
669
682
  return __awaiter(this, void 0, void 0, function* () {
670
683
  if (this.proxyContract == null) {
@@ -675,15 +688,11 @@ class MarketData extends perpetualDataHandler_1.default {
675
688
  let obj = yield this.priceFeedGetter.fetchPricesForPerpetual(symbol);
676
689
  indexPrices = [obj.idxPrices[0], obj.idxPrices[1]];
677
690
  }
678
- let mgnAcct = yield perpetualDataHandler_1.default.getMarginAccount(traderAddr, symbol, this.symbolToPerpStaticInfo, this.proxyContract, [indexPrices[0], indexPrices[1]]);
679
- let S2 = indexPrices[0];
680
- let ccyType = this.getPerpetualStaticInfo(symbol).collateralCurrencyType;
681
- let S3 = ccyType == nodeSDKTypes_1.COLLATERAL_CURRENCY_QUANTO ? indexPrices[1] : ccyType == nodeSDKTypes_1.COLLATERAL_CURRENCY_QUOTE ? 1 : S2;
682
- let perpInfo = this.symbolToPerpStaticInfo.get(symbol);
683
- let balanceCC = mgnAcct.collateralCC + mgnAcct.unrealizedPnlQuoteCCY / S3;
684
- let initalMarginCC = Math.abs((perpInfo.initialMarginRate * mgnAcct.positionNotionalBaseCCY * mgnAcct.markPrice) /
685
- mgnAcct.collToQuoteConversion);
686
- return balanceCC - initalMarginCC;
691
+ let perpID = perpetualDataHandler_1.default.symbolToPerpetualId(symbol, this.symbolToPerpStaticInfo);
692
+ let traderState = yield this.proxyContract.getTraderState(perpID, traderAddr, indexPrices.map((x) => (0, d8XMath_1.floatToABK64x64)(x)));
693
+ const idx_availableMargin = 1;
694
+ let mgn = (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_availableMargin]);
695
+ return mgn;
687
696
  });
688
697
  }
689
698
  /**
@@ -75,6 +75,7 @@ export interface PerpetualStaticInfo {
75
75
  S2Symbol: string;
76
76
  S3Symbol: string;
77
77
  lotSizeBC: number;
78
+ referralRebate: number;
78
79
  priceIds: string[];
79
80
  }
80
81
  /**
@@ -88,6 +88,7 @@ class PerpetualDataHandler {
88
88
  let initRate = [];
89
89
  let mgnRate = [];
90
90
  let lotSizes = [];
91
+ let refRebates = [];
91
92
  for (let k = 0; k < perpetualIDs.length; k++) {
92
93
  let perp = yield proxyContract.getPerpetual(perpetualIDs[k]);
93
94
  let base = (0, utils_1.contractSymbolToSymbol)(perp.S2BaseCCY, this.symbolList);
@@ -108,6 +109,7 @@ class PerpetualDataHandler {
108
109
  initRate.push((0, d8XMath_1.ABK64x64ToFloat)(perp.fInitialMarginRate));
109
110
  mgnRate.push((0, d8XMath_1.ABK64x64ToFloat)(perp.fMaintenanceMarginRate));
110
111
  lotSizes.push((0, d8XMath_1.ABK64x64ToFloat)(perp.fLotSizeBC));
112
+ refRebates.push((0, d8XMath_1.ABK64x64ToFloat)(perp.fReferralRebateCC));
111
113
  // try to find a limit order book
112
114
  let lobAddr = yield this.lobFactoryContract.getOrderBookAddress(perpetualIDs[k]);
113
115
  currentLimitOrderBookAddr.push(lobAddr);
@@ -146,6 +148,7 @@ class PerpetualDataHandler {
146
148
  // add price IDs
147
149
  let idsB32, isPyth;
148
150
  [idsB32, isPyth] = yield proxyContract.getPriceInfo(perpetualIDs[k]);
151
+ console.log("ref reb", refRebates[k]);
149
152
  this.symbolToPerpStaticInfo.set(currentSymbols3[k], {
150
153
  id: perpetualIDs[k],
151
154
  limitOrderBookAddr: currentLimitOrderBookAddr[k],
@@ -155,6 +158,7 @@ class PerpetualDataHandler {
155
158
  S2Symbol: currentSymbols[k],
156
159
  S3Symbol: currentSymbolsS3[k],
157
160
  lotSizeBC: lotSizes[k],
161
+ referralRebate: refRebates[k],
158
162
  priceIds: idsB32,
159
163
  });
160
164
  }
@@ -296,7 +300,7 @@ class PerpetualDataHandler {
296
300
  const idx_mark_price = 8;
297
301
  const idx_lvg = 7;
298
302
  const idx_s3 = 9;
299
- let traderState = yield _proxyContract.getTraderState(perpId, traderAddr, _pxS2S3.map(x => (0, d8XMath_1.floatToABK64x64)(x)));
303
+ let traderState = yield _proxyContract.getTraderState(perpId, traderAddr, _pxS2S3.map((x) => (0, d8XMath_1.floatToABK64x64)(x)));
300
304
  let isEmpty = traderState[idx_notional] == 0;
301
305
  let cash = (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_cash]);
302
306
  let S2Liq = 0, S3Liq = 0, tau = Infinity, pnl = 0, unpaidFundingCC = 0, fLockedIn = ethers_1.BigNumber.from(0), side = nodeSDKTypes_1.CLOSED_SIDE, entryPrice = 0;
@@ -413,6 +417,9 @@ class PerpetualDataHandler {
413
417
  else {
414
418
  S2Liq = (0, d8XMath_1.calculateLiquidationPriceCollateralQuote)(lockedInValueQC, position, cashCC, tau);
415
419
  }
420
+ // floor at 0
421
+ S2Liq = S2Liq < 0 ? 0 : S2Liq;
422
+ S3Liq = S3Liq && S3Liq < 0 ? 0 : S3Liq;
416
423
  // account cash + pnl = avail cash + pos Sm - L = margin balance
417
424
  let pnl = position * Sm - lockedInValueQC + unpaidFunding;
418
425
  return [S2Liq, S3Liq, tau, pnl, unpaidFundingCC];
@@ -30,16 +30,16 @@ export default class PriceFeeds {
30
30
  mktClosed: [boolean, boolean];
31
31
  }>;
32
32
  /**
33
- * Get all prices/isMarketClosed for the provided symbols via
34
- * "latest_price_feeds" and triangulation. Triangulation must be defined in config, unless
35
- * it is a direct price feed.
36
- * @returns map of feed-price symbol to price/isMarketClosed
37
- */
33
+ * Get all prices/isMarketClosed for the provided symbols via
34
+ * "latest_price_feeds" and triangulation. Triangulation must be defined in config, unless
35
+ * it is a direct price feed.
36
+ * @returns map of feed-price symbol to price/isMarketClosed
37
+ */
38
38
  fetchPrices(symbols: string[]): Promise<Map<string, [number, boolean]>>;
39
39
  /**
40
40
  * Get index prices and market closed information for the given perpetual
41
41
  * @param symbol perpetual symbol such as ETH-USD-MATIC
42
- * @returns
42
+ * @returns Index prices and market closed information
43
43
  */
44
44
  fetchPricesForPerpetual(symbol: string): Promise<{
45
45
  idxPrices: number[];
@@ -76,13 +76,13 @@ export default class PriceFeeds {
76
76
  */
77
77
  calculateTriangulatedPricesFromFeedInfo(symbols: string[], feeds: PriceFeedSubmission): [number[], boolean[]];
78
78
  /**
79
- * Extract pair-prices from underlying price feeds via triangulation
80
- * The function either needs a direct price feed or a defined triangulation to succesfully
81
- * return a triangulated price
82
- * @param symbols array of pairs for which we want prices, e.g., [BTC-USDC, ETH-USD]
83
- * @param feeds data obtained via fetchLatestFeedPriceInfo or fetchLatestFeedPrices
84
- * @returns array of prices with same order as symbols
85
- */
79
+ * Extract pair-prices from underlying price feeds via triangulation
80
+ * The function either needs a direct price feed or a defined triangulation to succesfully
81
+ * return a triangulated price
82
+ * @param symbols array of pairs for which we want prices, e.g., [BTC-USDC, ETH-USD]
83
+ * @param feeds data obtained via fetchLatestFeedPriceInfo or fetchLatestFeedPrices
84
+ * @returns array of prices with same order as symbols
85
+ */
86
86
  triangulatePricesFromFeedPrices(symbols: string[], feedPriceMap: Map<string, [number, boolean]>): [number[], boolean[]];
87
87
  /**
88
88
  * Queries the REST endpoint and returns parsed VAA price data
@@ -55,7 +55,7 @@ class PriceFeeds {
55
55
  // fetch prices from required price-feeds (REST)
56
56
  let submission = yield this.fetchLatestFeedPriceInfoForPerpetual(symbol);
57
57
  // calculate index-prices from price-feeds
58
- let [_idxPrices, _mktClosed] = this.calculateTriangulatedPricesFromFeedInfo(indexSymbols.filter((x) => x != ''), submission);
58
+ let [_idxPrices, _mktClosed] = this.calculateTriangulatedPricesFromFeedInfo(indexSymbols.filter((x) => x != ""), submission);
59
59
  let idxPrices = [_idxPrices[0], 0];
60
60
  let mktClosed = [_mktClosed[0], false];
61
61
  if (idxPrices.length > 1) {
@@ -66,16 +66,16 @@ class PriceFeeds {
66
66
  });
67
67
  }
68
68
  /**
69
- * Get all prices/isMarketClosed for the provided symbols via
70
- * "latest_price_feeds" and triangulation. Triangulation must be defined in config, unless
71
- * it is a direct price feed.
72
- * @returns map of feed-price symbol to price/isMarketClosed
73
- */
69
+ * Get all prices/isMarketClosed for the provided symbols via
70
+ * "latest_price_feeds" and triangulation. Triangulation must be defined in config, unless
71
+ * it is a direct price feed.
72
+ * @returns map of feed-price symbol to price/isMarketClosed
73
+ */
74
74
  fetchPrices(symbols) {
75
75
  return __awaiter(this, void 0, void 0, function* () {
76
76
  let feedPrices = yield this.fetchAllFeedPrices();
77
77
  let [prices, mktClosed] = this.triangulatePricesFromFeedPrices(symbols, feedPrices);
78
- let symMap = new Map;
78
+ let symMap = new Map();
79
79
  for (let k = 0; k < symbols.length; k++) {
80
80
  symMap.set(symbols[k], [prices[k], mktClosed[k]]);
81
81
  }
@@ -85,11 +85,11 @@ class PriceFeeds {
85
85
  /**
86
86
  * Get index prices and market closed information for the given perpetual
87
87
  * @param symbol perpetual symbol such as ETH-USD-MATIC
88
- * @returns
88
+ * @returns Index prices and market closed information
89
89
  */
90
90
  fetchPricesForPerpetual(symbol) {
91
91
  return __awaiter(this, void 0, void 0, function* () {
92
- let indexSymbols = this.dataHandler.getIndexSymbols(symbol).filter(x => x != "");
92
+ let indexSymbols = this.dataHandler.getIndexSymbols(symbol).filter((x) => x != "");
93
93
  // determine relevant price feeds
94
94
  let feedSymbols = new Array();
95
95
  for (let sym of indexSymbols) {
@@ -229,7 +229,13 @@ class PriceFeeds {
229
229
  prices.push(price);
230
230
  timestamps.push(pxInfo.publish_time);
231
231
  }
232
- return { "symbols": symbols, priceFeedVaas: priceFeedUpdates, prices: prices, isMarketClosed: mktClosed, timestamps: timestamps };
232
+ return {
233
+ symbols: symbols,
234
+ priceFeedVaas: priceFeedUpdates,
235
+ prices: prices,
236
+ isMarketClosed: mktClosed,
237
+ timestamps: timestamps,
238
+ };
233
239
  });
234
240
  }
235
241
  /**
@@ -248,13 +254,13 @@ class PriceFeeds {
248
254
  return this.triangulatePricesFromFeedPrices(symbols, priceMap);
249
255
  }
250
256
  /**
251
- * Extract pair-prices from underlying price feeds via triangulation
252
- * The function either needs a direct price feed or a defined triangulation to succesfully
253
- * return a triangulated price
254
- * @param symbols array of pairs for which we want prices, e.g., [BTC-USDC, ETH-USD]
255
- * @param feeds data obtained via fetchLatestFeedPriceInfo or fetchLatestFeedPrices
256
- * @returns array of prices with same order as symbols
257
- */
257
+ * Extract pair-prices from underlying price feeds via triangulation
258
+ * The function either needs a direct price feed or a defined triangulation to succesfully
259
+ * return a triangulated price
260
+ * @param symbols array of pairs for which we want prices, e.g., [BTC-USDC, ETH-USD]
261
+ * @param feeds data obtained via fetchLatestFeedPriceInfo or fetchLatestFeedPrices
262
+ * @returns array of prices with same order as symbols
263
+ */
258
264
  triangulatePricesFromFeedPrices(symbols, feedPriceMap) {
259
265
  let prices = new Array();
260
266
  let mktClosed = new Array();
@@ -284,7 +290,7 @@ class PriceFeeds {
284
290
  */
285
291
  fetchVAAQuery(query) {
286
292
  return __awaiter(this, void 0, void 0, function* () {
287
- const headers = { headers: { 'Content-Type': 'application/json' } };
293
+ const headers = { headers: { "Content-Type": "application/json" } };
288
294
  let response = yield fetch(query, headers);
289
295
  if (!response.ok) {
290
296
  throw new Error(`Failed to fetch posts (${response.status}): ${response.statusText}`);
@@ -306,7 +312,7 @@ class PriceFeeds {
306
312
  */
307
313
  fetchPriceQuery(query) {
308
314
  return __awaiter(this, void 0, void 0, function* () {
309
- const headers = { headers: { 'Content-Type': 'application/json' } };
315
+ const headers = { headers: { "Content-Type": "application/json" } };
310
316
  let response = yield fetch(query, headers);
311
317
  if (!response.ok) {
312
318
  throw new Error(`Failed to fetch posts (${response.status}): ${response.statusText}`);
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const D8X_SDK_VERSION = "0.1.4";
1
+ export declare const D8X_SDK_VERSION = "0.1.5";
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.D8X_SDK_VERSION = void 0;
4
- exports.D8X_SDK_VERSION = "0.1.4";
4
+ exports.D8X_SDK_VERSION = "0.1.5";
@@ -45,7 +45,7 @@ export default class WriteAccessHandler extends PerpetualDataHandler {
45
45
  * into a mock token used for trading on testnet, with a rate of 1:100_000
46
46
  * @param symbol Pool margin token e.g. MATIC
47
47
  * @param amountToPay Amount in chain currency, e.g. "0.1" for 0.1 MATIC
48
- * @returns
48
+ * @returns Transaction object
49
49
  */
50
- swapForMockToken(symbol: string, amountToPay: string): Promise<any>;
50
+ swapForMockToken(symbol: string, amountToPay: string): Promise<ethers.ContractTransaction>;
51
51
  }
@@ -106,7 +106,7 @@ class WriteAccessHandler extends perpetualDataHandler_1.default {
106
106
  * into a mock token used for trading on testnet, with a rate of 1:100_000
107
107
  * @param symbol Pool margin token e.g. MATIC
108
108
  * @param amountToPay Amount in chain currency, e.g. "0.1" for 0.1 MATIC
109
- * @returns
109
+ * @returns Transaction object
110
110
  */
111
111
  swapForMockToken(symbol, amountToPay) {
112
112
  return __awaiter(this, void 0, void 0, function* () {
package/package.json CHANGED
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "name": "@d8x/perpetuals-sdk",
35
35
  "description": "Node TypeScript SDK for D8X Perpetual Futures",
36
- "version": "0.1.4",
36
+ "version": "0.1.5",
37
37
  "main": "./dist/index.js",
38
38
  "types": "./dist/index.d.ts",
39
39
  "directories": {
@@ -1,6 +1,7 @@
1
1
  import WriteAccessHandler from "./writeAccessHandler";
2
2
  import { NodeSDKConfig, PriceFeedSubmission } from "./nodeSDKTypes";
3
3
  import { ethers } from "ethers";
4
+ import { ABK64x64ToFloat, floatToABK64x64 } from "./d8XMath";
4
5
 
5
6
  /**
6
7
  * Functions to liquidate traders. This class requires a private key
@@ -86,7 +87,7 @@ export default class LiquidatorTool extends WriteAccessHandler {
86
87
  * If not, the position can be liquidated.
87
88
  * @param {string} symbol Symbol of the form ETH-USD-MATIC.
88
89
  * @param {string} traderAddr Address of the trader whose position you want to assess.
89
- * @param {[number,number]} indexPrices optional, index price S2/S3 for which we test
90
+ * @param {number[]} indexPrices optional, index price S2/S3 for which we test
90
91
  * @example
91
92
  * import { LiquidatorTool, PerpetualDataHandler } from '@d8x/perpetuals-sdk';
92
93
  * async function main() {
@@ -106,7 +107,11 @@ export default class LiquidatorTool extends WriteAccessHandler {
106
107
  * @returns {boolean} True if the trader is maintenance margin safe in the perpetual.
107
108
  * False means that the trader's position can be liquidated.
108
109
  */
109
- public async isMaintenanceMarginSafe(symbol: string, traderAddr: string, indexPrices?: [number, number]): Promise<boolean> {
110
+ public async isMaintenanceMarginSafe(
111
+ symbol: string,
112
+ traderAddr: string,
113
+ indexPrices?: [number, number]
114
+ ): Promise<boolean> {
110
115
  if (this.proxyContract == null) {
111
116
  throw Error("no proxy contract initialized. Use createProxyInstance().");
112
117
  }
@@ -117,12 +122,27 @@ export default class LiquidatorTool extends WriteAccessHandler {
117
122
  let obj = await this.priceFeedGetter.fetchPricesForPerpetual(symbol);
118
123
  indexPrices = [obj.idxPrices[0], obj.idxPrices[1]];
119
124
  }
120
- let traderState = await this.proxyContract.getTraderState(perpID, traderAddr, indexPrices);
125
+ let traderState = await this.proxyContract.getTraderState(
126
+ perpID,
127
+ traderAddr,
128
+ indexPrices.map((x) => floatToABK64x64(x))
129
+ );
121
130
  if (traderState[idx_notional] == 0) {
122
131
  // trader does not have open position
123
- return false;
132
+ return true;
124
133
  }
125
- return await this.proxyContract.isTraderMaintenanceMarginSafe(perpID, traderAddr);
134
+ // calculate margin from traderstate
135
+ const idx_maintenanceMgnRate = 10;
136
+ const idx_marginAccountPositionBC = 4;
137
+ const idx_collateralToQuoteConversion = 9;
138
+ const idx_marginBalance = 0;
139
+ const maintMgnRate = ABK64x64ToFloat(traderState[idx_maintenanceMgnRate]);
140
+ const pos = ABK64x64ToFloat(traderState[idx_marginAccountPositionBC]);
141
+ const marginbalance = ABK64x64ToFloat(traderState[idx_marginBalance]);
142
+ const coll2quote = ABK64x64ToFloat(traderState[idx_collateralToQuoteConversion]);
143
+ const base2collateral = indexPrices[0] / coll2quote;
144
+ const threshold = Math.abs(pos * base2collateral * maintMgnRate);
145
+ return marginbalance >= threshold;
126
146
  }
127
147
 
128
148
  /**
package/src/marketData.ts CHANGED
@@ -290,10 +290,10 @@ export default class MarketData extends PerpetualDataHandler {
290
290
  );
291
291
  let exchangeFeeCC = (Math.abs(tradeAmountBC) * exchangeFeeTbps * 1e-5 * S2) / S3;
292
292
  let brokerFeeCC = (Math.abs(tradeAmountBC) * (order.brokerFeeTbps ?? 0) * 1e-5 * S2) / S3;
293
-
293
+ let referralFeeCC = this.symbolToPerpStaticInfo.get(account.symbol)!.referralRebate;
294
294
  // Trade type:
295
295
  let isClose = newPositionBC == 0 || newPositionBC * tradeAmountBC < 0;
296
- let isOpen = newPositionBC != 0 && (currentPositionBC == 0 || newPositionBC * currentPositionBC > 0); // regular open, no flip
296
+ let isOpen = newPositionBC != 0 && (currentPositionBC == 0 || tradeAmountBC * currentPositionBC > 0); // regular open, no flip
297
297
  let isFlip = Math.abs(newPositionBC) > Math.abs(currentPositionBC) && !isOpen; // flip position sign, not fully closed
298
298
  let keepPositionLvgOnClose = (order.keepPositionLvg ?? false) && !isOpen;
299
299
 
@@ -314,10 +314,10 @@ export default class MarketData extends PerpetualDataHandler {
314
314
  targetLvg = isFlip || isOpen ? order.leverage ?? 1 / initialMarginRate : 0;
315
315
  let [b0, pos0] = isOpen ? [0, 0] : [account.collateralCC, currentPositionBC];
316
316
  traderDepositCC = getDepositAmountForLvgTrade(b0, pos0, tradeAmountBC, targetLvg, tradePrice, S3, Sm);
317
- console.log("deposit for trget lvg:", traderDepositCC);
318
317
  // fees are paid from wallet in this case
319
318
  // referral rebate??
320
- traderDepositCC += exchangeFeeCC + brokerFeeCC;
319
+ console.log("deposit for trget lvg:", traderDepositCC);
320
+ traderDepositCC += exchangeFeeCC + brokerFeeCC + referralFeeCC;
321
321
  }
322
322
 
323
323
  // Contract: _executeTrade
@@ -329,7 +329,7 @@ export default class MarketData extends PerpetualDataHandler {
329
329
  deltaCashCC += pnl / S3;
330
330
  }
331
331
  // funding and fees
332
- deltaCashCC = deltaCashCC + account.unrealizedFundingCollateralCCY - exchangeFeeCC - brokerFeeCC;
332
+ deltaCashCC = deltaCashCC + account.unrealizedFundingCollateralCCY - exchangeFeeCC - brokerFeeCC - referralFeeCC;
333
333
 
334
334
  // New cash, locked-in, entry price & leverage after trade
335
335
  let newLockedInValueQC = currentLockedInQC + deltaLockedQC;
@@ -495,6 +495,9 @@ export default class MarketData extends PerpetualDataHandler {
495
495
  } else {
496
496
  S2Liq = calculateLiquidationPriceCollateralQuote(lockedInQC, signedPositionBC, marginCashCC, tau);
497
497
  }
498
+ // floor at 0
499
+ S2Liq = S2Liq < 0 ? 0 : S2Liq;
500
+ S3Liq = S3Liq && S3Liq < 0 ? 0 : S3Liq;
498
501
  return [S2Liq, S3Liq, tau];
499
502
  }
500
503
 
@@ -712,6 +715,7 @@ export default class MarketData extends PerpetualDataHandler {
712
715
  S2Symbol: perpInfo.S2Symbol,
713
716
  S3Symbol: perpInfo.S3Symbol,
714
717
  lotSizeBC: perpInfo.lotSizeBC,
718
+ referralRebate: perpInfo.referralRebate,
715
719
  priceIds: perpInfo.priceIds,
716
720
  };
717
721
  return res;
@@ -780,33 +784,33 @@ export default class MarketData extends PerpetualDataHandler {
780
784
  return digests;
781
785
  }
782
786
 
787
+ /**
788
+ * Query the available margin conditional on the given (or current) index prices
789
+ * Result is in collateral currency
790
+ * @param traderAddr address of the trader
791
+ * @param symbol perpetual symbol of the form BTC-USD-MATIC
792
+ * @param indexPrices optional index prices, will otherwise fetch from REST API
793
+ * @returns available margin in collateral currency
794
+ */
783
795
  public async getAvailableMargin(traderAddr: string, symbol: string, indexPrices?: [number, number]): Promise<number> {
784
796
  if (this.proxyContract == null) {
785
797
  throw Error("no proxy contract initialized. Use createProxyInstance().");
786
798
  }
787
-
799
+
788
800
  if (indexPrices == undefined) {
789
801
  // fetch from API
790
802
  let obj = await this.priceFeedGetter.fetchPricesForPerpetual(symbol);
791
803
  indexPrices = [obj.idxPrices[0], obj.idxPrices[1]];
792
804
  }
793
- let mgnAcct = await PerpetualDataHandler.getMarginAccount(
805
+ let perpID = PerpetualDataHandler.symbolToPerpetualId(symbol, this.symbolToPerpStaticInfo);
806
+ let traderState = await this.proxyContract.getTraderState(
807
+ perpID,
794
808
  traderAddr,
795
- symbol,
796
- this.symbolToPerpStaticInfo,
797
- this.proxyContract,
798
- [indexPrices[0], indexPrices[1]]
799
- );
800
- let S2 = indexPrices[0];
801
- let ccyType = this.getPerpetualStaticInfo(symbol).collateralCurrencyType;
802
- let S3 = ccyType == COLLATERAL_CURRENCY_QUANTO ? indexPrices[1] : ccyType == COLLATERAL_CURRENCY_QUOTE ? 1 : S2;
803
- let perpInfo = this.symbolToPerpStaticInfo.get(symbol);
804
- let balanceCC = mgnAcct.collateralCC + mgnAcct.unrealizedPnlQuoteCCY / S3;
805
- let initalMarginCC = Math.abs(
806
- (perpInfo!.initialMarginRate * mgnAcct.positionNotionalBaseCCY * mgnAcct.markPrice) /
807
- mgnAcct.collToQuoteConversion
809
+ indexPrices.map((x) => floatToABK64x64(x))
808
810
  );
809
- return balanceCC - initalMarginCC;
811
+ const idx_availableMargin = 1;
812
+ let mgn = ABK64x64ToFloat(traderState[idx_availableMargin]);
813
+ return mgn;
810
814
  }
811
815
 
812
816
  /**
@@ -815,15 +819,15 @@ export default class MarketData extends PerpetualDataHandler {
815
819
  * @param brokerAddr address of the trader's broker or undefined
816
820
  * @returns a loyality score (4 worst, 1 best)
817
821
  */
818
- public async getTraderLoyalityScore(traderAddr: string, brokerAddr?: string) : Promise<number> {
822
+ public async getTraderLoyalityScore(traderAddr: string, brokerAddr?: string): Promise<number> {
819
823
  if (this.proxyContract == null) {
820
824
  throw Error("no proxy contract or wallet initialized. Use createProxyInstance().");
821
825
  }
822
826
  // loop over all pools and query volumes
823
- let brokerProm : Array<Promise<BigNumber>>= [];
824
- let traderProm : Array<Promise<BigNumber>>= [];
825
- for(let k=0; k<this.poolStaticInfos.length; k++) {
826
- if (brokerAddr!="" && brokerAddr!=undefined) {
827
+ let brokerProm: Array<Promise<BigNumber>> = [];
828
+ let traderProm: Array<Promise<BigNumber>> = [];
829
+ for (let k = 0; k < this.poolStaticInfos.length; k++) {
830
+ if (brokerAddr != "" && brokerAddr != undefined) {
827
831
  let brkrVol = this.proxyContract.getCurrentBrokerVolume(this.poolStaticInfos[k].poolId, brokerAddr);
828
832
  brokerProm.push(brkrVol);
829
833
  }
@@ -835,18 +839,18 @@ export default class MarketData extends PerpetualDataHandler {
835
839
  let totalTraderVolume = 0;
836
840
  let brkrVol = await Promise.all(brokerProm);
837
841
  let trdrVol = await Promise.all(traderProm);
838
- for(let k=0; k<this.poolStaticInfos.length; k++) {
839
- if (brokerAddr!="" && brokerAddr!=undefined) {
842
+ for (let k = 0; k < this.poolStaticInfos.length; k++) {
843
+ if (brokerAddr != "" && brokerAddr != undefined) {
840
844
  totalBrokerVolume += ABK64x64ToFloat(brkrVol[k]);
841
845
  }
842
846
  totalTraderVolume += ABK64x64ToFloat(trdrVol[k]);
843
847
  }
844
848
  const volumeCap = 500_000;
845
- let score = totalBrokerVolume==0 ? totalTraderVolume/volumeCap : totalBrokerVolume;
849
+ let score = totalBrokerVolume == 0 ? totalTraderVolume / volumeCap : totalBrokerVolume;
846
850
  // 5 different equally spaced categories: (4 is best, 1 worst)
847
- let rank4 = 1+Math.floor(Math.min(score,1-1e-15)*4);
851
+ let rank4 = 1 + Math.floor(Math.min(score, 1 - 1e-15) * 4);
848
852
  // desired ranking starts at 4 (worst) and ends at 1 (best)
849
- return 5-rank4;
853
+ return 5 - rank4;
850
854
  }
851
855
 
852
856
  public static async _exchangeInfo(
@@ -85,6 +85,7 @@ export interface PerpetualStaticInfo {
85
85
  S2Symbol: string;
86
86
  S3Symbol: string;
87
87
  lotSizeBC: number;
88
+ referralRebate: number;
88
89
  priceIds: string[];
89
90
  }
90
91
 
@@ -154,7 +155,7 @@ export interface OrderStruct {
154
155
  }
155
156
 
156
157
  export interface Order {
157
- symbol: string;//symbol of the form ETH-USD-MATIC
158
+ symbol: string; //symbol of the form ETH-USD-MATIC
158
159
  side: string;
159
160
  type: string;
160
161
  quantity: number;
@@ -219,7 +220,7 @@ export interface PriceFeedConfig {
219
220
 
220
221
  export interface PriceFeedSubmission {
221
222
  symbols: string[];
222
- priceFeedVaas: string[];
223
+ priceFeedVaas: string[];
223
224
  prices: number[];
224
225
  isMarketClosed: boolean[];
225
226
  timestamps: number[];
@@ -143,6 +143,7 @@ export default class PerpetualDataHandler {
143
143
  let initRate: number[] = [];
144
144
  let mgnRate: number[] = [];
145
145
  let lotSizes: number[] = [];
146
+ let refRebates: number[] = [];
146
147
 
147
148
  for (let k = 0; k < perpetualIDs.length; k++) {
148
149
  let perp = await proxyContract.getPerpetual(perpetualIDs[k]);
@@ -163,6 +164,7 @@ export default class PerpetualDataHandler {
163
164
  initRate.push(ABK64x64ToFloat(perp.fInitialMarginRate));
164
165
  mgnRate.push(ABK64x64ToFloat(perp.fMaintenanceMarginRate));
165
166
  lotSizes.push(ABK64x64ToFloat(perp.fLotSizeBC));
167
+ refRebates.push(ABK64x64ToFloat(perp.fReferralRebateCC));
166
168
  // try to find a limit order book
167
169
  let lobAddr = await this.lobFactoryContract.getOrderBookAddress(perpetualIDs[k]);
168
170
  currentLimitOrderBookAddr.push(lobAddr);
@@ -200,7 +202,7 @@ export default class PerpetualDataHandler {
200
202
  // add price IDs
201
203
  let idsB32, isPyth;
202
204
  [idsB32, isPyth] = await proxyContract.getPriceInfo(perpetualIDs[k]);
203
-
205
+ console.log("ref reb", refRebates[k]);
204
206
  this.symbolToPerpStaticInfo.set(currentSymbols3[k], {
205
207
  id: perpetualIDs[k],
206
208
  limitOrderBookAddr: currentLimitOrderBookAddr[k],
@@ -210,6 +212,7 @@ export default class PerpetualDataHandler {
210
212
  S2Symbol: currentSymbols[k],
211
213
  S3Symbol: currentSymbolsS3[k],
212
214
  lotSizeBC: lotSizes[k],
215
+ referralRebate: refRebates[k],
213
216
  priceIds: idsB32,
214
217
  });
215
218
  }
@@ -364,7 +367,11 @@ export default class PerpetualDataHandler {
364
367
  const idx_mark_price = 8;
365
368
  const idx_lvg = 7;
366
369
  const idx_s3 = 9;
367
- let traderState = await _proxyContract.getTraderState(perpId, traderAddr, _pxS2S3.map(x=>floatToABK64x64(x)));
370
+ let traderState = await _proxyContract.getTraderState(
371
+ perpId,
372
+ traderAddr,
373
+ _pxS2S3.map((x) => floatToABK64x64(x))
374
+ );
368
375
  let isEmpty = traderState[idx_notional] == 0;
369
376
  let cash = ABK64x64ToFloat(traderState[idx_cash]);
370
377
  let S2Liq = 0,
@@ -509,6 +516,9 @@ export default class PerpetualDataHandler {
509
516
  } else {
510
517
  S2Liq = calculateLiquidationPriceCollateralQuote(lockedInValueQC, position, cashCC, tau);
511
518
  }
519
+ // floor at 0
520
+ S2Liq = S2Liq < 0 ? 0 : S2Liq;
521
+ S3Liq = S3Liq && S3Liq < 0 ? 0 : S3Liq;
512
522
  // account cash + pnl = avail cash + pos Sm - L = margin balance
513
523
  let pnl = position * Sm - lockedInValueQC + unpaidFunding;
514
524
  return [S2Liq, S3Liq, tau, pnl, unpaidFundingCC];
package/src/priceFeeds.ts CHANGED
@@ -1,8 +1,8 @@
1
- import {BigNumber} from "ethers";
1
+ import { BigNumber } from "ethers";
2
2
  import PerpetualDataHandler from "./perpetualDataHandler";
3
3
  import Triangulator from "./triangulator";
4
- import {PriceFeedConfig, PriceFeedSubmission, PriceFeedFormat} from "./nodeSDKTypes"
5
- import {decNToFloat} from "./d8XMath";
4
+ import { PriceFeedConfig, PriceFeedSubmission, PriceFeedFormat } from "./nodeSDKTypes";
5
+ import { decNToFloat } from "./d8XMath";
6
6
 
7
7
  /**
8
8
  * This class communicates with the REST API that provides price-data that is
@@ -12,19 +12,18 @@ import {decNToFloat} from "./d8XMath";
12
12
  export default class PriceFeeds {
13
13
  private config: PriceFeedConfig;
14
14
  private feedEndpoints: Array<string>; //feedEndpoints[endpointId] = endpointstring
15
- private feedInfo: Map<string, {symbol:string, endpointId: number}>; // priceFeedId -> symbol, endpointId
16
- private dataHandler : PerpetualDataHandler;
15
+ private feedInfo: Map<string, { symbol: string; endpointId: number }>; // priceFeedId -> symbol, endpointId
16
+ private dataHandler: PerpetualDataHandler;
17
17
  // store triangulation paths given the price feeds
18
- private triangulations : Map<string, [string[], boolean[]]>;
18
+ private triangulations: Map<string, [string[], boolean[]]>;
19
19
  private THRESHOLD_MARKET_CLOSED_SEC = 15; // smallest lag for which we consider the market as being closed
20
20
 
21
21
  constructor(dataHandler: PerpetualDataHandler, priceFeedConfigNetwork: string) {
22
-
23
22
  let configs = <PriceFeedConfig[]>require("../config/priceFeedConfig.json");
24
23
  this.config = PriceFeeds._selectConfig(configs, priceFeedConfigNetwork);
25
24
  [this.feedInfo, this.feedEndpoints] = PriceFeeds._constructFeedInfo(this.config);
26
25
  this.dataHandler = dataHandler;
27
- this.triangulations = new Map<string, [string[], boolean[]]>();
26
+ this.triangulations = new Map<string, [string[], boolean[]]>();
28
27
  }
29
28
 
30
29
  /**
@@ -33,10 +32,10 @@ export default class PriceFeeds {
33
32
  */
34
33
  public initializeTriangulations(symbols: Set<string>) {
35
34
  let feedSymbols = new Array<string>();
36
- for(let [key, value] of this.feedInfo) {
35
+ for (let [key, value] of this.feedInfo) {
37
36
  feedSymbols.push(value.symbol);
38
37
  }
39
- for(let symbol of symbols.values()) {
38
+ for (let symbol of symbols.values()) {
40
39
  let triangulation = Triangulator.triangulate(feedSymbols, symbol);
41
40
  this.triangulations.set(symbol, triangulation);
42
41
  }
@@ -48,35 +47,38 @@ export default class PriceFeeds {
48
47
  * @param symbol symbol of perpetual, e.g., BTC-USD-MATIC
49
48
  * @returns PriceFeedSubmission, index prices, market closed information
50
49
  */
51
- public async fetchFeedPriceInfoAndIndicesForPerpetual(symbol: string) :
52
- Promise<{submission: PriceFeedSubmission, pxS2S3: [number,number], mktClosed: [boolean, boolean]}>
53
- {
50
+ public async fetchFeedPriceInfoAndIndicesForPerpetual(
51
+ symbol: string
52
+ ): Promise<{ submission: PriceFeedSubmission; pxS2S3: [number, number]; mktClosed: [boolean, boolean] }> {
54
53
  let indexSymbols = this.dataHandler.getIndexSymbols(symbol);
55
54
  // fetch prices from required price-feeds (REST)
56
- let submission : PriceFeedSubmission = await this.fetchLatestFeedPriceInfoForPerpetual(symbol);
55
+ let submission: PriceFeedSubmission = await this.fetchLatestFeedPriceInfoForPerpetual(symbol);
57
56
  // calculate index-prices from price-feeds
58
- let [_idxPrices, _mktClosed] = this.calculateTriangulatedPricesFromFeedInfo(indexSymbols.filter((x)=>x!=''), submission);
59
- let idxPrices : [number, number]= [_idxPrices[0], 0];
60
- let mktClosed: [boolean, boolean]=[_mktClosed[0], false];
61
- if (idxPrices.length>1) {
57
+ let [_idxPrices, _mktClosed] = this.calculateTriangulatedPricesFromFeedInfo(
58
+ indexSymbols.filter((x) => x != ""),
59
+ submission
60
+ );
61
+ let idxPrices: [number, number] = [_idxPrices[0], 0];
62
+ let mktClosed: [boolean, boolean] = [_mktClosed[0], false];
63
+ if (idxPrices.length > 1) {
62
64
  idxPrices[1] = _idxPrices[1];
63
65
  mktClosed[1] = _mktClosed[1];
64
66
  }
65
- return {submission : submission, pxS2S3: idxPrices, mktClosed: mktClosed};
67
+ return { submission: submission, pxS2S3: idxPrices, mktClosed: mktClosed };
66
68
  }
67
69
 
68
- /**
69
- * Get all prices/isMarketClosed for the provided symbols via
70
+ /**
71
+ * Get all prices/isMarketClosed for the provided symbols via
70
72
  * "latest_price_feeds" and triangulation. Triangulation must be defined in config, unless
71
73
  * it is a direct price feed.
72
74
  * @returns map of feed-price symbol to price/isMarketClosed
73
75
  */
74
- public async fetchPrices(symbols: string[]) : Promise<Map<string, [number, boolean]>> {
76
+ public async fetchPrices(symbols: string[]): Promise<Map<string, [number, boolean]>> {
75
77
  let feedPrices = await this.fetchAllFeedPrices();
76
78
  let [prices, mktClosed] = this.triangulatePricesFromFeedPrices(symbols, feedPrices);
77
- let symMap = new Map<string, [number, boolean]>;
78
- for(let k=0; k<symbols.length; k++) {
79
- symMap.set(symbols[k], [prices[k], mktClosed[k]])
79
+ let symMap = new Map<string, [number, boolean]>();
80
+ for (let k = 0; k < symbols.length; k++) {
81
+ symMap.set(symbols[k], [prices[k], mktClosed[k]]);
80
82
  }
81
83
  return symMap;
82
84
  }
@@ -84,34 +86,34 @@ export default class PriceFeeds {
84
86
  /**
85
87
  * Get index prices and market closed information for the given perpetual
86
88
  * @param symbol perpetual symbol such as ETH-USD-MATIC
87
- * @returns
89
+ * @returns Index prices and market closed information
88
90
  */
89
- public async fetchPricesForPerpetual(symbol: string) : Promise<{idxPrices: number[], mktClosed: boolean[]}> {
90
- let indexSymbols = this.dataHandler.getIndexSymbols(symbol).filter(x=>x!="");
91
- // determine relevant price feeds
92
- let feedSymbols = new Array<string>();
93
- for(let sym of indexSymbols) {
94
- if(sym!="") {
95
- let triang : [string[], boolean[]] | undefined = this.triangulations.get(sym);
96
- if (triang==undefined) {
97
- // no triangulation defined, so symbol must be a feed (unless misconfigured)
98
- feedSymbols.push(sym);
99
- } else {
100
- // push all required feeds to array
101
- triang[0].map((feedSym)=> feedSymbols.push(feedSym));
102
- }
91
+ public async fetchPricesForPerpetual(symbol: string): Promise<{ idxPrices: number[]; mktClosed: boolean[] }> {
92
+ let indexSymbols = this.dataHandler.getIndexSymbols(symbol).filter((x) => x != "");
93
+ // determine relevant price feeds
94
+ let feedSymbols = new Array<string>();
95
+ for (let sym of indexSymbols) {
96
+ if (sym != "") {
97
+ let triang: [string[], boolean[]] | undefined = this.triangulations.get(sym);
98
+ if (triang == undefined) {
99
+ // no triangulation defined, so symbol must be a feed (unless misconfigured)
100
+ feedSymbols.push(sym);
101
+ } else {
102
+ // push all required feeds to array
103
+ triang[0].map((feedSym) => feedSymbols.push(feedSym));
103
104
  }
104
105
  }
105
- // get all feed prices
106
- let feedPrices = await this.fetchFeedPrices(feedSymbols);
107
- // triangulate
108
- let [prices, mktClosed] = this.triangulatePricesFromFeedPrices(indexSymbols, feedPrices);
109
- // ensure we return an array of 2 in all cases
110
- if (prices.length==1) {
111
- prices.push(0);
112
- mktClosed.push(false);
113
- }
114
- return {idxPrices: prices, mktClosed: mktClosed};
106
+ }
107
+ // get all feed prices
108
+ let feedPrices = await this.fetchFeedPrices(feedSymbols);
109
+ // triangulate
110
+ let [prices, mktClosed] = this.triangulatePricesFromFeedPrices(indexSymbols, feedPrices);
111
+ // ensure we return an array of 2 in all cases
112
+ if (prices.length == 1) {
113
+ prices.push(0);
114
+ mktClosed.push(false);
115
+ }
116
+ return { idxPrices: prices, mktClosed: mktClosed };
115
117
  }
116
118
 
117
119
  /**
@@ -124,16 +126,16 @@ export default class PriceFeeds {
124
126
  public async fetchFeedPrices(symbols?: string[]): Promise<Map<string, [number, boolean]>> {
125
127
  let queries = new Array<string>(this.feedEndpoints.length);
126
128
  let symbolsOfEndpoint: string[][] = [];
127
- for(let j=0;j<queries.length;j++) {
129
+ for (let j = 0; j < queries.length; j++) {
128
130
  symbolsOfEndpoint.push([]);
129
131
  }
130
- for(let k=0; k<this.config.ids.length; k++) {
132
+ for (let k = 0; k < this.config.ids.length; k++) {
131
133
  let currFeed = this.config.ids[k];
132
- if(symbols!=undefined && !symbols.includes(currFeed.symbol)) {
134
+ if (symbols != undefined && !symbols.includes(currFeed.symbol)) {
133
135
  continue;
134
136
  }
135
137
  // feedInfo: Map<string, {symbol:string, endpointId: number}>; // priceFeedId -> symbol, endpointId
136
- let endpointId = this.feedInfo.get(currFeed.id)!.endpointId;
138
+ let endpointId = this.feedInfo.get(currFeed.id)!.endpointId;
137
139
  symbolsOfEndpoint[endpointId].push(currFeed.symbol);
138
140
  if (queries[endpointId] == undefined) {
139
141
  // each id can have a different endpoint, but we cluster
@@ -143,30 +145,29 @@ export default class PriceFeeds {
143
145
  queries[endpointId] = queries[endpointId] + "ids[]=" + currFeed.id + "&";
144
146
  }
145
147
  let resultPrices = new Map<string, [number, boolean]>();
146
- for(let k=0; k<queries.length; k++) {
147
- if(queries[k]==undefined) {
148
+ for (let k = 0; k < queries.length; k++) {
149
+ if (queries[k] == undefined) {
148
150
  continue;
149
151
  }
150
- let [id, pxInfo] : [string[], PriceFeedFormat[]] = await this.fetchPriceQuery(queries[k]);
151
- let tsSecNow = Math.round(Date.now()/1000);
152
- for(let j=0; j<pxInfo.length; j++) {
152
+ let [id, pxInfo]: [string[], PriceFeedFormat[]] = await this.fetchPriceQuery(queries[k]);
153
+ let tsSecNow = Math.round(Date.now() / 1000);
154
+ for (let j = 0; j < pxInfo.length; j++) {
153
155
  let price = decNToFloat(BigNumber.from(pxInfo[j].price), -pxInfo[j].expo);
154
156
  let isMarketClosed = tsSecNow - pxInfo[j].publish_time > this.THRESHOLD_MARKET_CLOSED_SEC;
155
157
  resultPrices.set(symbolsOfEndpoint[k][j], [price, isMarketClosed]);
156
158
  }
157
159
  }
158
160
  return resultPrices;
159
-
160
161
  }
161
162
 
162
163
  /**
163
164
  * Get all configured feed prices via "latest_price_feeds"
164
165
  * @returns map of feed-price symbol to price/isMarketClosed
165
166
  */
166
- public async fetchAllFeedPrices() : Promise<Map<string, [number, boolean]>> {
167
+ public async fetchAllFeedPrices(): Promise<Map<string, [number, boolean]>> {
167
168
  return this.fetchFeedPrices();
168
169
  }
169
-
170
+
170
171
  /**
171
172
  * Get the latest prices for a given perpetual from the offchain oracle
172
173
  * networks
@@ -174,7 +175,7 @@ export default class PriceFeeds {
174
175
  * @returns array of price feed updates that can be submitted to the smart contract
175
176
  * and corresponding price information
176
177
  */
177
- public async fetchLatestFeedPriceInfoForPerpetual(symbol: string) : Promise<PriceFeedSubmission> {
178
+ public async fetchLatestFeedPriceInfoForPerpetual(symbol: string): Promise<PriceFeedSubmission> {
178
179
  let feedIds = this.dataHandler.getPriceIds(symbol);
179
180
  let queries = new Array<string>(this.feedEndpoints.length);
180
181
  // we need to preserve the order of the price feeds
@@ -215,10 +216,10 @@ export default class PriceFeeds {
215
216
  const prices = new Array<number>();
216
217
  const mktClosed = new Array<boolean>();
217
218
  const timestamps = new Array<number>();
218
- const tsSecNow = Math.round(Date.now()/1000);
219
- for(let k=0; k<orderEndpointNumber.length; k++) {
220
- let endpointId = Math.floor(orderEndpointNumber[k]/100);
221
- let idWithinEndpoint = orderEndpointNumber[k]-100*endpointId;
219
+ const tsSecNow = Math.round(Date.now() / 1000);
220
+ for (let k = 0; k < orderEndpointNumber.length; k++) {
221
+ let endpointId = Math.floor(orderEndpointNumber[k] / 100);
222
+ let idWithinEndpoint = orderEndpointNumber[k] - 100 * endpointId;
222
223
  priceFeedUpdates.push(data[endpointId][0][idWithinEndpoint]);
223
224
  let pxInfo: PriceFeedFormat = data[endpointId][1][idWithinEndpoint];
224
225
  let price = decNToFloat(BigNumber.from(pxInfo.price), -pxInfo.expo);
@@ -227,8 +228,14 @@ export default class PriceFeeds {
227
228
  prices.push(price);
228
229
  timestamps.push(pxInfo.publish_time);
229
230
  }
230
-
231
- return {"symbols": symbols, priceFeedVaas: priceFeedUpdates, prices: prices, isMarketClosed: mktClosed, timestamps: timestamps};
231
+
232
+ return {
233
+ symbols: symbols,
234
+ priceFeedVaas: priceFeedUpdates,
235
+ prices: prices,
236
+ isMarketClosed: mktClosed,
237
+ timestamps: timestamps,
238
+ };
232
239
  }
233
240
 
234
241
  /**
@@ -239,14 +246,14 @@ export default class PriceFeeds {
239
246
  * @param feeds data obtained via fetchLatestFeedPriceInfo or fetchLatestFeedPrices
240
247
  * @returns array of prices with same order as symbols
241
248
  */
242
- public calculateTriangulatedPricesFromFeedInfo(symbols: string[], feeds: PriceFeedSubmission) : [number[], boolean[]] {
243
- let priceMap = new Map<string, [number,boolean]>();
244
- for(let j=0; j<feeds.prices.length; j++) {
249
+ public calculateTriangulatedPricesFromFeedInfo(symbols: string[], feeds: PriceFeedSubmission): [number[], boolean[]] {
250
+ let priceMap = new Map<string, [number, boolean]>();
251
+ for (let j = 0; j < feeds.prices.length; j++) {
245
252
  priceMap.set(feeds.symbols[j], [feeds.prices[j], feeds.isMarketClosed[j]]);
246
253
  }
247
254
  return this.triangulatePricesFromFeedPrices(symbols, priceMap);
248
255
  }
249
- /**
256
+ /**
250
257
  * Extract pair-prices from underlying price feeds via triangulation
251
258
  * The function either needs a direct price feed or a defined triangulation to succesfully
252
259
  * return a triangulated price
@@ -254,22 +261,25 @@ export default class PriceFeeds {
254
261
  * @param feeds data obtained via fetchLatestFeedPriceInfo or fetchLatestFeedPrices
255
262
  * @returns array of prices with same order as symbols
256
263
  */
257
- public triangulatePricesFromFeedPrices(symbols: string[], feedPriceMap: Map<string, [number, boolean]>) : [number[], boolean[]] {
264
+ public triangulatePricesFromFeedPrices(
265
+ symbols: string[],
266
+ feedPriceMap: Map<string, [number, boolean]>
267
+ ): [number[], boolean[]] {
258
268
  let prices = new Array<number>();
259
269
  let mktClosed = new Array<boolean>();
260
- for(let k=0; k<symbols.length; k++) {
261
- let triangulation : [string[], boolean[]] | undefined = this.triangulations.get(symbols[k]);
262
- if(triangulation==undefined) {
270
+ for (let k = 0; k < symbols.length; k++) {
271
+ let triangulation: [string[], boolean[]] | undefined = this.triangulations.get(symbols[k]);
272
+ if (triangulation == undefined) {
263
273
  let feedPrice = feedPriceMap.get(symbols[k]);
264
- if (feedPrice==undefined) {
274
+ if (feedPrice == undefined) {
265
275
  throw new Error(`PriceFeeds: no triangulation defined for ${symbols[k]}`);
266
276
  } else {
267
- prices.push(feedPrice[0]);//price
268
- mktClosed.push(feedPrice[1]);//market closed?
277
+ prices.push(feedPrice[0]); //price
278
+ mktClosed.push(feedPrice[1]); //market closed?
269
279
  continue;
270
280
  }
271
281
  }
272
- let [px, isMktClosed] : [number, boolean]= Triangulator.calculateTriangulatedPrice(triangulation, feedPriceMap);
282
+ let [px, isMktClosed]: [number, boolean] = Triangulator.calculateTriangulatedPrice(triangulation, feedPriceMap);
273
283
  prices.push(px);
274
284
  mktClosed.push(isMktClosed);
275
285
  }
@@ -281,8 +291,8 @@ export default class PriceFeeds {
281
291
  * @param query query price-info from endpoint
282
292
  * @returns vaa and price info
283
293
  */
284
- private async fetchVAAQuery(query: string) : Promise<[string[], PriceFeedFormat[]]> {
285
- const headers = {headers: {'Content-Type': 'application/json'}};
294
+ private async fetchVAAQuery(query: string): Promise<[string[], PriceFeedFormat[]]> {
295
+ const headers = { headers: { "Content-Type": "application/json" } };
286
296
  let response = await fetch(query, headers);
287
297
  if (!response.ok) {
288
298
  throw new Error(`Failed to fetch posts (${response.status}): ${response.statusText}`);
@@ -302,8 +312,8 @@ export default class PriceFeeds {
302
312
  * @param query query price-info from endpoint
303
313
  * @returns vaa and price info
304
314
  */
305
- public async fetchPriceQuery(query: string) : Promise<[string[], PriceFeedFormat[]]> {
306
- const headers = {headers: {'Content-Type': 'application/json'}};
315
+ public async fetchPriceQuery(query: string): Promise<[string[], PriceFeedFormat[]]> {
316
+ const headers = { headers: { "Content-Type": "application/json" } };
307
317
  let response = await fetch(query, headers);
308
318
  if (!response.ok) {
309
319
  throw new Error(`Failed to fetch posts (${response.status}): ${response.statusText}`);
@@ -320,17 +330,17 @@ export default class PriceFeeds {
320
330
 
321
331
  /**
322
332
  * Searches for configuration for given network
323
- * @param configs pricefeed configuration from json
333
+ * @param configs pricefeed configuration from json
324
334
  * @param network e.g. testnet
325
335
  * @returns selected configuration
326
336
  */
327
- static _selectConfig(configs: PriceFeedConfig[], network: string) : PriceFeedConfig {
328
- let k=0;
329
- while(k<configs.length) {
330
- if (configs[k].network==network) {
337
+ static _selectConfig(configs: PriceFeedConfig[], network: string): PriceFeedConfig {
338
+ let k = 0;
339
+ while (k < configs.length) {
340
+ if (configs[k].network == network) {
331
341
  return configs[k];
332
342
  }
333
- k=k+1;
343
+ k = k + 1;
334
344
  }
335
345
  throw new Error(`PriceFeeds: config not found for network ${network}`);
336
346
  }
@@ -340,32 +350,31 @@ export default class PriceFeeds {
340
350
  * @param config configuration for the selected network
341
351
  * @returns feedInfo-map and endPoints-array
342
352
  */
343
- static _constructFeedInfo(config: PriceFeedConfig) : [Map<string, {symbol:string, endpointId: number}>, string[]]{
344
- let feed = new Map<string, {symbol:string, endpointId: number}>();
353
+ static _constructFeedInfo(config: PriceFeedConfig): [Map<string, { symbol: string; endpointId: number }>, string[]] {
354
+ let feed = new Map<string, { symbol: string; endpointId: number }>();
345
355
  let endpointId = -1;
346
- let type="";
356
+ let type = "";
347
357
  let feedEndpoints = new Array<string>();
348
- for(let k=0; k<config.endpoints.length; k++) {
358
+ for (let k = 0; k < config.endpoints.length; k++) {
349
359
  feedEndpoints.push(config.endpoints[k].endpoint);
350
360
  }
351
- for(let k=0; k<config.ids.length; k++) {
352
- if (type!=config.ids[k].type) {
361
+ for (let k = 0; k < config.ids.length; k++) {
362
+ if (type != config.ids[k].type) {
353
363
  type = config.ids[k].type;
354
- let j=0;
355
- while(j<config.endpoints.length) {
356
- if(config.endpoints[j].type == type) {
364
+ let j = 0;
365
+ while (j < config.endpoints.length) {
366
+ if (config.endpoints[j].type == type) {
357
367
  endpointId = j;
358
- j=config.endpoints.length;
368
+ j = config.endpoints.length;
359
369
  }
360
370
  j++;
361
371
  }
362
- if(config.endpoints[endpointId].type!=type) {
372
+ if (config.endpoints[endpointId].type != type) {
363
373
  throw new Error(`priceFeeds: no enpoint found for ${type} check priceFeedConfig`);
364
374
  }
365
375
  }
366
- feed.set(config.ids[k].id, {symbol: config.ids[k].symbol.toUpperCase(), endpointId: endpointId });
376
+ feed.set(config.ids[k].id, { symbol: config.ids[k].symbol.toUpperCase(), endpointId: endpointId });
367
377
  }
368
378
  return [feed, feedEndpoints];
369
379
  }
370
-
371
380
  }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const D8X_SDK_VERSION = "0.1.4";
1
+ export const D8X_SDK_VERSION = "0.1.5";
@@ -99,9 +99,9 @@ export default class WriteAccessHandler extends PerpetualDataHandler {
99
99
  * into a mock token used for trading on testnet, with a rate of 1:100_000
100
100
  * @param symbol Pool margin token e.g. MATIC
101
101
  * @param amountToPay Amount in chain currency, e.g. "0.1" for 0.1 MATIC
102
- * @returns
102
+ * @returns Transaction object
103
103
  */
104
- public async swapForMockToken(symbol: string, amountToPay: string) {
104
+ public async swapForMockToken(symbol: string, amountToPay: string): Promise<ethers.ContractTransaction> {
105
105
  if (this.signer == null) {
106
106
  throw Error("no wallet initialized. Use createProxyInstance().");
107
107
  }