From b642cbed2527afea721450958b865618da0dcd3b Mon Sep 17 00:00:00 2001 From: Finn Date: Wed, 8 May 2024 23:03:00 +0200 Subject: [PATCH 1/3] first --- pages/api/tokentiers.ts | 95 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 pages/api/tokentiers.ts diff --git a/pages/api/tokentiers.ts b/pages/api/tokentiers.ts new file mode 100644 index 00000000..db7ec348 --- /dev/null +++ b/pages/api/tokentiers.ts @@ -0,0 +1,95 @@ +import type { NextApiRequest, NextApiResponse } from 'next' +import { MangoClient } from '@blockworks-foundation/mango-v4' +import { AnchorProvider, Wallet } from '@coral-xyz/anchor' +import { Connection, PublicKey, Keypair } from '@solana/web3.js' +import { LISTING_PRESETS } from '@blockworks-foundation/mango-v4-settings/lib/helpers/listingTools' + +// Utility to build a MangoClient instance +async function buildClient(): Promise { + const clientKeypair = new Keypair() + const options = AnchorProvider.defaultOptions() + + const rpcUrl = process.env.NEXT_PUBLIC_RPC_URL // replace with ENV + if (!rpcUrl) { + throw new Error('MANGO_RPC_URL environment variable is not set') + } + + const connection = new Connection(rpcUrl, options) + const clientWallet = new Wallet(clientKeypair) + const clientProvider = new AnchorProvider(connection, clientWallet, options) + + return await MangoClient.connect( + clientProvider, + 'mainnet-beta', + new PublicKey('4MangoMjqJ2firMokCjjGgoK8d4MXcrgL7XJaL3w6fVg'), + { + idsSource: 'get-program-accounts', + }, + ) +} + +// API handler function +export default async function handler( + req: NextApiRequest, + res: NextApiResponse, +) { + try { + const client = await buildClient() + const group = await client.getGroup( + new PublicKey('78b8f4cGCwmZ9ysPFMWLaLTkkaYnUjwMJYStWe5RTSSX'), + ) + + const banks = Array.from(group.banksMapByTokenIndex.values()) + .map((banks) => banks[0]) + .sort((a, b) => a.name.localeCompare(b.name)) + + const currentTiers: Array<{ [key: string]: string }> = [] + const epsilon = 1e-8 + await Promise.all( + banks.map(async (bank) => { + if (bank?.reduceOnly != 1) { + const currentTier = Object.values(LISTING_PRESETS).find((x) => { + if (bank?.name == 'USDC' || bank?.name == 'USDT') return false + if (bank?.depositWeightScaleStartQuote != 20000000000) { + if ( + x.depositWeightScaleStartQuote === + bank?.depositWeightScaleStartQuote + ) { + return true + } + } else { + return ( + Math.abs( + x.loanOriginationFeeRate - + bank?.loanOriginationFeeRate.toNumber(), + ) < epsilon + ) + } + }) + currentTiers.push({ + [bank?.name]: currentTier?.preset_name + ? currentTier?.preset_name + : 'S', + }) + return bank + } + }), + ) + + res.status(200.0).json(currentTiers) + } catch (error: unknown) { + console.error('Failed to fetch tokens:', error) + if (error instanceof Error) { + res + .status(500) + .json({ error: 'Failed to fetch tokens', details: error.message }) + } else { + res + .status(500) + .json({ + error: 'Failed to fetch tokens', + details: 'An unexpected error occurred', + }) + } + } +} From 6e7005296efb65240172360e0cca4abfeec12d9e Mon Sep 17 00:00:00 2001 From: Finn Date: Sun, 12 May 2024 13:08:42 +0200 Subject: [PATCH 2/3] updates --- pages/api/tokentiers.ts | 220 +++++++++++++++++++++++++++++++--------- 1 file changed, 172 insertions(+), 48 deletions(-) diff --git a/pages/api/tokentiers.ts b/pages/api/tokentiers.ts index db7ec348..989cbf68 100644 --- a/pages/api/tokentiers.ts +++ b/pages/api/tokentiers.ts @@ -4,12 +4,42 @@ import { AnchorProvider, Wallet } from '@coral-xyz/anchor' import { Connection, PublicKey, Keypair } from '@solana/web3.js' import { LISTING_PRESETS } from '@blockworks-foundation/mango-v4-settings/lib/helpers/listingTools' -// Utility to build a MangoClient instance +interface TokenDetails { + reduceOnly?: number + maxBelow1Percent: { amount: number; preset: string } + maxBelow2Percent: { amount: number; preset: string } + initAssetWeight?: string + maintAssetWeight?: string + maintLiabWeight?: string + initLiabWeight?: string + mint?: string + currentTier?: string + collateralFeesPerDay?: number +} + +interface CurrentTier { + name: string + reduceOnly?: number + maxBelow1PercentAmount?: number + maxBelow1PercentPreset?: string + maxBelow2PercentAmount?: number + maxBelow2PercentPreset?: string + initAssetWeight?: string + maintAssetWeight?: string + maintLiabWeight?: string + initLiabWeight?: string + mint?: string + currentTier?: string + collateralFeesPerDay?: number +} + +type CurrentTiersResponse = CurrentTier[] + async function buildClient(): Promise { const clientKeypair = new Keypair() const options = AnchorProvider.defaultOptions() - const rpcUrl = process.env.NEXT_PUBLIC_RPC_URL // replace with ENV + const rpcUrl = process.env.NEXT_PUBLIC_RPC_URL if (!rpcUrl) { throw new Error('MANGO_RPC_URL environment variable is not set') } @@ -18,20 +48,19 @@ async function buildClient(): Promise { const clientWallet = new Wallet(clientKeypair) const clientProvider = new AnchorProvider(connection, clientWallet, options) - return await MangoClient.connect( + return MangoClient.connect( clientProvider, 'mainnet-beta', new PublicKey('4MangoMjqJ2firMokCjjGgoK8d4MXcrgL7XJaL3w6fVg'), - { - idsSource: 'get-program-accounts', - }, + { idsSource: 'get-program-accounts' }, ) } -// API handler function export default async function handler( req: NextApiRequest, - res: NextApiResponse, + res: NextApiResponse< + CurrentTiersResponse | { error: string; details: string } + >, ) { try { const client = await buildClient() @@ -40,56 +69,151 @@ export default async function handler( ) const banks = Array.from(group.banksMapByTokenIndex.values()) - .map((banks) => banks[0]) + .map((bank) => bank[0]) .sort((a, b) => a.name.localeCompare(b.name)) - const currentTiers: Array<{ [key: string]: string }> = [] - const epsilon = 1e-8 + const priceImpacts = group?.pis || [] + const tokenThresholds: { + [symbol: string]: { below1Percent: number; below2Percent: number } + } = {} + + for (const impact of priceImpacts) { + if (!tokenThresholds[impact.symbol]) { + tokenThresholds[impact.symbol] = { below1Percent: 0, below2Percent: 0 } + } + if ( + impact.avg_price_impact_percent < 1 && + impact.target_amount > tokenThresholds[impact.symbol].below1Percent + ) { + tokenThresholds[impact.symbol].below1Percent = impact.target_amount + } + if ( + impact.avg_price_impact_percent < 2 && + impact.target_amount > tokenThresholds[impact.symbol].below2Percent + ) { + tokenThresholds[impact.symbol].below2Percent = impact.target_amount + } + } + + const newRankings: { [symbol: string]: TokenDetails } = {} + + for (const [, preset] of Object.entries(LISTING_PRESETS)) { + for (const [symbol, thresholds] of Object.entries(tokenThresholds)) { + if (!newRankings[symbol]) { + newRankings[symbol] = { + maxBelow1Percent: { amount: 0, preset: 'C' }, + maxBelow2Percent: { amount: 0, preset: 'C' }, + } + } + + if (preset.preset_target_amount <= thresholds.below1Percent) { + if ( + preset.preset_target_amount >= + newRankings[symbol].maxBelow1Percent.amount + ) { + newRankings[symbol].maxBelow1Percent = { + amount: preset.preset_target_amount, + preset: preset.preset_name, + } + } + } + + if (preset.preset_target_amount <= thresholds.below2Percent) { + if ( + preset.preset_target_amount >= + newRankings[symbol].maxBelow2Percent.amount + ) { + newRankings[symbol].maxBelow2Percent = { + amount: preset.preset_target_amount, + preset: preset.preset_name, + } + } + } + } + } + await Promise.all( banks.map(async (bank) => { - if (bank?.reduceOnly != 1) { - const currentTier = Object.values(LISTING_PRESETS).find((x) => { - if (bank?.name == 'USDC' || bank?.name == 'USDT') return false - if (bank?.depositWeightScaleStartQuote != 20000000000) { - if ( - x.depositWeightScaleStartQuote === - bank?.depositWeightScaleStartQuote - ) { - return true - } - } else { - return ( - Math.abs( - x.loanOriginationFeeRate - - bank?.loanOriginationFeeRate.toNumber(), - ) < epsilon - ) - } - }) - currentTiers.push({ - [bank?.name]: currentTier?.preset_name - ? currentTier?.preset_name - : 'S', - }) - return bank + const currentTier = Object.values(LISTING_PRESETS).find((x) => { + if (bank?.platformLiquidationFee.toNumber() === 0) { + return ( + x.platformLiquidationFee.toFixed(2) === + bank?.platformLiquidationFee.toNumber().toFixed(2) + ) + } + if (bank?.initAssetWeight.toNumber() === 0) { + return ( + x.maintLiabWeight.toFixed(2) === + bank?.maintLiabWeight.toNumber().toFixed(2) + ) + } + if (bank?.depositWeightScaleStartQuote !== 20000000000) { + return ( + x.depositWeightScaleStartQuote === + bank?.depositWeightScaleStartQuote + ) + } else { + return ( + Math.abs( + x.loanOriginationFeeRate - + bank?.loanOriginationFeeRate.toNumber(), + ) < 1e-8 + ) + } + }) + + let bankName = bank?.name + if (bankName === 'WIF') bankName = '$WIF' + if (bankName === 'ETH (Portal)') bankName = 'ETH' + if (bank?.mint && newRankings[bankName]) { + newRankings[bankName].reduceOnly = bank?.reduceOnly + newRankings[bankName].currentTier = currentTier?.preset_name || 'S' + newRankings[bankName].mint = bank?.mint.toString() + newRankings[bankName].initAssetWeight = bank?.initAssetWeight + .toNumber() + .toFixed(2) + newRankings[bankName].maintAssetWeight = bank?.maintAssetWeight + .toNumber() + .toFixed(2) + newRankings[bankName].maintLiabWeight = bank?.maintLiabWeight + .toNumber() + .toFixed(2) + newRankings[bankName].initLiabWeight = bank?.initLiabWeight + .toNumber() + .toFixed(2) + newRankings[bankName].collateralFeesPerDay = bank?.collateralFeePerDay } }), ) - res.status(200.0).json(currentTiers) + const currentTiers: CurrentTiersResponse = Object.entries(newRankings).map( + ([name, details]) => ({ + name, + reduceOnly: details.reduceOnly, + maxBelow1PercentAmount: details.maxBelow1Percent.amount, + maxBelow1PercentPreset: details.maxBelow1Percent.preset, + maxBelow2PercentAmount: details.maxBelow2Percent.amount, + maxBelow2PercentPreset: details.maxBelow2Percent.preset, + initAssetWeight: details.initAssetWeight, + maintAssetWeight: details.maintAssetWeight, + maintLiabWeight: details.maintLiabWeight, + initLiabWeight: details.initLiabWeight, + mint: details.mint, + currentTier: details.currentTier, + collateralFeesPerDay: details.collateralFeesPerDay, + }), + ) + + res.status(200).json(currentTiers) } catch (error: unknown) { - console.error('Failed to fetch tokens:', error) + let errorMessage = 'An unexpected error occurred' if (error instanceof Error) { - res - .status(500) - .json({ error: 'Failed to fetch tokens', details: error.message }) - } else { - res - .status(500) - .json({ - error: 'Failed to fetch tokens', - details: 'An unexpected error occurred', - }) + errorMessage = error.message } + + console.error('Failed to fetch tokens:', error) + res + .status(500) + .json({ error: 'Failed to fetch tokens', details: errorMessage }) } } From 872d014efe53a3928a756c0af41d48622c9e6430 Mon Sep 17 00:00:00 2001 From: Finn Date: Sun, 12 May 2024 14:11:13 +0200 Subject: [PATCH 3/3] new --- pages/api/tokentiers.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pages/api/tokentiers.ts b/pages/api/tokentiers.ts index 989cbf68..124ad89c 100644 --- a/pages/api/tokentiers.ts +++ b/pages/api/tokentiers.ts @@ -2,7 +2,10 @@ import type { NextApiRequest, NextApiResponse } from 'next' import { MangoClient } from '@blockworks-foundation/mango-v4' import { AnchorProvider, Wallet } from '@coral-xyz/anchor' import { Connection, PublicKey, Keypair } from '@solana/web3.js' -import { LISTING_PRESETS } from '@blockworks-foundation/mango-v4-settings/lib/helpers/listingTools' +import { + LISTING_PRESETS, + getMidPriceImpacts, +} from '@blockworks-foundation/mango-v4-settings/lib/helpers/listingTools' interface TokenDetails { reduceOnly?: number @@ -73,11 +76,16 @@ export default async function handler( .sort((a, b) => a.name.localeCompare(b.name)) const priceImpacts = group?.pis || [] + const midPriceImpacts = getMidPriceImpacts( + priceImpacts.length ? priceImpacts : [], + ) + console.log(midPriceImpacts) + const tokenThresholds: { [symbol: string]: { below1Percent: number; below2Percent: number } } = {} - for (const impact of priceImpacts) { + for (const impact of midPriceImpacts) { if (!tokenThresholds[impact.symbol]) { tokenThresholds[impact.symbol] = { below1Percent: 0, below2Percent: 0 } }