Skip to content

Commit

Permalink
Mc/force close (#1006)
Browse files Browse the repository at this point in the history
* foce close serum markets, add force close oo, and improve logging

Signed-off-by: microwavedcola1 <[email protected]>

* force withdraw token script: improved logging

Signed-off-by: microwavedcola1 <[email protected]>

* script for force close token borrows, simplify, add better logging

Signed-off-by: microwavedcola1 <[email protected]>

---------

Signed-off-by: microwavedcola1 <[email protected]>
  • Loading branch information
microwavedcola1 authored Sep 4, 2024
1 parent 3f367be commit 690e709
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 106 deletions.
18 changes: 14 additions & 4 deletions ts/client/scripts/force-close-serum3-market.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,37 @@ async function forceCloseSerum3Market(): Promise<void> {
);

for (let a of mangoAccounts) {
console.log(`mango account ${a.publicKey}`);
// Cancel all orders and confirm that all have been cancelled
for (const _ of range(0, 10)) {
console.log(a.getSerum3OoAccount(MARKET_INDEX).freeSlotBits.zeroBits());
const sig = await client.serum3LiqForceCancelOrders(
group,
a,
serum3Market.serumMarketExternal,
10,
);
console.log(
` serum3LiqForceCancelOrders for ${
a.publicKey
}, sig https://explorer.solana.com/tx/${sig.signature}?cluster=${
` - serum3LiqForceCancelOrders https://explorer.solana.com/tx/${sig.signature}?cluster=${
CLUSTER == 'devnet' ? 'devnet' : ''
}`,
);

a = await a.reload(client);
if (a.getSerum3OoAccount(MARKET_INDEX).freeSlotBits.zeroBits() === 0) {
break;
}
}

const sig2 = await client.serum3CloseOpenOrders(
group,
a,
serum3Market.serumMarketExternal,
);
console.log(
` - serum3CloseOpenOrders https://explorer.solana.com/tx/${sig2.signature}?cluster=${
CLUSTER == 'devnet' ? 'devnet' : ''
}`,
);
}
}

Expand Down
116 changes: 39 additions & 77 deletions ts/client/scripts/force-close-token-borrows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@ import { AnchorProvider, Wallet } from '@coral-xyz/anchor';
import { Cluster, Connection, Keypair, PublicKey } from '@solana/web3.js';
import fs from 'fs';
import { TokenIndex } from '../src/accounts/bank';
import { HealthType } from '../src/accounts/mangoAccount';
import { MangoClient } from '../src/client';
import { MANGO_V4_ID } from '../src/constants';
import {
fetchJupiterTransaction,
fetchRoutes,
prepareMangoRouterInstructions,
} from '../src/router';
import { toNative, toUiDecimals } from '../src/utils';
import { ONE_I80F48 } from '../src/numbers/I80F48';

const CLUSTER: Cluster =
(process.env.CLUSTER_OVERRIDE as Cluster) || 'mainnet-beta';
Expand Down Expand Up @@ -56,96 +52,62 @@ async function forceCloseTokenBorrows(): Promise<void> {
);
}

const usdcBank = group.getFirstBankByTokenIndex(0 as TokenIndex);
// Get all mango accounts with borrows for given token
const mangoAccountsWithBorrows = (
await client.getAllMangoAccounts(group)
).filter((a) => a.getTokenBalanceUi(forceCloseTokenBank) < 0);

if (
forceCloseTokenBank.uiBorrows() >=
liqor.getTokenBalanceUi(forceCloseTokenBank)
) {
throw new Error(
`Ensure that liqor has enough deposits to cover borrows! forceCloseTokenBank.uiBorrows() ${forceCloseTokenBank.uiBorrows()}, liqor.getTokenBalanceUi(forceCloseTokenBank) ${liqor.getTokenBalanceUi(forceCloseTokenBank)}`,
);
}

console.log(`${liqor.toString(group, true)}`);

for (const liqee of mangoAccountsWithBorrows) {
liqor = await liqor.reload(client);
// Liqor can only liquidate borrow using deposits, since borrows are in reduce only
// Swap usdc worth token borrow (sub existing position), account for slippage using liquidation fee
// MAX_LIAB_TRANSFER guards against trying to swap to a very large amount
const amount =
Math.min(
liqee.getTokenBorrowsUi(forceCloseTokenBank) -
liqor.getTokenBalanceUi(forceCloseTokenBank),
MAX_LIAB_TRANSFER,
) *
forceCloseTokenBank.uiPrice *
(1 + forceCloseTokenBank.liquidationFee.toNumber());

console.log(
`liqor balance ${liqor.getTokenBalanceUi(
forceCloseTokenBank,
)}, liqee balance ${liqee.getTokenBalanceUi(
forceCloseTokenBank,
)}, liqor will swap further amount of $${toUiDecimals(
amount,
usdcBank.mintDecimals,
)} to ${forceCloseTokenBank.name}`,
);
const sortedByContribution = liqee
.getHealthContributionPerAssetUi(group, HealthType.init)
.filter((a) => {
const potentialAssetBank = group.getFirstBankByName(a.asset);

const amountBn = toNative(
Math.min(amount, 99999999999), // Jupiter API can't handle amounts larger than 99999999999
usdcBank.mintDecimals,
);
const { bestRoute } = await fetchRoutes(
usdcBank.mint,
forceCloseTokenBank.mint,
amountBn.toString(),
forceCloseTokenBank.liquidationFee.toNumber() * 100,
'ExactIn',
'0',
liqor.owner,
);
if (!bestRoute) {
await new Promise((r) => setTimeout(r, 500));
continue;
}
const [ixs, alts] =
bestRoute.routerName === 'Mango'
? await prepareMangoRouterInstructions(
bestRoute,
usdcBank.mint,
forceCloseTokenBank.mint,
user.publicKey,
)
: await fetchJupiterTransaction(
client.connection,
bestRoute,
user.publicKey,
0,
usdcBank.mint,
forceCloseTokenBank.mint,
const feeFactorTotal = ONE_I80F48()
.add(forceCloseTokenBank.liquidationFee)
.add(forceCloseTokenBank.platformLiquidationFee)
.mul(
ONE_I80F48()
.add(potentialAssetBank.liquidationFee)
.add(potentialAssetBank.platformLiquidationFee),
);
const sig = await client.marginTrade({
group: group,
mangoAccount: liqor,
inputMintPk: usdcBank.mint,
amountIn: amount,
outputMintPk: forceCloseTokenBank.mint,
userDefinedInstructions: ixs,
userDefinedAlts: alts,
flashLoanType: { swap: {} },
sequenceCheck: false,
});

return (
potentialAssetBank.reduceOnly != 2 &&
forceCloseTokenBank.initLiabWeight.gte(
potentialAssetBank.initLiabWeight.mul(feeFactorTotal),
)
);
})
.sort((a, b) => {
return a.contribution - b.contribution;
});
const assetBank = group.getFirstBankByName(sortedByContribution[0].asset);

console.log(
` - marginTrade, sig https://explorer.solana.com/tx/${sig}?cluster=${
CLUSTER == 'devnet' ? 'devnet' : ''
}`,
`${liqee.publicKey.toString()}, balance ${liqee.getTokenBalanceUi(forceCloseTokenBank)}, asset ${assetBank.name}, contribution ${sortedByContribution[0].contribution}`,
);

await client.tokenForceCloseBorrowsWithToken(
const sig = await client.tokenForceCloseBorrowsWithToken(
group,
liqor,
liqee,
usdcBank.tokenIndex,
assetBank.tokenIndex,
forceCloseTokenBank.tokenIndex,
);
console.log(` - sig ${sig.signature}`);
}
}

Expand Down
33 changes: 8 additions & 25 deletions ts/client/scripts/force-withdraw-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,43 +65,26 @@ async function forceWithdrawTokens(): Promise<void> {

for (const mangoAccount of mangoAccounts) {
console.log(
`${mangoAccount.getTokenBalanceUi(forceWithdrawBank)} for ${
mangoAccount.publicKey
}`,
`${mangoAccount.publicKey} ${forceWithdrawBank.name} balance ${mangoAccount.getTokenBalanceUi(forceWithdrawBank)}`,
);

try {
const sig = await client.serum3LiqForceCancelOrders(
group,
mangoAccount,
serum3Market.serumMarketExternal,
);
console.log(
` serum3LiqForceCancelOrders for ${mangoAccount.publicKey}, owner ${
mangoAccount.owner
}, sig https://explorer.solana.com/tx/${sig.signature}?cluster=${
CLUSTER == 'devnet' ? 'devnet' : ''
}`,
);
} catch (error) {
console.log(error);
}

await client
.tokenForceWithdraw(group, mangoAccount, TOKEN_INDEX)
.then((sig) => {
console.log(
` tokenForceWithdraw for ${mangoAccount.publicKey}, owner ${
mangoAccount.owner
}, sig https://explorer.solana.com/tx/${sig.signature}?cluster=${
` - tokenForceWithdraw https://explorer.solana.com/tx/${sig.signature}?cluster=${
CLUSTER == 'devnet' ? 'devnet' : ''
}`,
);
});
}

await group.reloadAll(client);
console.log(forceWithdrawBank.uiDeposits());
const groupFresh = await client.getGroup(new PublicKey(GROUP_PK));
const forceWithdrawBankFresh =
groupFresh.getFirstBankByTokenIndex(TOKEN_INDEX);
console.log(
`Final ${forceWithdrawBankFresh.name} deposits ${forceWithdrawBankFresh.uiDeposits()}`,
);
}

forceWithdrawTokens();
6 changes: 6 additions & 0 deletions ts/client/src/accounts/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,12 @@ export class Group {
return banks[0];
}

public getFirstBankByName(name: string): Bank {
const banks = this.banksMapByName.get(name);
if (!banks) throw new Error(`No bank found for name ${name}!`);
return banks[0];
}

public getFirstBankByOracle(oraclePk: PublicKey): Bank {
const banks = this.banksMapByOracle.get(oraclePk.toString());
if (!banks) throw new Error(`No bank found for oracle ${oraclePk}!`);
Expand Down

0 comments on commit 690e709

Please sign in to comment.