@d8x/perpetuals-sdk
Version:
Node TypeScript SDK for D8X Perpetual Futures
614 lines • 29.3 kB
JavaScript
import { JsonRpcProvider, ZeroHash, } from "ethers";
import { BUY_SIDE, MULTICALL_ADDRESS, OrderStatus, SELL_SIDE, ZERO_ADDRESS } from "./constants";
import { IPyth__factory, LimitOrderBook__factory, Multicall3__factory } from "./contracts";
import { ABK64x64ToFloat, floatToABK64x64 } from "./d8XMath";
import PerpetualDataHandler from "./perpetualDataHandler";
import WriteAccessHandler from "./writeAccessHandler";
/**
* Functions to execute existing conditional orders from the limit order book. This class
* requires a private key and executes smart-contract interactions that require
* gas-payments.
* @extends WriteAccessHandler
*/
export default class OrderExecutorTool extends WriteAccessHandler {
/**
* Constructor.
* @param {NodeSDKConfig} config Configuration object, see PerpetualDataHandler.readSDKConfig.
* @example
* import { OrderExecutorTool, PerpetualDataHandler } from '@d8x/perpetuals-sdk';
* async function main() {
* console.log(OrderExecutorTool);
* // load configuration for Polygon zkEVM (testnet)
* const config = PerpetualDataHandler.readSDKConfig("cardona");
* // OrderExecutorTool (authentication required, PK is an environment variable with a private key)
* const pk: string = <string>process.env.PK;
* let orderTool = new OrderExecutorTool(config, pk);
* // Create a proxy instance to access the blockchain
* await orderTool.createProxyInstance();
* }
* main();
*
* @param {string | Signer} signer Private key or ethers Signer of the account
*/
constructor(config, signer) {
super(config, signer);
// override parent's gas limit with a lower number
this.gasLimit = 4000000;
}
/**
* Executes an order by symbol and ID. This action interacts with the blockchain and incurs gas costs.
* @param {string} symbol Symbol of the form ETH-USD-MATIC.
* @param {string} orderId ID of the order to be executed.
* @param {string} executorAddr optional address of the wallet to be credited for executing the order, if different from the one submitting this transaction.
* @param {number} nonce optional nonce
* @param {PriceFeedSubmission=} submission optional signed prices obtained via PriceFeeds::fetchLatestFeedPriceInfoForPerpetual
* @example
* import { OrderExecutorTool, PerpetualDataHandler, Order } from "@d8x/perpetuals-sdk";
* async function main() {
* console.log(OrderExecutorTool);
* // Setup (authentication required, PK is an environment variable with a private key)
* const config = PerpetualDataHandler.readSDKConfig("cardona");
* const pk: string = <string>process.env.PK;
* const symbol = "ETH-USD-MATIC";
* let orderTool = new OrderExecutorTool(config, pk);
* await orderTool.createProxyInstance();
* // get some open orders
* const maxOrdersToGet = 5;
* let [orders, ids]: [Order[], string[]] = await orderTool.pollLimitOrders(symbol, maxOrdersToGet);
* console.log(`Got ${ids.length} orders`);
* for (let k = 0; k < ids.length; k++) {
* // check whether order meets conditions
* let doExecute = await orderTool.isTradeable(orders[k]);
* if (doExecute) {
* // execute
* let tx = await orderTool.executeOrder(symbol, ids[k]);
* console.log(`Sent order id ${ids[k]} for execution, tx hash = ${tx.hash}`);
* }
* }
* }
* main();
* @returns Transaction object.
*/
async executeOrder(symbol, orderId, executorAddr, submission, overrides) {
return this.executeOrders(symbol, [orderId], executorAddr, submission, overrides);
}
/**
* Executes a list of orders of the symbol. This action interacts with the blockchain and incurs gas costs.
* @param {string} symbol Symbol of the form ETH-USD-MATIC.
* @param {string[]} orderIds IDs of the orders to be executed.
* @param {string} executorAddr optional address of the wallet to be credited for executing the order, if different from the one submitting this transaction.
* @param {number} nonce optional nonce
* @param {PriceFeedSubmission=} submission optional signed prices obtained via PriceFeeds::fetchLatestFeedPriceInfoForPerpetual
* @example
* import { OrderExecutorTool, PerpetualDataHandler, Order } from "@d8x/perpetuals-sdk";
* async function main() {
* console.log(OrderExecutorTool);
* // Setup (authentication required, PK is an environment variable with a private key)
* const config = PerpetualDataHandler.readSDKConfig("cardona");
* const pk: string = <string>process.env.PK;
* const symbol = "ETH-USD-MATIC";
* let orderTool = new OrderExecutorTool(config, pk);
* await orderTool.createProxyInstance();
* // get some open orders
* const maxOrdersToGet = 5;
* let [orders, ids]: [Order[], string[]] = await orderTool.pollLimitOrders(symbol, maxOrdersToGet);
* console.log(`Got ${ids.length} orders`);
* // execute
* let tx = await orderTool.executeOrders(symbol, ids);
* console.log(`Sent order ids ${ids.join(", ")} for execution, tx hash = ${tx.hash}`);
* }
* main();
* @returns Transaction object.
*/
async executeOrders(symbol, orderIds, executorAddr, submission, overrides) {
if (this.proxyContract == null || this.signer == null) {
throw Error("no proxy contract or wallet initialized. Use createProxyInstance().");
}
let rpcURL;
let splitTx;
let maxGasLimit;
if (overrides) {
({ rpcURL, splitTx, maxGasLimit, ...overrides } = overrides);
}
const provider = new JsonRpcProvider(rpcURL ?? this.nodeURL);
if (typeof executorAddr == "undefined") {
executorAddr = this.traderAddr;
}
if (submission == undefined) {
submission = await this.priceFeedGetter.fetchLatestFeedPriceInfoForPerpetual(symbol);
}
const iOB = LimitOrderBook__factory.createInterface();
// update first
let nonceInc = 0;
let txData;
let value = overrides?.value;
if (splitTx) {
try {
const pyth = IPyth__factory.connect(this.pythAddr, provider).connect(this.signer);
const priceIds = this.symbolToPerpStaticInfo.get(symbol).priceIds;
const pythTx = await pyth.updatePriceFeedsIfNecessary(submission.priceFeedVaas, priceIds, submission.timestamps, {
value: this.PRICE_UPDATE_FEE_GWEI * submission.timestamps.length,
gasLimit: overrides?.gasLimit ?? this.gasLimit,
nonce: overrides?.nonce,
});
nonceInc += 1;
// await pythTx.wait();
}
catch (e) {
console.log(e);
}
txData = iOB.encodeFunctionData("executeOrders", [orderIds, executorAddr, [], []]);
}
else {
txData = iOB.encodeFunctionData("executeOrders", [
orderIds,
executorAddr,
submission.priceFeedVaas,
submission.timestamps,
]);
value = this.PRICE_UPDATE_FEE_GWEI * submission.timestamps.length;
}
if (overrides?.nonce != undefined) {
overrides.nonce = overrides.nonce + nonceInc;
}
const unsignedTx = {
to: this.getOrderBookContract(symbol).target,
from: this.traderAddr,
nonce: overrides?.nonce,
data: txData,
value: value,
gasLimit: overrides?.gasLimit,
chainId: this.chainId,
// fee data, is given
...this._getFeeData(overrides),
};
// no gas limit was specified, explicitly estimate
if (!overrides?.gasLimit) {
let gasLimit = await this.signer
.estimateGas(unsignedTx)
.then((gas) => (gas * 1500n) / 1000n)
.catch((_e) => undefined);
if (!gasLimit) {
// gas estimate failed - txn would probably revert, double check (and possibly re-throw):
overrides = { gasLimit: maxGasLimit ?? this.gasLimit, value: unsignedTx.value, ...overrides };
await this.getOrderBookContract(symbol).executeOrders.staticCall(orderIds, executorAddr, submission.priceFeedVaas, submission.timestamps, overrides);
gasLimit = BigInt(maxGasLimit ?? this.gasLimit);
}
unsignedTx.gasLimit = gasLimit;
}
return await this.signer.connect(provider).sendTransaction(unsignedTx);
}
/**
* Get order from the digest (=id)
* @param symbol symbol of order book, e.g. ETH-USD-MATIC
* @param digest digest of the order (=order ID)
* @example
* import { OrderExecutorTool, PerpetualDataHandler } from '@d8x/perpetuals-sdk';
* async function main() {
* console.log(OrderExecutorTool);
* // setup (authentication required, PK is an environment variable with a private key)
* const config = PerpetualDataHandler.readSDKConfig("cardona");
* const pk: string = <string>process.env.PK;
* let orderTool = new OrderExecutorTool(config, pk);
* await orderTool.createProxyInstance();
* // get order by ID
* let myorder = await orderTool.getOrderById("MATIC-USD-MATIC",
* "0x0091a1d878491479afd09448966c1403e9d8753122e25260d3b2b9688d946eae");
* console.log(myorder);
* }
* main();
*
* @returns order or undefined
*/
async getOrderById(symbol, id, overrides) {
let ob = this.getOrderBookContract(symbol);
// multicall
let rpcURL;
if (overrides) {
({ rpcURL, ...overrides } = overrides);
}
const provider = new JsonRpcProvider(rpcURL ?? this.nodeURL, this.network, { staticNetwork: true });
const multicall = Multicall3__factory.connect(this.config.multicall ?? MULTICALL_ADDRESS, provider);
const calls = [
// 0: orderOfDigest
{
target: ob.target,
allowFailure: false,
callData: ob.interface.encodeFunctionData("orderOfDigest", [id]),
},
// 1: orderDependency
{
target: ob.target,
allowFailure: false,
callData: ob.interface.encodeFunctionData("orderDependency", [id]),
},
];
const encodedResults = await multicall.aggregate3.staticCall(calls, overrides || {});
if (encodedResults.some(({ success }) => !success)) {
return undefined;
}
const smartContractOrder = ob.interface.decodeFunctionResult("orderOfDigest", encodedResults[0].returnData);
const orderDependency = ob.interface.decodeFunctionResult("orderDependency", encodedResults[1].returnData);
if (smartContractOrder.traderAddr == ZERO_ADDRESS) {
return undefined;
}
const order = OrderExecutorTool.fromSmartContractOrder(smartContractOrder, this.symbolToPerpStaticInfo);
order.parentChildOrderIds = [orderDependency.parentChildDigest1, orderDependency.parentChildDigest2];
return order;
}
/**
* Check if a conditional order can be executed
* @param order order structure
* @param indexPrices pair of index prices S2 and S3. S3 set to zero if not required. If undefined
* the function will fetch the latest prices from the REST API
* @example
* import { OrderExecutorTool, PerpetualDataHandler } from '@d8x/perpetuals-sdk';
* async function main() {
* console.log(OrderExecutorTool);
* // setup (authentication required, PK is an environment variable with a private key)
* const config = PerpetualDataHandler.readSDKConfig("cardona");
* const pk: string = <string>process.env.PK;
* let orderTool = new OrderExecutorTool(config, pk);
* await orderTool.createProxyInstance();
* // check if tradeable
* let openOrders = await orderTool.getAllOpenOrders("MATIC-USD-MATIC");
* let check = await orderTool.isTradeable(openOrders[0][0]);
* console.log(check);
* }
* main();
* @returns true if order can be executed for the current state of the perpetuals
*/
async isTradeable(order, orderId, blockTimestamp, indexPrices, overrides) {
if (this.proxyContract == null || this.multicall == null) {
throw Error("no proxy contract initialized. Use createProxyInstance().");
}
const isPred = this.isPredictionMarket(order.symbol);
if (indexPrices == undefined) {
indexPrices = await this.priceFeedGetter.fetchPricesForPerpetual(order.symbol);
}
let rpcURL;
if (overrides) {
({ rpcURL, ...overrides } = overrides);
}
const provider = new JsonRpcProvider(rpcURL ?? this.nodeURL, this.network, { staticNetwork: true });
const fS2S3 = [indexPrices.s2, indexPrices.s3].map((x) => floatToABK64x64(x == undefined || Number.isNaN(x) ? 0 : x));
const perpId = this.getPerpIdFromSymbol(order.symbol);
const fAmount = floatToABK64x64(order.quantity * (order.side == BUY_SIDE ? 1 : -1));
const orderBook = this.getOrderBookContract(order.symbol).connect(provider);
const proxyCalls = [
// 0: trade amount price
{
target: this.proxyContract.target,
allowFailure: true,
callData: this.proxyContract.interface.encodeFunctionData("queryPerpetualPrice", [
perpId,
fAmount,
fS2S3,
indexPrices.conf * 10n,
indexPrices.predMktCLOBParams,
]),
},
// 1: amm state to get the mark price
{
target: this.proxyContract.target,
allowFailure: true,
callData: this.proxyContract.interface.encodeFunctionData("getAMMState", [perpId, fS2S3]),
},
// 2: order status to see if it's still open
{
target: orderBook.target,
allowFailure: true,
callData: orderBook.interface.encodeFunctionData("getOrderStatus", [orderId]),
},
// 3: block timestamp
{
target: this.multicall.target,
allowFailure: false,
callData: this.multicall.interface.encodeFunctionData("getCurrentBlockTimestamp"),
},
];
const hasParent = order.parentChildOrderIds != undefined &&
order.parentChildOrderIds[0] == ZeroHash &&
order.parentChildOrderIds[1] != ZeroHash;
if (hasParent) {
// 4: order has a parent, one more call needed:
proxyCalls.push({
target: orderBook.target,
allowFailure: true,
callData: orderBook.interface.encodeFunctionData("getOrderStatus", [order.parentChildOrderIds[1]]),
});
}
// multicall
const multicall = Multicall3__factory.connect(this.config.multicall ?? MULTICALL_ADDRESS, provider);
const encodedResults = await multicall.aggregate3.staticCall(proxyCalls, overrides || {});
// order status
let iOrderStatus;
if (encodedResults[2].success) {
iOrderStatus = orderBook.interface.decodeFunctionResult("getOrderStatus", encodedResults[2].returnData)[0];
}
else {
iOrderStatus = await orderBook.getOrderStatus(orderId);
}
if (iOrderStatus != OrderStatus.OPEN) {
// no need to continue - order is no longer open
return false;
}
// parent status
if (hasParent) {
let iParentOrderStatus;
if (encodedResults[4].success) {
iParentOrderStatus = orderBook.interface.decodeFunctionResult("getOrderStatus", encodedResults[4].returnData)[0];
}
else {
iParentOrderStatus = await orderBook.getOrderStatus(order.parentChildOrderIds[1]);
}
if (iParentOrderStatus != OrderStatus.EXECUTED && iParentOrderStatus != OrderStatus.CANCELED) {
// no need to continue - parent order is still pending
return false;
}
}
// mark price
let ammState;
if (encodedResults[1].success) {
ammState = this.proxyContract.interface.decodeFunctionResult("getAMMState", encodedResults[1].returnData)[0];
}
else {
ammState = await this.proxyContract.getAMMState(perpId, fS2S3);
}
let markPrice;
const idx_markPremRate = 8;
if (isPred) {
markPrice = indexPrices.ema + ABK64x64ToFloat(ammState[idx_markPremRate]);
}
else {
markPrice = indexPrices.s2 * (1 + ABK64x64ToFloat(ammState[idx_markPremRate]));
}
// price
let fOrderPrice;
if (encodedResults[0].success) {
fOrderPrice = this.proxyContract.interface.decodeFunctionResult("queryPerpetualPrice", encodedResults[0].returnData)[0];
}
else {
fOrderPrice = await this.proxyContract.queryPerpetualPrice(perpId, fAmount, fS2S3, indexPrices.conf * 10n, indexPrices.predMktCLOBParams);
}
const orderPrice = ABK64x64ToFloat(fOrderPrice);
// block timestamp
const ts = this.multicall.interface.decodeFunctionResult("getCurrentBlockTimestamp", encodedResults[3].returnData)[0];
blockTimestamp = Math.max(Number(ts) + 1, blockTimestamp ?? 0);
return this._isTradeable(order, orderPrice, markPrice, blockTimestamp, this.symbolToPerpStaticInfo);
}
/**
* Check for a batch of orders on the same perpetual whether they can be traded
* @param orders orders belonging to 1 perpetual
* @param indexPrice S2,S3-index prices for the given perpetual. Will fetch prices from REST API
* if not defined.
* @returns array of tradeable boolean
* @example
* import { OrderExecutorTool, PerpetualDataHandler } from '@d8x/perpetuals-sdk';
* async function main() {
* console.log(OrderExecutorTool);
* // setup (authentication required, PK is an environment variable with a private key)
* const config = PerpetualDataHandler.readSDKConfig("cardona");
* const pk: string = <string>process.env.PK;
* let orderTool = new OrderExecutorTool(config, pk);
* await orderTool.createProxyInstance();
* // check if tradeable
* let openOrders = await orderTool.getAllOpenOrders("MATIC-USD-MATIC");
* let check = await orderTool.isTradeableBatch(
* [openOrders[0][0], openOrders[0][1]],
* [openOrders[1][0], openOrders[1][1]]
* );
* console.log(check);
* }
* main();
*/
async isTradeableBatch(orders, orderIds, blockTimestamp, indexPrices, overrides) {
const MAX_ORDERS_CHECKED = 10;
let totalOrders = orders.length;
let checks = await this._isTradeableBatch(orders.slice(0, MAX_ORDERS_CHECKED), orderIds.slice(0, MAX_ORDERS_CHECKED), blockTimestamp, indexPrices, overrides ?? {});
while (checks.length < totalOrders) {
let res = await this._isTradeableBatch(orders.slice(checks.length, checks.length + MAX_ORDERS_CHECKED), orderIds.slice(checks.length, checks.length + MAX_ORDERS_CHECKED), blockTimestamp, indexPrices, overrides ?? {});
checks = checks.concat(res);
}
return checks;
}
/**
* Performs on-chain checks via multicall
* @param orders orders to check
* @param orderIds order ids
* @ignore
*/
async _isTradeableBatch(orders, orderIds, blockTimestamp, indexPrices, overrides) {
if (orders.length == 0) {
return [];
}
if (this.proxyContract == null || this.multicall == null) {
throw Error("no proxy contract initialized. Use createProxyInstance().");
}
if (orders.filter((o) => o.symbol == orders[0].symbol).length < orders.length) {
throw Error("all orders in a batch must have the same symbol");
}
if (indexPrices == undefined) {
indexPrices = await this.priceFeedGetter.fetchPricesForPerpetual(orders[0].symbol);
}
if (indexPrices.s2MktClosed || indexPrices.s3MktClosed) {
// market closed
return orders.map(() => false);
}
let rpcURL;
if (overrides) {
({ rpcURL, ...overrides } = overrides);
}
const provider = new JsonRpcProvider(rpcURL ?? this.nodeURL, this.network, { staticNetwork: true });
const isPred = this.isPredictionMarket(orders[0].symbol);
const fS2S3 = [indexPrices.s2, indexPrices.s3].map((x) => floatToABK64x64(x == undefined || Number.isNaN(x) ? 0 : x));
const perpId = this.getPerpIdFromSymbol(orders[0].symbol);
const fAmounts = orders.map((order) => floatToABK64x64(order.quantity * (order.side == BUY_SIDE ? 1 : -1)));
const orderBook = this.getOrderBookContract(orders[0].symbol).connect(provider);
const multicall = Multicall3__factory.connect(this.config.multicall ?? MULTICALL_ADDRESS, provider);
// mark price and timestamp
let proxyCalls = [
// 0: amm state to get the mark price
{
target: this.proxyContract.target,
allowFailure: false,
callData: this.proxyContract.interface.encodeFunctionData("getAMMState", [perpId, fS2S3]),
},
// 1: block timestamp
{
target: this.multicall.target,
allowFailure: false,
callData: this.multicall.interface.encodeFunctionData("getCurrentBlockTimestamp"),
},
];
// status calls
const statusCalls = orderIds.map((orderId) => ({
target: orderBook.target,
allowFailure: false,
callData: orderBook.interface.encodeFunctionData("getOrderStatus", [orderId]),
}));
proxyCalls = proxyCalls.concat(statusCalls);
// price calls
const priceCalls = fAmounts.map((fAmount) => ({
target: this.proxyContract.target,
allowFailure: false,
callData: this.proxyContract.interface.encodeFunctionData("queryPerpetualPrice", [
perpId,
fAmount,
fS2S3,
indexPrices.conf * 10n,
indexPrices.predMktCLOBParams,
]),
}));
proxyCalls = proxyCalls.concat(priceCalls);
// possibly also get parent orders' status
const parentStatusCalls = orders
.filter((order) => order.parentChildOrderIds != undefined &&
order.parentChildOrderIds[0] == ZeroHash &&
order.parentChildOrderIds[1] != ZeroHash)
.map((order) => {
return {
target: orderBook.target,
allowFailure: false,
callData: orderBook.interface.encodeFunctionData("getOrderStatus", [order.parentChildOrderIds[1]]),
};
});
proxyCalls = proxyCalls.concat(parentStatusCalls);
// --- multicall ---
const encodedResults = await multicall.aggregate3.staticCall(proxyCalls, overrides || {});
// mark price
const ammState = this.proxyContract.interface.decodeFunctionResult("getAMMState", encodedResults[0].returnData)[0];
let markprice;
if (isPred) {
markprice = indexPrices.ema + ABK64x64ToFloat(ammState[8]);
}
else {
markprice = indexPrices.s2 * (1 + ABK64x64ToFloat(ammState[8]));
}
// block timestamp
const ts = this.multicall.interface.decodeFunctionResult("getCurrentBlockTimestamp", encodedResults[1].returnData)[0];
blockTimestamp = Math.max(Number(ts), blockTimestamp ?? 0);
// order status
const isOrderOpen = encodedResults.slice(2, 2 + orders.length).map((encodedResult) => {
const iOrderStatus = orderBook.interface.decodeFunctionResult("getOrderStatus", encodedResult.returnData)[0];
return iOrderStatus == OrderStatus.OPEN;
});
// order prices
const orderPrices = encodedResults.slice(2 + orders.length, 2 + 2 * orders.length).map((encodedResult) => {
const orderPrice = ABK64x64ToFloat(this.proxyContract.interface.decodeFunctionResult("queryPerpetualPrice", encodedResult.returnData)[0]);
return orderPrice;
});
// check parent status
let idxInResults = 2 + 2 * orders.length;
let isParentReady = new Array(orders.length).fill(true);
for (let i = 0; i < orders.length; i++) {
const order = orders[i];
const hasParent = order.parentChildOrderIds != undefined &&
order.parentChildOrderIds[0] == ZeroHash &&
order.parentChildOrderIds[1] != ZeroHash;
if (hasParent) {
const iParentStatus = orderBook.interface.decodeFunctionResult("getOrderStatus", encodedResults[idxInResults].returnData)[0];
isParentReady[i] = iParentStatus == OrderStatus.EXECUTED || iParentStatus == OrderStatus.CANCELED;
idxInResults += 1;
}
}
// sync checks
return orders.map((o, idx) => {
if (!isOrderOpen[idx] || !isParentReady[idx]) {
return false;
}
return this._isTradeable(o, orderPrices[idx], markprice, blockTimestamp, this.symbolToPerpStaticInfo);
});
}
/**
* Can the order be executed?
* @param order order struct
* @param tradePrice "preview" price of this order
* @param markPrice current mark price
* @param atBlockTimestamp block timestamp when execution would take place
* @param symbolToPerpInfoMap metadata
* @returns true if trading conditions met, false otherwise
* @ignore
*/
_isTradeable(order, tradePrice, markPrice, atBlockTimestamp, symbolToPerpInfoMap) {
// check expiration date
if (order.deadline != undefined && order.deadline < Date.now() / 1000) {
// console.log("order expired");
return false;
}
// check execution timestamp
if (order.executionTimestamp > 0 && atBlockTimestamp < order.executionTimestamp) {
// console.log(`execution deferred by ${order.executionTimestamp - atBlockTimestamp} more seconds`);
return false;
}
if (order.submittedTimestamp != undefined && atBlockTimestamp <= order.submittedTimestamp) {
// console.log(`on hold for ${order.submittedTimestamp - atBlockTimestamp} more seconds`);
return false;
}
// check order size
const lotSize = PerpetualDataHandler._getLotSize(order.symbol, symbolToPerpInfoMap);
if (order.quantity < lotSize) {
// console.log(`order size too small: ${order.quantity} < ${lotSize}`);
return false;
}
// check limit price: fromSmartContractOrder will set it to undefined when not tradeable
if (order.limitPrice == undefined) {
// console.log("limit price undefined");
return false;
}
let limitPrice = order.limitPrice;
if ((order.side == BUY_SIDE && tradePrice > limitPrice) || (order.side == SELL_SIDE && tradePrice < limitPrice)) {
// console.log(`limit price not met: ${limitPrice} ${order.side} @ ${tradePrice}`);
return false;
}
// check stop price
if (order.stopPrice != undefined &&
((order.side == BUY_SIDE && markPrice < order.stopPrice) ||
(order.side == SELL_SIDE && markPrice > order.stopPrice))) {
// console.log("stop price not met");
return false;
}
// all checks passed -> order is tradeable
return true;
}
/**
* Wrapper of static method to use after mappings have been loaded into memory.
* @param scOrder Perpetual order as received in the proxy events.
* @returns A user-friendly order struct.
*/
smartContractOrderToOrder(scOrder) {
return PerpetualDataHandler.fromSmartContractOrder(scOrder, this.symbolToPerpStaticInfo);
}
/**
* Gets the current transaction count for the connected signer
* @param blockTag
* @returns The nonce for the next transaction
*/
async getTransactionCount(blockTag) {
if (this.signer == null) {
throw Error("no wallet initialized. Use createProxyInstance().");
}
return await this.signer.getNonce(blockTag);
}
}
//# sourceMappingURL=orderExecutorTool.js.map