-
Notifications
You must be signed in to change notification settings - Fork 176
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into caip-native-sol
- Loading branch information
Showing
13 changed files
with
684 additions
and
186 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import type { AssetId } from '@shapeshiftoss/caip' | ||
import { | ||
adapters, | ||
baseAssetId, | ||
bchAssetId, | ||
bscAssetId, | ||
btcAssetId, | ||
cosmosAssetId, | ||
dogeAssetId, | ||
ethAssetId, | ||
gnosisAssetId, | ||
ltcAssetId, | ||
polygonAssetId, | ||
thorchainAssetId, | ||
} from '@shapeshiftoss/caip' | ||
|
||
export const COINGECKO_NATIVE_ASSET_ID_TO_ASSET_ID: Partial<Record<string, AssetId>> = { | ||
bitcoin: btcAssetId, | ||
'bitcoin-cash': bchAssetId, | ||
dogecoin: dogeAssetId, | ||
litecoin: ltcAssetId, | ||
[adapters.CoingeckoAssetPlatform.Ethereum]: ethAssetId, | ||
[adapters.CoingeckoAssetPlatform.Thorchain]: thorchainAssetId, | ||
[adapters.CoingeckoAssetPlatform.Gnosis]: gnosisAssetId, | ||
[adapters.CoingeckoAssetPlatform.Cosmos]: cosmosAssetId, | ||
// This isn't a mistake - the network and id are different in the case of MATIC/POS | ||
'polygon-ecosystem-token': polygonAssetId, | ||
[adapters.CoingeckoAssetPlatform.Base]: baseAssetId, | ||
// This isn't a mistake - the network and id are different in the case of BSC | ||
binanceCoin: bscAssetId, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import type { AssetId, ChainId } from '@shapeshiftoss/caip' | ||
import type { | ||
CoinGeckoMarketCap, | ||
CoinGeckoMarketData, | ||
} from 'lib/market-service/coingecko/coingecko-types' | ||
|
||
// Non-exhaustive types, refer to https://docs.coingecko.com/reference/coins-id and other endpoints for full response schema | ||
export type CoingeckoAssetDetails = { | ||
market_data: CoinGeckoMarketData | ||
asset_platform_id: string | ||
id: string | ||
image: Record<string, string> | ||
name: string | ||
symbol: string | ||
detail_platforms: Record<string, { decimal_place: number; contract_address: string }> | ||
platforms: Record<string, string> | ||
} | ||
|
||
export type MoverAsset = Pick< | ||
CoinGeckoMarketCap, | ||
'id' | 'symbol' | 'name' | 'image' | 'market_cap_rank' | ||
> & { | ||
usd: string | ||
usd_24h_vol: string | ||
usd_1y_change: string | ||
} | ||
|
||
export type MoversResponse = { | ||
top_gainers: MoverAsset[] | ||
top_losers: MoverAsset[] | ||
} | ||
|
||
// Non-exhaustive | ||
export type TrendingCoin = { | ||
id: string | ||
coin_id: number | ||
name: string | ||
symbol: string | ||
market_cap_rank: number | ||
thumb: string | ||
small: string | ||
large: string | ||
slug: string | ||
price_btc: number | ||
score: number | ||
data: { | ||
price: number | ||
price_btc: string | ||
price_change_percentage_24h: Record<string, number> | ||
market_cap: string | ||
market_cap_btc: string | ||
total_volume: string | ||
} | ||
} | ||
|
||
export type TrendingResponse = { | ||
coins: { | ||
item: TrendingCoin | ||
}[] | ||
} | ||
|
||
export type RecentlyAddedCoin = { | ||
id: string | ||
symbol: string | ||
name: string | ||
activated_at: number | ||
} | ||
|
||
export type RecentlyAddedResponse = RecentlyAddedCoin[] | ||
|
||
export type CoingeckoAsset = { | ||
assetId: AssetId | ||
details: CoingeckoAssetDetails | ||
} | ||
|
||
export type CoingeckoList = { | ||
byId: Record<AssetId, CoingeckoAsset> | ||
ids: AssetId[] | ||
chainIds: ChainId[] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import { adapters, ASSET_NAMESPACE, bscChainId, toAssetId } from '@shapeshiftoss/caip' | ||
import type { CoingeckoAssetPlatform } from '@shapeshiftoss/caip/src/adapters' | ||
import axios from 'axios' | ||
import { queryClient } from 'context/QueryClientProvider/queryClient' | ||
import type { CoinGeckoMarketCap } from 'lib/market-service/coingecko/coingecko-types' | ||
|
||
import { COINGECKO_NATIVE_ASSET_ID_TO_ASSET_ID } from './constants' | ||
import type { | ||
CoingeckoAsset, | ||
CoingeckoAssetDetails, | ||
MoverAsset, | ||
MoversResponse, | ||
RecentlyAddedCoin, | ||
RecentlyAddedResponse, | ||
TrendingCoin, | ||
TrendingResponse, | ||
} from './types' | ||
|
||
const coingeckoBaseUrl = 'https://api.proxy.shapeshift.com/api/v1/markets' | ||
|
||
const getCoinDetails = async ( | ||
marketCap: CoinGeckoMarketCap | RecentlyAddedCoin | TrendingCoin | MoverAsset, | ||
i: number, | ||
all: CoingeckoAsset[], | ||
) => { | ||
try { | ||
const { data } = await queryClient.fetchQuery({ | ||
queryKey: ['coingecko', 'coin', marketCap.id], | ||
// Shared query across consumers, so make it infinite as there will be a lot of overlap | ||
queryFn: () => axios.get<CoingeckoAssetDetails>(`${coingeckoBaseUrl}/coins/${marketCap.id}`), | ||
gcTime: Infinity, | ||
staleTime: Infinity, | ||
}) | ||
const { asset_platform_id, id } = data | ||
|
||
const address = data.platforms?.[asset_platform_id] | ||
|
||
if (!address) return | ||
|
||
const assetId = (() => { | ||
// Handles native assets, which *may* not contain a platform_id | ||
if (COINGECKO_NATIVE_ASSET_ID_TO_ASSET_ID[id]) | ||
return COINGECKO_NATIVE_ASSET_ID_TO_ASSET_ID[id] | ||
|
||
const chainId = adapters.coingeckoAssetPlatformToChainId( | ||
asset_platform_id as CoingeckoAssetPlatform, | ||
) | ||
if (!chainId) return | ||
|
||
const assetId = toAssetId({ | ||
chainId, | ||
assetNamespace: chainId === bscChainId ? ASSET_NAMESPACE.bep20 : ASSET_NAMESPACE.erc20, | ||
assetReference: address, | ||
}) | ||
return assetId | ||
})() | ||
|
||
if (!assetId) return marketCap | ||
|
||
all[i] = { | ||
assetId, | ||
details: data, | ||
} | ||
} catch (error) { | ||
console.error(`Error fetching asset details for ${marketCap.id}:`, error) | ||
return null | ||
} | ||
} | ||
|
||
export const getCoingeckoTopMovers = async (): Promise<CoingeckoAsset[]> => { | ||
const { data } = await axios.get<MoversResponse>( | ||
`${coingeckoBaseUrl}/coins/top_gainers_losers?vs_currency=usd`, | ||
) | ||
|
||
const all: CoingeckoAsset[] = [] | ||
|
||
await Promise.allSettled( | ||
data.top_gainers | ||
.concat(data.top_losers) | ||
.map((marketData, i) => getCoinDetails(marketData, i, all)), | ||
) | ||
|
||
return all.filter(mover => Boolean(mover.assetId)) | ||
} | ||
|
||
export const getCoingeckoTrending = async (): Promise<CoingeckoAsset[]> => { | ||
const { data } = await axios.get<TrendingResponse>(`${coingeckoBaseUrl}/search/trending`) | ||
|
||
const all: CoingeckoAsset[] = [] | ||
|
||
await Promise.allSettled( | ||
data.coins.map(({ item }) => item).map((marketData, i) => getCoinDetails(marketData, i, all)), | ||
) | ||
|
||
return all.filter(mover => Boolean(mover.assetId)) | ||
} | ||
|
||
export const getCoingeckoRecentlyAdded = async (): Promise<CoingeckoAsset[]> => { | ||
const { data } = await axios.get<RecentlyAddedResponse>(`${coingeckoBaseUrl}/coins/list/new`) | ||
|
||
const all: CoingeckoAsset[] = [] | ||
|
||
await Promise.allSettled(data.map((marketData, i) => getCoinDetails(marketData, i, all))) | ||
|
||
return all.filter(mover => Boolean(mover.assetId)) | ||
} | ||
|
||
export const getCoingeckoMarkets = async ( | ||
order: 'market_cap_desc' | 'volume_desc', | ||
): Promise<CoingeckoAsset[]> => { | ||
const { data } = await axios.get<CoinGeckoMarketCap[]>( | ||
`${coingeckoBaseUrl}/coins/markets?vs_currency=usd&order=${order}`, | ||
) | ||
|
||
const all: CoingeckoAsset[] = [] | ||
|
||
await Promise.allSettled(data.map((marketData, i) => getCoinDetails(marketData, i, all))) | ||
|
||
return all.filter(mover => Boolean(mover.assetId)) | ||
} |
Oops, something went wrong.