diff --git a/packages/cli/src/actions.ts b/packages/cli/src/actions.ts index aec3c4446..fc00af91e 100644 --- a/packages/cli/src/actions.ts +++ b/packages/cli/src/actions.ts @@ -13,7 +13,13 @@ import { } from '@convergence-rfq/sdk'; import { createCvg, Opts } from './cvg'; -import { getInstrumentType, getSigConfirmation, getSize } from './helpers'; +import { + fetchBirdeyeTokenPrice, + fetchCoinGeckoTokenPrice, + getInstrumentType, + getSigConfirmation, + getSize, +} from './helpers'; import { logPk, logResponse, @@ -30,7 +36,7 @@ import { logToken, logMint, } from './logger'; -import { CoinGeckoResponse, JupTokenList } from './types'; +import { JupTokenList } from './types'; export const createMint = async (opts: Opts) => { const cvg = await createCvg(opts); @@ -502,18 +508,23 @@ export const airdropDevnetTokens = async (opts: Opts) => { export const addBaseAssetsFromJupiter = async (opts: Opts) => { try { const cvg = await createCvg(opts); - + const { birdeyeApiKey, coinGeckoApiKey } = opts; const baseAssets = await cvg.protocol().getBaseAssets(); + const registerMints = await cvg.protocol().getRegisteredMints(); // eslint-disable-next-line no-console console.log('Base assets:', baseAssets); const baseAssetsSymbols = baseAssets.map((b) => b.ticker); + const registerMintAddresses = registerMints.map((r) => + r.mintAddress.toBase58() + ); const baseAssetAddresses = baseAssets.map((b) => b.address.toBase58()); const res = await fetch('https://token.jup.ag/all'); const jupTokens: JupTokenList[] = await res.json(); const jupTokensToAdd = jupTokens.filter( (t) => !baseAssetsSymbols.includes(t.symbol) && - !baseAssetAddresses.includes(t.address) + !baseAssetAddresses.includes(t.address) && + !registerMintAddresses.includes(t.address.toString()) ); let baseAssetIndexToStart = Math.max(...baseAssets.map((b) => b.index)) + 1; @@ -522,23 +533,26 @@ export const addBaseAssetsFromJupiter = async (opts: Opts) => { for (const token of jupTokensToAdd) { try { const coingeckoId = token?.extensions?.coingeckoId; - if (!coingeckoId || coingeckoId === '') { - // eslint-disable-next-line no-console - console.log( - 'skipping token: because missing coingecko id', - token.symbol + let tokenPrice: number | undefined = undefined; + if (coingeckoId && coingeckoId !== '') { + tokenPrice = await fetchCoinGeckoTokenPrice( + coinGeckoApiKey, + coingeckoId + ); + if (!tokenPrice) { + tokenPrice = await fetchBirdeyeTokenPrice( + opts.birdeyeAPIKey, + token.address + ); + } + } else { + tokenPrice = await fetchBirdeyeTokenPrice( + birdeyeApiKey, + token.address ); - continue; } - const coinGeckoAPIKey = opts.coinGeckoApiKey; - const tokenPriceResponse = await fetch( - `https://pro-api.coingecko.com/api/v3/simple/price?ids=${coingeckoId}&vs_currencies=usd&x_cg_pro_api_key=${coinGeckoAPIKey}` - ); - const tokenPriceJson: CoinGeckoResponse = - await tokenPriceResponse.json(); - const tokenPrice = tokenPriceJson[coingeckoId]?.usd; - if (!tokenPrice) { + if (tokenPrice === undefined) { // eslint-disable-next-line no-console console.log( 'skipping token: because missing price', @@ -564,7 +578,7 @@ export const addBaseAssetsFromJupiter = async (opts: Opts) => { // eslint-disable-next-line no-console console.log('Adding base asset:', token.symbol); // eslint-disable-next-line no-console - console.log(' current baseAssetIndex:', baseAssetIndexToStart); + console.log('current baseAssetIndex:', baseAssetIndexToStart); const registerMintTxBuilder = await registerMintBuilder(cvg, { mint: new PublicKey(token.address), baseAssetIndex: baseAssetIndexToStart, diff --git a/packages/cli/src/groups/protocol.ts b/packages/cli/src/groups/protocol.ts index 9a47454dc..e0715a0ab 100644 --- a/packages/cli/src/groups/protocol.ts +++ b/packages/cli/src/groups/protocol.ts @@ -149,6 +149,10 @@ const addBaseAssetsFromJupiterCmd = (c: Command) => flags: '--coin-gecko-api-key ', description: 'coin gecko api key', }, + { + flags: '--birdeye-api-key ', + description: 'birdeye api key', + }, ] ); diff --git a/packages/cli/src/helpers.ts b/packages/cli/src/helpers.ts index afaf7d023..4de470230 100644 --- a/packages/cli/src/helpers.ts +++ b/packages/cli/src/helpers.ts @@ -9,7 +9,7 @@ import { import { Command } from 'commander'; import { Connection } from '@solana/web3.js'; -import { Instrument } from './types'; +import { CoinGeckoResponse, Instrument } from './types'; import { DEFAULT_KEYPAIR_FILE, DEFAULT_RPC_ENDPOINT } from './constants'; export const getInstrumentType = (type: string): InstrumentType => { @@ -108,3 +108,38 @@ export const getSigConfirmation = async ( }); return result?.value?.confirmationStatus; }; + +export const fetchCoinGeckoTokenPrice = async ( + coinGeckoApiKey: string, + coingeckoId: string +) => { + const tokenPriceResponse = await fetch( + `https://pro-api.coingecko.com/api/v3/simple/price?ids=${coingeckoId}&vs_currencies=usd&x_cg_pro_api_key=${coinGeckoApiKey}` + ); + const tokenPriceJson: CoinGeckoResponse = await tokenPriceResponse.json(); + + if (tokenPriceJson[coingeckoId]?.usd) { + return Number(tokenPriceJson[coingeckoId]?.usd); + } + return undefined; +}; + +export const fetchBirdeyeTokenPrice = async ( + birdeyeApiKey: string, + tokenAddress: string +) => { + const options = { + method: 'GET', + headers: { 'X-API-KEY': birdeyeApiKey }, + }; + + const tokenPriceResponse = await fetch( + `https://public-api.birdeye.so/defi/price?address=${tokenAddress}`, + options + ); + const tokenPriceJson: any = await tokenPriceResponse.json(); + if (tokenPriceJson?.success === true) { + return Number(tokenPriceJson?.data?.value); + } + return undefined; +};