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:39:06 -07:00
|
|
|
console.log(markets)
|
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)
|
|
|
|
|
|
|
|
return new PublicKey(bestMarket[0].id)
|
|
|
|
} catch (e) {
|
|
|
|
notify({
|
|
|
|
title: 'Openbook market not found',
|
|
|
|
description: `${e}`,
|
|
|
|
type: 'error',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|