2021-04-06 17:43:51 -07:00
|
|
|
import {
|
|
|
|
findLargestTokenAccountForOwner,
|
|
|
|
getMultipleAccounts,
|
|
|
|
IDS,
|
2021-04-09 13:18:06 -07:00
|
|
|
MangoClient,
|
2021-04-18 07:08:44 -07:00
|
|
|
MangoGroup, MarginAccount,
|
2021-04-09 13:18:06 -07:00
|
|
|
nativeToUi,
|
|
|
|
NUM_MARKETS,
|
|
|
|
NUM_TOKENS,
|
|
|
|
parseTokenAccount,
|
2021-04-18 07:08:44 -07:00
|
|
|
parseTokenAccountData,
|
|
|
|
tokenToDecimals,
|
2021-04-06 17:43:51 -07:00
|
|
|
} from '@blockworks-foundation/mango-client';
|
2021-05-13 19:10:46 -07:00
|
|
|
import { Account, Commitment, Connection, PublicKey, Transaction } from '@solana/web3.js';
|
2021-04-06 17:43:51 -07:00
|
|
|
import { homedir } from 'os';
|
|
|
|
import fs from 'fs';
|
|
|
|
import { notify, sleep } from './utils';
|
2021-04-09 13:18:06 -07:00
|
|
|
import { Market, OpenOrders } from '@project-serum/serum';
|
2021-04-06 17:43:51 -07:00
|
|
|
import {
|
|
|
|
makeForceCancelOrdersInstruction,
|
|
|
|
makePartialLiquidateInstruction,
|
|
|
|
} from '@blockworks-foundation/mango-client/lib/instruction';
|
2021-04-09 13:18:06 -07:00
|
|
|
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
|
|
|
|
Convert excess into quote currency
|
|
|
|
*/
|
|
|
|
async function balanceWallets(
|
|
|
|
connection: Connection,
|
|
|
|
mangoGroup: MangoGroup,
|
|
|
|
prices: number[],
|
|
|
|
markets: Market[],
|
|
|
|
liqor: Account,
|
|
|
|
liqorWallets: PublicKey[],
|
|
|
|
liqorValuesUi: number[],
|
2021-04-09 14:37:10 -07:00
|
|
|
liqorOpenOrdersKeys: PublicKey[],
|
|
|
|
targets: number[]
|
2021-04-09 13:18:06 -07:00
|
|
|
) {
|
2021-05-13 19:10:46 -07:00
|
|
|
|
|
|
|
// Retrieve orders from order book by owner
|
|
|
|
const liqorOrders = await Promise.all(markets.map((m) =>
|
|
|
|
m.loadOrdersForOwner(connection, liqor.publicKey)));
|
|
|
|
|
|
|
|
// Cancel all
|
|
|
|
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}`)
|
|
|
|
cancelTransactions.push(markets[i].cancelOrder(connection, liqor, order));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
await Promise.all(cancelTransactions)
|
|
|
|
|
|
|
|
// Load open orders accounts
|
2021-04-09 13:18:06 -07:00
|
|
|
const liqorOpenOrders = await Promise.all(liqorOpenOrdersKeys.map((pk) => OpenOrders.load(connection, pk, mangoGroup.dexProgramId)))
|
2021-05-13 19:10:46 -07:00
|
|
|
|
|
|
|
// Settle all
|
|
|
|
const settleTransactions: Promise<string>[] = []
|
2021-04-09 13:18:06 -07:00
|
|
|
for (let i = 0; i < NUM_MARKETS; i++) {
|
|
|
|
const oo = liqorOpenOrders[i]
|
|
|
|
if (parseFloat(oo.quoteTokenTotal.toString()) > 0 || parseFloat(oo.baseTokenTotal.toString()) > 0) {
|
2021-05-13 19:10:46 -07:00
|
|
|
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]))
|
2021-04-09 13:18:06 -07:00
|
|
|
}
|
|
|
|
}
|
2021-05-13 19:10:46 -07:00
|
|
|
await Promise.all(settleTransactions)
|
2021-04-09 13:18:06 -07:00
|
|
|
|
2021-05-13 19:10:46 -07:00
|
|
|
// Account wallets should instantly update
|
|
|
|
const liqorWalletAccounts = await getMultipleAccounts(connection, liqorWallets, 'processed' as Commitment)
|
2021-04-09 16:47:57 -07:00
|
|
|
liqorValuesUi = liqorWalletAccounts.map(
|
|
|
|
(a, i) => nativeToUi(parseTokenAccountData(a.accountInfo.data).amount, mangoGroup.mintDecimals[i])
|
|
|
|
)
|
2021-04-09 13:18:06 -07:00
|
|
|
|
|
|
|
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]])
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort in decreasing order so you sell first then buy
|
|
|
|
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
|
2021-04-09 17:25:53 -07:00
|
|
|
const tokenDecimalAdj = Math.pow(10, tokenDecimals)
|
2021-04-09 13:18:06 -07:00
|
|
|
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
|
|
|
|
}
|
|
|
|
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],
|
2021-04-18 07:08:44 -07:00
|
|
|
feeDiscountPubkey: null // TODO find liqor's SRM fee account
|
2021-04-09 13:18:06 -07:00
|
|
|
}
|
|
|
|
)
|
2021-04-18 07:08:44 -07:00
|
|
|
// TODO add a SettleFunds instruction to this transaction
|
2021-04-09 14:37:10 -07:00
|
|
|
console.log(`Place order successful: ${txid}; Settling funds`)
|
2021-04-09 13:18:06 -07:00
|
|
|
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(
|
|
|
|
connection,
|
|
|
|
{
|
|
|
|
owner: liqor,
|
|
|
|
payer: liqorWallets[NUM_TOKENS-1],
|
|
|
|
side: 'buy',
|
|
|
|
price,
|
|
|
|
size,
|
|
|
|
orderType: 'ioc',
|
|
|
|
openOrdersAddressKey: liqorOpenOrdersKeys[marketIndex],
|
|
|
|
feeDiscountPubkey: null
|
|
|
|
}
|
|
|
|
)
|
2021-04-09 14:37:10 -07:00
|
|
|
console.log(`Place order successful: ${txid}; Settling funds`)
|
2021-04-09 13:18:06 -07:00
|
|
|
await market.settleFunds(connection, liqor, liqorOpenOrders[marketIndex], liqorWallets[marketIndex], liqorWallets[NUM_TOKENS-1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-06 17:43:51 -07:00
|
|
|
|
|
|
|
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]
|
2021-04-09 14:37:10 -07:00
|
|
|
const targetsStr = process.env.TARGETS || "0.1 2"
|
2021-04-18 07:08:44 -07:00
|
|
|
const checkInterval = parseFloat(process.env.CHECK_INTERVAL || "1000.0")
|
2021-04-09 14:37:10 -07:00
|
|
|
const targets = targetsStr.split(' ').map((s) => parseFloat(s))
|
2021-04-06 17:43:51 -07:00
|
|
|
const connection = new Connection(clusterUrl, 'singleGossip')
|
|
|
|
|
|
|
|
// The address of the Mango Program on the blockchain
|
|
|
|
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)
|
|
|
|
|
|
|
|
// Address of the MangoGroup
|
|
|
|
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')))
|
|
|
|
|
2021-04-09 14:37:10 -07:00
|
|
|
notify(`partial liquidator launched cluster=${cluster} group=${group_name}`);
|
2021-04-06 17:43:51 -07:00
|
|
|
|
|
|
|
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
|
|
|
|
)
|
|
|
|
)
|
|
|
|
))
|
|
|
|
|
|
|
|
// load all markets
|
|
|
|
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
|
2021-04-09 13:18:06 -07:00
|
|
|
const liqorOpenOrdersKeys: PublicKey[] = []
|
|
|
|
|
|
|
|
for (let i = 0; i < NUM_MARKETS; i++) {
|
|
|
|
let openOrdersAccounts: OpenOrders[] = await markets[i].findOpenOrdersAccountsForOwner(connection, payer.publicKey)
|
2021-04-30 05:49:48 -07:00
|
|
|
if(openOrdersAccounts.length) {
|
|
|
|
liqorOpenOrdersKeys.push(openOrdersAccounts[0].publicKey)
|
|
|
|
} else {
|
|
|
|
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.')
|
2021-04-09 13:18:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
const cancelLimit = 5
|
2021-04-06 17:43:51 -07:00
|
|
|
while (true) {
|
|
|
|
try {
|
|
|
|
mangoGroup = await client.getMangoGroup(connection, mangoGroupPk)
|
|
|
|
|
2021-04-09 13:18:06 -07:00
|
|
|
let [marginAccounts, prices, vaultAccs, liqorAccs] = await Promise.all([
|
|
|
|
client.getAllMarginAccounts(connection, programId, mangoGroup),
|
|
|
|
mangoGroup.getPrices(connection),
|
|
|
|
getMultipleAccounts(connection, mangoGroup.vaults),
|
2021-05-13 19:10:46 -07:00
|
|
|
getMultipleAccounts(connection, tokenWallets, 'processed' as Commitment),
|
2021-04-09 13:18:06 -07:00
|
|
|
])
|
2021-04-06 17:43:51 -07:00
|
|
|
|
2021-04-09 13:18:06 -07:00
|
|
|
const vaultValues = vaultAccs.map(
|
2021-04-06 17:43:51 -07:00
|
|
|
(a, i) => nativeToUi(parseTokenAccountData(a.accountInfo.data).amount, mangoGroup.mintDecimals[i])
|
|
|
|
)
|
2021-04-09 13:18:06 -07:00
|
|
|
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])
|
|
|
|
)
|
|
|
|
|
2021-04-18 07:08:44 -07:00
|
|
|
console.log(prices)
|
2021-04-06 17:43:51 -07:00
|
|
|
console.log(vaultValues)
|
2021-04-09 13:18:06 -07:00
|
|
|
console.log(liqorTokenUi)
|
2021-04-06 17:43:51 -07:00
|
|
|
|
|
|
|
// FIXME: added bias to collRatio to allow other liquidators to step in for testing
|
|
|
|
let coll_bias = 0
|
|
|
|
if (process.env.COLL_BIAS) {
|
|
|
|
coll_bias = parseFloat(process.env.COLL_BIAS)
|
|
|
|
}
|
|
|
|
|
2021-04-18 07:08:44 -07:00
|
|
|
let maxBorrAcc: MarginAccount | undefined = undefined;
|
2021-04-06 17:43:51 -07:00
|
|
|
let maxBorrVal = 0;
|
2021-05-13 19:10:46 -07:00
|
|
|
let minCollAcc: MarginAccount | undefined = undefined;
|
|
|
|
let minCollVal = 99999;
|
2021-04-06 17:43:51 -07:00
|
|
|
for (let ma of marginAccounts) { // parallelize this if possible
|
|
|
|
|
|
|
|
let description = ''
|
2021-04-14 14:41:15 -07:00
|
|
|
try {
|
|
|
|
const assetsVal = ma.getAssetsVal(mangoGroup, prices)
|
|
|
|
const liabsVal = ma.getLiabsVal(mangoGroup, prices)
|
|
|
|
if (liabsVal > maxBorrVal) {
|
|
|
|
maxBorrVal = liabsVal
|
2021-04-18 07:08:44 -07:00
|
|
|
maxBorrAcc = ma
|
2021-04-14 14:41:15 -07:00
|
|
|
}
|
2021-04-06 17:43:51 -07:00
|
|
|
|
2021-04-17 20:54:47 -07:00
|
|
|
if (liabsVal < 0.1) { // too small of an account; number precision may cause errors
|
2021-04-14 14:48:32 -07:00
|
|
|
continue
|
2021-04-14 14:41:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!ma.beingLiquidated) {
|
|
|
|
let collRatio = (assetsVal / liabsVal)
|
2021-04-06 17:43:51 -07:00
|
|
|
|
2021-04-14 14:41:15 -07:00
|
|
|
const deficit = liabsVal * mangoGroup.initCollRatio - assetsVal
|
|
|
|
if (deficit < 0.1) { // too small of an account; number precision may cause errors
|
2021-04-14 14:48:32 -07:00
|
|
|
continue
|
2021-04-06 17:43:51 -07:00
|
|
|
}
|
2021-05-13 19:10:46 -07:00
|
|
|
|
|
|
|
if (collRatio < minCollVal)
|
|
|
|
{
|
|
|
|
minCollVal = collRatio
|
|
|
|
minCollAcc = ma
|
|
|
|
}
|
|
|
|
|
|
|
|
if (collRatio + coll_bias >= mangoGroup.maintCollRatio) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-04-14 14:41:15 -07:00
|
|
|
}
|
|
|
|
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
|
|
|
|
for (let i = 0; i < NUM_MARKETS; i++) {
|
|
|
|
const openOrdersAccount = ma.openOrdersAccounts[i]
|
|
|
|
if (openOrdersAccount === undefined) {
|
|
|
|
continue
|
2021-04-06 17:43:51 -07:00
|
|
|
}
|
2021-04-14 14:41:15 -07:00
|
|
|
const marketVal = openOrdersAccount.quoteTokenTotal.toNumber() + openOrdersAccount.baseTokenTotal.toNumber() * prices[i]
|
|
|
|
if (marketVal > maxMarketVal) {
|
|
|
|
maxMarketIndex = i
|
|
|
|
maxMarketVal = marketVal
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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]
|
|
|
|
if (openOrdersAccount === undefined) {
|
|
|
|
console.log('error state')
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
let numOrders = spotMarket.filterForOpenOrders(bids, asks, [openOrdersAccount]).length
|
|
|
|
const dexSigner = await PublicKey.createProgramAddress(
|
|
|
|
[
|
|
|
|
spotMarket.publicKey.toBuffer(),
|
|
|
|
spotMarket['_decoded'].vaultSignerNonce.toArrayLike(Buffer, 'le', 8)
|
|
|
|
],
|
|
|
|
spotMarket.programId
|
|
|
|
)
|
|
|
|
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
|
|
const instruction = makeForceCancelOrdersInstruction(
|
|
|
|
programId,
|
|
|
|
mangoGroup.publicKey,
|
|
|
|
payer.publicKey,
|
|
|
|
ma.publicKey,
|
|
|
|
mangoGroup.vaults[maxMarketIndex],
|
|
|
|
mangoGroup.vaults[NUM_TOKENS-1],
|
|
|
|
spotMarket.publicKey,
|
|
|
|
spotMarket.bidsAddress,
|
|
|
|
spotMarket.asksAddress,
|
|
|
|
mangoGroup.signerKey,
|
|
|
|
spotMarket['_decoded'].eventQueue,
|
|
|
|
spotMarket['_decoded'].baseVault,
|
|
|
|
spotMarket['_decoded'].quoteVault,
|
|
|
|
dexSigner,
|
|
|
|
spotMarket.programId,
|
|
|
|
ma.openOrders,
|
|
|
|
mangoGroup.oracles,
|
|
|
|
new BN(cancelLimit)
|
2021-04-06 17:43:51 -07:00
|
|
|
)
|
2021-04-14 14:41:15 -07:00
|
|
|
transaction.add(instruction)
|
|
|
|
numOrders -= cancelLimit
|
|
|
|
if (numOrders <= 0) {
|
|
|
|
break
|
2021-04-07 17:54:06 -07:00
|
|
|
}
|
2021-04-06 17:43:51 -07:00
|
|
|
}
|
2021-04-14 14:41:15 -07:00
|
|
|
}
|
2021-04-06 17:43:51 -07:00
|
|
|
|
2021-04-14 14:41:15 -07:00
|
|
|
// 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
|
|
|
|
for (let i = 0; i < NUM_TOKENS; i++) {
|
|
|
|
const netDeposit = (deposits[i] - borrows[i]) * prices[i]
|
|
|
|
if (netDeposit < minNet) {
|
|
|
|
minNet = netDeposit
|
|
|
|
minNetIndex = i
|
|
|
|
} else if (netDeposit > maxNet) {
|
|
|
|
maxNet = netDeposit
|
|
|
|
maxNetIndex = i
|
2021-04-06 17:43:51 -07:00
|
|
|
}
|
2021-04-14 14:41:15 -07:00
|
|
|
}
|
2021-04-06 17:43:51 -07:00
|
|
|
|
2021-04-14 14:41:15 -07:00
|
|
|
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}`)
|
2021-04-18 07:10:30 -07:00
|
|
|
break // This is so wallets get balanced
|
2021-04-14 14:41:15 -07:00
|
|
|
} catch (e) {
|
|
|
|
if (!e.timeout) {
|
|
|
|
throw e
|
|
|
|
} else {
|
|
|
|
notify(`unknown error: ${e}`);
|
|
|
|
console.error(e);
|
2021-04-06 17:43:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-09 13:18:06 -07:00
|
|
|
|
2021-04-18 07:08:44 -07:00
|
|
|
const maxBorrAccPk = maxBorrAcc ? maxBorrAcc.publicKey.toBase58() : ""
|
|
|
|
const maxBorrAccCr = maxBorrAcc ? maxBorrAcc.getCollateralRatio(mangoGroup, prices) : 0
|
2021-05-13 19:10:46 -07:00
|
|
|
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}`)
|
2021-04-30 05:49:48 -07:00
|
|
|
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')
|
|
|
|
}
|
2021-04-09 13:18:06 -07:00
|
|
|
|
2021-04-06 17:43:51 -07:00
|
|
|
} catch (e) {
|
|
|
|
notify(`unknown error: ${e}`);
|
|
|
|
console.error(e);
|
|
|
|
} finally {
|
2021-04-18 07:08:44 -07:00
|
|
|
await sleep(checkInterval)
|
2021-04-06 17:43:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-04-22 14:00:48 -07:00
|
|
|
runPartialLiquidator()
|
|
|
|
|