@d8x/perpetuals-sdk 0.0.1 → 0.0.3
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/dist/accountTrade.d.ts +19 -2
- package/dist/accountTrade.js +41 -3
- package/dist/brokerTool.d.ts +97 -24
- package/dist/brokerTool.js +202 -59
- package/dist/d8XMath.d.ts +17 -14
- package/dist/d8XMath.js +17 -15
- package/dist/liquidatorTool.d.ts +58 -0
- package/dist/liquidatorTool.js +125 -0
- package/dist/liquidityProviderTool.d.ts +11 -11
- package/dist/liquidityProviderTool.js +11 -11
- package/dist/marketData.d.ts +27 -8
- package/dist/marketData.js +35 -8
- package/dist/orderReferrerTool.d.ts +37 -6
- package/dist/orderReferrerTool.js +120 -5
- package/dist/perpetualDataHandler.d.ts +2 -0
- package/dist/perpetualDataHandler.js +24 -6
- package/dist/utils.d.ts +8 -8
- package/dist/utils.js +11 -9
- package/package.json +2 -2
- package/src/accountTrade.ts +41 -23
- package/src/brokerTool.ts +253 -106
- package/src/d8XMath.ts +18 -15
- package/src/liquidatorTool.ts +106 -0
- package/src/liquidityProviderTool.ts +11 -11
- package/src/marketData.ts +153 -122
- package/src/orderReferrerTool.ts +132 -6
- package/src/perpetualDataHandler.ts +33 -7
- package/src/utils.ts +40 -39
- package/dist/liquiditatorTool.d.ts +0 -14
- package/dist/liquiditatorTool.js +0 -21
- package/src/liquiditatorTool.ts +0 -21
package/src/marketData.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
ExchangeInfo,
|
|
3
|
+
NodeSDKConfig,
|
|
4
|
+
MarginAccount,
|
|
5
|
+
PoolState,
|
|
6
|
+
PerpetualState,
|
|
7
|
+
COLLATERAL_CURRENCY_BASE,
|
|
8
|
+
COLLATERAL_CURRENCY_QUANTO,
|
|
9
|
+
PERP_STATE_STR,
|
|
10
|
+
ZERO_ADDRESS,
|
|
11
11
|
} from "./nodeSDKTypes";
|
|
12
12
|
import { BigNumber, BytesLike, ethers } from "ethers";
|
|
13
13
|
import { floatToABK64x64, ABK64x64ToFloat } from "./d8XMath";
|
|
14
|
-
import { fromBytes4HexString } from "./utils";
|
|
14
|
+
import { fromBytes4HexString, toBytes4 } from "./utils";
|
|
15
15
|
import PerpetualDataHandler from "./perpetualDataHandler";
|
|
16
16
|
import { SmartContractOrder, Order } from "./nodeSDKTypes";
|
|
17
17
|
|
|
@@ -20,130 +20,161 @@ import { SmartContractOrder, Order } from "./nodeSDKTypes";
|
|
|
20
20
|
* No gas required for the queries here.
|
|
21
21
|
*/
|
|
22
22
|
export default class MarketData extends PerpetualDataHandler {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
public constructor(config: NodeSDKConfig) {
|
|
24
|
+
super(config);
|
|
25
|
+
}
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
public async createProxyInstance() {
|
|
28
|
+
this.provider = new ethers.providers.JsonRpcProvider(this.nodeURL);
|
|
29
|
+
await this.initContractsAndData(this.provider);
|
|
30
|
+
}
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Information about the products traded in the exchange.
|
|
34
|
+
* @returns {ExchangeInfo} Array of static data for all the pools and perpetuals in the system.
|
|
35
|
+
*/
|
|
36
|
+
public async exchangeInfo(): Promise<ExchangeInfo> {
|
|
37
|
+
if (this.proxyContract == null) {
|
|
38
|
+
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
37
39
|
}
|
|
40
|
+
return await MarketData._exchangeInfo(this.proxyContract);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* All open orders for a trader-address and a symbol.
|
|
45
|
+
* @param {string} traderAddr Address of the trader for which we get the open orders.
|
|
46
|
+
* @param {string} symbol Symbol of the form ETH-USD-MATIC.
|
|
47
|
+
* @returns {Array<Array<Order>, Array<string>>} Array of open orders and corresponding order-ids.
|
|
48
|
+
*/
|
|
49
|
+
public async openOrders(traderAddr: string, symbol: string): Promise<{ orders: Order[]; orderIds: string[] }> {
|
|
50
|
+
// open orders requested only for given symbol
|
|
51
|
+
let orderBookContract = this.getOrderBookContract(symbol);
|
|
52
|
+
let [orders, digests] = await Promise.all([
|
|
53
|
+
this.openOrdersOnOrderBook(traderAddr, orderBookContract),
|
|
54
|
+
this.orderIdsOfTrader(traderAddr, orderBookContract),
|
|
55
|
+
]);
|
|
56
|
+
return { orders: orders, orderIds: digests };
|
|
57
|
+
}
|
|
38
58
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
let [orders, digests] = await Promise.all([
|
|
49
|
-
this.openOrdersOnOrderBook(traderAddr, orderBookContract),
|
|
50
|
-
this.orderIdsOfTrader(traderAddr, orderBookContract),
|
|
51
|
-
]);
|
|
52
|
-
return { orders: orders, orderIds: digests };
|
|
59
|
+
/**
|
|
60
|
+
* Information about the position open by a given trader in a given perpetual contract.
|
|
61
|
+
* @param {string} traderAddr Address of the trader for which we get the position risk.
|
|
62
|
+
* @param {string} symbol Symbol of the form ETH-USD-MATIC.
|
|
63
|
+
* @returns {MarginAccount}
|
|
64
|
+
*/
|
|
65
|
+
public async positionRisk(traderAddr: string, symbol: string): Promise<MarginAccount> {
|
|
66
|
+
if (this.proxyContract == null) {
|
|
67
|
+
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
53
68
|
}
|
|
69
|
+
let mgnAcct = await PerpetualDataHandler.getMarginAccount(
|
|
70
|
+
traderAddr,
|
|
71
|
+
symbol,
|
|
72
|
+
this.symbolToPerpStaticInfo,
|
|
73
|
+
this.proxyContract
|
|
74
|
+
);
|
|
75
|
+
return mgnAcct;
|
|
76
|
+
}
|
|
54
77
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Uses the Oracle(s) in the exchange to get the latest price of a given index in a given currency, if a route exists.
|
|
80
|
+
* @param {string} base Index name, e.g. ETH.
|
|
81
|
+
* @param {string} quote Quote currency, e.g. USD.
|
|
82
|
+
* @returns {number} Price of index in given currency.
|
|
83
|
+
*/
|
|
84
|
+
public async getOraclePrice(base: string, quote: string): Promise<number | undefined> {
|
|
85
|
+
if (this.proxyContract == null) {
|
|
86
|
+
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
61
87
|
}
|
|
88
|
+
let px = await this.proxyContract.getOraclePrice([toBytes4(base), toBytes4(quote)]);
|
|
89
|
+
return px == undefined ? undefined : ABK64x64ToFloat(px);
|
|
90
|
+
}
|
|
62
91
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return userFriendlyOrders;
|
|
92
|
+
/**
|
|
93
|
+
* Query smart contract to get user orders and convert to user friendly order format.
|
|
94
|
+
* @param {string} traderAddr Address of trader.
|
|
95
|
+
* @param {ethers.Contract} orderBookContract Instance of order book.
|
|
96
|
+
* @returns {Order[]} Array of user friendly order struct.
|
|
97
|
+
* @ignore
|
|
98
|
+
*/
|
|
99
|
+
protected async openOrdersOnOrderBook(traderAddr: string, orderBookContract: ethers.Contract): Promise<Order[]> {
|
|
100
|
+
let orders: SmartContractOrder[] = await orderBookContract.getOrders(traderAddr, 0, 15);
|
|
101
|
+
//eliminate empty orders and map to user friendly orders
|
|
102
|
+
let userFriendlyOrders: Order[] = new Array<Order>();
|
|
103
|
+
let k = 0;
|
|
104
|
+
while (k < orders.length && orders[k].traderAddr != ZERO_ADDRESS) {
|
|
105
|
+
userFriendlyOrders.push(PerpetualDataHandler.fromSmartContractOrder(orders[k], this.symbolToPerpStaticInfo));
|
|
106
|
+
k++;
|
|
79
107
|
}
|
|
108
|
+
return userFriendlyOrders;
|
|
109
|
+
}
|
|
80
110
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return digests;
|
|
111
|
+
/**
|
|
112
|
+
*
|
|
113
|
+
* @param traderAddr address of the trader
|
|
114
|
+
* @param orderBookContract instance of order book contract
|
|
115
|
+
* @returns array of order-id's
|
|
116
|
+
* @ignore
|
|
117
|
+
*/
|
|
118
|
+
protected async orderIdsOfTrader(traderAddr: string, orderBookContract: ethers.Contract): Promise<string[]> {
|
|
119
|
+
let digestsRaw: string[] = await orderBookContract.limitDigestsOfTrader(traderAddr, 0, 15);
|
|
120
|
+
let k: number = 0;
|
|
121
|
+
let digests: string[] = [];
|
|
122
|
+
while (k < digestsRaw.length && BigNumber.from(digestsRaw[k]).gt(0)) {
|
|
123
|
+
digests.push(digestsRaw[k]);
|
|
124
|
+
k++;
|
|
96
125
|
}
|
|
126
|
+
return digests;
|
|
127
|
+
}
|
|
97
128
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
let markPremiumRate = ABK64x64ToFloat(perp.currentMarkPremiumRate.fPrice);
|
|
127
|
-
let currentFundingRateBps = 1e4 * ABK64x64ToFloat(perp.fCurrentFundingRate);
|
|
128
|
-
let state = PERP_STATE_STR[perp.state];
|
|
129
|
-
let PerpetualState: PerpetualState = {
|
|
130
|
-
id: perp.id,
|
|
131
|
-
state: state,
|
|
132
|
-
baseCurrency: fromBytes4HexString(perp.S2BaseCCY),
|
|
133
|
-
quoteCurrency: fromBytes4HexString(perp.S2QuoteCCY),
|
|
134
|
-
indexPrice: indexS2,
|
|
135
|
-
collToQuoteIndexPrice: indexS3,
|
|
136
|
-
markPrice: indexS2 * (1 + markPremiumRate),
|
|
137
|
-
currentFundingRateBps: currentFundingRateBps,
|
|
138
|
-
initialMarginRate: ABK64x64ToFloat(perp.fInitialMarginRate),
|
|
139
|
-
maintenanceMarginRate: ABK64x64ToFloat(perp.fMaintenanceMarginRate),
|
|
140
|
-
openInterestBC: ABK64x64ToFloat(perp.fOpenInterest),
|
|
141
|
-
maxPositionBC: ABK64x64ToFloat(perp.fMaxPositionBC),
|
|
142
|
-
};
|
|
143
|
-
PoolState.perpetuals.push(PerpetualState);
|
|
144
|
-
}
|
|
145
|
-
info.pools.push(PoolState);
|
|
129
|
+
public static async _exchangeInfo(_proxyContract: ethers.Contract): Promise<ExchangeInfo> {
|
|
130
|
+
let nestedPerpetualIDs = await PerpetualDataHandler.getNestedPerpetualIds(_proxyContract);
|
|
131
|
+
let info: ExchangeInfo = { pools: [] };
|
|
132
|
+
const numPools = nestedPerpetualIDs.length;
|
|
133
|
+
for (var j = 0; j < numPools; j++) {
|
|
134
|
+
let perpetualIDs = nestedPerpetualIDs[j];
|
|
135
|
+
let pool = await _proxyContract.getLiquidityPool(j + 1);
|
|
136
|
+
let PoolState: PoolState = {
|
|
137
|
+
isRunning: pool.isRunning,
|
|
138
|
+
marginTokenAddr: pool.marginTokenAddress,
|
|
139
|
+
poolShareTokenAddr: pool.shareTokenAddress,
|
|
140
|
+
defaultFundCashCC: ABK64x64ToFloat(pool.fDefaultFundCashCC),
|
|
141
|
+
pnlParticipantCashCC: ABK64x64ToFloat(pool.fPnLparticipantsCashCC),
|
|
142
|
+
totalAMMFundCashCC: ABK64x64ToFloat(pool.fAMMFundCashCC),
|
|
143
|
+
totalTargetAMMFundSizeCC: ABK64x64ToFloat(pool.fTargetAMMFundSize),
|
|
144
|
+
brokerCollateralLotSize: ABK64x64ToFloat(pool.fBrokerCollateralLotSize),
|
|
145
|
+
perpetuals: [],
|
|
146
|
+
};
|
|
147
|
+
for (var k = 0; k < perpetualIDs.length; k++) {
|
|
148
|
+
let perp = await _proxyContract.getPerpetual(perpetualIDs[k]);
|
|
149
|
+
let fIndexS2 = await _proxyContract.getOraclePrice([perp.S2BaseCCY, perp.S2QuoteCCY]);
|
|
150
|
+
let indexS2 = ABK64x64ToFloat(fIndexS2);
|
|
151
|
+
let indexS3 = 1;
|
|
152
|
+
if (perp.eCollateralCurrency == COLLATERAL_CURRENCY_BASE) {
|
|
153
|
+
indexS3 = indexS2;
|
|
154
|
+
} else if (perp.eCollateralCurrency == COLLATERAL_CURRENCY_QUANTO) {
|
|
155
|
+
indexS3 = ABK64x64ToFloat(await _proxyContract.getOraclePrice([perp.S3BaseCCY, perp.S3QuoteCCY]));
|
|
146
156
|
}
|
|
147
|
-
|
|
157
|
+
let markPremiumRate = ABK64x64ToFloat(perp.currentMarkPremiumRate.fPrice);
|
|
158
|
+
let currentFundingRateBps = 1e4 * ABK64x64ToFloat(perp.fCurrentFundingRate);
|
|
159
|
+
let state = PERP_STATE_STR[perp.state];
|
|
160
|
+
let PerpetualState: PerpetualState = {
|
|
161
|
+
id: perp.id,
|
|
162
|
+
state: state,
|
|
163
|
+
baseCurrency: fromBytes4HexString(perp.S2BaseCCY),
|
|
164
|
+
quoteCurrency: fromBytes4HexString(perp.S2QuoteCCY),
|
|
165
|
+
indexPrice: indexS2,
|
|
166
|
+
collToQuoteIndexPrice: indexS3,
|
|
167
|
+
markPrice: indexS2 * (1 + markPremiumRate),
|
|
168
|
+
currentFundingRateBps: currentFundingRateBps,
|
|
169
|
+
initialMarginRate: ABK64x64ToFloat(perp.fInitialMarginRate),
|
|
170
|
+
maintenanceMarginRate: ABK64x64ToFloat(perp.fMaintenanceMarginRate),
|
|
171
|
+
openInterestBC: ABK64x64ToFloat(perp.fOpenInterest),
|
|
172
|
+
maxPositionBC: ABK64x64ToFloat(perp.fMaxPositionBC),
|
|
173
|
+
};
|
|
174
|
+
PoolState.perpetuals.push(PerpetualState);
|
|
175
|
+
}
|
|
176
|
+
info.pools.push(PoolState);
|
|
148
177
|
}
|
|
178
|
+
return info;
|
|
179
|
+
}
|
|
149
180
|
}
|
package/src/orderReferrerTool.ts
CHANGED
|
@@ -1,17 +1,143 @@
|
|
|
1
1
|
import WriteAccessHandler from "./writeAccessHandler";
|
|
2
|
-
import { NodeSDKConfig } from "./nodeSDKTypes";
|
|
2
|
+
import { BUY_SIDE, NodeSDKConfig, Order, SELL_SIDE, ZERO_ADDRESS } from "./nodeSDKTypes";
|
|
3
|
+
import { ethers } from "ethers";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
-
* Methods to refer orders from the limit order book
|
|
6
|
+
* Methods to execute existing orders from the limit order book.
|
|
7
7
|
*/
|
|
8
8
|
export default class OrderReferrerTool extends WriteAccessHandler {
|
|
9
9
|
/**
|
|
10
|
-
* Constructor
|
|
11
|
-
* @param config
|
|
12
|
-
* @param privateKey
|
|
10
|
+
* Constructor.
|
|
11
|
+
* @param {NodeSDKConfig} config Configuration object.
|
|
12
|
+
* @param {string} privateKey Private key of the wallet that executes the conditional orders.
|
|
13
13
|
*/
|
|
14
14
|
public constructor(config: NodeSDKConfig, privateKey: string) {
|
|
15
15
|
super(config, privateKey);
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Executes an order by symbol and ID. This action interacts with the blockchain and incurs in gas costs.
|
|
20
|
+
* @param {string} symbol Symbol of the form ETH-USD-MATIC.
|
|
21
|
+
* @param {string} orderId ID of the order to be executed.
|
|
22
|
+
* @param {string=} referrerAddr Address of the wallet to be credited for executing the order,
|
|
23
|
+
* if different from the one submitting this transaction.
|
|
24
|
+
* @returns Transaction object.
|
|
25
|
+
*/
|
|
26
|
+
public async executeOrder(
|
|
27
|
+
symbol: string,
|
|
28
|
+
orderId: string,
|
|
29
|
+
referrerAddr?: string
|
|
30
|
+
): Promise<ethers.providers.TransactionResponse> {
|
|
31
|
+
if (this.proxyContract == null || this.signer == null) {
|
|
32
|
+
throw Error("no proxy contract or wallet initialized. Use createProxyInstance().");
|
|
33
|
+
}
|
|
34
|
+
const orderBookSC = this.getOrderBookContract(symbol);
|
|
35
|
+
if (typeof referrerAddr == "undefined") {
|
|
36
|
+
referrerAddr = this.traderAddr;
|
|
37
|
+
}
|
|
38
|
+
return await orderBookSC.executeLimitOrderByDigest(orderId, referrerAddr);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* All the orders in the order book for a given symbol that are currently open.
|
|
43
|
+
* @param {string} symbol Symbol of the form ETH-USD-MATIC.
|
|
44
|
+
* @returns Array with all open orders and their IDs.
|
|
45
|
+
*/
|
|
46
|
+
public async getAllOpenOrders(symbol: string): Promise<[Order[], string[]]> {
|
|
47
|
+
let totalOrders = await this.numberOfOpenOrders(symbol);
|
|
48
|
+
return await this.pollLimitOrders(symbol, totalOrders);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Total number of limit orders for this symbol, excluding those that have been cancelled/removed.
|
|
53
|
+
* @param {string} symbol Symbol of the form ETH-USD-MATIC.
|
|
54
|
+
* @returns {number} Number of open orders.
|
|
55
|
+
*/
|
|
56
|
+
public async numberOfOpenOrders(symbol: string): Promise<number> {
|
|
57
|
+
if (this.proxyContract == null) {
|
|
58
|
+
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
59
|
+
}
|
|
60
|
+
const orderBookSC = this.getOrderBookContract(symbol);
|
|
61
|
+
return await orderBookSC.numberOfOrderBookDigests();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get a list of active conditional orders in the order book.
|
|
66
|
+
* This a read-only action and does not incur in gas costs.
|
|
67
|
+
* @param {string} symbol Symbol of the form ETH-USD-MATIC.
|
|
68
|
+
* @param {number} numElements Maximum number of orders to poll.
|
|
69
|
+
* @param {string=} startAfter Optional order ID from where to start polling. Defaults to the first order.
|
|
70
|
+
* @returns Array of orders and corresponding order IDs
|
|
71
|
+
*/
|
|
72
|
+
public async pollLimitOrders(symbol: string, numElements: number, startAfter?: string): Promise<[Order[], string[]]> {
|
|
73
|
+
if (this.proxyContract == null) {
|
|
74
|
+
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
75
|
+
}
|
|
76
|
+
if (typeof startAfter == "undefined") {
|
|
77
|
+
startAfter = ethers.constants.HashZero;
|
|
78
|
+
}
|
|
79
|
+
const orderBookSC = this.getOrderBookContract(symbol);
|
|
80
|
+
let [orders, orderIds] = await orderBookSC.pollLimitOrders(startAfter, numElements);
|
|
81
|
+
let userFriendlyOrders: Order[] = new Array<Order>();
|
|
82
|
+
let k = 0;
|
|
83
|
+
while (k < orders.length && orders[k].traderAddr != ZERO_ADDRESS) {
|
|
84
|
+
userFriendlyOrders.push(WriteAccessHandler.fromSmartContractOrder(orders[k], this.symbolToPerpStaticInfo));
|
|
85
|
+
k++;
|
|
86
|
+
}
|
|
87
|
+
return [userFriendlyOrders, orderIds];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public async isTradeable(order: Order) {
|
|
91
|
+
if (this.proxyContract == null) {
|
|
92
|
+
throw Error("no proxy contract initialized. Use createProxyInstance().");
|
|
93
|
+
}
|
|
94
|
+
if (order.limitPrice == undefined) {
|
|
95
|
+
throw Error("order does not have a limit price");
|
|
96
|
+
}
|
|
97
|
+
// check expiration date
|
|
98
|
+
if (order.deadline != undefined && order.deadline < Date.now()) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
// check limit price
|
|
102
|
+
let orderPrice = await WriteAccessHandler._queryPerpetualPrice(
|
|
103
|
+
order.symbol,
|
|
104
|
+
order.quantity,
|
|
105
|
+
this.symbolToPerpStaticInfo,
|
|
106
|
+
this.proxyContract
|
|
107
|
+
);
|
|
108
|
+
if (
|
|
109
|
+
(order.side == BUY_SIDE && orderPrice > order.limitPrice) ||
|
|
110
|
+
(order.side == SELL_SIDE && orderPrice < order.limitPrice)
|
|
111
|
+
) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
// do we need to check trigger/stop?
|
|
115
|
+
if (order.stopPrice == undefined) {
|
|
116
|
+
// nothing to check, order is tradeable
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
// we need the mark price to check
|
|
120
|
+
let markPrice = await WriteAccessHandler._queryPerpetualMarkPrice(
|
|
121
|
+
order.symbol,
|
|
122
|
+
this.symbolToPerpStaticInfo,
|
|
123
|
+
this.proxyContract
|
|
124
|
+
);
|
|
125
|
+
if (
|
|
126
|
+
(order.side == BUY_SIDE && markPrice < order.stopPrice) ||
|
|
127
|
+
(order.side == SELL_SIDE && markPrice > order.stopPrice)
|
|
128
|
+
) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
// all checks passed -> order is tradeable
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* TODO:
|
|
137
|
+
* - [x] executeLimitOrderByDigest
|
|
138
|
+
* - [x] pollLimitOrders
|
|
139
|
+
* - [x] isTradeable
|
|
140
|
+
* - [ ] get all limit orders
|
|
141
|
+
* - [ ] tests
|
|
142
|
+
*/
|
|
17
143
|
}
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
DEFAULT_CONFIG_MAINNET,
|
|
29
29
|
DEFAULT_CONFIG_TESTNET_NAME,
|
|
30
30
|
DEFAULT_CONFIG_TESTNET,
|
|
31
|
+
ONE_64x64,
|
|
31
32
|
} from "./nodeSDKTypes";
|
|
32
33
|
import { fromBytes4HexString, to4Chars, combineFlags, containsFlag } from "./utils";
|
|
33
34
|
import {
|
|
@@ -282,6 +283,27 @@ export default class PerpetualDataHandler {
|
|
|
282
283
|
return mgn;
|
|
283
284
|
}
|
|
284
285
|
|
|
286
|
+
protected static async _queryPerpetualPrice(
|
|
287
|
+
symbol: string,
|
|
288
|
+
tradeAmount: number,
|
|
289
|
+
symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>,
|
|
290
|
+
_proxyContract: ethers.Contract
|
|
291
|
+
): Promise<number> {
|
|
292
|
+
let perpId = PerpetualDataHandler.symbolToPerpetualId(symbol, symbolToPerpStaticInfo);
|
|
293
|
+
let fPrice = await _proxyContract.queryPerpetualPrice(perpId, floatToABK64x64(tradeAmount));
|
|
294
|
+
return ABK64x64ToFloat(fPrice);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
protected static async _queryPerpetualMarkPrice(
|
|
298
|
+
symbol: string,
|
|
299
|
+
symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>,
|
|
300
|
+
_proxyContract: ethers.Contract
|
|
301
|
+
): Promise<number> {
|
|
302
|
+
let perpId = PerpetualDataHandler.symbolToPerpetualId(symbol, symbolToPerpStaticInfo);
|
|
303
|
+
let ammState = await _proxyContract.getAMMState(perpId);
|
|
304
|
+
return ABK64x64ToFloat(ammState[6].mul(ONE_64x64.add(ammState[8])));
|
|
305
|
+
}
|
|
306
|
+
|
|
285
307
|
/**
|
|
286
308
|
* Liquidation price
|
|
287
309
|
* @param cleanSymbol symbol after calling symbolToBytes4Symbol
|
|
@@ -368,12 +390,13 @@ export default class PerpetualDataHandler {
|
|
|
368
390
|
return symbols[0] + "-" + symbols[1] + "-" + symbols[2];
|
|
369
391
|
}
|
|
370
392
|
|
|
371
|
-
private static _getByValue(map: any, searchValue: any) {
|
|
393
|
+
private static _getByValue(map: any, searchValue: any, valueField: any) {
|
|
372
394
|
for (let [key, value] of map.entries()) {
|
|
373
|
-
if (value === searchValue) {
|
|
395
|
+
if (value[valueField] === searchValue) {
|
|
374
396
|
return key;
|
|
375
397
|
}
|
|
376
398
|
}
|
|
399
|
+
return undefined;
|
|
377
400
|
}
|
|
378
401
|
|
|
379
402
|
protected static fromSmartContractOrder(
|
|
@@ -381,7 +404,10 @@ export default class PerpetualDataHandler {
|
|
|
381
404
|
symbolToPerpInfoMap: Map<string, PerpetualStaticInfo>
|
|
382
405
|
): Order {
|
|
383
406
|
// find symbol of perpetual id
|
|
384
|
-
let symbol = PerpetualDataHandler._getByValue(symbolToPerpInfoMap, order.iPerpetualId);
|
|
407
|
+
let symbol = PerpetualDataHandler._getByValue(symbolToPerpInfoMap, order.iPerpetualId, "id");
|
|
408
|
+
if (symbol == undefined) {
|
|
409
|
+
throw Error(`Perpetual id ${order.iPerpetualId} not found. Check with marketData.exchangeInfo().`);
|
|
410
|
+
}
|
|
385
411
|
let side = order.fAmount > 0 ? BUY_SIDE : SELL_SIDE;
|
|
386
412
|
let limitPrice, stopPrice;
|
|
387
413
|
let fLimitPrice: BigNumber | undefined = BigNumber.from(order.fLimitPrice);
|
|
@@ -397,16 +423,16 @@ export default class PerpetualDataHandler {
|
|
|
397
423
|
stopPrice = ABK64x64ToFloat(fStopPrice);
|
|
398
424
|
}
|
|
399
425
|
let userOrder: Order = {
|
|
400
|
-
symbol: symbol
|
|
426
|
+
symbol: symbol!,
|
|
401
427
|
side: side,
|
|
402
428
|
type: PerpetualDataHandler._flagToOrderType(order),
|
|
403
429
|
quantity: Math.abs(ABK64x64ToFloat(BigNumber.from(order.fAmount))),
|
|
404
430
|
reduceOnly: containsFlag(BigNumber.from(order.flags), MASK_CLOSE_ONLY),
|
|
405
431
|
limitPrice: limitPrice,
|
|
406
432
|
keepPositionLvg: containsFlag(BigNumber.from(order.flags), MASK_KEEP_POS_LEVERAGE),
|
|
407
|
-
brokerFeeTbps: Number(order.brokerFeeTbps),
|
|
408
|
-
brokerAddr: order.brokerAddr,
|
|
409
|
-
brokerSignature: order.brokerSignature,
|
|
433
|
+
brokerFeeTbps: order.brokerFeeTbps == 0 ? undefined : Number(order.brokerFeeTbps),
|
|
434
|
+
brokerAddr: order.brokerAddr == ZERO_ADDRESS ? undefined : order.brokerAddr,
|
|
435
|
+
brokerSignature: order.brokerSignature == "0x" ? undefined : order.brokerSignature,
|
|
410
436
|
stopPrice: stopPrice,
|
|
411
437
|
leverage: ABK64x64ToFloat(BigNumber.from(order.fLeverage)),
|
|
412
438
|
deadline: Number(order.iDeadline),
|
package/src/utils.ts
CHANGED
|
@@ -1,32 +1,33 @@
|
|
|
1
1
|
import { BigNumber } from "ethers";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* @module utils
|
|
4
|
+
*/
|
|
4
5
|
|
|
5
6
|
function _isVocal(char: string) {
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
char = char.toLowerCase();
|
|
8
|
+
return char == "a" || char == "e" || char == "i" || char == "o" || char == "u";
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
*
|
|
12
|
-
* @param
|
|
13
|
-
* @returns string with 4 characters (or characters + null chars)
|
|
13
|
+
* @param {string} s String to shorten/extend to 4 characters
|
|
14
|
+
* @returns {string} String with 4 characters (or characters + null chars)
|
|
14
15
|
*/
|
|
15
16
|
export function to4Chars(s: string) {
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
while (s.length < 4) {
|
|
18
|
+
s = s + "\0";
|
|
19
|
+
}
|
|
20
|
+
let k = s.length - 1;
|
|
21
|
+
while (s.length > 4 && k >= 0) {
|
|
22
|
+
// chop off vocals from the end of string
|
|
23
|
+
// e.g. MATIC -> MATC
|
|
24
|
+
if (_isVocal(s.charAt(k))) {
|
|
25
|
+
s = s.substring(0, k) + s.substring(k + 1, s.length);
|
|
18
26
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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;
|
|
27
|
+
k--;
|
|
28
|
+
}
|
|
29
|
+
s = s.substring(0, 4);
|
|
30
|
+
return s;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
/**
|
|
@@ -35,49 +36,49 @@ export function to4Chars(s: string) {
|
|
|
35
36
|
* 4 characters.
|
|
36
37
|
* Resulting buffer can be used with smart contract to
|
|
37
38
|
* identify tokens (BTC, USDC, MATIC etc.)
|
|
38
|
-
* @param s
|
|
39
|
-
* @returns
|
|
39
|
+
* @param {string} s String to encode into bytes4
|
|
40
|
+
* @returns {Buffer} 4-character bytes4.
|
|
40
41
|
*/
|
|
41
42
|
export function toBytes4(s: string): Buffer {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
s = to4Chars(s);
|
|
44
|
+
let valBuff: Buffer = Buffer.from(s, "ascii");
|
|
45
|
+
return valBuff;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
/**
|
|
48
49
|
* Decodes a buffer encoded with toBytes4 into
|
|
49
50
|
* a string. The string is the result of to4Chars of the
|
|
50
51
|
* originally encoded string stripped from null-chars
|
|
51
|
-
* @param b
|
|
52
|
-
* @returns string decoded into to4Chars-type string without null characters
|
|
52
|
+
* @param {Buffer} b Correctly encoded bytes4 buffer using toBytes4
|
|
53
|
+
* @returns {string} String decoded into to4Chars-type string without null characters
|
|
53
54
|
*/
|
|
54
55
|
export function fromBytes4(b: Buffer): string {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
let val: string = b.toString("ascii");
|
|
57
|
+
val = val.replace(/\0/g, "");
|
|
58
|
+
return val;
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
/**
|
|
61
62
|
* Decodes the bytes4 encoded string received from the
|
|
62
63
|
* 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,
|
|
64
|
+
* @param {string} s string representing a hex-number ("0x...")
|
|
65
|
+
* @returns {string} x of to4Chars(x) stripped from null-chars,
|
|
65
66
|
* where x was originally encoded and
|
|
66
67
|
* returned by the smart contract as bytes4
|
|
67
68
|
*/
|
|
68
69
|
export function fromBytes4HexString(s: string): string {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
let res = "";
|
|
71
|
+
for (let k = 2; k < s.length; k = k + 2) {
|
|
72
|
+
res = res + String.fromCharCode(parseInt(s.substring(k, k + 2), 16));
|
|
73
|
+
}
|
|
74
|
+
res = res.replace(/\0/g, "");
|
|
75
|
+
return res;
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
export function combineFlags(f1: BigNumber, f2: BigNumber): BigNumber {
|
|
78
|
-
|
|
79
|
+
return BigNumber.from(parseInt(f1.toString()) | parseInt(f2.toString()));
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
export function containsFlag(f1: BigNumber, f2: BigNumber): boolean {
|
|
82
|
-
|
|
83
|
+
return (parseInt(f1.toString()) & parseInt(f2.toString())) > 0;
|
|
83
84
|
}
|