mango-v4-ui/utils/governance/listingTools.ts

258 lines
6.5 KiB
TypeScript
Raw Normal View History

2023-05-22 16:05:43 -07:00
import { AnchorProvider, Program } from '@project-serum/anchor'
import { PythHttpClient } from '@pythnetwork/client'
import { notify } from 'utils/notifications'
import { MAINNET_PYTH_PROGRAM } from './constants'
import { OPENBOOK_PROGRAM_ID } from '@blockworks-foundation/mango-v4'
import { Market } from '@project-serum/serum'
import { Connection, Keypair, PublicKey } from '@solana/web3.js'
import EmptyWallet from 'utils/wallet'
export const getOracle = async ({
2023-05-22 16:17:53 -07:00
baseSymbol,
quoteSymbol,
2023-05-22 16:05:43 -07:00
connection,
}: {
2023-05-22 16:17:53 -07:00
baseSymbol: string
quoteSymbol: string
2023-05-22 16:05:43 -07:00
connection: Connection
}) => {
try {
let oraclePk = ''
2023-05-22 16:17:53 -07:00
const pythOracle = await getPythOracle({
baseSymbol,
quoteSymbol,
connection,
})
2023-05-22 16:05:43 -07:00
if (pythOracle) {
oraclePk = pythOracle
} else {
const switchBoardOracle = await getSwitchBoardOracle({
2023-05-22 16:17:53 -07:00
baseSymbol,
quoteSymbol,
2023-05-22 16:05:43 -07:00
connection,
})
oraclePk = switchBoardOracle
}
return oraclePk
} catch (e) {
notify({
title: 'Oracle not found',
description: `${e}`,
type: 'error',
})
}
}
export const getPythOracle = async ({
2023-05-22 16:17:53 -07:00
baseSymbol,
quoteSymbol,
2023-05-22 16:05:43 -07:00
connection,
}: {
2023-05-22 16:17:53 -07:00
baseSymbol: string
quoteSymbol: string
2023-05-22 16:05:43 -07:00
connection: Connection
}) => {
try {
const pythClient = new PythHttpClient(connection, MAINNET_PYTH_PROGRAM)
const pythAccounts = await pythClient.getData()
const product = pythAccounts.products.find(
2023-05-22 16:17:53 -07:00
(x) =>
x.base === baseSymbol.toUpperCase() &&
x.quote_currency === quoteSymbol.toUpperCase()
2023-05-22 16:05:43 -07:00
)
return product?.price_account || ''
} catch (e) {
notify({
title: 'Pyth oracle fetch error',
description: `${e}`,
type: 'error',
})
return ''
}
}
export const getSwitchBoardOracle = async ({
2023-05-22 16:17:53 -07:00
baseSymbol,
quoteSymbol,
2023-05-22 16:05:43 -07:00
connection,
}: {
2023-05-22 16:17:53 -07:00
baseSymbol: string
quoteSymbol: string
2023-05-22 16:05:43 -07:00
connection: Connection
}) => {
try {
const SWITCHBOARD_PROGRAM_ID = 'SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f'
const options = AnchorProvider.defaultOptions()
const provider = new AnchorProvider(
connection,
new EmptyWallet(Keypair.generate()),
options
)
const idl = await Program.fetchIdl(
new PublicKey(SWITCHBOARD_PROGRAM_ID),
provider
)
const switchboardProgram = new Program(
idl!,
new PublicKey(SWITCHBOARD_PROGRAM_ID),
provider
)
const allFeeds =
await switchboardProgram.account.aggregatorAccountData.all()
const feedNames = allFeeds.map((x) =>
String.fromCharCode(...[...(x.account.name as number[])].filter((x) => x))
)
2023-05-22 16:17:53 -07:00
2023-05-22 16:05:43 -07:00
const possibleFeedIndexes = feedNames.reduce(function (r, v, i) {
return r.concat(
2023-05-22 16:17:53 -07:00
v.toLowerCase().includes(baseSymbol.toLowerCase()) &&
v.toLowerCase().includes(quoteSymbol.toLowerCase())
2023-05-22 16:05:43 -07:00
? i
: []
)
}, [] as number[])
const possibleFeeds = allFeeds.filter(
(x, i) => possibleFeedIndexes.includes(i) && x.account.isLocked
)
return possibleFeeds.length ? possibleFeeds[0].publicKey.toBase58() : ''
} catch (e) {
notify({
title: 'Switchboard oracle fetch error',
description: `${e}`,
type: 'error',
})
return ''
}
}
export const getBestMarket = async ({
2023-05-22 16:17:53 -07:00
baseMint,
quoteMint,
2023-05-22 16:05:43 -07:00
cluster,
connection,
}: {
2023-05-22 16:17:53 -07:00
baseMint: string
quoteMint: string
2023-05-22 16:05:43 -07:00
cluster: 'devnet' | 'mainnet-beta'
connection: Connection
}) => {
try {
const dexProgramPk = OPENBOOK_PROGRAM_ID[cluster]
const markets = await Market.findAccountsByMints(
connection,
2023-05-22 16:17:53 -07:00
new PublicKey(baseMint),
new PublicKey(quoteMint),
2023-05-22 16:05:43 -07:00
dexProgramPk
)
2023-05-22 16:05:43 -07:00
if (!markets.length) {
return undefined
}
if (markets.length === 1) {
return markets[0].publicKey
}
const marketsDataJsons = await Promise.all([
...markets.map((x) =>
fetch(`/openSerumApi/market/${x.publicKey.toBase58()}`)
),
])
const marketsData = await Promise.all([
...marketsDataJsons.map((x) => x.json()),
])
const bestMarket = marketsData.sort((a, b) => b.volume24h - a.volume24h)
2023-05-24 04:50:23 -07:00
console.log(bestMarket)
2023-05-22 16:05:43 -07:00
return new PublicKey(bestMarket[0].id)
} catch (e) {
notify({
title: 'Openbook market not found',
description: `${e}`,
type: 'error',
})
}
}
2023-05-23 07:10:22 -07:00
2023-05-24 04:50:23 -07:00
// definitions:
// baseLots = 10 ^ baseLotExponent
// quoteLots = 10 ^ quoteLotExponent
// minOrderSize = 10^(baseLotExponent - baseDecimals)
// minOrderValue = basePrice * minOrderSize
// priceIncrement = 10^(quoteLotExponent + baseDecimals - baseLotExponent - quoteDecimals)
// derive: baseLotExponent <= max[ basePrice * minOrderSize < 0.5] (start high -> go low until condition is met)
// baseLotExponent = 20
// While (baseLotExponent > 0):
// minOrderSize = 10^(baseLotExponent - baseDecimals)
// minOrderValue = basePrice * minOrderSize
// if minOrderValue < 0.5:
// break;
// Derive: quoteLotExponent <= max[ priceIncrement * basePrice / quotePrice < 0.0001 ]
// quoteLotExponent = 20
// While (quoteLotExponent > 0):
// priceIncrement = 10^(quoteLotExponent + baseDecimals - baseLotExponent - quoteDecimals)
// priceIncrementRelative = priceIncrement * basePrice / quotePrice
// if priceIncrementRelative < 0.0002:
// break;
export function calculateTradingParameters(
basePrice: number,
2023-05-24 04:50:23 -07:00
quotePrice: number,
baseDecimals: number,
quoteDecimals: number
) {
const MAX_MIN_ORDER_VALUE = 0.5
const MIN_PRICE_INCREMENT_RELATIVE = 0.0002
let minOrderSize = 0
let priceIncrement = 0
let baseLotExponent = 20
let quoteLotExponent = 20
// Calculate minimum order size
do {
minOrderSize = Math.pow(10, baseLotExponent - baseDecimals)
const minOrderValue = basePrice * minOrderSize
2023-05-24 04:50:23 -07:00
if (minOrderValue < MAX_MIN_ORDER_VALUE) {
break
}
baseLotExponent--
} while (baseLotExponent > 0)
// Calculate price increment
do {
priceIncrement = Math.pow(
10,
quoteLotExponent + baseDecimals - baseLotExponent - quoteDecimals
)
const priceIncrementRelative = (priceIncrement * basePrice) / quotePrice
if (priceIncrementRelative < MIN_PRICE_INCREMENT_RELATIVE) {
break
}
2023-05-23 07:10:22 -07:00
2023-05-24 04:50:23 -07:00
quoteLotExponent--
} while (quoteLotExponent > 0)
2023-05-23 07:10:22 -07:00
return {
2023-05-24 04:50:23 -07:00
minOrder: minOrderSize,
priceTick: priceIncrement,
2023-05-23 07:10:22 -07:00
}
}
export const getQuoteSymbol = (quoteTokenSymbol: string) => {
if (
quoteTokenSymbol.toLowerCase() === 'usdc' ||
quoteTokenSymbol.toLocaleLowerCase() === 'usdt'
) {
return 'usd'
}
return quoteTokenSymbol
}