@algodex/algodex-sdk
Version:
API calls for interacting with the Algorand blockchain
753 lines (641 loc) • 26.9 kB
JavaScript
/*
* Copyright (C) 2021-2022 Algodex VASP (BVI) Corp.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
const algosdk = require('algosdk');
const AlgodError = require('../../../error/AlgodError');
const logger = require('../../../logger');
const withExecuteAssetTxns = require('../../txns/sell/withExecuteAssetTxns');
const withExecuteAlgoTxns = require('../../txns/buy/withExecuteAlgoTxns');
const compile = require('../../compile/compile');
const BigN = require('js-big-decimal');
const LESS_THAN = -1;
const GREATER_THAN = 1;
/**
*
* @param {*} orderBookEscrowEntry
* @param {*} takerCombOrderBalance
* @return {Object}
* @ignore
*/
function getExecuteAlgoOrderTakerTxnAmounts(
orderBookEscrowEntry,
takerCombOrderBalance,
) {
logger.debug({orderBookEscrowEntry, takerCombOrderBalance}, 'orderBookEscrowEntry, takerCombOrderBalance');
const orderCreatorAddr = orderBookEscrowEntry['orderCreatorAddr'];
const orderBookEntry = orderBookEscrowEntry['orderEntry'];
const currentEscrowAlgoBalance = orderBookEscrowEntry['algoBalance'];
let algoAmountReceiving = orderBookEscrowEntry['algoBalance'];
const assetId = orderBookEscrowEntry['assetId'];
const takerAddr = takerCombOrderBalance['takerAddr'];
logger.debug('assetid: ' + assetId);
const orderBookEntrySplit = orderBookEntry.split('-');
const n = orderBookEntrySplit[0];
const d = orderBookEntrySplit[1];
const appAccts = [];
appAccts.push(orderCreatorAddr);
appAccts.push(takerAddr);
// Call stateful contract
const txnFee = 0.002 * 1000000;
algoAmountReceiving -= txnFee; // this will be the transfer amount
logger.debug('here1');
logger.debug('takerOrderBalance: ' + JSON.stringify(takerCombOrderBalance));
logger.debug('algoAmount: ' + algoAmountReceiving);
const price = new BigN(d).divide(new BigN(n), 30);
const bDecOne = new BigN(1);
const emptyReturnVal = {
'algoAmountReceiving': 0,
'asaAmountSending': 0,
'txnFee': 0,
};
if (algoAmountReceiving <= 0) {
logger.debug('here5');
logger.debug('can\'t afford, returning early');
return emptyReturnVal; // can't afford any transaction!
}
algoAmountReceiving = new BigN(algoAmountReceiving);
let asaAmount = algoAmountReceiving.divide(price, 30);
logger.debug('here6');
logger.debug('asa amount: ' + asaAmount.getValue());
let hasSpecialCaseOkPrice = false;
if (asaAmount.getValue().includes('.') &&
asaAmount.compareTo(bDecOne) === LESS_THAN) {
// Since we can only sell at least one unit, figure out the 'real' price we are selling at,
// since we will need to adjust upwards the ASA amount to 1, giving a worse deal for the seller (taker)
const adjPrice = asaAmount.multiply(price);
const takerLimitPrice = new BigN(takerCombOrderBalance['limitPrice']);
logger.debug('here6a2 figuring out adjusted price for hasSpecialCaseGoodPrice',
{adjPrice, asaAmount, price, takerLimitPrice});
if (adjPrice.compareTo(takerLimitPrice) === GREATER_THAN) {
hasSpecialCaseOkPrice = true;
}
}
if (asaAmount.getValue().includes('.') &&
asaAmount.compareTo(bDecOne) === LESS_THAN && hasSpecialCaseOkPrice) {
logger.debug('here6aa asa less than one, changing ASA amount to 1');
asaAmount = bDecOne;
algoAmountReceiving = price.multiply(bDecOne);
if (algoAmountReceiving.getValue().includes('.')) {
// give slightly worse deal for taker if decimal
algoAmountReceiving = algoAmountReceiving.floor();
logger.debug('here6aa decreasing algoAmount due to decimal: ' + algoAmountReceiving.getValue());
}
if (new BigN(currentEscrowAlgoBalance).compareTo(algoAmountReceiving) === LESS_THAN) {
algoAmountReceiving = new BigN(currentEscrowAlgoBalance);
}
algoAmountReceiving = algoAmountReceiving.subtract(new BigN(0.002 * 1000000)); // reduce for fees
} else if (asaAmount.getValue().includes('.')) {
// round down decimals. possibly change this later?
asaAmount = asaAmount.floor();
logger.debug('here7');
logger.debug('increasing from decimal asa amount: ' + asaAmount.getValue());
// recalculating receiving amount
// use math.floor to give slightly worse deal for taker
algoAmountReceiving = asaAmount.multiply(price).floor();
logger.debug('recalculating receiving amount to: ' + algoAmountReceiving.getValue());
}
if (new BigN(takerCombOrderBalance['asaBalance']).compareTo(asaAmount) === LESS_THAN) {
logger.debug('here8');
logger.debug('here8 reducing asa amount due to taker balance: ', asaAmount.getValue());
asaAmount = new BigN(takerCombOrderBalance['asaBalance']);
logger.debug('here8 asa amount is now: ', asaAmount.getValue());
algoAmountReceiving = price.multiply(asaAmount);
logger.debug('here9');
logger.debug('recalculating algoamount: ' + algoAmountReceiving.getValue());
if (algoAmountReceiving.getValue().includes('.')) {
// give slightly worse deal for taker if decimal
algoAmountReceiving = algoAmountReceiving.floor();
logger.debug('here10 increasing algoAmount due to decimal: ' + algoAmountReceiving.getValue());
}
}
logger.debug('almost final ASA amount: ' + asaAmount.getValue());
// These are expected to be integers now
algoAmountReceiving = parseInt(algoAmountReceiving.getValue());
asaAmount = parseInt(asaAmount.getValue());
algoAmountReceiving = Math.max(0, algoAmountReceiving);
return {
'algoAmountReceiving': algoAmountReceiving,
'asaAmountSending': asaAmount,
'txnFee': txnFee,
};
}
/**
*
* @param {*} takerCombOrderBalance
* @param {*} orderBookEscrowEntry
* @return {Object}
* @ignore
*/
function getExecuteASAOrderTakerTxnAmounts(
takerCombOrderBalance,
orderBookEscrowEntry,
) {
logger.debug('printing!!!');
logger.debug({takerCombOrderBalance, orderBookEscrowEntry});
const orderBookEntry = orderBookEscrowEntry['orderEntry'];
const min_asa_balance = 0;
// 1000000-250000-0-15322902
// n-d-minOrderSize-assetId
const orderBookEntrySplit = orderBookEntry.split('-');
const n = orderBookEntrySplit[0];
const d = orderBookEntrySplit[1];
let escrowAsaTradeAmount = orderBookEscrowEntry['asaBalance'];
const currentEscrowASABalance = orderBookEscrowEntry['asaBalance'];
const price = new BigN(d).divide(new BigN(n), 30);
const bDecOne = new BigN(1);
const executionFees = 0.004 * 1000000;
let closeoutFromASABalance = true;
escrowAsaTradeAmount = new BigN(escrowAsaTradeAmount);
let algoTradeAmount = price.multiply(escrowAsaTradeAmount);
if (algoTradeAmount.getValue().includes('.')) {
algoTradeAmount = algoTradeAmount.floor().add(bDecOne); // round up to give seller more money
}
// FIXME - check if lower than order balance
const maxTradeAmount = Math.min(takerCombOrderBalance['algoBalance'], takerCombOrderBalance['walletAlgoBalance'] - executionFees);
const emptyReturnVal = {
'algoTradeAmount': 0,
'escrowAsaTradeAmount': 0,
'executionFees': 0,
'closeoutFromASABalance': false,
};
if (algoTradeAmount.compareTo(new BigN(maxTradeAmount)) == GREATER_THAN &&
algoTradeAmount.compareTo(bDecOne) == GREATER_THAN &&
algoTradeAmount.subtract(new BigN(maxTradeAmount)).compareTo(bDecOne) == GREATER_THAN) {
logger.debug('here999a reducing algoTradeAmount, currently at: ' + algoTradeAmount.getValue());
algoTradeAmount = new BigN(maxTradeAmount);
escrowAsaTradeAmount = algoTradeAmount.divide(price, 30);
logger.debug('checking max: ' + escrowAsaTradeAmount.getValue() + ' ' + 1);
if (escrowAsaTradeAmount.compareTo(bDecOne) == LESS_THAN) { // don't allow 0 value
escrowAsaTradeAmount = bDecOne;
}
logger.debug('here999b reduced to algoTradeAmount escrowAsaAmount', algoTradeAmount.getValue(), escrowAsaTradeAmount.getValue());
if (escrowAsaTradeAmount.getValue().includes('.')) {
// round ASA amount
escrowAsaTradeAmount = escrowAsaTradeAmount.floor();
algoTradeAmount = price.multiply(escrowAsaTradeAmount);
if (algoTradeAmount.getValue().includes('.')) {
algoTradeAmount = algoTradeAmount.floor().add(bDecOne); // round up to give seller more money
logger.debug('here999bc increased algo to algoTradeAmount escrowAsaAmount', algoTradeAmount.getValue(), escrowAsaTradeAmount.getValue());
}
logger.debug('here999c changed to algoTradeAmount escrowAsaAmount', algoTradeAmount.getValue(), escrowAsaTradeAmount.getValue());
}
} // FIXME: factor in fees?
if (new BigN(currentEscrowASABalance).subtract(escrowAsaTradeAmount)
.compareTo(new BigN(min_asa_balance)) == GREATER_THAN) {
logger.debug('asa escrow here9992 (currentASABalance - escrowAsaAmount) > min_asa_balance',
currentEscrowASABalance, escrowAsaTradeAmount.getValue(), min_asa_balance);
closeoutFromASABalance = false;
}
if (takerCombOrderBalance['walletAlgoBalance'] < executionFees + parseInt(algoTradeAmount.getValue())) {
logger.debug('here9992b algo balance too low, returning early! ', executionFees, algoTradeAmount.getValue(), takerCombOrderBalance);
return emptyReturnVal; // no balance left to use for buying ASAs
}
escrowAsaTradeAmount = parseInt(escrowAsaTradeAmount.getValue());
algoTradeAmount = parseInt(algoTradeAmount.getValue());
if (escrowAsaTradeAmount <= 0) {
logger.debug('here77zz escrowAsaTradeAmount is at 0 or below. returning early! nothing to do');
return emptyReturnVal;
}
if (algoTradeAmount <= 0) {
logger.debug('here77zb algoTradeAmount is at 0 or below. returning early! nothing to do');
return emptyReturnVal;
}
// FIXME - need more logic to transact correct price in case balances dont match order balances
logger.debug('closeoutFromASABalance: ' + closeoutFromASABalance);
logger.debug('almost final amounts algoTradeAmount escrowAsaAmount ', algoTradeAmount, escrowAsaTradeAmount);
// algoTradeAmount = algoTradeAmount / 2;
logger.debug('n: ', n, ' d: ', d, ' asset amount: ', escrowAsaTradeAmount);
return {
'algoTradeAmount': algoTradeAmount,
'escrowAsaTradeAmount': escrowAsaTradeAmount,
'executionFees': executionFees,
'closeoutFromASABalance': closeoutFromASABalance,
};
}
/**
* ## ✉ withUserWallet
* @todo decide on useCase and location for withUserWallet
* @param {Object} walletObj WalletObject of User
* @param {Order} order ExecutableOrder
*
* @return {Order} returns a compiled executable with relevant splitOrder amounts and totals
* single transList
* @ignore
*/
function withUserWallet(walletObj, order) {
return {
...order,
wallet: walletObj,
};
// TODO: do other side if implementation is agreed upon
}
/**
* ## ✉ withTakerBalanceAmounts
*
* @param {Order} order Compiled executable object
* @param {Object} takerOrderBalance Object containing the relevant balance information
* @return {Order} returns a compiled executable with relevant splitOrder amounts and totals
* single transList
* @ignore
*/
function withTakerBalanceAmounts(order, takerOrderBalance) {
if (takerOrderBalance.isExecuteASA) {
return {
...order,
contract: {
...order.contract,
total: takerOrderBalance.algoTradeAmount, // equivalent to algoamount sending
amount: takerOrderBalance.escrowASATradeAmount,
},
};
// TODO: do other side if implementation is agreed upon
} else {
return {
...order,
contract: {
...order.contract,
total: takerOrderBalance.algoAmountReceiving,
amount: takerOrderBalance.asaAmountSending,
},
};
}
}
/**
* ## ✉ withStructureSingleTransListWithGroupOrder
* takes a queued order object and returns a new object that has cutOrdertTimes
* logic if needed
*
* @todo Remove mutations to allTransList and return a new array
* @param {Array} singleOrderTransList Array of cut transactions
* @param {Array} allTransList Array of all transactions
* @param {Number} txOrderNum Object Of Order And Wallet amounts
* @param {Number} groupNum Object Of Order And Wallet amounts
*
* @return {Number} returns number to persist txOrderNum for outerloop
* @ignore
*/
function getTxnOrderNumber(singleOrderTransList, allTransList, txOrderNum, groupNum) {
if (!Array.isArray(singleOrderTransList) || !Array.isArray(allTransList)) throw new TypeError('TransLists must be arrays');
if (singleOrderTransList.length < 1) throw new Error('singleOrderTransList must not be empty');
for (let k = 0; k < singleOrderTransList.length; k++) {
const trans = singleOrderTransList[k];
trans['txOrderNum'] = txOrderNum;
trans['groupNum'] = groupNum;
txOrderNum++;
allTransList.push(trans); // perhaps not the best practice but works well with the for loop
}
return txOrderNum;
}
/**
* ## ✉ updateTakerBalance
* takes a queued order object and taker balance object and returns the balance after
* the ith iteration
* @param {Object} queuedOrder Object Of Order And Wallet amounts
* @param {Object} takerOrderBalance Object Of Order And Wallet amounts
* @param {boolean} isAsaEscrow Flag for Buy/Sell
* @return {Object} Object containing new takerOrderBalance
* @ignore
*/
function updateTakerBalance(queuedOrder, takerOrderBalance, isAsaEscrow) {
if (isAsaEscrow) {
const {
algoTradeAmount,
escrowAsaTradeAmount,
executionFees,
closeoutFromASABalance: initialCloseoutFromASABalance,
} = getExecuteASAOrderTakerTxnAmounts(
takerOrderBalance,
queuedOrder,
);
if (algoTradeAmount === 0) return 'escrowEmpty';
let closeoutFromASABalance = initialCloseoutFromASABalance;
if (queuedOrder.useForceShouldCloseOrNot) {
closeoutFromASABalance = queuedOrder.forceShouldClose;
}
takerOrderBalance['algoBalance'] -= executionFees;
takerOrderBalance['algoBalance'] -= algoTradeAmount;
takerOrderBalance['walletAlgoBalance'] -= executionFees;
takerOrderBalance['walletAlgoBalance'] -= algoTradeAmount;
takerOrderBalance['asaBalance'] += escrowAsaTradeAmount;
takerOrderBalance['walletASABalance'] += escrowAsaTradeAmount;
takerOrderBalance['algoTradeAmount'] = algoTradeAmount;
takerOrderBalance['escrowASATradeAmount'] = escrowAsaTradeAmount;
takerOrderBalance['isExecuteASA'] = true;
takerOrderBalance['closeOutFromASA'] = closeoutFromASABalance;
return takerOrderBalance;
} else {
const {algoAmountReceiving, asaAmountSending, txnFee} =
getExecuteAlgoOrderTakerTxnAmounts(queuedOrder, takerOrderBalance);
if (algoAmountReceiving === 0) {
console.debug('algoAmountReceiving is 0, nothing to do, returning early');
return 'escrowEmpty';
}
takerOrderBalance['algoBalance'] -= txnFee;
takerOrderBalance['algoBalance'] += algoAmountReceiving;
takerOrderBalance['asaBalance'] -= asaAmountSending;
takerOrderBalance['asaAmountSending'] = asaAmountSending;
takerOrderBalance['algoAmountReceiving'] = algoAmountReceiving;
takerOrderBalance['isExecuteAsa'] = false;
return takerOrderBalance;
}
}
/**
* ## ✉ withDetermineFinaleOrderAndWalletAmounts
* takes a queued order object and returns a new object that has cutOrdertTimes
* logic if needed
*
* @param {Object} cutQueuedOrderObject Object Of Order And Wallet amounts
* @return {Object} Object containing queued order after being cut
* @ignore
*/
function getCutQueuedOrder(cutQueuedOrderObject) {
const {queuedOrder, cutOrder, splitTimes, loopIndex, runningBalance} = cutQueuedOrderObject;
const cutQueuedOrder = Object.assign({}, queuedOrder);
if (cutOrder != null) {
const shouldClose = (loopIndex >= cutOrder.splitTimes - 1);
const useForceShouldCloseOrNot = (loopIndex < cutOrder.splitTimes - 1);
cutQueuedOrder.forceShouldClose = shouldClose;
cutQueuedOrder.useForceShouldCloseOrNot = useForceShouldCloseOrNot;
cutQueuedOrder.txnNum = loopIndex;
if (loopIndex >= splitTimes - 1) {
// This is the last iteration, so simply use the running balance
if (cutQueuedOrder.isASAEscrow) {
cutQueuedOrder.asaBalance = runningBalance;
} else {
cutQueuedOrder.algoBalance = runningBalance;
}
} else {
if (cutQueuedOrder.isASAEscrow) {
cutQueuedOrder.asaBalance = Math.min(
cutOrder.cutOrderAmount,
runningBalance,
);
} else {
cutQueuedOrder.algoBalance = Math.min(
cutOrder.cutOrderAmount,
runningBalance,
);
}
}
}
return cutQueuedOrder;
}
/**
* @param {Object} queuedOrder
* @return {object}
* @ignore
*/
function getCutOrderTimes(queuedOrder) {
logger.debug('in getCutOrderTimes: ', JSON.stringify(queuedOrder));
let cutOrderAmount = null; let splitTimes = null;
if (queuedOrder.isASAEscrow) {
cutOrderAmount = Math.max(1, queuedOrder.asaBalance / 4);
splitTimes = Math.floor(queuedOrder.asaBalance / cutOrderAmount);
} else {
const minOrderAmount = Math.max(queuedOrder.price + 1, 500000);
cutOrderAmount = Math.max(minOrderAmount, queuedOrder.algoBalance / 4);
splitTimes = Math.floor(queuedOrder.algoBalance / cutOrderAmount);
}
cutOrderAmount = Math.floor(cutOrderAmount);
if (splitTimes === 0) {
splitTimes = 1;
}
return {
'cutOrderAmount': cutOrderAmount,
'splitTimes': splitTimes,
};
}
/**
* ## ✉ getRunningBalance
* Validates and returns algoWalletAmount associated
* with an account
*
* @param {Account} queuedOrder order in current iteration of the structure loop
* @return {Number} Asset or Algo balance of order
* @ignore
*/
function getRunningBalance(queuedOrder) {
if (typeof queuedOrder.isASAEscrow === 'undefined') {
throw new TypeError('Invalid isASAEscrow Flag');
}
if (typeof queuedOrder.algoBalance === 'undefined') {
throw new TypeError('Invalid Algobalance');
}
return queuedOrder.isASAEscrow ?
queuedOrder.asaBalance :
queuedOrder.algoBalance;
}
/**
* ## ✉ getStructureLoopCheck
* Validates and returns algoWalletAmount associated
* with an account
*
* @param {TakerInformation} takerOrderBalance orderBalanceObject of executor
* @param {boolean} isSellingASA
* @param {number} price
* @return {boolean}
* @ignore
*/
function getStructureLoopCheck(takerOrderBalance, isSellingASA, price) {
const takerMissingProps = !(Object.prototype.hasOwnProperty.call(takerOrderBalance, 'walletAlgoBalance') &&
Object.prototype.hasOwnProperty.call(takerOrderBalance, 'asaBalance') &&
Object.prototype.hasOwnProperty.call(takerOrderBalance, 'algoBalance') &&
Object.prototype.hasOwnProperty.call(takerOrderBalance, 'limitPrice')
);
if (takerMissingProps) {
throw new Error('invalid orderBalance object');
}
if (isSellingASA && parseFloat(takerOrderBalance['asaBalance']) <= 0) {
logger.debug('breaking due to 0 asaBalance balance!');
return false;
}
if (!isSellingASA && parseFloat(takerOrderBalance['algoBalance']) <= 0) {
logger.debug('breaking due to 0 algoBalance balance!');
return false;
}
if (isSellingASA && takerOrderBalance['limitPrice'] > price) {
logger.debug('breaking because queuedOrder price is lower than taker sellPrice');
return false;
}
if (!isSellingASA && parseFloat(takerOrderBalance['limitPrice']) < price) {
logger.debug('breaking because queuedOrder price is higher than taker buy price');
return false;
}
return true;
}
/**
* ## ✉ withGetSplitTimesByIter
* calculates cutOrder and splitTimes
*
* @param {Object} queueOrder Object Of Order And Wallet amounts
* @param {Number} loopIndex index of structure loop
* @return {Object} Object containing splitTimes and cutOrder properties
* @ignore
*/
function getSplitTimesByIter(queueOrder, loopIndex) {
let cutOrder = null;
let splitTimes = 1;
if (loopIndex === 0) {
cutOrder = getCutOrderTimes(queueOrder);
splitTimes = cutOrder.splitTimes;
} else {
cutOrder = null;
}
logger.debug('cutOrder, splitTimes: ', {cutOrder, splitTimes});
return {cutOrder, splitTimes};
}
/**
* @typedef {Object} QueuedOrder
* @property {number} asaBalance
* @property {number} algoBalance
* @property {string} escrowAddr
* @property {string} escrowOrderType
* @property {number} price
* @property {string} takerAddr
* @property {number} assetId
* @property {boolean} isASAEscrow
* @property {number} n
* @property {number} d
* @property {number} version
* @property {number} min
* @property {string} formattedASAAmount
* @property {string} orderEntry
* @ignore
*/
/**
* ## ✉ getCutTakerOrders
* Composes taker orders and returns an Object with relevant splitTaker Txns
*
* @param {Object} api Instance of Algodex Api needed for mapping format of executable orders to compilation
* @param {Object} order The User's Order
* @param {Array<QueuedOrder>} queuedOrders Array of queuedOrders used for identifying potenital taker orders
* @param {Object} takerOrderBalance An object containing relevant information related to the current balance. Used to decide when to stop executing on queuedOrders
* @return {Object} An object with array of taker transactions which as a whole represents the taker side .
* @ignore
*/
async function getCutTakerOrders(api, order, queuedOrders, takerOrderBalance) {
if (!(order.client instanceof algosdk.Algodv2)) {
throw new AlgodError('Order must have a valid SDK client');
}
if (typeof order.appId !== 'number') {
throw new TypeError('Must have valid Application Index');
}
if (typeof order.contract !== 'undefined' && typeof order.contract.entry !== 'string') {
throw new TypeError('Order must have a valid contract state with an entry!');
}
// Currently Breaking because we are no longer returning an orderObject with the takerORderBalance attached, we are just returning the takerBalance
if (typeof takerOrderBalance === 'undefined') {
throw new TypeError('Must have a takerOrderBalance object property attached to the Order Object');
}
if (!Array.isArray(queuedOrders)) {
throw new TypeError('Must have an array of queuedOrders attached to order.asset');
}
const _isSellingAsset = order.type === 'sell';
let txOrderNum = 0;
let groupNum = 0;
// let lastExecutedPrice = -1;
const allTransList = [];
for (let i = 0; i < queuedOrders.length; i++) {
if (!getStructureLoopCheck(
takerOrderBalance,
_isSellingAsset,
queuedOrders[i]['price'])) {
break;
}
const {cutOrder, splitTimes} = getSplitTimesByIter(queuedOrders[i], i);
let runningBalance = getRunningBalance(queuedOrders[i]);
let outerBreak = false;
for (let jj = 0; jj < splitTimes; jj++) {
if (runningBalance <= 0) {
throw new Error('Unexpected 0 or below balance');
}
logger.debug(
'running balance: ' +
runningBalance +
' isASAEscrow: ' +
queuedOrders[i].isASAEscrow,
);
const cutQueuedOrderObject = {
queuedOrder: queuedOrders[i],
cutOrder: cutOrder,
splitTimes: splitTimes,
loopIndex: jj,
runningBalance: runningBalance,
};
const queuedOrder = getCutQueuedOrder(cutQueuedOrderObject);
const compiledEscrow = await compile(api.http.dexd.mapOrderbookToOrders({
...queuedOrder,
client: order.client,
indexer: api.indexer,
}));
// updatedTakerOrderBalance does not do anything with compileEscrow
const updatedTakerOrderBalance = updateTakerBalance(queuedOrder, takerOrderBalance, queuedOrder.isASAEscrow);
if (updatedTakerOrderBalance === 'escrowEmpty') {
outerBreak = true;
break;
}
// singleOrderTransList changed per Michael and I's discussion
// TODO: change the above logic to map queuedOrder to a compilable object.
// queuedOrder.algoBalance - updatedTakerOrderBalance.algoAmountReceiving
// queuedOrder.algoBalance - updatedTakerOrderBalance.algoAmountReceiving < 500000
const compiledEscrowWithTxns = !queuedOrder.isASAEscrow ?
await withExecuteAlgoTxns(
withTakerBalanceAmounts(
withUserWallet(order.wallet, compiledEscrow),
updatedTakerOrderBalance,
),
queuedOrder?.forceShouldClose ||
(cutQueuedOrderObject.cutOrder === null &&
queuedOrder.algoBalance - updatedTakerOrderBalance.algoAmountReceiving <500000),
) :
await withExecuteAssetTxns(
withTakerBalanceAmounts(
withUserWallet(order.wallet, compiledEscrow),
updatedTakerOrderBalance,
),
updatedTakerOrderBalance.closeOutFromASA,
);
if (compiledEscrowWithTxns == null) {
// Overspending issue
outerBreak = true;
break;
}
// Taking out final price check for now because new paymentStructure does not append data found in v1 structures
// const [algo, asa] = getAlgoandAsaAmounts(singleOrderTransList);
// finalPriceCheck(algo, asa, takerOrderBalance.limitPrice, _isSellingAsset);
// TODO: Return an array of compiled executable objects. Each object represents a group.
// lastExecutedPrice = queuedOrder['price'];
allTransList.push({
...compiledEscrowWithTxns,
groupNum: groupNum,
txOrderNum: `${txOrderNum} - ${txOrderNum + compiledEscrowWithTxns.contract.txns.length}`, // represent beggining and end txnNum
});
txOrderNum += compiledEscrowWithTxns.contract.txns.length;
// Be Warned: allTransList is persisted via a side effect
// txOrderNum = getTxnOrderNumber(
// compiledEscrowWithTxns.contract.txns, allTransList, txOrderNum, groupNum,
// ); // so next loop remembers total
groupNum++;
runningBalance -= cutOrder != null ? cutOrder.cutOrderAmount : 0;
}
if (outerBreak) {
break;
}
}
return allTransList;
}
module.exports = getCutTakerOrders;
if (process.env.NODE_ENV === 'test') {
module.exports.getRunningBalance = getRunningBalance;
module.exports.getStructureLoopCheck = getStructureLoopCheck;
module.exports.getCutOrderTimes = getCutOrderTimes;
module.exports.getCutQueuedOrder = getCutQueuedOrder;
module.exports.getExecuteAlgoOrderTakerTxnAmounts = getExecuteAlgoOrderTakerTxnAmounts;
module.exports.getExecuteASAOrderTakerTxnAmounts = getExecuteASAOrderTakerTxnAmounts;
module.exports.getSplitTimesByIter = getSplitTimesByIter;
module.exports.getTxnOrderNumber = getTxnOrderNumber;
}