Send partial liquidate instruction just to force settle borrow

This commit is contained in:
dd 2021-06-22 09:36:15 -04:00
parent 39b5ab3b4f
commit 1c16de7171
1 changed files with 331 additions and 209 deletions

View File

@ -3,7 +3,8 @@ import {
getMultipleAccounts,
IDS,
MangoClient,
MangoGroup, MarginAccount,
MangoGroup,
MarginAccount,
nativeToUi,
NUM_MARKETS,
NUM_TOKENS,
@ -11,7 +12,13 @@ import {
parseTokenAccountData,
tokenToDecimals,
} from '@blockworks-foundation/mango-client';
import { Account, Commitment, Connection, PublicKey, Transaction } from '@solana/web3.js';
import {
Account,
Commitment,
Connection,
PublicKey,
Transaction,
} from '@solana/web3.js';
import { homedir } from 'os';
import fs from 'fs';
import { notify, sleep } from './utils';
@ -22,7 +29,6 @@ import {
} from '@blockworks-foundation/mango-client/lib/instruction';
import BN = require('bn.js');
/*
After a liquidation, the amounts in each wallet become unbalanced
Make sure to sell or buy quantities different from the target on base currencies
@ -37,269 +43,348 @@ async function balanceWallets(
liqorWallets: PublicKey[],
liqorValuesUi: number[],
liqorOpenOrdersKeys: PublicKey[],
targets: number[]
targets: number[],
) {
// Retrieve orders from order book by owner
const liqorOrders = await Promise.all(markets.map((m) =>
m.loadOrdersForOwner(connection, liqor.publicKey)));
const liqorOrders = await Promise.all(
markets.map((m) => m.loadOrdersForOwner(connection, liqor.publicKey)),
);
// Cancel all
const cancelTransactions: Promise<string>[] = []
const cancelTransactions: Promise<string>[] = [];
for (let i = 0; i < NUM_MARKETS; i++) {
for (let order of liqorOrders[i]) {
console.log(`Cancelling liqor order on market ${i} size=${order.size} price=${order.price}`)
console.log(
`Cancelling liqor order on market ${i} size=${order.size} price=${order.price}`,
);
cancelTransactions.push(markets[i].cancelOrder(connection, liqor, order));
}
}
await Promise.all(cancelTransactions)
await Promise.all(cancelTransactions);
// Load open orders accounts
const liqorOpenOrders = await Promise.all(liqorOpenOrdersKeys.map((pk) => OpenOrders.load(connection, pk, mangoGroup.dexProgramId)))
const liqorOpenOrders = await Promise.all(
liqorOpenOrdersKeys.map((pk) =>
OpenOrders.load(connection, pk, mangoGroup.dexProgramId),
),
);
// Settle all
const settleTransactions: Promise<string>[] = []
const settleTransactions: Promise<string>[] = [];
for (let i = 0; i < NUM_MARKETS; i++) {
const oo = liqorOpenOrders[i]
if (parseFloat(oo.quoteTokenTotal.toString()) > 0 || parseFloat(oo.baseTokenTotal.toString()) > 0) {
console.log(`Settling funds to liqor wallet ${i} quote:${oo.quoteTokenTotal.toString()} base:${oo.baseTokenTotal.toString()}`)
settleTransactions.push(markets[i].settleFunds(connection, liqor, oo, liqorWallets[i], liqorWallets[NUM_TOKENS-1]))
const oo = liqorOpenOrders[i];
if (
parseFloat(oo.quoteTokenTotal.toString()) > 0 ||
parseFloat(oo.baseTokenTotal.toString()) > 0
) {
console.log(
`Settling funds to liqor wallet ${i} quote:${oo.quoteTokenTotal.toString()} base:${oo.baseTokenTotal.toString()}`,
);
settleTransactions.push(
markets[i].settleFunds(
connection,
liqor,
oo,
liqorWallets[i],
liqorWallets[NUM_TOKENS - 1],
),
);
}
}
await Promise.all(settleTransactions)
await Promise.all(settleTransactions);
// Account wallets should instantly update
const liqorWalletAccounts = await getMultipleAccounts(connection, liqorWallets, 'processed' as Commitment)
liqorValuesUi = liqorWalletAccounts.map(
(a, i) => nativeToUi(parseTokenAccountData(a.accountInfo.data).amount, mangoGroup.mintDecimals[i])
)
const liqorWalletAccounts = await getMultipleAccounts(
connection,
liqorWallets,
'processed' as Commitment,
);
liqorValuesUi = liqorWalletAccounts.map((a, i) =>
nativeToUi(
parseTokenAccountData(a.accountInfo.data).amount,
mangoGroup.mintDecimals[i],
),
);
const diffs: number[] = []
const netValues: [number, number][] = []
const diffs: number[] = [];
const netValues: [number, number][] = [];
// Go to each base currency and see if it's above or below target
for (let i = 0; i < NUM_TOKENS - 1; i++) {
const diff = liqorValuesUi[i] - targets[i]
diffs.push(diff)
netValues.push([i, diff * prices[i]])
const diff = liqorValuesUi[i] - targets[i];
diffs.push(diff);
netValues.push([i, diff * prices[i]]);
}
// Sort in decreasing order so you sell first then buy
netValues.sort((a, b) => (b[1] - a[1]))
netValues.sort((a, b) => b[1] - a[1]);
for (let i = 0; i < NUM_TOKENS - 1; i++) {
const marketIndex = netValues[i][0]
const market = markets[marketIndex]
const tokenDecimals = tokenToDecimals[marketIndex === 0 ? 'BTC' : 'ETH'] // TODO make this mapping allow arbitrary mango groups
const tokenDecimalAdj = Math.pow(10, tokenDecimals)
if (netValues[i][1] > 0) { // sell to close
const price = prices[marketIndex] * 0.95
const size = Math.floor(diffs[marketIndex] * tokenDecimalAdj) / tokenDecimalAdj // round down the size
const marketIndex = netValues[i][0];
const market = markets[marketIndex];
const tokenDecimals = tokenToDecimals[marketIndex === 0 ? 'BTC' : 'ETH']; // TODO make this mapping allow arbitrary mango groups
const tokenDecimalAdj = Math.pow(10, tokenDecimals);
if (netValues[i][1] > 0) {
// sell to close
const price = prices[marketIndex] * 0.95;
const size =
Math.floor(diffs[marketIndex] * tokenDecimalAdj) / tokenDecimalAdj; // round down the size
if (size === 0) {
continue
continue;
}
console.log(`Sell to close ${marketIndex} ${size} @ ${price}`)
let txid = await market.placeOrder(
connection,
{
owner: liqor,
payer: liqorWallets[marketIndex],
side: 'sell',
price,
size,
orderType: 'ioc',
openOrdersAddressKey: liqorOpenOrdersKeys[marketIndex],
feeDiscountPubkey: null // TODO find liqor's SRM fee account
}
)
console.log(`Sell to close ${marketIndex} ${size} @ ${price}`);
let txid = await market.placeOrder(connection, {
owner: liqor,
payer: liqorWallets[marketIndex],
side: 'sell',
price,
size,
orderType: 'ioc',
openOrdersAddressKey: liqorOpenOrdersKeys[marketIndex],
feeDiscountPubkey: null, // TODO find liqor's SRM fee account
});
// TODO add a SettleFunds instruction to this transaction
console.log(`Place order successful: ${txid}; Settling funds`)
await market.settleFunds(connection, liqor, liqorOpenOrders[marketIndex], liqorWallets[marketIndex], liqorWallets[NUM_TOKENS-1])
} else if (netValues[i][1] < 0) { // buy to close
const price = prices[marketIndex] * 1.05 // buy at up to 5% higher than oracle price
const size = Math.ceil(-diffs[marketIndex] * tokenDecimalAdj) / tokenDecimalAdj
console.log(`Buy to close ${marketIndex} ${size} @ ${price}`)
let txid = await market.placeOrder(
console.log(`Place order successful: ${txid}; Settling funds`);
await market.settleFunds(
connection,
{
owner: liqor,
payer: liqorWallets[NUM_TOKENS-1],
side: 'buy',
price,
size,
orderType: 'ioc',
openOrdersAddressKey: liqorOpenOrdersKeys[marketIndex],
feeDiscountPubkey: null
}
)
console.log(`Place order successful: ${txid}; Settling funds`)
await market.settleFunds(connection, liqor, liqorOpenOrders[marketIndex], liqorWallets[marketIndex], liqorWallets[NUM_TOKENS-1])
liqor,
liqorOpenOrders[marketIndex],
liqorWallets[marketIndex],
liqorWallets[NUM_TOKENS - 1],
);
} else if (netValues[i][1] < 0) {
// buy to close
const price = prices[marketIndex] * 1.05; // buy at up to 5% higher than oracle price
const size =
Math.ceil(-diffs[marketIndex] * tokenDecimalAdj) / tokenDecimalAdj;
console.log(`Buy to close ${marketIndex} ${size} @ ${price}`);
let txid = await market.placeOrder(connection, {
owner: liqor,
payer: liqorWallets[NUM_TOKENS - 1],
side: 'buy',
price,
size,
orderType: 'ioc',
openOrdersAddressKey: liqorOpenOrdersKeys[marketIndex],
feeDiscountPubkey: null,
});
console.log(`Place order successful: ${txid}; Settling funds`);
await market.settleFunds(
connection,
liqor,
liqorOpenOrders[marketIndex],
liqorWallets[marketIndex],
liqorWallets[NUM_TOKENS - 1],
);
}
}
}
async function runPartialLiquidator() {
const client = new MangoClient()
const cluster = process.env.CLUSTER || 'mainnet-beta'
const group_name = process.env.GROUP_NAME || 'BTC_ETH_USDT'
const clusterUrl = process.env.CLUSTER_URL || IDS.cluster_urls[cluster]
const targetsStr = process.env.TARGETS || "0.1 2"
const checkInterval = parseFloat(process.env.CHECK_INTERVAL || "1000.0")
const targets = targetsStr.split(' ').map((s) => parseFloat(s))
const connection = new Connection(clusterUrl, 'singleGossip')
const client = new MangoClient();
const cluster = process.env.CLUSTER || 'mainnet-beta';
const group_name = process.env.GROUP_NAME || 'BTC_ETH_USDT';
const clusterUrl = process.env.CLUSTER_URL || IDS.cluster_urls[cluster];
const targetsStr = process.env.TARGETS || '0.1 2';
const checkInterval = parseFloat(process.env.CHECK_INTERVAL || '1000.0');
const targets = targetsStr.split(' ').map((s) => parseFloat(s));
const connection = new Connection(clusterUrl, 'singleGossip');
// The address of the Mango Program on the blockchain
const programId = new PublicKey(IDS[cluster].mango_program_id)
const programId = new PublicKey(IDS[cluster].mango_program_id);
// The address of the serum dex program on the blockchain: https://github.com/project-serum/serum-dex
const dexProgramId = new PublicKey(IDS[cluster].dex_program_id)
const dexProgramId = new PublicKey(IDS[cluster].dex_program_id);
// Address of the MangoGroup
const mangoGroupPk = new PublicKey(IDS[cluster].mango_groups[group_name].mango_group_pk)
const mangoGroupPk = new PublicKey(
IDS[cluster].mango_groups[group_name].mango_group_pk,
);
// liquidator's keypair
const keyPairPath = process.env.KEYPAIR || homedir() + '/.config/solana/id.json'
const payer = new Account(JSON.parse(fs.readFileSync(keyPairPath, 'utf-8')))
const keyPairPath =
process.env.KEYPAIR || homedir() + '/.config/solana/id.json';
const payer = new Account(JSON.parse(fs.readFileSync(keyPairPath, 'utf-8')));
notify(`partial liquidator launched cluster=${cluster} group=${group_name}`);
let mangoGroup = await client.getMangoGroup(connection, mangoGroupPk)
let mangoGroup = await client.getMangoGroup(connection, mangoGroupPk);
const tokenWallets = (await Promise.all(
mangoGroup.tokens.map(
(mint) => findLargestTokenAccountForOwner(connection, payer.publicKey, mint).then(
(response) => response.publicKey
)
)
))
const tokenWallets = await Promise.all(
mangoGroup.tokens.map((mint) =>
findLargestTokenAccountForOwner(connection, payer.publicKey, mint).then(
(response) => response.publicKey,
),
),
);
// load all markets
const markets = await Promise.all(mangoGroup.spotMarkets.map(
(pk) => Market.load(connection, pk, {skipPreflight: true, commitment: 'singleGossip'}, dexProgramId)
))
const markets = await Promise.all(
mangoGroup.spotMarkets.map((pk) =>
Market.load(
connection,
pk,
{ skipPreflight: true, commitment: 'singleGossip' },
dexProgramId,
),
),
);
// TODO handle failures in any of the steps
// Find a way to get all margin accounts without querying fresh--get incremental updates to margin accounts
const liqorOpenOrdersKeys: PublicKey[] = []
const liqorOpenOrdersKeys: PublicKey[] = [];
for (let i = 0; i < NUM_MARKETS; i++) {
let openOrdersAccounts: OpenOrders[] = await markets[i].findOpenOrdersAccountsForOwner(connection, payer.publicKey)
if(openOrdersAccounts.length) {
liqorOpenOrdersKeys.push(openOrdersAccounts[0].publicKey)
let openOrdersAccounts: OpenOrders[] = await markets[
i
].findOpenOrdersAccountsForOwner(connection, payer.publicKey);
if (openOrdersAccounts.length) {
liqorOpenOrdersKeys.push(openOrdersAccounts[0].publicKey);
} else {
console.log(`No OpenOrders account found for market ${markets[i].publicKey.toBase58()}`)
console.log(
`No OpenOrders account found for market ${markets[
i
].publicKey.toBase58()}`,
);
}
}
if(liqorOpenOrdersKeys.length != NUM_MARKETS) {
console.log('Warning: Missing OpenOrders accounts. Wallet balancing has been disabled.')
if (liqorOpenOrdersKeys.length != NUM_MARKETS) {
console.log(
'Warning: Missing OpenOrders accounts. Wallet balancing has been disabled.',
);
}
const cancelLimit = 5
const cancelLimit = 5;
while (true) {
try {
mangoGroup = await client.getMangoGroup(connection, mangoGroupPk)
mangoGroup = await client.getMangoGroup(connection, mangoGroupPk);
let [marginAccounts, prices, vaultAccs, liqorAccs] = await Promise.all([
client.getAllMarginAccounts(connection, programId, mangoGroup),
mangoGroup.getPrices(connection),
getMultipleAccounts(connection, mangoGroup.vaults),
getMultipleAccounts(connection, tokenWallets, 'processed' as Commitment),
])
getMultipleAccounts(
connection,
tokenWallets,
'processed' as Commitment,
),
]);
const vaultValues = vaultAccs.map(
(a, i) => nativeToUi(parseTokenAccountData(a.accountInfo.data).amount, mangoGroup.mintDecimals[i])
)
const vaultValues = vaultAccs.map((a, i) =>
nativeToUi(
parseTokenAccountData(a.accountInfo.data).amount,
mangoGroup.mintDecimals[i],
),
);
const liqorTokenValues = liqorAccs.map(
(a) => parseTokenAccount(a.accountInfo.data).amount
)
const liqorTokenUi = liqorAccs.map(
(a, i) => nativeToUi(parseTokenAccountData(a.accountInfo.data).amount, mangoGroup.mintDecimals[i])
)
(a) => parseTokenAccount(a.accountInfo.data).amount,
);
const liqorTokenUi = liqorAccs.map((a, i) =>
nativeToUi(
parseTokenAccountData(a.accountInfo.data).amount,
mangoGroup.mintDecimals[i],
),
);
console.log(prices)
console.log(vaultValues)
console.log(liqorTokenUi)
console.log(prices);
console.log(vaultValues);
console.log(liqorTokenUi);
// FIXME: added bias to collRatio to allow other liquidators to step in for testing
let coll_bias = 0
let coll_bias = 0;
if (process.env.COLL_BIAS) {
coll_bias = parseFloat(process.env.COLL_BIAS)
coll_bias = parseFloat(process.env.COLL_BIAS);
}
let maxBorrAcc: MarginAccount | undefined = undefined;
let maxBorrVal = 0;
let minCollAcc: MarginAccount | undefined = undefined;
let minCollVal = 99999;
for (let ma of marginAccounts) { // parallelize this if possible
for (let ma of marginAccounts) {
// parallelize this if possible
let description = ''
let description = '';
try {
const assetsVal = ma.getAssetsVal(mangoGroup, prices)
const liabsVal = ma.getLiabsVal(mangoGroup, prices)
const assetsVal = ma.getAssetsVal(mangoGroup, prices);
const liabsVal = ma.getLiabsVal(mangoGroup, prices);
if (liabsVal > maxBorrVal) {
maxBorrVal = liabsVal
maxBorrAcc = ma
maxBorrVal = liabsVal;
maxBorrAcc = ma;
}
if (liabsVal < 0.1) { // too small of an account; number precision may cause errors
continue
if (liabsVal < 0.1) {
// too small of an account; number precision may cause errors
continue;
}
if (!ma.beingLiquidated) {
let collRatio = (assetsVal / liabsVal)
let collRatio = assetsVal / liabsVal;
const deficit = liabsVal * mangoGroup.initCollRatio - assetsVal
if (deficit < 0.1) { // too small of an account; number precision may cause errors
continue
const deficit = liabsVal * mangoGroup.initCollRatio - assetsVal;
if (deficit < 0.1) {
// too small of an account; number precision may cause errors
continue;
}
if (collRatio < minCollVal)
{
minCollVal = collRatio
minCollAcc = ma
if (collRatio < minCollVal) {
minCollVal = collRatio;
minCollAcc = ma;
}
if (collRatio + coll_bias >= mangoGroup.maintCollRatio) {
continue
continue;
}
}
description = ma.toPrettyString(mangoGroup, prices)
console.log(`Liquidatable\n${description}\nbeingLiquidated: ${ma.beingLiquidated}`)
notify(`Liquidatable\n${description}\nbeingLiquidated: ${ma.beingLiquidated}`)
description = ma.toPrettyString(mangoGroup, prices);
console.log(
`Liquidatable\n${description}\nbeingLiquidated: ${ma.beingLiquidated}`,
);
notify(
`Liquidatable\n${description}\nbeingLiquidated: ${ma.beingLiquidated}`,
);
// find the market with the most value in OpenOrdersAccount
let maxMarketIndex = -1
let maxMarketVal = 0
let maxMarketIndex = -1;
let maxMarketVal = 0;
for (let i = 0; i < NUM_MARKETS; i++) {
const openOrdersAccount = ma.openOrdersAccounts[i]
const openOrdersAccount = ma.openOrdersAccounts[i];
if (openOrdersAccount === undefined) {
continue
continue;
}
const marketVal = openOrdersAccount.quoteTokenTotal.toNumber() + openOrdersAccount.baseTokenTotal.toNumber() * prices[i]
const marketVal =
openOrdersAccount.quoteTokenTotal.toNumber() +
openOrdersAccount.baseTokenTotal.toNumber() * prices[i];
if (marketVal > maxMarketVal) {
maxMarketIndex = i
maxMarketVal = marketVal
maxMarketIndex = i;
maxMarketVal = marketVal;
}
}
const transaction = new Transaction()
const transaction = new Transaction();
if (maxMarketIndex !== -1) {
// force cancel orders on this particular market
const spotMarket = markets[maxMarketIndex]
const [bids, asks] = await Promise.all([spotMarket.loadBids(connection), spotMarket.loadAsks(connection)])
const openOrdersAccount = ma.openOrdersAccounts[maxMarketIndex]
const spotMarket = markets[maxMarketIndex];
const [bids, asks] = await Promise.all([
spotMarket.loadBids(connection),
spotMarket.loadAsks(connection),
]);
const openOrdersAccount = ma.openOrdersAccounts[maxMarketIndex];
if (openOrdersAccount === undefined) {
console.log('error state')
continue
console.log('error state');
continue;
}
let numOrders = spotMarket.filterForOpenOrders(bids, asks, [openOrdersAccount]).length
let numOrders = spotMarket.filterForOpenOrders(bids, asks, [
openOrdersAccount,
]).length;
const dexSigner = await PublicKey.createProgramAddress(
[
spotMarket.publicKey.toBuffer(),
spotMarket['_decoded'].vaultSignerNonce.toArrayLike(Buffer, 'le', 8)
spotMarket['_decoded'].vaultSignerNonce.toArrayLike(
Buffer,
'le',
8,
),
],
spotMarket.programId
)
spotMarket.programId,
);
for (let i = 0; i < 10; i++) {
const instruction = makeForceCancelOrdersInstruction(
@ -308,7 +393,7 @@ async function runPartialLiquidator() {
payer.publicKey,
ma.publicKey,
mangoGroup.vaults[maxMarketIndex],
mangoGroup.vaults[NUM_TOKENS-1],
mangoGroup.vaults[NUM_TOKENS - 1],
spotMarket.publicKey,
spotMarket.bidsAddress,
spotMarket.asksAddress,
@ -320,58 +405,78 @@ async function runPartialLiquidator() {
spotMarket.programId,
ma.openOrders,
mangoGroup.oracles,
new BN(cancelLimit)
)
transaction.add(instruction)
numOrders -= cancelLimit
new BN(cancelLimit),
);
transaction.add(instruction);
numOrders -= cancelLimit;
if (numOrders <= 0) {
break
break;
}
}
}
// Find the market with the highest borrows and lowest deposits
const deposits = ma.getAssets(mangoGroup)
const borrows = ma.getLiabs(mangoGroup)
let minNet = 0
let minNetIndex = -1
let maxNet = 0
let maxNetIndex = NUM_TOKENS-1
const deposits = ma.getAssets(mangoGroup);
const borrows = ma.getLiabs(mangoGroup);
let minNet = 0;
let minNetIndex = -1;
let maxNet = 0;
let maxNetIndex = NUM_TOKENS - 1;
for (let i = 0; i < NUM_TOKENS; i++) {
const netDeposit = (deposits[i] - borrows[i]) * prices[i]
const netDeposit = (deposits[i] - borrows[i]) * prices[i];
if (netDeposit < minNet) {
minNet = netDeposit
minNetIndex = i
minNet = netDeposit;
minNetIndex = i;
} else if (netDeposit > maxNet) {
maxNet = netDeposit
maxNetIndex = i
maxNet = netDeposit;
maxNetIndex = i;
}
}
transaction.add(makePartialLiquidateInstruction(
programId,
mangoGroup.publicKey,
payer.publicKey,
liqorAccs[minNetIndex].publicKey,
liqorAccs[maxNetIndex].publicKey,
ma.publicKey,
mangoGroup.vaults[minNetIndex],
mangoGroup.vaults[maxNetIndex],
mangoGroup.signerKey,
ma.openOrders,
mangoGroup.oracles,
liqorTokenValues[minNetIndex]
))
if (minNetIndex === -1) {
// pick random index not maxNetIndex to force a settle borrows
minNetIndex = (maxNetIndex + 1) % NUM_TOKENS;
}
transaction.add(
makePartialLiquidateInstruction(
programId,
mangoGroup.publicKey,
payer.publicKey,
liqorAccs[minNetIndex].publicKey,
liqorAccs[maxNetIndex].publicKey,
ma.publicKey,
mangoGroup.vaults[minNetIndex],
mangoGroup.vaults[maxNetIndex],
mangoGroup.signerKey,
ma.openOrders,
mangoGroup.oracles,
liqorTokenValues[minNetIndex],
),
);
await client.sendTransaction(connection, transaction, payer, [])
await sleep(2000)
ma = await client.getMarginAccount(connection, ma.publicKey, dexProgramId)
console.log(`Successful partial liquidation\n${ma.toPrettyString(mangoGroup, prices)}\nbeingLiquidated: ${ma.beingLiquidated}`)
notify(`Successful partial liquidation\n${ma.toPrettyString(mangoGroup, prices)}\nbeingLiquidated: ${ma.beingLiquidated}`)
break // This is so wallets get balanced
await client.sendTransaction(connection, transaction, payer, []);
await sleep(2000);
ma = await client.getMarginAccount(
connection,
ma.publicKey,
dexProgramId,
);
console.log(
`Successful partial liquidation\n${ma.toPrettyString(
mangoGroup,
prices,
)}\nbeingLiquidated: ${ma.beingLiquidated}`,
);
notify(
`Successful partial liquidation\n${ma.toPrettyString(
mangoGroup,
prices,
)}\nbeingLiquidated: ${ma.beingLiquidated}`,
);
break; // This is so wallets get balanced
} catch (e) {
if (!e.timeout) {
throw e
throw e;
} else {
notify(`unknown error: ${e}`);
console.error(e);
@ -379,28 +484,45 @@ async function runPartialLiquidator() {
}
}
const maxBorrAccPk = maxBorrAcc ? maxBorrAcc.publicKey.toBase58() : ""
const maxBorrAccCr = maxBorrAcc ? maxBorrAcc.getCollateralRatio(mangoGroup, prices) : 0
const minCollAccPk = minCollAcc ? minCollAcc.publicKey.toBase58() : ""
const minCollBorrVal = minCollAcc ? minCollAcc.getLiabsVal(mangoGroup, prices) : 0
const maxBorrAccPk = maxBorrAcc ? maxBorrAcc.publicKey.toBase58() : '';
const maxBorrAccCr = maxBorrAcc
? maxBorrAcc.getCollateralRatio(mangoGroup, prices)
: 0;
const minCollAccPk = minCollAcc ? minCollAcc.publicKey.toBase58() : '';
const minCollBorrVal = minCollAcc
? minCollAcc.getLiabsVal(mangoGroup, prices)
: 0;
console.log(`Max Borrow Account: ${maxBorrAccPk} | Borrow Val: ${maxBorrVal} | CR: ${maxBorrAccCr}`)
console.log(`Min Coll-Ratio Account: ${minCollAccPk} | Borrow Val: ${minCollBorrVal} | CR: ${minCollVal}`)
if(liqorOpenOrdersKeys.length == NUM_MARKETS) {
await balanceWallets(connection, mangoGroup, prices, markets, payer, tokenWallets, liqorTokenUi, liqorOpenOrdersKeys, targets)
console.log(
`Max Borrow Account: ${maxBorrAccPk} | Borrow Val: ${maxBorrVal} | CR: ${maxBorrAccCr}`,
);
console.log(
`Min Coll-Ratio Account: ${minCollAccPk} | Borrow Val: ${minCollBorrVal} | CR: ${minCollVal}`,
);
if (liqorOpenOrdersKeys.length == NUM_MARKETS) {
await balanceWallets(
connection,
mangoGroup,
prices,
markets,
payer,
tokenWallets,
liqorTokenUi,
liqorOpenOrdersKeys,
targets,
);
} else {
console.log('Could not balance wallets due to missing OpenOrders account')
console.log(
'Could not balance wallets due to missing OpenOrders account',
);
}
} catch (e) {
notify(`unknown error: ${e}`);
console.error(e);
} finally {
await sleep(checkInterval)
await sleep(checkInterval);
}
}
}
runPartialLiquidator()
runPartialLiquidator();