Skip to content

Commit

Permalink
feat: add solana caip adapters and market data (#7798)
Browse files Browse the repository at this point in the history
  • Loading branch information
NeOMakinG committed Sep 25, 2024
1 parent 8a1b4cb commit 2760692
Show file tree
Hide file tree
Showing 41 changed files with 29,506 additions and 3,199 deletions.
1 change: 1 addition & 0 deletions .env.base
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ REACT_APP_FEATURE_POLYGON=true
REACT_APP_FEATURE_GNOSIS=true
REACT_APP_FEATURE_ARBITRUM=true
REACT_APP_FEATURE_ARBITRUM_NOVA=false
REACT_APP_FEATURE_SOLANA=false
REACT_APP_FEATURE_BASE=true
REACT_APP_FEATURE_OPTIMISM=true
REACT_APP_FEATURE_JAYPEGZ=true
Expand Down
1 change: 1 addition & 0 deletions .env.dev
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# feature flags
REACT_APP_FEATURE_SOLANA=true

# logging
REACT_APP_REDUX_WINDOW=false
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion packages/caip/src/adapters/coingecko/generated/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ import arbitrumNova from './eip155_42170/adapter.json'
import base from './eip155_8453/adapter.json'
import cosmos from './cosmos_cosmoshub-4/adapter.json'
import thorchain from './cosmos_thorchain-1/adapter.json'
import solana from './solana_5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/adapter.json'

export { bitcoin, bitcoincash, dogecoin, litecoin, ethereum, cosmos, avalanche, thorchain, optimism, bnbsmartchain, polygon, gnosis, arbitrum, arbitrumNova, base }
export { bitcoin, bitcoincash, dogecoin, litecoin, ethereum, cosmos, avalanche, thorchain, optimism, bnbsmartchain, polygon, gnosis, arbitrum, arbitrumNova, base, solana }

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion packages/caip/src/adapters/coingecko/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ describe('adapters:coingecko', () => {
expect(coingeckoToAssetIds('cosmos')).toEqual([atomOnCosmos, atomOnBsc])
})

it('can get AssetIds for USD Coin on EVM Chains', () => {
it('can get AssetIds for USD Coin on Solana and EVM chains using coingecko', () => {
const chainNamespace = CHAIN_NAMESPACE.Evm
const assetNamespace = 'erc20'
const usdcOnEthereum = toAssetId({
Expand Down Expand Up @@ -163,13 +163,20 @@ describe('adapters:coingecko', () => {
assetNamespace,
assetReference: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
})
const usdcOnSolana = toAssetId({
chainNamespace: CHAIN_NAMESPACE.Solana,
chainReference: CHAIN_REFERENCE.SolanaMainnet,
assetNamespace: 'spl',
assetReference: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
})
expect(coingeckoToAssetIds('usd-coin')).toEqual([
usdcOnEthereum,
usdcOnAvalanche,
usdcOnOptimism,
usdcOnPolygon,
usdcOnArbitrum,
usdcOnBase,
usdcOnSolana,
])
})
it('can get AssetIds for bridged USD Coin on EVM Chains', () => {
Expand Down
13 changes: 11 additions & 2 deletions packages/caip/src/adapters/coingecko/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import invertBy from 'lodash/invertBy'
import toLower from 'lodash/toLower'

import type { AssetId } from '../../assetId/assetId'
import { fromAssetId } from '../../assetId/assetId'
Expand Down Expand Up @@ -35,6 +34,7 @@ export enum CoingeckoAssetPlatform {
Arbitrum = 'arbitrum-one',
ArbitrumNova = 'arbitrum-nova',
Base = 'base',
Solana = 'solana',
}

type CoinGeckoId = string
Expand All @@ -57,7 +57,7 @@ export const coingeckoToAssetIds = (id: CoinGeckoId): AssetId[] =>
generatedCoingeckoToAssetIdsMap[id]

export const assetIdToCoingecko = (assetId: AssetId): CoinGeckoId | undefined =>
generatedAssetIdToCoingeckoMap[toLower(assetId)]
generatedAssetIdToCoingeckoMap[assetId]

// https://www.coingecko.com/en/api/documentation - See asset_platforms
export const chainIdToCoingeckoAssetPlatform = (chainId: ChainId): string => {
Expand Down Expand Up @@ -99,6 +99,15 @@ export const chainIdToCoingeckoAssetPlatform = (chainId: ChainId): string => {
`chainNamespace ${chainNamespace}, chainReference ${chainReference} not supported.`,
)
}
case CHAIN_NAMESPACE.Solana:
switch (chainReference) {
case CHAIN_REFERENCE.SolanaMainnet:
return CoingeckoAssetPlatform.Solana
default:
throw new Error(
`chainNamespace ${chainNamespace}, chainReference ${chainReference} not supported.`,
)
}
// No valid asset platform: https://api.coingecko.com/api/v3/asset_platforms
case CHAIN_NAMESPACE.Utxo:
default:
Expand Down
3 changes: 3 additions & 0 deletions packages/caip/src/adapters/coingecko/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ describe('adapters:coingecko:utils', () => {
'eip155:8453/slip44:60': 'ethereum',
'eip155:8453/erc20:0x4200000000000000000000000000000000000006': 'weth',
},
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp': {
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501': 'solana',
},
}
expect(result).toEqual(expected)
})
Expand Down
17 changes: 17 additions & 0 deletions packages/caip/src/adapters/coingecko/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import {
optimismChainId,
polygonAssetId,
polygonChainId,
solanaChainId,
solAssetId,
thorchainChainId,
} from '../../constants'
import {
Expand Down Expand Up @@ -180,6 +182,20 @@ export const parseData = (coins: CoingeckoCoin[]): AssetMap => {
}
}

if (Object.keys(platforms).includes(CoingeckoAssetPlatform.Solana)) {
try {
const assetId = toAssetId({
chainNamespace: CHAIN_NAMESPACE.Solana,
chainReference: CHAIN_REFERENCE.SolanaMainnet,
assetNamespace: 'spl',
assetReference: platforms[CoingeckoAssetPlatform.Solana],
})
prev[solanaChainId][assetId] = id
} catch {
// unable to create assetId, skip token
}
}

return prev
},
{
Expand All @@ -192,6 +208,7 @@ export const parseData = (coins: CoingeckoCoin[]): AssetMap => {
[arbitrumChainId]: { [arbitrumAssetId]: 'ethereum' },
[arbitrumNovaChainId]: { [arbitrumNovaAssetId]: 'ethereum' },
[baseChainId]: { [baseAssetId]: 'ethereum' },
[solanaChainId]: { [solAssetId]: 'solana' },
},
)

Expand Down
10 changes: 10 additions & 0 deletions packages/caip/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const gnosisAssetId: AssetId = 'eip155:100/slip44:60'
export const arbitrumAssetId: AssetId = 'eip155:42161/slip44:60'
export const arbitrumNovaAssetId: AssetId = 'eip155:42170/slip44:60'
export const baseAssetId: AssetId = 'eip155:8453/slip44:60'
export const solAssetId: AssetId = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501'

export const foxOnGnosisAssetId: AssetId =
'eip155:100/erc20:0x21a42669643f45bc0e086b8fc2ed70c23d67509d'
Expand Down Expand Up @@ -50,10 +51,13 @@ export const cosmosChainId: ChainId = 'cosmos:cosmoshub-4'
export const thorchainChainId: ChainId = 'cosmos:thorchain-1'
export const binanceChainId: ChainId = 'cosmos:binance-chain-tigris'

export const solanaChainId: ChainId = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'

export const CHAIN_NAMESPACE = {
Evm: 'eip155',
Utxo: 'bip122',
CosmosSdk: 'cosmos',
Solana: 'solana',
} as const

type ValidChainMap = {
Expand All @@ -77,6 +81,7 @@ export const CHAIN_REFERENCE = {
ArbitrumMainnet: '42161', // https://chainlist.org/chain/42161
ArbitrumNovaMainnet: '42170', // https://chainlist.org/chain/42170
BaseMainnet: '8453', // https://chainlist.org/chain/8453
SolanaMainnet: '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', // https://namespaces.chainagnostic.org/solana/caip2
} as const

export const ASSET_NAMESPACE = {
Expand All @@ -88,6 +93,7 @@ export const ASSET_NAMESPACE = {
bep1155: 'bep1155',
slip44: 'slip44',
ibc: 'ibc',
spl: 'spl',
} as const

export const ASSET_REFERENCE = {
Expand All @@ -107,6 +113,7 @@ export const ASSET_REFERENCE = {
Arbitrum: '60', // evm chain which uses ethereum derivation path as common practice
ArbitrumNova: '60', // evm chain which uses ethereum derivation path as common practice
Base: '60', // evm chain which uses ethereum derivation path as common practice
Solana: '501',
} as const

export const VALID_CHAIN_IDS: ValidChainMap = Object.freeze({
Expand All @@ -132,6 +139,7 @@ export const VALID_CHAIN_IDS: ValidChainMap = Object.freeze({
CHAIN_REFERENCE.ThorchainMainnet,
CHAIN_REFERENCE.BinanceMainnet,
],
[CHAIN_NAMESPACE.Solana]: [CHAIN_REFERENCE.SolanaMainnet],
})

type ValidAssetNamespace = {
Expand All @@ -150,6 +158,7 @@ export const VALID_ASSET_NAMESPACE: ValidAssetNamespace = Object.freeze({
ASSET_NAMESPACE.bep1155,
],
[CHAIN_NAMESPACE.CosmosSdk]: [ASSET_NAMESPACE.ibc, ASSET_NAMESPACE.slip44],
[CHAIN_NAMESPACE.Solana]: [ASSET_NAMESPACE.spl, ASSET_NAMESPACE.slip44],
})

// We should prob change this once we add more chains
Expand All @@ -169,4 +178,5 @@ export const FEE_ASSET_IDS = [
arbitrumAssetId,
arbitrumNovaAssetId,
baseAssetId,
solAssetId,
]
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
[
"eip155:1/erc20:0x77e06c9eccf2e797fd462a92b6d7642ef85b0a44",
"eip155:1/erc20:0x58cb30368ceb2d194740b144eab4c2da8a917dcb",
"eip155:1/erc20:0xba386a4ca26b85fd057ab1ef86e3dc7bdeb5ce70",
"eip155:1/erc20:0xaaee1a9723aadb7afa2810263653a34ba2c21c7a",
"eip155:1/erc20:0xa6c0c097741d55ecd9a3a7def3a8253fd022ceb9",
"eip155:1/erc20:0x9506d37f70eb4c3d79c398d326c871abbf10521d",
"eip155:1/erc20:0xf819d9cb1c2a819fd991781a822de3ca8607c3c9",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ export const getL1quote = async (

const { chainNamespace } = fromAssetId(sellAsset.assetId)

if (chainNamespace === CHAIN_NAMESPACE.Solana) {
return Err(
makeSwapErrorRight({
message: 'Solana is not supported',
code: TradeQuoteError.UnsupportedTradePair,
}),
)
}

const slippageTolerancePercentageDecimal =
input.slippageTolerancePercentageDecimal ??
getDefaultSlippageDecimalPercentageForSwapper(SwapperName.Thorchain)
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export enum KnownChainIds {
LitecoinMainnet = 'bip122:12a765e31ffd4059bada1e25190f6e98',
CosmosMainnet = 'cosmos:cosmoshub-4',
ThorchainMainnet = 'cosmos:thorchain-1',
SolanaMainnet = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
}

export type EvmChainId =
Expand Down
3 changes: 3 additions & 0 deletions packages/utils/src/chainIdToFeeAssetId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
ltcAssetId,
optimismAssetId,
polygonAssetId,
solAssetId,
thorchainAssetId,
} from '@shapeshiftoss/caip'
import { KnownChainIds } from '@shapeshiftoss/types'
Expand Down Expand Up @@ -53,6 +54,8 @@ export const chainIdToFeeAssetId = (_chainId: ChainId): AssetId => {
return polygonAssetId
case KnownChainIds.ThorchainMainnet:
return thorchainAssetId
case KnownChainIds.SolanaMainnet:
return solAssetId
default:
assertUnreachable(chainId)
}
Expand Down
16 changes: 16 additions & 0 deletions scripts/generateAssetData/baseAssets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,19 @@ export const base: AssetWithNetworkName = {
explorerTxLink: 'https://basescan.org/tx/',
relatedAssetKey: null,
}

export const solana: AssetWithNetworkName = {
assetId: caip.solAssetId,
chainId: caip.solanaChainId,
name: 'Solana',
networkName: 'Solana',
symbol: 'SOL',
precision: 9,
color: '#9971d8',
networkColor: '#9971d8',
icon: 'https://rawcdn.githack.com/trustwallet/assets/426526def2f327476e868ecb902c515ab17518af/blockchains/solana/info/logo.png',
explorer: 'https://explorer.solana.com',
explorerAddressLink: 'https://explorer.solana.com/address/',
explorerTxLink: 'https://explorer.solana.com/tx/',
relatedAssetKey: null,
}
10 changes: 10 additions & 0 deletions scripts/generateAssetData/coingecko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
gnosisChainId,
optimismChainId,
polygonChainId,
solanaChainId,
toAssetId,
} from '@shapeshiftoss/caip'
import type { Asset } from '@shapeshiftoss/types'
Expand All @@ -27,6 +28,7 @@ import {
gnosis,
optimism,
polygon,
solana,
} from './baseAssets'

type Token = {
Expand Down Expand Up @@ -120,6 +122,14 @@ export async function getAssets(chainId: ChainId): Promise<Asset[]> {
explorerAddressLink: base.explorerAddressLink,
explorerTxLink: base.explorerTxLink,
}
case solanaChainId:
return {
assetNamespace: ASSET_NAMESPACE.spl,
category: adapters.chainIdToCoingeckoAssetPlatform(chainId),
explorer: solana.explorer,
explorerAddressLink: solana.explorerAddressLink,
explorerTxLink: solana.explorerTxLink,
}
default:
throw new Error(`no coingecko token support for chainId: ${chainId}`)
}
Expand Down
3 changes: 3 additions & 0 deletions scripts/generateAssetData/generateAssetData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import * as gnosis from './gnosis'
import * as optimism from './optimism'
import { overrideAssets } from './overrides'
import * as polygon from './polygon'
import * as solana from './solana'
import { filterOutBlacklistedAssets } from './utils'

const generatedAssetsPath = path.join(
Expand All @@ -46,6 +47,7 @@ const generateAssetData = async () => {
const arbitrumAssets = await arbitrum.getAssets()
const arbitrumNovaAssets = await arbitrumNova.getAssets()
const baseAssets = await base.getAssets()
const solanaAssets = await solana.getAssets()

// all assets, included assets to be blacklisted
const unfilteredAssetData: Asset[] = [
Expand All @@ -65,6 +67,7 @@ const generateAssetData = async () => {
...arbitrumAssets,
...arbitrumNovaAssets,
...baseAssets,
...solanaAssets,
]

// remove blacklisted assets
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const generateTrustWalletUrl = (assetId: AssetId) => {
bip122: 'bitcoin/info',
cosmos: 'cosmos/info',
eip155: 'ethereum',
solana: 'solana',
}

const trustWalletChainName = chainNamespaceToTrustWallet[chainNamespace]
Expand Down
20 changes: 20 additions & 0 deletions scripts/generateAssetData/solana/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { solanaChainId } from '@shapeshiftoss/caip'
import type { Asset } from '@shapeshiftoss/types'
import uniqBy from 'lodash/uniqBy'

import { solana } from '../baseAssets'
import * as coingecko from '../coingecko'

export const getAssets = async (): Promise<Asset[]> => {
const results = await Promise.allSettled([coingecko.getAssets(solanaChainId)])

const [assets] = results.map(result => {
if (result.status === 'fulfilled') return result.value
console.error(result.reason)
return []
})

const allAssets = uniqBy(assets, 'assetId')

return [solana, ...allAssets]
}
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,8 @@ export const useTradeExecution = (
cancelPollingRef.current = output?.cancelPolling
return
}
case CHAIN_NAMESPACE.Solana:
return
default:
assertUnreachable(stepSellAssetChainNamespace)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export const getChainShortName = (chainId: KnownChainIds) => {
return 'DOGE'
case KnownChainIds.LitecoinMainnet:
return 'LTC'
case KnownChainIds.SolanaMainnet:
return 'SOL'
default: {
assertUnreachable(chainId)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ export const getTradeQuoteInput = async ({
sendAddress,
}
}
case CHAIN_NAMESPACE.Solana: {
throw new Error('Solana is not supported in getTradeQuoteInput')
}
default:
assertUnreachable(chainNamespace)
}
Expand Down
1 change: 1 addition & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ const validators = {
REACT_APP_FEATURE_GNOSIS: bool({ default: false }),
REACT_APP_FEATURE_ARBITRUM: bool({ default: false }),
REACT_APP_FEATURE_ARBITRUM_NOVA: bool({ default: false }),
REACT_APP_FEATURE_SOLANA: bool({ default: false }),
REACT_APP_FEATURE_BASE: bool({ default: false }),
REACT_APP_FEATURE_ZRX_SWAP: bool({ default: false }),
REACT_APP_FEATURE_THOR_SWAP: bool({ default: false }),
Expand Down
Loading

0 comments on commit 2760692

Please sign in to comment.