diff --git a/ts/client/scripts/update-risk-params.ts b/ts/client/scripts/update-risk-params.ts index d0258f063..5873fc060 100644 --- a/ts/client/scripts/update-risk-params.ts +++ b/ts/client/scripts/update-risk-params.ts @@ -30,12 +30,10 @@ import { MANGO_V4_MAIN_GROUP as MANGO_V4_PRIMARY_GROUP } from '../src/constants' import { LiqorPriceImpact, buildGroupGrid, - findLargestAssetBatchUi, getEquityForMangoAccounts, } from '../src/risk'; import { buildFetch, - toNative, toNativeI80F48ForQuote, toUiDecimalsForQuote, } from '../src/utils'; @@ -167,20 +165,20 @@ async function updateTokenParams(): Promise { } // eslint-disable-next-line no-constant-condition - if (false) { - // Deposit limits header - console.log( - `${'name'.padStart(20)} ${'maxLiqBatchUi'.padStart( - 15, - )} ${'maxLiqBatchUi'.padStart(15)} ${'sellImpact'.padStart( - 12, - )}$ ${'pi %'.padStart(12)}% ${'aNDUi'.padStart( - 20, - )}${'aNDQuoteUi'.padStart(20)} ${'uiDeposits'.padStart( - 12, - )} ${'uiDeposits'.padStart(12)} ${'depositLimitsUi'.padStart(12)}`, - ); - } + // if (false) { + // // Deposit limits header + // console.log( + // `${'name'.padStart(20)} ${'maxLiqBatchUi'.padStart( + // 15, + // )} ${'maxLiqBatchUi'.padStart(15)} ${'sellImpact'.padStart( + // 12, + // )}$ ${'pi %'.padStart(12)}% ${'aNDUi'.padStart( + // 20, + // )}${'aNDQuoteUi'.padStart(20)} ${'uiDeposits'.padStart( + // 12, + // )} ${'uiDeposits'.padStart(12)} ${'depositLimitsUi'.padStart(12)}`, + // ); + // } Array.from(group.banksMapByTokenIndex.values()) .map((banks) => banks[0]) @@ -189,266 +187,270 @@ async function updateTokenParams(): Promise { const builder = Builder(NullTokenEditParams); let change = false; - try { - const tier = Object.values(LISTING_PRESETS).find((x) => - x.initLiabWeight.toFixed(1) === '1.8' - ? x.initLiabWeight.toFixed(1) === - bank?.initLiabWeight.toNumber().toFixed(1) && - x.reduceOnly === bank.reduceOnly - : x.initLiabWeight.toFixed(1) === - bank?.initLiabWeight.toNumber().toFixed(1), - ); - - // eslint-disable-next-line no-constant-condition - if (true) { - if ( - bank.uiBorrows() == 0 && - bank.reduceOnly == 2 && - bank.initAssetWeight.toNumber() == 0 && - bank.maintAssetWeight.toNumber() == 0 - ) { - builder.disableAssetLiquidation(true); - builder.oracleConfig({ - confFilter: 1000, - maxStalenessSlots: -1, - }); - change = true; - } - } + const tier = Object.values(LISTING_PRESETS).find((x) => + x.initLiabWeight.toFixed(1) === '1.8' + ? x.initLiabWeight.toFixed(1) === + bank?.initLiabWeight.toNumber().toFixed(1) && + x.reduceOnly === bank.reduceOnly + : x.initLiabWeight.toFixed(1) === + bank?.initLiabWeight.toNumber().toFixed(1), + ); + + if (!tier) { + console.log(`Cant estimate tier for ${bank.name}!`); + return; + } - // // eslint-disable-next-line no-constant-condition - // if (true) { - // if (bank.uiBorrows() == 0 && bank.reduceOnly == 1) { - // builder.disableAssetLiquidation(true); - // builder.forceWithdraw(true); - // change = true; - // } - // } - - // // eslint-disable-next-line no-constant-condition - // if (true) { - // if (!tier) { - // console.log(`${bank.name}, no tier found`); - // } else if (tier.preset_name != 'C') { - // if (tier.preset_name.includes('A')) { - // builder.liquidationFee(bank.liquidationFee.toNumber() * 0.2); - // builder.platformLiquidationFee( - // bank.liquidationFee.toNumber() * 0.8, - // ); - // } else if (tier.preset_name.includes('B')) { - // builder.liquidationFee(bank.liquidationFee.toNumber() * 0.4); - // builder.platformLiquidationFee( - // bank.liquidationFee.toNumber() * 0.6, - // ); - // } - // change = true; - // } - // } - - // eslint-disable-next-line no-constant-condition - // if (true) { - // if (!tier) { - // console.log(`${bank.name}, no tier found`); - // } else { - // console.log( - // `${bank.name.padStart(10)}, ${bank.loanFeeRate - // .mul(I80F48.fromNumber(100)) - // .toFixed(2)}, ${bank.loanOriginationFeeRate - // .mul(I80F48.fromNumber(100)) - // .toFixed(2)}, ${tier?.preset_name.padStart(5)}, ${( - // tier.loanFeeRate * 100 - // ).toFixed(2)}, ${(tier!.loanOriginationFeeRate * 100).toFixed(2)}`, - // ); - - // builder.loanFeeRate(tier!.loanFeeRate); - // builder.loanOriginationFeeRate(tier!.loanOriginationFeeRate); - // builder.flashLoanSwapFeeRate(tier!.loanOriginationFeeRate); - // change = true; - // } - // } - - // formulas are sourced from here - // https://www.notion.so/mango-markets/Mango-v4-Risk-parameter-recommendations-d309cdf5faac4aeea7560356e68532ab - - // const priceImpact = getPriceImpactForBank(midPriceImpacts, bank); - // const scaleStartQuoteUi = Math.min( - // 50 * ttlLiqorEquityUi, - // 4 * priceImpact.target_amount, - // ); - - // eslint-disable-next-line no-constant-condition - if (false) { - // Net borrow limits - const netBorrowLimitPerWindowQuote = Math.max( - 10_000, - Math.min(bank.uiDeposits() * bank.uiPrice, 300_000) / 3 + - Math.max(0, bank.uiDeposits() * bank.uiPrice - 300_000) / 5, - ); - builder.netBorrowLimitPerWindowQuote( - toNativeI80F48ForQuote(netBorrowLimitPerWindowQuote).toNumber(), + // eslint-disable-next-line no-constant-condition + // if (true) { + // if ( + // bank.uiBorrows() == 0 && + // bank.reduceOnly == 2 && + // bank.initAssetWeight.toNumber() == 0 && + // bank.maintAssetWeight.toNumber() == 0 + // ) { + // builder.disableAssetLiquidation(true); + // builder.oracleConfig({ + // confFilter: 1000, + // maxStalenessSlots: -1, + // }); + // change = true; + // } + // } + + // // eslint-disable-next-line no-constant-condition + // if (true) { + // if (bank.uiBorrows() == 0 && bank.reduceOnly == 1) { + // builder.disableAssetLiquidation(true); + // builder.forceWithdraw(true); + // change = true; + // } + // } + + // // eslint-disable-next-line no-constant-condition + // if (true) { + // if (!tier) { + // console.log(`${bank.name}, no tier found`); + // } else if (tier.preset_name != 'C') { + // if (tier.preset_name.includes('A')) { + // builder.liquidationFee(bank.liquidationFee.toNumber() * 0.2); + // builder.platformLiquidationFee( + // bank.liquidationFee.toNumber() * 0.8, + // ); + // } else if (tier.preset_name.includes('B')) { + // builder.liquidationFee(bank.liquidationFee.toNumber() * 0.4); + // builder.platformLiquidationFee( + // bank.liquidationFee.toNumber() * 0.6, + // ); + // } + // change = true; + // } + // } + + // eslint-disable-next-line no-constant-condition + // if (true) { + // if (!tier) { + // console.log(`${bank.name}, no tier found`); + // } else { + // console.log( + // `${bank.name.padStart(10)}, ${bank.loanFeeRate + // .mul(I80F48.fromNumber(100)) + // .toFixed(2)}, ${bank.loanOriginationFeeRate + // .mul(I80F48.fromNumber(100)) + // .toFixed(2)}, ${tier?.preset_name.padStart(5)}, ${( + // tier.loanFeeRate * 100 + // ).toFixed(2)}, ${(tier!.loanOriginationFeeRate * 100).toFixed(2)}`, + // ); + + // builder.loanFeeRate(tier!.loanFeeRate); + // builder.loanOriginationFeeRate(tier!.loanOriginationFeeRate); + // builder.flashLoanSwapFeeRate(tier!.loanOriginationFeeRate); + // change = true; + // } + // } + + // formulas are sourced from here + // https://www.notion.so/mango-markets/Mango-v4-Risk-parameter-recommendations-d309cdf5faac4aeea7560356e68532ab + + // const priceImpact = getPriceImpactForBank(midPriceImpacts, bank); + // const scaleStartQuoteUi = Math.min( + // 50 * ttlLiqorEquityUi, + // 4 * priceImpact.target_amount, + // ); + + // eslint-disable-next-line no-constant-condition + if (!bank.areBorrowsReduceOnly()) { + // Net borrow limits + let netBorrowLimitPerWindowQuote = Math.max( + toUiDecimalsForQuote(tier!.netBorrowLimitPerWindowQuote), + Math.min(bank.uiDeposits() * bank.uiPrice, 300_000) / 3 + + Math.max(0, bank.uiDeposits() * bank.uiPrice - 300_000) / 5, + ); + netBorrowLimitPerWindowQuote = + Math.round(netBorrowLimitPerWindowQuote / 10_000) * 10_000; + builder.netBorrowLimitPerWindowQuote( + toNativeI80F48ForQuote(netBorrowLimitPerWindowQuote).toNumber(), + ); + change = true; + if ( + netBorrowLimitPerWindowQuote != + toUiDecimalsForQuote(bank.netBorrowLimitPerWindowQuote) + ) { + console.log( + `${bank.name}, ${bank.uiDeposits() * bank.uiPrice}$ (${ + Math.min(bank.uiDeposits() * bank.uiPrice, 300_000) / 3 + }, ${ + Math.max(0, bank.uiDeposits() * bank.uiPrice - 300_000) / 5 + }), ${ + tier?.netBorrowLimitPerWindowQuote + } , new - ${netBorrowLimitPerWindowQuote}, old - ${toUiDecimalsForQuote( + bank.netBorrowLimitPerWindowQuote, + )}`, ); - change = true; - if ( - netBorrowLimitPerWindowQuote != - toUiDecimalsForQuote(bank.netBorrowLimitPerWindowQuote) - ) { - console.log( - `${ - bank.name - } new - ${netBorrowLimitPerWindowQuote.toLocaleString()}, old - ${toUiDecimalsForQuote( - bank.netBorrowLimitPerWindowQuote, - ).toLocaleString()}`, - ); - } } + } - // Deposit limits - // eslint-disable-next-line no-constant-condition - if (false) { - if (bank.maintAssetWeight.toNumber() > 0) { - { - // Find asset's largest batch in $ we would need to liquidate, batches are extreme points of a range of price drop, - // range is constrained by leverage provided - // i.e. how much volatility we expect - const r = findLargestAssetBatchUi( - pisForLiqor, - bank.name, - Math.round(bank.maintAssetWeight.toNumber() * 100), - 100 - Math.round(bank.maintAssetWeight.toNumber() * 100), - stepSize, - ); - - const maxLiqBatchQuoteUi = r[0]; - const maxLiqBatchUi = r[1]; - - const sellImpact = getPriceImpactForBank( - midPriceImpacts, - bank, - (bank.liquidationFee.toNumber() * 100) / 2, - ); - - // Deposit limit = sell impact - largest batch - const allowedNewDepositsQuoteUi = - sellImpact.target_amount - maxLiqBatchQuoteUi; - const allowedNewDepositsUi = - sellImpact.target_amount / bank.uiPrice - - maxLiqBatchQuoteUi / bank.uiPrice; - - const depositLimitUi = bank.uiDeposits() + allowedNewDepositsUi; - - // LOG - // console.log( - // `${bank.name.padStart(20)} ${maxLiqBatchUi - // .toLocaleString() - // .padStart(15)} ${maxLiqBatchQuoteUi - // .toLocaleString() - // .padStart(15)}$ ${sellImpact.target_amount - // .toLocaleString() - // .padStart(12)}$ ${sellImpact.avg_price_impact_percent - // .toLocaleString() - // .padStart(12)}% ${allowedNewDepositsUi - // .toLocaleString() - // .padStart(20)}${allowedNewDepositsQuoteUi - // .toLocaleString() - // .padStart(20)}$ ${bank - // .uiDeposits() - // .toLocaleString() - // .padStart(12)} ${(bank.uiDeposits() * bank.uiPrice) - // .toLocaleString() - // .padStart(12)}$ ${depositLimitUi - // .toLocaleString() - // .padStart(12)}`, - // ); - - builder.depositLimit(toNative(depositLimitUi, bank.mintDecimals)); - change = true; - } - } - } - - const params = builder.build(); - console.log( - `${bank.name}, ${params.disableAssetLiquidation} ${params.oracleConfig?.maxStalenessSlots} ${params.oracleConfig?.confFilter}`, - ); - - const ix = await client.program.methods - .tokenEdit( - params.oracle, - params.oracleConfig, - params.groupInsuranceFund, - params.interestRateParams, - params.loanFeeRate, - params.loanOriginationFeeRate, - params.maintAssetWeight, - params.initAssetWeight, - params.maintLiabWeight, - params.initLiabWeight, - params.liquidationFee, - params.stablePriceDelayIntervalSeconds, - params.stablePriceDelayGrowthLimit, - params.stablePriceGrowthLimit, - params.minVaultToDepositsRatio, - params.netBorrowLimitPerWindowQuote !== null - ? new BN(params.netBorrowLimitPerWindowQuote) - : null, - params.netBorrowLimitWindowSizeTs !== null - ? new BN(params.netBorrowLimitWindowSizeTs) - : null, - params.borrowWeightScaleStartQuote, - params.depositWeightScaleStartQuote, - params.resetStablePrice ?? false, - params.resetNetBorrowLimit ?? false, - params.reduceOnly, - params.name, - params.forceClose, - params.tokenConditionalSwapTakerFeeRate, - params.tokenConditionalSwapMakerFeeRate, - params.flashLoanSwapFeeRate, - params.interestCurveScaling, - params.interestTargetUtilization, - params.maintWeightShiftStart, - params.maintWeightShiftEnd, - params.maintWeightShiftAssetTarget, - params.maintWeightShiftLiabTarget, - params.maintWeightShiftAbort ?? false, - false, // setFallbackOracle, unused - params.depositLimit, - params.zeroUtilRate, - params.platformLiquidationFee, - params.disableAssetLiquidation, - params.collateralFeePerDay, - params.forceWithdraw, - ) - .accounts({ - group: group.publicKey, - oracle: bank.oracle, - admin: group.admin, - mintInfo: group.mintInfosMapByTokenIndex.get(bank.tokenIndex) - ?.publicKey, - fallbackOracle: PublicKey.default, - }) - .remainingAccounts([ - { - pubkey: bank.publicKey, - isWritable: true, - isSigner: false, - } as AccountMeta, - ]) - .instruction(); - - const tx = new Transaction({ feePayer: wallet.publicKey }).add(ix); - const simulated = await client.connection.simulateTransaction(tx); - - if (simulated.value.err) { - console.log('error', simulated.value.logs); - throw simulated.value.logs; - } + // Deposit limits + // eslint-disable-next-line no-constant-condition + // if (false) { + // if (bank.maintAssetWeight.toNumber() > 0) { + // { + // // Find asset's largest batch in $ we would need to liquidate, batches are extreme points of a range of price drop, + // // range is constrained by leverage provided + // // i.e. how much volatility we expect + // const r = findLargestAssetBatchUi( + // pisForLiqor, + // bank.name, + // Math.round(bank.maintAssetWeight.toNumber() * 100), + // 100 - Math.round(bank.maintAssetWeight.toNumber() * 100), + // stepSize, + // ); + + // const maxLiqBatchQuoteUi = r[0]; + // const maxLiqBatchUi = r[1]; + + // const sellImpact = getPriceImpactForBank( + // midPriceImpacts, + // bank, + // (bank.liquidationFee.toNumber() * 100) / 2, + // ); + + // // Deposit limit = sell impact - largest batch + // const allowedNewDepositsQuoteUi = + // sellImpact.target_amount - maxLiqBatchQuoteUi; + // const allowedNewDepositsUi = + // sellImpact.target_amount / bank.uiPrice - + // maxLiqBatchQuoteUi / bank.uiPrice; + + // const depositLimitUi = bank.uiDeposits() + allowedNewDepositsUi; + + // // LOG + // // console.log( + // // `${bank.name.padStart(20)} ${maxLiqBatchUi + // // .toLocaleString() + // // .padStart(15)} ${maxLiqBatchQuoteUi + // // .toLocaleString() + // // .padStart(15)}$ ${sellImpact.target_amount + // // .toLocaleString() + // // .padStart(12)}$ ${sellImpact.avg_price_impact_percent + // // .toLocaleString() + // // .padStart(12)}% ${allowedNewDepositsUi + // // .toLocaleString() + // // .padStart(20)}${allowedNewDepositsQuoteUi + // // .toLocaleString() + // // .padStart(20)}$ ${bank + // // .uiDeposits() + // // .toLocaleString() + // // .padStart(12)} ${(bank.uiDeposits() * bank.uiPrice) + // // .toLocaleString() + // // .padStart(12)}$ ${depositLimitUi + // // .toLocaleString() + // // .padStart(12)}`, + // // ); + + // builder.depositLimit(toNative(depositLimitUi, bank.mintDecimals)); + // change = true; + // } + // } + // } + + const params = builder.build(); + + const ix = await client.program.methods + .tokenEdit( + params.oracle, + params.oracleConfig, + params.groupInsuranceFund, + params.interestRateParams, + params.loanFeeRate, + params.loanOriginationFeeRate, + params.maintAssetWeight, + params.initAssetWeight, + params.maintLiabWeight, + params.initLiabWeight, + params.liquidationFee, + params.stablePriceDelayIntervalSeconds, + params.stablePriceDelayGrowthLimit, + params.stablePriceGrowthLimit, + params.minVaultToDepositsRatio, + params.netBorrowLimitPerWindowQuote !== null + ? new BN(params.netBorrowLimitPerWindowQuote) + : null, + params.netBorrowLimitWindowSizeTs !== null + ? new BN(params.netBorrowLimitWindowSizeTs) + : null, + params.borrowWeightScaleStartQuote, + params.depositWeightScaleStartQuote, + params.resetStablePrice ?? false, + params.resetNetBorrowLimit ?? false, + params.reduceOnly, + params.name, + params.forceClose, + params.tokenConditionalSwapTakerFeeRate, + params.tokenConditionalSwapMakerFeeRate, + params.flashLoanSwapFeeRate, + params.interestCurveScaling, + params.interestTargetUtilization, + params.maintWeightShiftStart, + params.maintWeightShiftEnd, + params.maintWeightShiftAssetTarget, + params.maintWeightShiftLiabTarget, + params.maintWeightShiftAbort ?? false, + false, // setFallbackOracle, unused + params.depositLimit, + params.zeroUtilRate, + params.platformLiquidationFee, + params.disableAssetLiquidation, + params.collateralFeePerDay, + params.forceWithdraw, + ) + .accounts({ + group: group.publicKey, + oracle: bank.oracle, + admin: group.admin, + mintInfo: group.mintInfosMapByTokenIndex.get(bank.tokenIndex) + ?.publicKey, + fallbackOracle: PublicKey.default, + }) + .remainingAccounts([ + { + pubkey: bank.publicKey, + isWritable: true, + isSigner: false, + } as AccountMeta, + ]) + .instruction(); + + const tx = new Transaction({ feePayer: wallet.publicKey }).add(ix); + const simulated = await client.connection.simulateTransaction(tx); + + if (simulated.value.err) { + console.log('error', simulated.value.logs); + throw simulated.value.logs; + } - if (change) { - instructions.push(ix); - } - } catch (error) { - console.log(`....Skipping ${bank.name}, ${error}`); + if (change) { + instructions.push(ix); } }); @@ -470,8 +472,6 @@ async function updateTokenParams(): Promise { const walletSigner = wallet as never; - console.log(DRY_RUN); - if (!DRY_RUN) { const proposalAddress = await createProposal( client.connection, @@ -480,7 +480,7 @@ async function updateTokenParams(): Promise { tokenOwnerRecord, PROPOSAL_TITLE ? PROPOSAL_TITLE - : 'Disable asset liquidation for C tier tokens in mango-v4, part 2', + : 'Update net borrow limits for tokens in mango-v4', PROPOSAL_LINK ?? '', Object.values(proposals).length, instructions,