Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(mfi-v2-ui): action box #391

Merged
merged 88 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
486748a
feat: action box / dialog
chambaz Nov 30, 2023
56e4855
feat: action box static ui wip
chambaz Nov 30, 2023
f9b61bd
feat: actionbox token combobox
chambaz Dec 1, 2023
3fba321
feat: action box position preview
chambaz Dec 1, 2023
6d66bd4
feat: new portoflio component, health factor % bar, refactor other le…
chambaz Dec 1, 2023
1440cb9
feat: add user mode type (lite / pro) to ui store
chambaz Dec 1, 2023
b19f624
feat: new portfolio comp WIP
k0beLeenders Dec 1, 2023
a1ac421
feat: new stats component, added stats / action box to mobile layout
chambaz Dec 1, 2023
7d4a310
feat: format stats values
chambaz Dec 1, 2023
63ad9da
feat: new portfolio comp improvements
k0beLeenders Dec 4, 2023
4583bf9
feat: action box tokens abstracted and bank filtering implemented
chambaz Dec 4, 2023
6b23e59
feat: supply mode token search
chambaz Dec 4, 2023
4a8ae93
feat: borrow mode token search
chambaz Dec 4, 2023
e34f0ba
feat: action box token dropdown user holdings
chambaz Dec 4, 2023
301f0af
feat: add apys to actionbox token dropdown
chambaz Dec 4, 2023
66ddf22
feat: fix native sol holdings / formatting small numbers
chambaz Dec 5, 2023
370f6a5
feat: improved token dropdown styling
chambaz Dec 5, 2023
0a960a0
chore: store selected token in uistore
chambaz Dec 5, 2023
c0da80d
chore: store current token in state and render preview on change
chambaz Dec 5, 2023
d190989
feat: trigger action box dialog from asset list
chambaz Dec 5, 2023
6b2a0dd
feat: usemode toggle
k0beLeenders Dec 4, 2023
0c3df34
feat: usemode toggle footer
k0beLeenders Dec 5, 2023
f44a04e
feat: improved action box
k0beLeenders Dec 7, 2023
1ab2e04
fix: build errors
k0beLeenders Dec 7, 2023
dc10196
feat: improved action box
k0beLeenders Dec 7, 2023
6442e0e
fix: build errors
k0beLeenders Dec 7, 2023
b81946a
wip: checkpoint
k0beLeenders Dec 7, 2023
1785767
wip: checkpoint
k0beLeenders Dec 7, 2023
b407e54
feat: improved asset list
k0beLeenders Dec 8, 2023
1d914cb
feat: improved action box
k0beLeenders Dec 8, 2023
3f03a58
feat: action box mobile
k0beLeenders Dec 9, 2023
67fbfd3
fix: merge errors
chambaz Dec 9, 2023
b7cb486
wip action preview logic
losman0s Dec 10, 2023
5049332
feat: action box feedback
k0beLeenders Dec 10, 2023
b18e89f
chore: remove new asset banner for now
chambaz Dec 11, 2023
cab5740
feat: style action box mode dropdown
chambaz Dec 11, 2023
046c75f
fix: remove action mode override in action box useeffect
chambaz Dec 11, 2023
250a4df
fix: account for caps in max calcs
losman0s Dec 12, 2023
909d744
fix: liquidation price calc
losman0s Dec 12, 2023
1a0339a
feat: actionbox onramp feature
k0beLeenders Dec 11, 2023
ca274a7
feat: bg hover on asset rows
chambaz Dec 12, 2023
0049cf9
feat: portfolio asset card hover states
chambaz Dec 12, 2023
a6f0917
chore: escape apos
chambaz Dec 12, 2023
2144d23
fix: various bugs
losman0s Dec 13, 2023
0e52438
fix: clean up portfolio on small screens
chambaz Dec 13, 2023
2223a84
feat: add health factor tooltip
chambaz Dec 13, 2023
2656a9f
chore: improve token dropdown filtering
chambaz Dec 13, 2023
587694d
fix: remove isolated pool filter in portfolio
chambaz Dec 13, 2023
bc6c00d
chore: further token dropdown filtering
chambaz Dec 13, 2023
f3d2240
feat: make all ytokens always available
chambaz Dec 13, 2023
274f6b9
feat: show global and isolated pools
chambaz Dec 13, 2023
df92d11
chore: set token dropdown search auto focus to false
chambaz Dec 13, 2023
a34722f
fix: full width portfolio positions on mobile
chambaz Dec 13, 2023
4d4fb4a
wip: action stats
k0beLeenders Dec 12, 2023
0178f34
feat: asset box improvements and various fixes
k0beLeenders Dec 13, 2023
df9afe2
fix: build errors
k0beLeenders Dec 13, 2023
b354e88
fix: asset row fixes
k0beLeenders Dec 13, 2023
6af9200
fix: liqudation price fix
k0beLeenders Dec 14, 2023
e284c37
feat: portfolio header
k0beLeenders Dec 14, 2023
8bde71e
feat: final touchups
k0beLeenders Dec 14, 2023
727b944
fix: preview deposit amount
losman0s Dec 15, 2023
db8f190
fix: unwanted token reset
losman0s Dec 15, 2023
ca616f1
feat: filter improvements
k0beLeenders Dec 15, 2023
92c6172
fix: build error
k0beLeenders Dec 15, 2023
72a4430
feat: account for soft USD limit + add token symbol to bank
losman0s Dec 15, 2023
9b11342
feat: actionbox added wallet tokens in dropdown
k0beLeenders Dec 15, 2023
9b07a83
fix: repay token not selectable
k0beLeenders Dec 15, 2023
73fd7d2
fix: removed toast
k0beLeenders Dec 15, 2023
15f3c70
feat: remove command search on mobile
chambaz Dec 15, 2023
d9d4f9a
feat: actionbox simplified
k0beLeenders Dec 15, 2023
ac3ddbc
feat: component state selected token
k0beLeenders Dec 15, 2023
787bf9b
fix: build error
k0beLeenders Dec 15, 2023
2a7c151
feat: actionbox filter & sorting improvement
k0beLeenders Dec 16, 2023
979013a
feat: preview available collateral
losman0s Dec 16, 2023
16f097a
feat: warning in action box
losman0s Dec 17, 2023
2b098ee
chore: log cleanup
losman0s Dec 17, 2023
16f32cc
fix: rebase fix
losman0s Dec 17, 2023
504772b
chore: format
losman0s Dec 17, 2023
7bd1b1b
feat: actionbox actions improvement & filter
k0beLeenders Dec 17, 2023
21e07d5
fix: build errors
k0beLeenders Dec 17, 2023
0f85036
feat: preview improvements
losman0s Dec 18, 2023
7efc81c
fix: more specific error message
losman0s Dec 18, 2023
bb9c0e5
feat: improved instructions in action box actions
k0beLeenders Dec 18, 2023
bb47ad5
fix: health and liquidation colors
k0beLeenders Dec 18, 2023
cf4c80e
fix: build error
k0beLeenders Dec 18, 2023
4b5ea83
fix: refrsh flicker
k0beLeenders Dec 18, 2023
a097b72
fix: skeleton adjustment
k0beLeenders Dec 18, 2023
5e4b601
fix: rebase shenanigans
losman0s Dec 18, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions apps/alpha-liquidator/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,20 @@ if (!process.env.RPC_ENDPOINT) {

/*eslint sort-keys: "error"*/
let envSchema = z.object({
ACCOUNT_COOL_DOWN_SECONDS: z.string().default("120").transform((s) => parseInt(s, 10)),
ACCOUNT_COOL_DOWN_SECONDS: z
.string()
.default("120")
.transform((s) => parseInt(s, 10)),
/// 30 minutes
ACCOUNT_REFRESH_INTERVAL_SECONDS: z.string().default("1800").transform((s) => parseInt(s, 10)),
EXCLUDE_ISOLATED_BANKS: z.string().optional().default("false").transform((s) => s === "true" || s === "1"),
ACCOUNT_REFRESH_INTERVAL_SECONDS: z
.string()
.default("1800")
.transform((s) => parseInt(s, 10)),
EXCLUDE_ISOLATED_BANKS: z
.string()
.optional()
.default("false")
.transform((s) => s === "true" || s === "1"),
IS_DEV: z
.string()
.optional()
Expand All @@ -52,7 +62,11 @@ let envSchema = z.object({
return pkArrayStr.split(",").map((pkStr) => new PublicKey(pkStr));
})
.optional(),
MAX_SLIPPAGE_BPS: z.string().optional().default("250").transform((s) => Number.parseInt(s, 10)),
MAX_SLIPPAGE_BPS: z
.string()
.optional()
.default("250")
.transform((s) => Number.parseInt(s, 10)),
MIN_LIQUIDATION_AMOUNT_USD_UI: z
.string()
.default("0.1")
Expand Down Expand Up @@ -86,7 +100,11 @@ let envSchema = z.object({
}
}),
WS_ENDPOINT: z.string().url().optional(),
WS_RESET_INTERVAL_SECONDS: z.string().optional().default("300").transform((s) => parseInt(s, 10)),
WS_RESET_INTERVAL_SECONDS: z
.string()
.optional()
.default("300")
.transform((s) => parseInt(s, 10)),
});

