@d8x/perpetuals-sdk 0.6.3 → 0.6.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.
Files changed (48) hide show
  1. package/dist/cjs/version.d.ts +1 -1
  2. package/dist/cjs/version.js +1 -1
  3. package/dist/esm/version.d.ts +1 -1
  4. package/dist/esm/version.js +1 -1
  5. package/package.json +5 -4
  6. package/src/abi/ERC20.json +288 -0
  7. package/src/abi/IPerpetualManager.json +5888 -0
  8. package/src/abi/LimitOrderBook.json +1062 -0
  9. package/src/abi/LimitOrderBookFactory.json +161 -0
  10. package/src/abi/MockTokenSwap.json +186 -0
  11. package/src/abi/ShareToken.json +428 -0
  12. package/src/accountTrade.ts +428 -0
  13. package/src/brokerTool.ts +555 -0
  14. package/src/config/defaultConfig.json +62 -0
  15. package/src/config/mockSwap.json +6 -0
  16. package/src/config/priceFeedConfig.json +104 -0
  17. package/src/config/symbolList.json +13 -0
  18. package/src/contracts/ERC20.ts +444 -0
  19. package/src/contracts/IPerpetualManager.ts +7227 -0
  20. package/src/contracts/LimitOrderBook.ts +1251 -0
  21. package/src/contracts/LimitOrderBookFactory.ts +348 -0
  22. package/src/contracts/MockTokenSwap.ts +373 -0
  23. package/src/contracts/ShareToken.ts +695 -0
  24. package/src/contracts/common.ts +44 -0
  25. package/src/contracts/factories/ERC20__factory.ts +306 -0
  26. package/src/contracts/factories/IPerpetualManager__factory.ts +5912 -0
  27. package/src/contracts/factories/LimitOrderBookFactory__factory.ts +189 -0
  28. package/src/contracts/factories/LimitOrderBook__factory.ts +1086 -0
  29. package/src/contracts/factories/MockTokenSwap__factory.ts +207 -0
  30. package/src/contracts/factories/ShareToken__factory.ts +449 -0
  31. package/src/contracts/factories/index.ts +9 -0
  32. package/src/contracts/index.ts +16 -0
  33. package/src/d8XMath.ts +376 -0
  34. package/src/index.ts +29 -0
  35. package/src/liquidatorTool.ts +270 -0
  36. package/src/liquidityProviderTool.ts +148 -0
  37. package/src/marketData.ts +1310 -0
  38. package/src/nodeSDKTypes.ts +332 -0
  39. package/src/orderReferrerTool.ts +516 -0
  40. package/src/perpetualDataHandler.ts +1161 -0
  41. package/src/perpetualEventHandler.ts +455 -0
  42. package/src/priceFeeds.ts +382 -0
  43. package/src/traderDigests.ts +86 -0
  44. package/src/traderInterface.ts +172 -0
  45. package/src/triangulator.ts +105 -0
  46. package/src/utils.ts +134 -0
  47. package/src/version.ts +1 -0
  48. package/src/writeAccessHandler.ts +139 -0
