@d8x/perpetuals-sdk 0.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/LICENSE +21 -0
- package/README.md +17 -0
- package/abi/ERC20.json +288 -0
- package/abi/IPerpetualManager.json +4674 -0
- package/abi/LimitOrderBook.json +865 -0
- package/abi/LimitOrderBookFactory.json +166 -0
- package/config/defaultConfig.json +9 -0
- package/config/oldConfig.json +9 -0
- package/dist/accountTrade.d.ts +54 -0
- package/dist/accountTrade.js +164 -0
- package/dist/brokerTool.d.ts +41 -0
- package/dist/brokerTool.js +129 -0
- package/dist/d8XMath.d.ts +71 -0
- package/dist/d8XMath.js +162 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +49 -0
- package/dist/liquiditatorTool.d.ts +14 -0
- package/dist/liquiditatorTool.js +21 -0
- package/dist/liquidityProviderTool.d.ts +39 -0
- package/dist/liquidityProviderTool.js +100 -0
- package/dist/marketData.d.ts +39 -0
- package/dist/marketData.js +160 -0
- package/dist/nodeSDKTypes.d.ts +130 -0
- package/dist/nodeSDKTypes.js +52 -0
- package/dist/orderReferrerTool.d.ts +14 -0
- package/dist/orderReferrerTool.js +21 -0
- package/dist/perpetualDataHandler.d.ts +85 -0
- package/dist/perpetualDataHandler.js +474 -0
- package/dist/utils.d.ts +37 -0
- package/dist/utils.js +84 -0
- package/dist/writeAccessHandler.d.ts +36 -0
- package/dist/writeAccessHandler.js +95 -0
- package/module.d.ts +1 -0
- package/package.json +63 -0
- package/src/accountTrade.ts +217 -0
- package/src/brokerTool.ts +155 -0
- package/src/d8XMath.ts +176 -0
- package/src/index.ts +32 -0
- package/src/liquiditatorTool.ts +21 -0
- package/src/liquidityProviderTool.ts +100 -0
- package/src/marketData.ts +149 -0
- package/src/nodeSDKTypes.ts +158 -0
- package/src/orderReferrerTool.ts +17 -0
- package/src/perpetualDataHandler.ts +549 -0
- package/src/utils.ts +83 -0
- package/src/writeAccessHandler.ts +83 -0
|
@@ -0,0 +1,474 @@
|
|
|
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 ethers_1 = require("ethers");
|
|
13
|
+
const nodeSDKTypes_1 = require("./nodeSDKTypes");
|
|
14
|
+
const utils_1 = require("./utils");
|
|
15
|
+
const d8XMath_1 = require("./d8XMath");
|
|
16
|
+
/**
|
|
17
|
+
* Parent class for AccountTrade and MarketData that handles
|
|
18
|
+
* common data and chain operations
|
|
19
|
+
*/
|
|
20
|
+
class PerpetualDataHandler {
|
|
21
|
+
constructor(config) {
|
|
22
|
+
this.proxyContract = null;
|
|
23
|
+
// limit order book
|
|
24
|
+
this.lobFactoryContract = null;
|
|
25
|
+
this.provider = null;
|
|
26
|
+
this.signerOrProvider = null;
|
|
27
|
+
this.symbolToPerpStaticInfo = new Map();
|
|
28
|
+
this.poolStaticInfos = new Array();
|
|
29
|
+
this.symbolToTokenAddrMap = new Map();
|
|
30
|
+
this.nestedPerpetualIDs = new Array();
|
|
31
|
+
this.proxyAddr = config.proxyAddr;
|
|
32
|
+
this.lobFactoryAddr = config.limitOrderBookFactoryAddr;
|
|
33
|
+
this.nodeURL = config.nodeURL;
|
|
34
|
+
this.proxyABI = require(config.proxyABILocation);
|
|
35
|
+
this.lobFactoryABI = require(config.limitOrderBookFactoryABILocation);
|
|
36
|
+
this.lobABI = require(config.limitOrderBookABILocation);
|
|
37
|
+
}
|
|
38
|
+
initContractsAndData(signerOrProvider) {
|
|
39
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
40
|
+
this.signerOrProvider = signerOrProvider;
|
|
41
|
+
this.proxyContract = new ethers_1.ethers.Contract(this.proxyAddr, this.proxyABI, signerOrProvider);
|
|
42
|
+
this.lobFactoryContract = new ethers_1.ethers.Contract(this.lobFactoryAddr, this.lobFactoryABI, signerOrProvider);
|
|
43
|
+
yield this._fillSymbolMaps(this.proxyContract);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Returns the order-book contract for the symbol if found or fails
|
|
48
|
+
* @param symbol symbol of the form ETH-USD-MATIC
|
|
49
|
+
* @returns order book contract for the perpetual
|
|
50
|
+
*/
|
|
51
|
+
getOrderBookContract(symbol) {
|
|
52
|
+
var _a;
|
|
53
|
+
let cleanSymbol = PerpetualDataHandler.symbolToBytes4Symbol(symbol);
|
|
54
|
+
let orderBookAddr = (_a = this.symbolToPerpStaticInfo.get(cleanSymbol)) === null || _a === void 0 ? void 0 : _a.limitOrderBookAddr;
|
|
55
|
+
if (orderBookAddr == "" || orderBookAddr == undefined || this.signerOrProvider == null) {
|
|
56
|
+
throw Error(`no limit order book found for ${symbol} or no signer`);
|
|
57
|
+
}
|
|
58
|
+
let lobContract = new ethers_1.ethers.Contract(orderBookAddr, this.lobABI, this.signerOrProvider);
|
|
59
|
+
return lobContract;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Called when initializing. This function fills this.symbolToTokenAddrMap,
|
|
63
|
+
* and this.nestedPerpetualIDs and this.symbolToPerpStaticInfo
|
|
64
|
+
*
|
|
65
|
+
*/
|
|
66
|
+
_fillSymbolMaps(proxyContract) {
|
|
67
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
if (proxyContract == null || this.lobFactoryContract == null) {
|
|
69
|
+
throw Error("proxy or limit order book not defined");
|
|
70
|
+
}
|
|
71
|
+
this.nestedPerpetualIDs = yield PerpetualDataHandler.getNestedPerpetualIds(proxyContract);
|
|
72
|
+
for (let j = 0; j < this.nestedPerpetualIDs.length; j++) {
|
|
73
|
+
let pool = yield proxyContract.getLiquidityPool(j + 1);
|
|
74
|
+
let poolMarginTokenAddr = pool.marginTokenAddress;
|
|
75
|
+
let perpetualIDs = this.nestedPerpetualIDs[j];
|
|
76
|
+
let poolCCY = undefined;
|
|
77
|
+
let currentSymbols = [];
|
|
78
|
+
let currentSymbolsS3 = [];
|
|
79
|
+
let currentLimitOrderBookAddr = [];
|
|
80
|
+
let ccy = [];
|
|
81
|
+
let mgnRate = [];
|
|
82
|
+
for (let k = 0; k < perpetualIDs.length; k++) {
|
|
83
|
+
let perp = yield proxyContract.getPerpetual(perpetualIDs[k]);
|
|
84
|
+
let base = (0, utils_1.fromBytes4HexString)(perp.S2BaseCCY);
|
|
85
|
+
let quote = (0, utils_1.fromBytes4HexString)(perp.S2QuoteCCY);
|
|
86
|
+
let base3 = (0, utils_1.fromBytes4HexString)(perp.S3BaseCCY);
|
|
87
|
+
let quote3 = (0, utils_1.fromBytes4HexString)(perp.S3QuoteCCY);
|
|
88
|
+
currentSymbols.push(base + "-" + quote);
|
|
89
|
+
currentSymbolsS3.push(base3 + "-" + quote3);
|
|
90
|
+
mgnRate.push((0, d8XMath_1.ABK64x64ToFloat)(perp.fMaintenanceMarginRate));
|
|
91
|
+
// try to find a limit order book
|
|
92
|
+
let lobAddr = yield this.lobFactoryContract.getOrderBookAddress(perpetualIDs[k]);
|
|
93
|
+
currentLimitOrderBookAddr.push(lobAddr);
|
|
94
|
+
// we find out the pool currency by looking at all perpetuals
|
|
95
|
+
// unless for quanto perpetuals, we know the pool currency
|
|
96
|
+
// from the perpetual. This fails if we have a pool with only
|
|
97
|
+
// quanto perpetuals
|
|
98
|
+
if (perp.eCollateralCurrency == nodeSDKTypes_1.COLLATERAL_CURRENCY_BASE) {
|
|
99
|
+
poolCCY = base;
|
|
100
|
+
ccy.push(nodeSDKTypes_1.CollaterlCCY.BASE);
|
|
101
|
+
}
|
|
102
|
+
else if (perp.eCollateralCurrency == nodeSDKTypes_1.COLLATERAL_CURRENCY_QUOTE) {
|
|
103
|
+
poolCCY = quote;
|
|
104
|
+
ccy.push(nodeSDKTypes_1.CollaterlCCY.QUOTE);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
ccy.push(nodeSDKTypes_1.CollaterlCCY.QUANTO);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (perpetualIDs.length == 0) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (poolCCY == undefined) {
|
|
114
|
+
throw Error("Pool only has quanto perps, unable to determine collateral currency");
|
|
115
|
+
}
|
|
116
|
+
let oracleFactoryAddr = yield proxyContract.getOracleFactory();
|
|
117
|
+
let info = {
|
|
118
|
+
poolId: j + 1,
|
|
119
|
+
poolMarginSymbol: poolCCY,
|
|
120
|
+
poolMarginTokenAddr: poolMarginTokenAddr,
|
|
121
|
+
shareTokenAddr: pool.shareTokenAddress,
|
|
122
|
+
oracleFactoryAddr: oracleFactoryAddr,
|
|
123
|
+
};
|
|
124
|
+
this.poolStaticInfos.push(info);
|
|
125
|
+
let currentSymbols3 = currentSymbols.map((x) => x + "-" + poolCCY);
|
|
126
|
+
// push into map
|
|
127
|
+
for (let k = 0; k < perpetualIDs.length; k++) {
|
|
128
|
+
this.symbolToPerpStaticInfo.set(currentSymbols3[k], {
|
|
129
|
+
id: perpetualIDs[k],
|
|
130
|
+
limitOrderBookAddr: currentLimitOrderBookAddr[k],
|
|
131
|
+
maintenanceMarginRate: mgnRate[k],
|
|
132
|
+
collateralCurrencyType: ccy[k],
|
|
133
|
+
S2Symbol: currentSymbols[k],
|
|
134
|
+
S3Symbol: currentSymbolsS3[k],
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// push margin token address into map
|
|
138
|
+
this.symbolToTokenAddrMap.set(poolCCY, poolMarginTokenAddr);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
getSymbolFromPoolId(poolId) {
|
|
143
|
+
return PerpetualDataHandler._getSymbolFromPoolId(poolId, this.poolStaticInfos);
|
|
144
|
+
}
|
|
145
|
+
getPoolIdFromSymbol(symbol) {
|
|
146
|
+
return PerpetualDataHandler._getPoolIdFromSymbol(symbol, this.poolStaticInfos);
|
|
147
|
+
}
|
|
148
|
+
static _getSymbolFromPoolId(poolId, staticInfos) {
|
|
149
|
+
let idx = poolId - 1;
|
|
150
|
+
return staticInfos[idx].poolMarginSymbol;
|
|
151
|
+
}
|
|
152
|
+
static _getPoolIdFromSymbol(symbol, staticInfos) {
|
|
153
|
+
let symbols = symbol.split("-");
|
|
154
|
+
//in case user provided ETH-USD-MATIC instead of MATIC; or similar
|
|
155
|
+
if (symbols.length == 3) {
|
|
156
|
+
symbol = symbols[2];
|
|
157
|
+
}
|
|
158
|
+
let cleanSymbol = (0, utils_1.to4Chars)(symbol);
|
|
159
|
+
cleanSymbol = cleanSymbol.replace(/\0/g, "");
|
|
160
|
+
let j = 0;
|
|
161
|
+
while (j < staticInfos.length && staticInfos[j].poolMarginSymbol != cleanSymbol) {
|
|
162
|
+
j++;
|
|
163
|
+
}
|
|
164
|
+
if (j == staticInfos.length) {
|
|
165
|
+
throw new Error(`no pool found for symbol ${symbol}`);
|
|
166
|
+
}
|
|
167
|
+
return j + 1;
|
|
168
|
+
}
|
|
169
|
+
static getNestedPerpetualIds(_proxyContract) {
|
|
170
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
171
|
+
let poolCount = yield _proxyContract.getPoolCount();
|
|
172
|
+
let poolIds = new Array(poolCount);
|
|
173
|
+
for (let i = 1; i < poolCount + 1; i++) {
|
|
174
|
+
let perpetualCount = yield _proxyContract.getPerpetualCountInPool(i);
|
|
175
|
+
poolIds[i - 1] = new Array(perpetualCount);
|
|
176
|
+
for (let j = 0; j < perpetualCount; j++) {
|
|
177
|
+
let id = yield _proxyContract.getPerpetualId(i, j);
|
|
178
|
+
poolIds[i - 1][j] = id;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return poolIds;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
static getMarginAccount(traderAddr, symbol, symbolToPerpStaticInfo, _proxyContract) {
|
|
185
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
186
|
+
let cleanSymbol = PerpetualDataHandler.symbolToBytes4Symbol(symbol);
|
|
187
|
+
let perpId = PerpetualDataHandler.symbolToPerpetualId(cleanSymbol, symbolToPerpStaticInfo);
|
|
188
|
+
const idx_cash = 3;
|
|
189
|
+
const idx_notional = 4;
|
|
190
|
+
const idx_locked_in = 5;
|
|
191
|
+
const idx_mark_price = 8;
|
|
192
|
+
const idx_lvg = 7;
|
|
193
|
+
const idx_s3 = 9;
|
|
194
|
+
let traderState = yield _proxyContract.getTraderState(perpId, traderAddr);
|
|
195
|
+
let isEmpty = traderState[idx_notional] == 0;
|
|
196
|
+
let cash = (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_cash]);
|
|
197
|
+
let S2Liq = 0, S3Liq = 0, tau = Infinity, pnl = 0, unpaidFundingCC = 0, fLockedIn = ethers_1.BigNumber.from(0), side = nodeSDKTypes_1.CLOSED_SIDE, entryPrice = 0;
|
|
198
|
+
if (!isEmpty) {
|
|
199
|
+
[S2Liq, S3Liq, tau, pnl, unpaidFundingCC] = PerpetualDataHandler._calculateLiquidationPrice(cleanSymbol, traderState, symbolToPerpStaticInfo);
|
|
200
|
+
fLockedIn = traderState[idx_locked_in];
|
|
201
|
+
side = traderState[idx_locked_in] > 0 ? nodeSDKTypes_1.BUY_SIDE : nodeSDKTypes_1.SELL_SIDE;
|
|
202
|
+
entryPrice = (0, d8XMath_1.ABK64x64ToFloat)((0, d8XMath_1.div64x64)(fLockedIn, traderState[idx_notional]));
|
|
203
|
+
}
|
|
204
|
+
let mgn = {
|
|
205
|
+
symbol: symbol,
|
|
206
|
+
positionNotionalBaseCCY: isEmpty ? 0 : (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_notional]),
|
|
207
|
+
side: isEmpty ? nodeSDKTypes_1.CLOSED_SIDE : side,
|
|
208
|
+
entryPrice: isEmpty ? 0 : entryPrice,
|
|
209
|
+
leverage: isEmpty ? 0 : (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_lvg]),
|
|
210
|
+
markPrice: (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_mark_price].abs()),
|
|
211
|
+
unrealizedPnlQuoteCCY: isEmpty ? 0 : pnl,
|
|
212
|
+
unrealizedFundingCollateralCCY: isEmpty ? 0 : unpaidFundingCC,
|
|
213
|
+
collateralCC: cash,
|
|
214
|
+
liquidationLvg: isEmpty ? 0 : 1 / tau,
|
|
215
|
+
liquidationPrice: isEmpty ? [0, 0] : [S2Liq, S3Liq],
|
|
216
|
+
collToQuoteConversion: (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_s3]),
|
|
217
|
+
};
|
|
218
|
+
return mgn;
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Liquidation price
|
|
223
|
+
* @param cleanSymbol symbol after calling symbolToBytes4Symbol
|
|
224
|
+
* @param traderState BigInt array according to smart contract
|
|
225
|
+
* @param symbolToPerpStaticInfo mapping symbol->PerpStaticInfo
|
|
226
|
+
* @returns liquidation mark-price, corresponding collateral/quote conversion
|
|
227
|
+
*/
|
|
228
|
+
static _calculateLiquidationPrice(cleanSymbol, traderState, symbolToPerpStaticInfo) {
|
|
229
|
+
const idx_availableCashCC = 2;
|
|
230
|
+
const idx_cash = 3;
|
|
231
|
+
const idx_notional = 4;
|
|
232
|
+
const idx_locked_in = 5;
|
|
233
|
+
const idx_mark_price = 8;
|
|
234
|
+
const idx_s3 = 9;
|
|
235
|
+
const idx_s2 = 10;
|
|
236
|
+
let S2Liq;
|
|
237
|
+
let S3Liq = (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_s3]);
|
|
238
|
+
let perpInfo = symbolToPerpStaticInfo.get(cleanSymbol);
|
|
239
|
+
if (perpInfo == undefined) {
|
|
240
|
+
throw new Error(`no info for perpetual ${cleanSymbol}`);
|
|
241
|
+
}
|
|
242
|
+
let tau = perpInfo.maintenanceMarginRate;
|
|
243
|
+
let lockedInValueQC = (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_locked_in]);
|
|
244
|
+
let position = (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_notional]);
|
|
245
|
+
let cashCC = (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_availableCashCC]);
|
|
246
|
+
let Sm = (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_mark_price]);
|
|
247
|
+
let unpaidFundingCC = (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_availableCashCC].sub(traderState[idx_cash]));
|
|
248
|
+
let unpaidFunding = unpaidFundingCC;
|
|
249
|
+
if (perpInfo.collateralCurrencyType == nodeSDKTypes_1.CollaterlCCY.BASE) {
|
|
250
|
+
S2Liq = (0, d8XMath_1.calculateLiquidationPriceCollateralBase)(lockedInValueQC, position, cashCC, tau);
|
|
251
|
+
S3Liq = S2Liq;
|
|
252
|
+
unpaidFunding = unpaidFunding / (0, d8XMath_1.ABK64x64ToFloat)(traderState[idx_s2]);
|
|
253
|
+
}
|
|
254
|
+
else if (perpInfo.collateralCurrencyType == nodeSDKTypes_1.CollaterlCCY.QUANTO) {
|
|
255
|
+
let S3 = S3Liq;
|
|
256
|
+
S3Liq = S3;
|
|
257
|
+
S2Liq = (0, d8XMath_1.calculateLiquidationPriceCollateralQuanto)(lockedInValueQC, position, cashCC, tau, S3, Sm);
|
|
258
|
+
unpaidFunding = unpaidFunding / S3;
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
S2Liq = (0, d8XMath_1.calculateLiquidationPriceCollateralQuote)(lockedInValueQC, position, cashCC, tau);
|
|
262
|
+
}
|
|
263
|
+
let pnl = position * Sm - lockedInValueQC - unpaidFunding;
|
|
264
|
+
return [S2Liq, S3Liq, tau, pnl, unpaidFundingCC];
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Finds the perpetual id for a symbol of the form
|
|
268
|
+
* <base>-<quote>-<collateral>. The function first converts the
|
|
269
|
+
* token names into bytes4 representation
|
|
270
|
+
* @param symbol symbol (e.g., BTC-USD-MATIC)
|
|
271
|
+
* @param symbolToPerpStaticInfo map that contains the bytes4-symbol to PerpetualStaticInfo
|
|
272
|
+
* including id mapping
|
|
273
|
+
* @returns perpetual id or it fails
|
|
274
|
+
*/
|
|
275
|
+
static symbolToPerpetualId(symbol, symbolToPerpStaticInfo) {
|
|
276
|
+
var _a;
|
|
277
|
+
let cleanSymbol = PerpetualDataHandler.symbolToBytes4Symbol(symbol);
|
|
278
|
+
let id = (_a = symbolToPerpStaticInfo.get(cleanSymbol)) === null || _a === void 0 ? void 0 : _a.id;
|
|
279
|
+
if (id == undefined) {
|
|
280
|
+
throw Error(`No perpetual found for symbol ${symbol}`);
|
|
281
|
+
}
|
|
282
|
+
return id;
|
|
283
|
+
}
|
|
284
|
+
static symbolToBytes4Symbol(symbol) {
|
|
285
|
+
//split by dashes BTC-USD-MATIC
|
|
286
|
+
let symbols = symbol.split("-");
|
|
287
|
+
if (symbols.length != 3) {
|
|
288
|
+
throw Error(`Symbol ${symbol} not valid. Expecting CCY-CCY-CCY format`);
|
|
289
|
+
}
|
|
290
|
+
//transform into bytes4 currencies (without the space): "BTC", "USD", "MATC"
|
|
291
|
+
symbols = symbols.map((x) => {
|
|
292
|
+
let v = (0, utils_1.to4Chars)(x);
|
|
293
|
+
v = v.replace(/\0/g, "");
|
|
294
|
+
return v;
|
|
295
|
+
});
|
|
296
|
+
// concatenate and find perpetual Id in map
|
|
297
|
+
return symbols[0] + "-" + symbols[1] + "-" + symbols[2];
|
|
298
|
+
}
|
|
299
|
+
static _getByValue(map, searchValue) {
|
|
300
|
+
for (let [key, value] of map.entries()) {
|
|
301
|
+
if (value === searchValue) {
|
|
302
|
+
return key;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
static fromSmartContractOrder(order, symbolToPerpInfoMap) {
|
|
307
|
+
// find symbol of perpetual id
|
|
308
|
+
let symbol = PerpetualDataHandler._getByValue(symbolToPerpInfoMap, order.iPerpetualId);
|
|
309
|
+
let side = order.fAmount > 0 ? nodeSDKTypes_1.BUY_SIDE : nodeSDKTypes_1.SELL_SIDE;
|
|
310
|
+
let limitPrice, stopPrice;
|
|
311
|
+
let fLimitPrice = ethers_1.BigNumber.from(order.fLimitPrice);
|
|
312
|
+
if (fLimitPrice.eq(0) || fLimitPrice.eq(nodeSDKTypes_1.MAX_64x64)) {
|
|
313
|
+
limitPrice = undefined;
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
limitPrice = (0, d8XMath_1.ABK64x64ToFloat)(fLimitPrice);
|
|
317
|
+
}
|
|
318
|
+
let fStopPrice = ethers_1.BigNumber.from(order.fTriggerPrice);
|
|
319
|
+
if (fStopPrice.eq(0) || fStopPrice.eq(nodeSDKTypes_1.MAX_64x64)) {
|
|
320
|
+
stopPrice = undefined;
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
stopPrice = (0, d8XMath_1.ABK64x64ToFloat)(fStopPrice);
|
|
324
|
+
}
|
|
325
|
+
let userOrder = {
|
|
326
|
+
symbol: symbol,
|
|
327
|
+
side: side,
|
|
328
|
+
type: PerpetualDataHandler._flagToOrderType(order),
|
|
329
|
+
quantity: Math.abs((0, d8XMath_1.ABK64x64ToFloat)(ethers_1.BigNumber.from(order.fAmount))),
|
|
330
|
+
reduceOnly: (0, utils_1.containsFlag)(ethers_1.BigNumber.from(order.flags), nodeSDKTypes_1.MASK_CLOSE_ONLY),
|
|
331
|
+
limitPrice: limitPrice,
|
|
332
|
+
keepPositionLvg: (0, utils_1.containsFlag)(ethers_1.BigNumber.from(order.flags), nodeSDKTypes_1.MASK_KEEP_POS_LEVERAGE),
|
|
333
|
+
brokerFeeTbps: Number(order.brokerFeeTbps),
|
|
334
|
+
brokerAddr: order.brokerAddr,
|
|
335
|
+
brokerSignature: order.brokerSignature,
|
|
336
|
+
stopPrice: stopPrice,
|
|
337
|
+
leverage: (0, d8XMath_1.ABK64x64ToFloat)(ethers_1.BigNumber.from(order.fLeverage)),
|
|
338
|
+
deadline: Number(order.iDeadline),
|
|
339
|
+
timestamp: Number(order.createdTimestamp),
|
|
340
|
+
};
|
|
341
|
+
return userOrder;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Transform the convenient form of the order into a smart-contract accepted type of order
|
|
345
|
+
* @param order order type
|
|
346
|
+
* @param traderAddr address of the trader
|
|
347
|
+
* @param symbolToPerpetualMap mapping of symbol to perpetual Id
|
|
348
|
+
* @returns SmartContractOrder
|
|
349
|
+
*/
|
|
350
|
+
static toSmartContractOrder(order, traderAddr, perpStaticInfo) {
|
|
351
|
+
let flags = PerpetualDataHandler._orderTypeToFlag(order);
|
|
352
|
+
let brokerSig = order.brokerSignature == undefined ? [] : order.brokerSignature;
|
|
353
|
+
let perpetualId = PerpetualDataHandler.symbolToPerpetualId(order.symbol, perpStaticInfo);
|
|
354
|
+
let fAmount;
|
|
355
|
+
if (order.side == nodeSDKTypes_1.BUY_SIDE) {
|
|
356
|
+
fAmount = (0, d8XMath_1.floatToABK64x64)(Math.abs(order.quantity));
|
|
357
|
+
}
|
|
358
|
+
else if (order.side == nodeSDKTypes_1.SELL_SIDE) {
|
|
359
|
+
fAmount = (0, d8XMath_1.floatToABK64x64)(-Math.abs(order.quantity));
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
throw Error(`invalid side in order spec, use ${nodeSDKTypes_1.BUY_SIDE} or ${nodeSDKTypes_1.SELL_SIDE}`);
|
|
363
|
+
}
|
|
364
|
+
let fLimitPrice;
|
|
365
|
+
if (order.limitPrice == undefined) {
|
|
366
|
+
// we need to set the limit price to infinity or zero for
|
|
367
|
+
// the trade to go through
|
|
368
|
+
// Also: stop orders always have limits set, so even for this case
|
|
369
|
+
// we set the limit to 0 or infinity
|
|
370
|
+
fLimitPrice = order.side == nodeSDKTypes_1.BUY_SIDE ? nodeSDKTypes_1.MAX_64x64 : ethers_1.BigNumber.from(0);
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
fLimitPrice = (0, d8XMath_1.floatToABK64x64)(order.limitPrice);
|
|
374
|
+
}
|
|
375
|
+
let iDeadline = order.deadline == undefined ? Date.now() + nodeSDKTypes_1.ORDER_MAX_DURATION_SEC : order.deadline;
|
|
376
|
+
let fTriggerPrice = order.stopPrice == undefined ? ethers_1.BigNumber.from(0) : (0, d8XMath_1.floatToABK64x64)(order.stopPrice);
|
|
377
|
+
if (order.reduceOnly != undefined && order.reduceOnly == true) {
|
|
378
|
+
}
|
|
379
|
+
let smOrder = {
|
|
380
|
+
flags: flags,
|
|
381
|
+
iPerpetualId: ethers_1.BigNumber.from(perpetualId),
|
|
382
|
+
brokerFeeTbps: order.brokerFeeTbps == undefined ? ethers_1.BigNumber.from(0) : ethers_1.BigNumber.from(order.brokerFeeTbps),
|
|
383
|
+
traderAddr: traderAddr,
|
|
384
|
+
brokerAddr: order.brokerAddr == undefined ? nodeSDKTypes_1.ZERO_ADDRESS : order.brokerAddr,
|
|
385
|
+
referrerAddr: nodeSDKTypes_1.ZERO_ADDRESS,
|
|
386
|
+
brokerSignature: brokerSig,
|
|
387
|
+
fAmount: fAmount,
|
|
388
|
+
fLimitPrice: fLimitPrice,
|
|
389
|
+
fTriggerPrice: fTriggerPrice,
|
|
390
|
+
fLeverage: order.leverage == undefined ? ethers_1.BigNumber.from(0) : (0, d8XMath_1.floatToABK64x64)(order.leverage),
|
|
391
|
+
iDeadline: ethers_1.BigNumber.from(iDeadline),
|
|
392
|
+
createdTimestamp: ethers_1.BigNumber.from(order.timestamp),
|
|
393
|
+
};
|
|
394
|
+
return smOrder;
|
|
395
|
+
}
|
|
396
|
+
static _flagToOrderType(order) {
|
|
397
|
+
let hasTrigger = ethers_1.BigNumber.from(order.fTriggerPrice).eq(0);
|
|
398
|
+
let hasLimit = !ethers_1.BigNumber.from(order.fTriggerPrice).eq(0) || !ethers_1.BigNumber.from(order.fTriggerPrice).eq(nodeSDKTypes_1.MAX_64x64);
|
|
399
|
+
if (hasTrigger && hasLimit) {
|
|
400
|
+
return nodeSDKTypes_1.ORDER_TYPE_STOP_LIMIT;
|
|
401
|
+
}
|
|
402
|
+
else if (hasTrigger && !hasLimit) {
|
|
403
|
+
return nodeSDKTypes_1.ORDER_TYPE_STOP_MARKET;
|
|
404
|
+
}
|
|
405
|
+
else if (hasLimit && (0, utils_1.containsFlag)(ethers_1.BigNumber.from(order.flags), nodeSDKTypes_1.MASK_LIMIT_ORDER)) {
|
|
406
|
+
return nodeSDKTypes_1.ORDER_TYPE_LIMIT;
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
return nodeSDKTypes_1.ORDER_TYPE_MARKET;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Determine the correct order flags based on the order-properties.
|
|
414
|
+
* Checks for some misspecifications.
|
|
415
|
+
* @param order order type
|
|
416
|
+
* @returns BigNumber flags
|
|
417
|
+
*/
|
|
418
|
+
static _orderTypeToFlag(order) {
|
|
419
|
+
let flag;
|
|
420
|
+
switch (order.type) {
|
|
421
|
+
case nodeSDKTypes_1.ORDER_TYPE_LIMIT:
|
|
422
|
+
flag = nodeSDKTypes_1.MASK_LIMIT_ORDER;
|
|
423
|
+
break;
|
|
424
|
+
case nodeSDKTypes_1.ORDER_TYPE_MARKET:
|
|
425
|
+
flag = nodeSDKTypes_1.MASK_MARKET_ORDER;
|
|
426
|
+
break;
|
|
427
|
+
case nodeSDKTypes_1.ORDER_TYPE_STOP_MARKET:
|
|
428
|
+
flag = nodeSDKTypes_1.MASK_STOP_ORDER;
|
|
429
|
+
break;
|
|
430
|
+
case nodeSDKTypes_1.ORDER_TYPE_STOP_LIMIT:
|
|
431
|
+
flag = nodeSDKTypes_1.MASK_STOP_ORDER;
|
|
432
|
+
break;
|
|
433
|
+
default: {
|
|
434
|
+
throw Error(`Order type ${order.type} not found.`);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (order.keepPositionLvg != undefined && order.keepPositionLvg) {
|
|
438
|
+
flag = (0, utils_1.combineFlags)(flag, nodeSDKTypes_1.MASK_KEEP_POS_LEVERAGE);
|
|
439
|
+
}
|
|
440
|
+
if (order.reduceOnly != undefined && order.reduceOnly) {
|
|
441
|
+
flag = (0, utils_1.combineFlags)(flag, nodeSDKTypes_1.MASK_CLOSE_ONLY);
|
|
442
|
+
}
|
|
443
|
+
if ((order.type == nodeSDKTypes_1.ORDER_TYPE_LIMIT || order.type == nodeSDKTypes_1.ORDER_TYPE_STOP_LIMIT) && order.limitPrice == undefined) {
|
|
444
|
+
throw Error(`Order type ${order.type} requires limit price.`);
|
|
445
|
+
}
|
|
446
|
+
if ((order.type == nodeSDKTypes_1.ORDER_TYPE_STOP_MARKET || order.type == nodeSDKTypes_1.ORDER_TYPE_STOP_LIMIT) && order.stopPrice == undefined) {
|
|
447
|
+
throw Error(`Order type ${order.type} requires trigger price.`);
|
|
448
|
+
}
|
|
449
|
+
if ((order.type == nodeSDKTypes_1.ORDER_TYPE_MARKET || order.type == nodeSDKTypes_1.ORDER_TYPE_LIMIT) && order.stopPrice != undefined) {
|
|
450
|
+
throw Error(`Order type ${order.type} has no trigger price.`);
|
|
451
|
+
}
|
|
452
|
+
if (order.type != nodeSDKTypes_1.ORDER_TYPE_MARKET && order.stopPrice != undefined) {
|
|
453
|
+
throw Error(`Order type ${order.type} has no trigger price.`);
|
|
454
|
+
}
|
|
455
|
+
return flag;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Read config file into NodeSDKConfig interface
|
|
459
|
+
* @param fileLocation json-file with required variables for config
|
|
460
|
+
* @returns NodeSDKConfig
|
|
461
|
+
*/
|
|
462
|
+
static readSDKConfig(fileLocation) {
|
|
463
|
+
if (fileLocation == nodeSDKTypes_1.DEFAULT_CONFIG_MAINNET_NAME) {
|
|
464
|
+
fileLocation = nodeSDKTypes_1.DEFAULT_CONFIG_MAINNET;
|
|
465
|
+
}
|
|
466
|
+
else if (fileLocation == nodeSDKTypes_1.DEFAULT_CONFIG_TESTNET_NAME) {
|
|
467
|
+
fileLocation = nodeSDKTypes_1.DEFAULT_CONFIG_TESTNET;
|
|
468
|
+
}
|
|
469
|
+
let configFile = require(fileLocation);
|
|
470
|
+
let config = configFile;
|
|
471
|
+
return config;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
exports.default = PerpetualDataHandler;
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { BigNumber } from "ethers";
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param s string to shorten/extend to 4 characters
|
|
6
|
+
* @returns string with 4 characters (or characters + null chars)
|
|
7
|
+
*/
|
|
8
|
+
export declare function to4Chars(s: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Converts string into 4-character bytes4
|
|
11
|
+
* uses to4Chars to first convert the string into
|
|
12
|
+
* 4 characters.
|
|
13
|
+
* Resulting buffer can be used with smart contract to
|
|
14
|
+
* identify tokens (BTC, USDC, MATIC etc.)
|
|
15
|
+
* @param s string to encode into bytes4
|
|
16
|
+
* @returns buffer
|
|
17
|
+
*/
|
|
18
|
+
export declare function toBytes4(s: string): Buffer;
|
|
19
|
+
/**
|
|
20
|
+
* Decodes a buffer encoded with toBytes4 into
|
|
21
|
+
* a string. The string is the result of to4Chars of the
|
|
22
|
+
* originally encoded string stripped from null-chars
|
|
23
|
+
* @param b correctly encoded bytes4 buffer using toBytes4
|
|
24
|
+
* @returns string decoded into to4Chars-type string without null characters
|
|
25
|
+
*/
|
|
26
|
+
export declare function fromBytes4(b: Buffer): string;
|
|
27
|
+
/**
|
|
28
|
+
* Decodes the bytes4 encoded string received from the
|
|
29
|
+
* smart contract as a hex-number in string-format
|
|
30
|
+
* @param s string representing a hex-number ("0x...")
|
|
31
|
+
* @returns x of to4Chars(x) stripped from null-chars,
|
|
32
|
+
* where x was originally encoded and
|
|
33
|
+
* returned by the smart contract as bytes4
|
|
34
|
+
*/
|
|
35
|
+
export declare function fromBytes4HexString(s: string): string;
|
|
36
|
+
export declare function combineFlags(f1: BigNumber, f2: BigNumber): BigNumber;
|
|
37
|
+
export declare function containsFlag(f1: BigNumber, f2: BigNumber): boolean;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.containsFlag = exports.combineFlags = exports.fromBytes4HexString = exports.fromBytes4 = exports.toBytes4 = exports.to4Chars = void 0;
|
|
4
|
+
const ethers_1 = require("ethers");
|
|
5
|
+
const ethers = require("ethers");
|
|
6
|
+
function _isVocal(char) {
|
|
7
|
+
char = char.toLowerCase();
|
|
8
|
+
return char == "a" || char == "e" || char == "i" || char == "o" || char == "u";
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* @param s string to shorten/extend to 4 characters
|
|
13
|
+
* @returns string with 4 characters (or characters + null chars)
|
|
14
|
+
*/
|
|
15
|
+
function to4Chars(s) {
|
|
16
|
+
while (s.length < 4) {
|
|
17
|
+
s = s + "\0";
|
|
18
|
+
}
|
|
19
|
+
let k = s.length - 1;
|
|
20
|
+
while (s.length > 4 && k >= 0) {
|
|
21
|
+
// chop off vocals from the end of string
|
|
22
|
+
// e.g. MATIC -> MATC
|
|
23
|
+
if (_isVocal(s.charAt(k))) {
|
|
24
|
+
s = s.substring(0, k) + s.substring(k + 1, s.length);
|
|
25
|
+
}
|
|
26
|
+
k--;
|
|
27
|
+
}
|
|
28
|
+
s = s.substring(0, 4);
|
|
29
|
+
return s;
|
|
30
|
+
}
|
|
31
|
+
exports.to4Chars = to4Chars;
|
|
32
|
+
/**
|
|
33
|
+
* Converts string into 4-character bytes4
|
|
34
|
+
* uses to4Chars to first convert the string into
|
|
35
|
+
* 4 characters.
|
|
36
|
+
* Resulting buffer can be used with smart contract to
|
|
37
|
+
* identify tokens (BTC, USDC, MATIC etc.)
|
|
38
|
+
* @param s string to encode into bytes4
|
|
39
|
+
* @returns buffer
|
|
40
|
+
*/
|
|
41
|
+
function toBytes4(s) {
|
|
42
|
+
s = to4Chars(s);
|
|
43
|
+
let valBuff = Buffer.from(s, "ascii");
|
|
44
|
+
return valBuff;
|
|
45
|
+
}
|
|
46
|
+
exports.toBytes4 = toBytes4;
|
|
47
|
+
/**
|
|
48
|
+
* Decodes a buffer encoded with toBytes4 into
|
|
49
|
+
* a string. The string is the result of to4Chars of the
|
|
50
|
+
* originally encoded string stripped from null-chars
|
|
51
|
+
* @param b correctly encoded bytes4 buffer using toBytes4
|
|
52
|
+
* @returns string decoded into to4Chars-type string without null characters
|
|
53
|
+
*/
|
|
54
|
+
function fromBytes4(b) {
|
|
55
|
+
let val = b.toString("ascii");
|
|
56
|
+
val = val.replace(/\0/g, "");
|
|
57
|
+
return val;
|
|
58
|
+
}
|
|
59
|
+
exports.fromBytes4 = fromBytes4;
|
|
60
|
+
/**
|
|
61
|
+
* Decodes the bytes4 encoded string received from the
|
|
62
|
+
* smart contract as a hex-number in string-format
|
|
63
|
+
* @param s string representing a hex-number ("0x...")
|
|
64
|
+
* @returns x of to4Chars(x) stripped from null-chars,
|
|
65
|
+
* where x was originally encoded and
|
|
66
|
+
* returned by the smart contract as bytes4
|
|
67
|
+
*/
|
|
68
|
+
function fromBytes4HexString(s) {
|
|
69
|
+
let res = "";
|
|
70
|
+
for (let k = 2; k < s.length; k = k + 2) {
|
|
71
|
+
res = res + String.fromCharCode(parseInt(s.substring(k, k + 2), 16));
|
|
72
|
+
}
|
|
73
|
+
res = res.replace(/\0/g, "");
|
|
74
|
+
return res;
|
|
75
|
+
}
|
|
76
|
+
exports.fromBytes4HexString = fromBytes4HexString;
|
|
77
|
+
function combineFlags(f1, f2) {
|
|
78
|
+
return ethers_1.BigNumber.from(parseInt(f1.toString()) | parseInt(f2.toString()));
|
|
79
|
+
}
|
|
80
|
+
exports.combineFlags = combineFlags;
|
|
81
|
+
function containsFlag(f1, f2) {
|
|
82
|
+
return (parseInt(f1.toString()) & parseInt(f2.toString())) > 0;
|
|
83
|
+
}
|
|
84
|
+
exports.containsFlag = containsFlag;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { BigNumber, ethers } from "ethers";
|
|
2
|
+
import PerpetualDataHandler from "./perpetualDataHandler";
|
|
3
|
+
import { NodeSDKConfig } from "./nodeSDKTypes";
|
|
4
|
+
/**
|
|
5
|
+
* This is a parent class for the classes that require
|
|
6
|
+
* write access to the contracts.
|
|
7
|
+
* This class requires a private key and executes smart-contract interaction that
|
|
8
|
+
* require gas-payments.
|
|
9
|
+
*/
|
|
10
|
+
export default class WriteAccessHandler extends PerpetualDataHandler {
|
|
11
|
+
protected privateKey: string;
|
|
12
|
+
protected traderAddr: string;
|
|
13
|
+
protected signer: ethers.Wallet | null;
|
|
14
|
+
protected gasLimit: number;
|
|
15
|
+
protected chainId: number;
|
|
16
|
+
/**
|
|
17
|
+
* Constructor
|
|
18
|
+
* @param config configuration
|
|
19
|
+
* @param privateKey private key of account that trades
|
|
20
|
+
*/
|
|
21
|
+
constructor(config: NodeSDKConfig, privateKey: string);
|
|
22
|
+
/**
|
|
23
|
+
* Initialize the AccountTrade-Class with this function
|
|
24
|
+
* to create instance of D8X perpetual contract and gather information
|
|
25
|
+
* about perpetual currencies
|
|
26
|
+
*/
|
|
27
|
+
createProxyInstance(): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Set allowance for ar margin token (e.g., MATIC, ETH, USDC)
|
|
30
|
+
* @param symbol token in 'long-form' such as MATIC, symbol also fine (ETH-USD-MATIC)
|
|
31
|
+
* @param amount optional, amount to approve if not 'infinity'
|
|
32
|
+
* @returns transaction hash
|
|
33
|
+
*/
|
|
34
|
+
setAllowance(symbol: string, amount?: number | undefined): Promise<string>;
|
|
35
|
+
protected static _setAllowance(tokenAddr: string, proxyAddr: string, signer: ethers.Wallet, amount: BigNumber): Promise<string>;
|
|
36
|
+
}
|