type EnvSchema = z.infer<typeof envSchema>;
Expand Down
99 changes: 49 additions & 50 deletions apps/alpha-liquidator/src/liquidator.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import { AccountInfo, Connection, LAMPORTS_PER_SOL, PublicKey, VersionedTransaction } from "@solana/web3.js";
import { Connection, LAMPORTS_PER_SOL, PublicKey, VersionedTransaction } from "@solana/web3.js";
import {
MarginRequirementType,
MarginfiAccount,
MarginfiAccountWrapper,
MarginfiClient,
PriceBias,
USDC_DECIMALS,
} from "@mrgnlabs/marginfi-client-v2";
import { nativeToUi, NodeWallet, shortenAddress, sleep, toBigNumber, uiToNative, uiToNativeBigNumber } from "@mrgnlabs/mrgn-common";
import { nativeToUi, NodeWallet, shortenAddress, sleep, uiToNative } from "@mrgnlabs/mrgn-common";
import BigNumber from "bignumber.js";
import { associatedAddress } from "@project-serum/anchor/dist/cjs/utils/token";
import { NATIVE_MINT } from "@solana/spl-token";
import { captureException, captureMessage, env_config } from "./config";
import BN, { min } from "bn.js";
import BN from "bn.js";
import { BankMetadataMap, loadBankMetadatas } from "./utils/bankMetadata";
import { Bank } from "@mrgnlabs/marginfi-client-v2/dist/models/bank";
import { chunkedGetRawMultipleAccountInfos, convertBase64StringArrayToBuffer } from "./utils/chunks";
import { chunkedGetRawMultipleAccountInfos } from "./utils/chunks";