@@ -0,0 +1,428 @@
1
+ import { Signer } from "@ethersproject/abstract-signer";
2
+ import { CallOverrides, Contract, ContractTransaction, Overrides, PayableOverrides } from "@ethersproject/contracts";
3
+ import { Buffer } from "buffer";
4
+ import { IPerpetualManager, LimitOrderBook } from "./contracts";
5
+ import { ABK64x64ToFloat, floatToABK64x64 } from "./d8XMath";
6
+ import MarketData from "./marketData";
7
+ import {
8
+ NodeSDKConfig,
9
+ Order,
10
+ OrderResponse,
11
+ PerpetualStaticInfo,
12
+ PriceFeedSubmission,
13
+ SmartContractOrder,
14
+ ZERO_ADDRESS,
15
+ } from "./nodeSDKTypes";
16
+ import PerpetualDataHandler from "./perpetualDataHandler";
17
+ import TraderDigests from "./traderDigests";
18
+ import WriteAccessHandler from "./writeAccessHandler";
19
+
20
+ /**
21
+ * Functions to create, submit and cancel orders on the exchange.
22
+ * This class requires a private key and executes smart-contract interactions that
23
+ * require gas-payments.
24
+ * @extends WriteAccessHandler
25
+ */
26
+ export default class AccountTrade extends WriteAccessHandler {
27
+ protected digestTool: TraderDigests;
28
+
29
+ /**
30
+ * Constructor
31
+ * @param {NodeSDKConfig} config Configuration object, see PerpetualDataHandler.
32
+ * readSDKConfig.
33
+ * @example
34
+ * import { AccountTrade, PerpetualDataHandler } from '@d8x/perpetuals-sdk';
35
+ * async function main() {
36
+ * console.log(AccountTrade);
37
+ * // load configuration for testnet
38
+ * const config = PerpetualDataHandler.readSDKConfig("testnet");
39
+ * // AccountTrade (authentication required, PK is an environment variable with a private key)
40
+ * const pk: string = <string>process.env.PK;
41
+ * let accTrade = new AccountTrade(config, pk);
42
+ * // Create a proxy instance to access the blockchain
43
+ * await accTrade.createProxyInstance();
44
+ * }
45
+ * main();
46
+ *
47
+ * @param {string} privateKey Private key of account that trades.
48
+ */
49
+ public constructor(config: NodeSDKConfig, privateKey: string) {
50
+ super(config, privateKey);
51
+ this.digestTool = new TraderDigests();
52
+ }
53
+
54
+ /**
55
+ * Cancels an existing order on the exchange.
56
+ * @param {string} symbol Symbol of the form ETH-USD-MATIC.
57
+ * @param {string} orderId ID of the order to be cancelled.
58
+ * @example
59
+ * import { AccountTrade, PerpetualDataHandler, Order } from '@d8x/perpetuals-sdk';
60
+ * async function main() {
61
+ * console.log(AccountTrade);
62
+ * // setup (authentication required, PK is an environment variable with a private key)
63
+ * const config = PerpetualDataHandler.readSDKConfig("testnet");
64
+ * const pk: string = <string>process.env.PK;
65
+ * let accTrade = new AccountTrade(config, pk);
66
+ * await accTrade.createProxyInstance();
67
+ * // cancel order
68
+ * let cancelTransaction = accTrade.cancelOrder("MATIC-USD-MATIC",
69
+ * "0x4639061a58dcf34f4c9c703f49f1cb00d6a4fba490d62c0eb4a4fb06e1c76c19")
70
+ * console.log(cancelTransaction);
71
+ * }
72
+ * main();
73
+ * @returns {ContractTransaction} Contract Transaction (containing events).
74
+ */
75
+ public async cancelOrder(
76
+ symbol: string,
77
+ orderId: string,
78
+ submission?: PriceFeedSubmission,
79
+ overrides?: Overrides
80
+ ): Promise<ContractTransaction> {
81
+ if (this.proxyContract == null || this.signer == null) {
82
+ throw Error("no proxy contract or wallet initialized. Use createProxyInstance().");
83
+ }
84
+ if (submission == undefined) {
85
+ submission = await this.fetchLatestFeedPriceInfo(symbol);
86
+ }
87
+ const orderBookContract = this.getOrderBookContract(symbol);
88
+
89
+ return await this._cancelOrder(symbol, orderId, orderBookContract, submission, overrides);
90
+ }
91
+
92
+ /**
93
+ * Submits an order to the exchange.
94
+ * @param {Order} order Order structure. As a minimum the structure needs to
95
+ * specify symbol, side, type and quantity.
96
+ * @example
97
+ * import { AccountTrade, PerpetualDataHandler, Order } from '@d8x/perpetuals-sdk';
98
+ * async function main() {
99
+ * console.log(AccountTrade);
100
+ * // setup (authentication required, PK is an environment variable with a private key)
101
+ * const config = PerpetualDataHandler.readSDKConfig("testnet");
102
+ * const pk: string = <string>process.env.PK;
103
+ * let accTrade = new AccountTrade(config, pk);
104
+ * await accTrade.createProxyInstance();
105
+ * // set allowance
106
+ * await accTrade.setAllowance("MATIC");
107
+ * // set an order
108
+ * let order: Order = {
109
+ * symbol: "MATIC-USD-MATIC",
110
+ * side: "BUY",
111
+ * type: "MARKET",
112
+ * quantity: 100,
113
+ * leverage: 2,
114
+ * executionTimestamp: Date.now()/1000,
115
+ * };
116
+ * let orderTransaction = await accTrade.order(order);
117
+ * console.log(orderTransaction);
118
+ * }
119
+ * main();
120
+ *
121
+ * @example
122
+ * import { AccountTrade, PerpetualDataHandler, Order } from '@d8x/perpetuals-sdk';
123
+ * async function main() {
124
+ * console.log(AccountTrade);
125
+ * // setup (authentication required, PK is an environment variable with a private key)
126
+ * const config = PerpetualDataHandler.readSDKConfig("testnet");
127
+ * const pk: string = <string>process.env.PK;
128
+ * let accTrade = new AccountTrade(config, pk);
129
+ * await accTrade.createProxyInstance();
130
+ * // set allowance
131
+ * await accTrade.setAllowance("MATIC");
132
+ * // set an order
133
+ * let order: Order = {
134
+ * symbol: "MATIC-USD-MATIC",
135
+ * side: "BUY",
136
+ * type: "LIMIT",
137
+ * limitPrice: 1,
138
+ * quantity: 5,
139
+ * leverage: 2,
140
+ * executionTimestamp: Date.now() / 1000,
141
+ * deadline: Date.now() / 1000 + 8*60*60, // order expires 8 hours from now
142
+ * };
143
+ * let orderTransaction = await accTrade.order(order);
144
+ * console.log(orderTransaction);
145
+ * }
146
+ * main();
147
+ *
148
+ * @returns {ContractTransaction} Contract Transaction (containing events).
149
+ */
150
+ public async order(order: Order, parentChildIds?: [string, string], overrides?: Overrides): Promise<OrderResponse> {
151
+ if (this.proxyContract == null || this.signer == null) {
152
+ throw Error("no proxy contract or wallet initialized. Use createProxyInstance().");
153
+ }
154
+ let minSize = PerpetualDataHandler._getMinimalPositionSize(order.symbol, this.symbolToPerpStaticInfo);
155
+ if (Math.abs(order.quantity) < minSize) {
156
+ throw Error("order size too small");
157
+ }
158
+ let orderBookContract = this.getOrderBookContract(order.symbol);
159
+ let res: OrderResponse = await this._order(
160
+ order,
161
+ this.traderAddr,
162
+ this.symbolToPerpStaticInfo,
163
+ this.proxyContract,
164
+ orderBookContract,
165
+ this.chainId,
166
+ this.signer,
167
+ parentChildIds,
168
+ overrides
169
+ );
170
+ return res;
171
+ }
172
+
173
+ /**
174
+ * Fee charged by the exchange for trading any perpetual on a given pool.
175
+ * It accounts for the current trader's fee tier (based on the trader's D8X balance and trading volume).
176
+ * If trading with a broker, it also accounts for the selected broker's fee tier.
177
+ * Note that this result only includes exchange fees, additional broker fees are not included.
178
+ * @param {string} poolSymbolName Pool symbol name (e.g. MATIC, USDC, etc).
179
+ * @param {string=} brokerAddr Optional address of a broker this trader may use to trade under.
180
+ * @example
181
+ * import { AccountTrade, PerpetualDataHandler } from '@d8x/perpetuals-sdk';
182
+ * async function main() {
183
+ * console.log(AccountTrade);
184
+ * // setup (authentication required, PK is an environment variable with a private key)
185
+ * const config = PerpetualDataHandler.readSDKConfig("testnet");
186
+ * const pk: string = <string>process.env.PK;
187
+ * let accTrade = new AccountTrade(config, pk);
188
+ * await accTrade.createProxyInstance();
189
+ * // query exchange fee
190
+ * let fees = await accTrade.queryExchangeFee("MATIC");
191
+ * console.log(fees);
192
+ * }
193
+ * main();
194
+ *
195
+ * @returns Exchange fee, in decimals (i.e. 0.1% is 0.001).
196
+ */
197
+ public async queryExchangeFee(poolSymbolName: string, brokerAddr?: string, overrides?: Overrides): Promise<number> {
198
+ if (this.proxyContract == null) {
199
+ throw Error("no proxy contract or wallet initialized. Use createProxyInstance().");
200
+ }
201
+ if (typeof brokerAddr == "undefined") {
202
+ brokerAddr = ZERO_ADDRESS;
203
+ }
204
+ let poolId = PerpetualDataHandler._getPoolIdFromSymbol(poolSymbolName, this.poolStaticInfos);
205
+ let feeTbps = await this.proxyContract.queryExchangeFee(poolId, this.traderAddr, brokerAddr, overrides || {});
206
+ return feeTbps / 100_000;
207
+ }
208
+
209
+ /**
210
+ * Exponentially weighted EMA of the total USD trading volume of all trades performed by this trader.
211
+ * The weights are chosen so that in average this coincides with the 30 day volume.
212
+ * @param {string} poolSymbolName Pool symbol name (e.g. MATIC, USDC, etc).
213
+ * @example
214
+ * import { AccountTrade, PerpetualDataHandler } from '@d8x/perpetuals-sdk';
215
+ * async function main() {
216
+ * console.log(AccountTrade);
217
+ * // setup (authentication required, PK is an environment variable with a private key)
218
+ * const config = PerpetualDataHandler.readSDKConfig("testnet");
219
+ * const pk: string = <string>process.env.PK;
220
+ * let accTrade = new AccountTrade(config, pk);
221
+ * await accTrade.createProxyInstance();
222
+ * // query 30 day volume
223
+ * let vol = await accTrade.getCurrentTraderVolume("MATIC");
224
+ * console.log(vol);
225
+ * }
226
+ * main();
227
+ *
228
+ * @returns {number} Current trading volume for this trader, in USD.
229
+ */
230
+ public async getCurrentTraderVolume(poolSymbolName: string, overrides?: CallOverrides): Promise<number> {
231
+ if (this.proxyContract == null || this.signer == null) {
232
+ throw Error("no proxy contract or wallet initialized. Use createProxyInstance().");
233
+ }
234
+ let poolId = WriteAccessHandler._getPoolIdFromSymbol(poolSymbolName, this.poolStaticInfos);
235
+ let volume = await this.proxyContract.getCurrentTraderVolume(poolId, this.traderAddr, overrides || {});
236
+ return ABK64x64ToFloat(volume);
237
+ }
238
+
239
+ /**
240
+ *
241
+ * @param symbol Symbol of the form ETH-USD-MATIC.
242
+ * @example
243
+ * import { AccountTrade, PerpetualDataHandler } from '@d8x/perpetuals-sdk';
244
+ * async function main() {
245
+ * console.log(AccountTrade);
246
+ * // setup (authentication required, PK is an environment variable with a private key)
247
+ * const config = PerpetualDataHandler.readSDKConfig("testnet");
248
+ * const pk: string = <string>process.env.PK;
249
+ * let accTrade = new AccountTrade(config, pk);
250
+ * await accTrade.createProxyInstance();
251
+ * // get order IDs
252
+ * let orderIds = await accTrade.getOrderIds("MATIC-USD-MATIC");
253
+ * console.log(orderIds);
254
+ * }
255
+ * main();
256
+ *
257
+ * @returns {string[]} Array of Ids for all the orders currently open by this trader.
258
+ */
259
+ public async getOrderIds(symbol: string, overrides?: CallOverrides): Promise<string[]> {
260
+ if (this.proxyContract == null || this.signer == null) {
261
+ throw Error("no proxy contract or wallet initialized. Use createProxyInstance().");
262
+ }
263
+ let orderBookContract = this.getOrderBookContract(symbol);
264
+ return await MarketData.orderIdsOfTrader(this.traderAddr, orderBookContract, overrides);
265
+ }
266
+
267
+ /**
268
+ * Static order function
269
+ * @param order order type (not SmartContractOrder but Order)
270
+ * @param traderAddr trader address
271
+ * @param symbolToPerpetualMap maps the symbol (MATIC-USD-MATIC)-type format to the perpetual id
272
+ * @param proxyContract contract instance of D8X perpetuals
273
+ * @param orderBookContract order book contract or null
274
+ * @param chainId chain Id of network
275
+ * @param signer instance of ethers wallet that can write
276
+ * @param gasLimit gas limit to be used for the trade
277
+ * @returns [transaction hash, order id]
278
+ * @ignore
279
+ */
280
+ public async _order(
281
+ order: Order,
282
+ traderAddr: string,
283
+ symbolToPerpetualMap: Map<string, PerpetualStaticInfo>,
284
+ proxyContract: IPerpetualManager,
285
+ orderBookContract: LimitOrderBook,
286
+ chainId: number,
287
+ signer: Signer,
288
+ parentChildIds?: [string, string],
289
+ overrides?: Overrides
290
+ ): Promise<OrderResponse> {
291
+ let scOrder = AccountTrade.toSmartContractOrder(order, traderAddr, symbolToPerpetualMap);
292
+ let clientOrder = AccountTrade.fromSmartContratOrderToClientOrder(scOrder, parentChildIds);
293
+ // if we are here, we have a clean order
294
+ // decide whether to send order to Limit Order Book or AMM based on order type
295
+ let tx: ContractTransaction;
296
+ // all orders are sent to the order-book
297
+ let [signature, digest] = await this._createSignature(scOrder, chainId, true, signer, proxyContract.address);
298
+ tx = await orderBookContract.postOrder(clientOrder, signature, overrides || { gasLimit: this.gasLimit });
299
+ let id = await this.digestTool.createOrderId(digest);
300
+ return { tx: tx, orderId: id };
301
+ }
302
+
303
+ protected async _cancelOrder(
304
+ symbol: string,
305
+ orderId: string,
306
+ orderBookContract: LimitOrderBook,
307
+ submission?: PriceFeedSubmission,
308
+ overrides?: PayableOverrides
309
+ ): Promise<ContractTransaction> {
310
+ if (orderBookContract == null || this.signer == null) {
311
+ throw Error(`Order Book contract for symbol ${symbol} or signer not defined`);
312
+ }
313
+ if (submission == undefined) {
314
+ submission = await this.fetchLatestFeedPriceInfo(symbol);
315
+ }
316
+ let scOrder: SmartContractOrder = await orderBookContract.orderOfDigest(orderId);
317
+ let [signature] = await this._createSignature(scOrder, this.chainId, false, this.signer, this.proxyAddr);
318
+ // value is minimal necessary by default, but can be overriden
319
+ if (!overrides || overrides.value == undefined) {
320
+ overrides = {
321
+ value: submission.timestamps.length * this.PRICE_UPDATE_FEE_GWEI,
322
+ gasLimit: overrides?.gasLimit ?? this.gasLimit,
323
+ ...overrides,
324
+ } as PayableOverrides;
325
+ }
326
+
327
+ return await orderBookContract.cancelOrder(
328
+ orderId,
329
+ signature,
330
+ submission.priceFeedVaas,
331
+ submission.timestamps,
332
+ overrides
333
+ );
334
+ }
335
+
336
+ /**
337
+ * Creates a signature
338
+ * @param order smart-contract-type order
339
+ * @param chainId chainId of network
340
+ * @param isNewOrder true unless we cancel
341
+ * @param signer ethereum-type wallet
342
+ * @param proxyAddress address of the contract
343
+ * @returns signature as string
344
+ * @ignore
345
+ */
346
+ private async _createSignature(
347
+ order: SmartContractOrder,
348
+ chainId: number,
349
+ isNewOrder: boolean,
350
+ signer: Signer,
351
+ proxyAddress: string
352
+ ): Promise<string[]> {
353
+ let digest = await this.digestTool.createDigest(order, chainId, isNewOrder, proxyAddress);
354
+ let digestBuffer = Buffer.from(digest.substring(2, digest.length), "hex");
355
+ let signature = await signer.signMessage(digestBuffer);
356
+ return [signature, digest];
357
+ }
358
+
359
+ /**
360
+ *
361
+ * @param {string} symbol Symbol of the form ETH-USD-MATIC.
362
+ * @param {number} amount How much collateral to add, in units of collateral currency, e.g. MATIC
363
+ */
364
+ public async addCollateral(
365
+ symbol: string,
366
+ amount: number,
367
+ submission?: PriceFeedSubmission,
368
+ overrides?: PayableOverrides
369
+ ): Promise<ContractTransaction> {
370
+ if (this.proxyContract == null || this.signer == null) {
371
+ throw Error("no proxy contract or wallet initialized. Use createProxyInstance().");
372
+ }
373
+ let perpId = this.getPerpIdFromSymbol(symbol);
374
+ let fAmountCC = floatToABK64x64(amount);
375
+ if (submission == undefined) {
376
+ submission = await this.fetchLatestFeedPriceInfo(symbol);
377
+ }
378
+ if (!overrides || overrides.value == undefined) {
379
+ overrides = {
380
+ value: submission.timestamps.length * this.PRICE_UPDATE_FEE_GWEI,
381
+ gasLimit: overrides?.gasLimit ?? this.gasLimit,
382
+ ...overrides,
383
+ } as PayableOverrides;
384
+ }
385
+ return await this.proxyContract.deposit(
386
+ perpId,
387
+ fAmountCC,
388
+ submission.priceFeedVaas,
389
+ submission.timestamps,
390
+ overrides || { gasLimit: this.gasLimit }
391
+ );
392
+ }
393
+
394
+ /**
395
+ *
396
+ * @param {string} symbol Symbol of the form ETH-USD-MATIC.
397
+ * @param {number} amount How much collateral to remove, in units of collateral currency, e.g. MATIC
398
+ */
399
+ public async removeCollateral(
400
+ symbol: string,
401
+ amount: number,
402
+ submission?: PriceFeedSubmission,
403
+ overrides?: PayableOverrides
404
+ ): Promise<ContractTransaction> {
405
+ if (this.proxyContract == null || this.signer == null) {
406
+ throw Error("no proxy contract or wallet initialized. Use createProxyInstance().");
407
+ }
408
+ let perpId = this.getPerpIdFromSymbol(symbol);
409
+ let fAmountCC = floatToABK64x64(amount);
410
+ if (submission == undefined) {
411
+ submission = await this.fetchLatestFeedPriceInfo(symbol);
412
+ }
413
+ if (!overrides || overrides.value == undefined) {
414
+ overrides = {
415
+ value: submission.timestamps.length * this.PRICE_UPDATE_FEE_GWEI,
416
+ gasLimit: overrides?.gasLimit ?? this.gasLimit,
417
+ ...overrides,
418
+ } as PayableOverrides;
419
+ }
420
+ return await this.proxyContract.withdraw(
421
+ perpId,
422
+ fAmountCC,
423
+ submission.priceFeedVaas,
424
+ submission.timestamps,
425
+ overrides || { gasLimit: this.gasLimit }
426
+ );
427
+ }
428
+ }