const DUST_THRESHOLD = new BigNumber(10).pow(USDC_DECIMALS - 2);
const DUST_THRESHOLD_UI = new BigNumber(0.01);
Expand Down Expand Up @@ -93,7 +92,9 @@ class Liquidator {

private async printAccountValue() {
try {
const { assets, liabilities } = await this.account.computeHealthComponentsWithoutBias(MarginRequirementType.Equity);
const { assets, liabilities } = await this.account.computeHealthComponentsWithoutBias(
MarginRequirementType.Equity
);
const accountValue = assets.minus(liabilities);
console.log("Account Value: $%s", accountValue);
} catch (e) {
Expand Down Expand Up @@ -152,34 +153,26 @@ class Liquidator {
const mintInSymbol = this.getTokenSymbol(mintInBank);
const mintOutSymbol = this.getTokenSymbol(mintOutBank);
const amountScaled = nativeToUi(amount, mintInBank.mintDecimals);
console.log("Swapping %s %s to %s",
amountScaled,
mintInSymbol,
mintOutSymbol
);
console.log("Swapping %s %s to %s", amountScaled, mintInSymbol, mintOutSymbol);
} else {
const mintInBank = this.client.getBankByMint(mintIn)!;
const mintOutBank = this.client.getBankByMint(mintOut)!;
const mintInSymbol = this.getTokenSymbol(mintInBank);
const mintOutSymbol = this.getTokenSymbol(mintOutBank);
const amountScaled = nativeToUi(amount, mintOutBank.mintDecimals);
console.log("Swapping %s to %s %s",
mintInSymbol,
amountScaled,
mintOutSymbol,
);
console.log("Swapping %s to %s %s", mintInSymbol, amountScaled, mintOutSymbol);
}

const swapMode = swapModeExactOut ? 'ExactOut' : 'ExactIn';
const swapMode = swapModeExactOut ? "ExactOut" : "ExactIn";
const swapUrl = `https://quote-api.jup.ag/v6/quote?inputMint=${mintIn.toBase58()}&outputMint=${mintOut.toBase58()}&amount=${amount.toString()}&slippageBps=${SLIPPAGE_BPS}&swapMode=${swapMode}`;
const quoteApiResponse = await fetch(swapUrl);
const data = await quoteApiResponse.json();

const transactionResponse = await (
await fetch('https://quote-api.jup.ag/v6/swap', {
method: 'POST',
await fetch("https://quote-api.jup.ag/v6/swap", {
method: "POST",
headers: {
'Content-Type': 'application/json'
"Content-Type": "application/json",
},
body: JSON.stringify({
// quoteResponse from /quote api
Expand All @@ -191,23 +184,23 @@ class Liquidator {
// feeAccount is optional. Use if you want to charge a fee. feeBps must have been passed in /quote API.
// feeAccount: "fee_account_public_key"
computeUnitPriceMicroLamports: 50000,
})
}),
})
).json();

const { swapTransaction } = transactionResponse;

const swapTransactionBuf = Buffer.from(swapTransaction, 'base64');
const swapTransactionBuf = Buffer.from(swapTransaction, "base64");
const transaction = VersionedTransaction.deserialize(swapTransactionBuf);

transaction.sign([this.wallet.payer]);

const rawTransaction = transaction.serialize()
const rawTransaction = transaction.serialize();
const txid = await this.connection.sendRawTransaction(rawTransaction, {
maxRetries: 2,
});

await this.connection.confirmTransaction(txid, 'confirmed');
await this.connection.confirmTransaction(txid, "confirmed");

debug("Swap transaction sent: %s", txid);
}
Expand All @@ -230,7 +223,7 @@ class Liquidator {
}

private async loadAllMarginfiAccounts() {
console.log("Loading data, this may take a moment...")
console.log("Loading data, this may take a moment...");
const debug = getDebugLogger("load-all-marginfi-accounts");
debug("Loading all Marginfi accounts");
let allKeys = [];
Expand All @@ -239,18 +232,22 @@ class Liquidator {
if (env_config.MARGINFI_ACCOUNT_WHITELIST) {
allKeys = env_config.MARGINFI_ACCOUNT_WHITELIST;
} else {
allKeys = (await this.client.getAllMarginfiAccountAddresses());
allKeys = await this.client.getAllMarginfiAccountAddresses();
}

debug("Retrieved all Marginfi account addresses, found: %d", allKeys.length);
const [slot, ais] = await chunkedGetRawMultipleAccountInfos(this.connection, allKeys.map((k) => k.toBase58()), 16 * 64, 64);
const [slot, ais] = await chunkedGetRawMultipleAccountInfos(
this.connection,
allKeys.map((k) => k.toBase58()),
16 * 64,
64
);
debug("Received account information for slot %d, got: %d accounts", slot, ais.size);
this.accountKeys = allKeys;

const totalAccounts = ais.size;
let processedAccounts = 0;
for (const [key, accountInfo] of ais) {

const pubkey = new PublicKey(key);
const account = MarginfiAccountWrapper.fromAccountDataRaw(pubkey, this.client, accountInfo.data);
this.accountInfos.set(pubkey, account);
Expand Down Expand Up @@ -293,10 +290,10 @@ class Liquidator {
debug("Failed to decode Marginfi account for public key: %s, Error: %s", pubkey.toBase58(), error);
}
});
}
};

setInterval(() => fn, env_config.WS_RESET_INTERVAL_SECONDS * 1000);
fn()
fn();
}

/**
Expand All @@ -321,7 +318,10 @@ class Liquidator {

for (let { bank } of balancesWithNonUsdcDeposits) {
const maxWithdrawAmount = this.account.computeMaxWithdrawForBank(bank.address);
const balanceAssetAmount = nativeToUi(this.account.getBalance(bank.address).computeQuantity(bank).assets, bank.mintDecimals);
const balanceAssetAmount = nativeToUi(
this.account.getBalance(bank.address).computeQuantity(bank).assets,
bank.mintDecimals
);
const withdrawAmount = BigNumber.min(maxWithdrawAmount, balanceAssetAmount);

debug("Balance: %d, max withdraw: %d", balanceAssetAmount, withdrawAmount);
Expand All @@ -338,7 +338,7 @@ class Liquidator {
withdrawAmount.gte(balanceAssetAmount * 0.95)
);

debug("Withdraw tx: %s", withdrawSig)
debug("Withdraw tx: %s", withdrawSig);
this.reload();
}

Expand Down Expand Up @@ -413,11 +413,7 @@ class Liquidator {

debug("Swapping %d USDC to %s", usdcBuyingPower, this.getTokenSymbol(bank));

await this.swap(
USDC_MINT,
bank.mint,
uiToNative(usdcBuyingPower, usdcBank.mintDecimals),
);
await this.swap(USDC_MINT, bank.mint, uiToNative(usdcBuyingPower, usdcBank.mintDecimals));

const liabsUi = new BigNumber(nativeToUi(liabilities, bank.mintDecimals));
const liabsTokenAccountUi = await this.getTokenAccountBalance(bank.mint, false);
Expand All @@ -426,7 +422,11 @@ class Liquidator {
debug("Got %d %s (debt: %d), depositing to marginfi", liabsUiAmountToRepay, this.getTokenSymbol(bank), liabsUi);
debug("Paying off %d %s liabilities", liabsUiAmountToRepay, this.getTokenSymbol(bank));

const depositSig = await this.account.repay(liabsUiAmountToRepay, bank.address, liabsUiAmountToRepay.gte(liabsUi));
const depositSig = await this.account.repay(
liabsUiAmountToRepay,
bank.address,
liabsUiAmountToRepay.gte(liabsUi)
);
debug("Deposit tx: %s", depositSig);

await this.reload();
Expand Down Expand Up @@ -764,11 +764,7 @@ class Liquidator {
liabBank.mint
);

debug(
"Collateral amount to liquidate: %d for bank %s",
maxCollateralAmountToLiquidate,
collateralBank.mint
)
debug("Collateral amount to liquidate: %d for bank %s", maxCollateralAmountToLiquidate, collateralBank.mint);

const collateralAmountToLiquidate = BigNumber.min(
maxCollateralAmountToLiquidate,
Expand All @@ -780,7 +776,7 @@ class Liquidator {
const collateralUsdValue = collateralBank.computeUsdValue(
collateralPriceInfo,
new BigNumber(uiToNative(slippageAdjustedCollateralAmountToLiquidate, collateralBank.mintDecimals).toNumber()),
PriceBias.None,
PriceBias.None
);

if (collateralUsdValue.lt(MIN_LIQUIDATION_AMOUNT_USD_UI)) {
Expand All @@ -806,7 +802,6 @@ class Liquidator {
);

console.log("Liquidation tx: %s", sig);

} catch (e) {
console.error("Failed to liquidate account %s", marginfiAccount.address.toBase58());
console.error(e);
Expand Down Expand Up @@ -836,11 +831,15 @@ class Liquidator {

sortByLiquidationAmount(accounts: MarginfiAccountWrapper[]): MarginfiAccountWrapper[] {
return accounts
.filter(a => a.canBeLiquidated())
.filter(a => !this.isAccountInCoolDown(a.address))
.filter((a) => a.canBeLiquidated())
.filter((a) => !this.isAccountInCoolDown(a.address))
.sort((a, b) => {
const { assets: aAssets, liabilities: aLiabilities } = a.computeHealthComponents(MarginRequirementType.Maintenance);
const { assets: bAssets, liabilities: bLiabilities } = b.computeHealthComponents(MarginRequirementType.Maintenance);
const { assets: aAssets, liabilities: aLiabilities } = a.computeHealthComponents(
MarginRequirementType.Maintenance
);
const { assets: bAssets, liabilities: bLiabilities } = b.computeHealthComponents(
MarginRequirementType.Maintenance
);

const aMaxLiabilityPaydown = aAssets.minus(aLiabilities);
const bMaxLiabilityPaydown = bAssets.minus(bLiabilities);
Expand Down Expand Up @@ -887,4 +886,4 @@ function drawSpinner(message: string) {

function nativeToBigNumber(amount: BN): BigNumber {
return new BigNumber(amount.toString());
}
}
1 change: 0 additions & 1 deletion apps/alpha-liquidator/src/runLiquidatorJupApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,3 @@ async function startWithRestart() {
}

startWithRestart();

4 changes: 1 addition & 3 deletions apps/alpha-liquidator/src/utils/chunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export async function chunkedGetRawMultipleAccountInfos(
}

export function convertBase64StringArrayToBuffer(stringArray: string[]): Buffer {
return Buffer.concat(stringArray.map(s => Buffer.from(s, 'base64')));
return Buffer.concat(stringArray.map((s) => Buffer.from(s, "base64")));
}

export function chunkArray<T>(array: T[], chunkSize: number): T[][] {
Expand All @@ -93,5 +93,3 @@ export function chunkArray<T>(array: T[], chunkSize: number): T[][] {
}
return chunks;
}


1 change: 1 addition & 0 deletions apps/marginfi-v2-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@mui/material": "^5.11.2",
"@next/bundle-analyzer": "^13.4.19",
"@next/font": "13.1.1",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-icons": "^1.3.0",
Expand Down
Loading