diff --git a/.beads/ss-dx5.9.json b/.beads/ss-dx5.9.json new file mode 100644 index 00000000000..73486493ab2 --- /dev/null +++ b/.beads/ss-dx5.9.json @@ -0,0 +1,39 @@ +[ + { + "id": "ss-dx5.9", + "title": "Integrate BOB (chainId: 60808, ETH, OP Stack L2)", + "description": "TVL: $84.9M | RPC: https://rpc.gobob.xyz/ | Explorer: https://explorer.gobob.xyz | Token Support: Limited | Pattern B (RPC-only) | Bitcoin-focused L2", + "notes": "Logo: https://assets.relay.link/icons/60808/light.png (dark: dark.png)\nCoinGecko: bob-network (⚠️ chain_identifier is null - may need name-based lookup)\nArch: OP Stack L2 (Bitcoin-focused)\nViem: standard OP Stack\nChecklist: [x] chain-adapters [x] caip [x] asset-service [x] feature-flag [x] plugin [x] env-config [x] icon/logo\n\n--- RELAY ACTIVATION ---\n[x] Add to chainIdToRelayChainId in packages/swapper/src/swappers/RelaySwapper/constant.ts\n[x] Add native asset case in packages/swapper/src/swappers/RelaySwapper/utils/relayTokenToAssetId.ts\n--- ASSET DATA REGEN ---\n[x] Create scripts/generateAssetData/bob/index.ts\n[x] Add case to scripts/generateAssetData/coingecko.ts\n[x] Add import + getAssets call to scripts/generateAssetData/generateAssetData.ts\n[x] Run: yarn generate:asset-data\n--- DRAFT PR ---\n[x] Branch: feat/integrate-bob-relay (off develop)\n[x] gh pr create --draft using .github/PULL_REQUEST_TEMPLATE.md\n[x] Fill: Description, Issue (Part of #11902), Risk (Low - behind feature flag), Testing (enable flag + verify chain + relay bridge quote), Operations (behind flag checkbox)\n[x] yarn lint --fix && yarn type-check must pass before PR", + "status": "in-review", + "priority": 2, + "issue_type": "task", + "owner": "14963751+NeOMakinG@users.noreply.github.com", + "created_at": "2026-02-17T10:55:59Z", + "created_by": "NeOMakinG", + "updated_at": "2026-02-21T11:15:00Z", + "labels": [ + "evm", + "op-stack", + "relay", + "tier-2" + ], + "dependencies": [ + { + "id": "ss-dx5", + "title": "Add support for missing Relay.link EVM chains", + "description": "Master epic tracking integration of ALL chains supported by Relay.link that ShapeShift web doesn't currently support. Enables cross-chain bridging capabilities via Relay infrastructure. See https://github.com/shapeshift/web/issues/11902 for full details. All new EVM chains will use Pattern B (RPC-only + getKnownTokens) since no ShapeShift unchained indexer infrastructure exists for these chains.", + "notes": "=== FROM ISSUE COMMENTS ===\n\nLOGO PATTERN: https://assets.relay.link/icons/{chainId}/light.png (dark: dark.png)\n\nARCHITECTURE GROUPS:\n- OP Stack (standard): Ink, Unichain, BOB, Mode, Hemi, World Chain, Soneium, Zircuit, Lisk, Cyber, Blast, Zora, Redstone, Shape, Superseed, Manta Pacific\n- zkEVM (special handling): Scroll, zkSync Era, Linea, Polygon zkEVM, Taiko, Abstract\n- Arbitrum Orbit: ApeChain, Xai, Sanko\n- Non-ETH gas (10 chains): Mantle(MNT), Metis(METIS), Cronos(CRO), Ronin(RON), Sonic(S), Flow(FLOW), Berachain(BERA), Plume(PLUME), Story(IP), Stable(gUSDT)\n\nCRITICAL CHAINS:\n1. Celo (42220): Native token is BOTH native AND ERC20. Relay uses wrapped CELO. Check relayTransactionMetadata.assetRequiringApproval\n2. Blast (81457): Rebasing ETH from L1 staking yield. USDB also rebases. Handle in balance displays.\n3. Ethereal (5064014): NOT in viem. Needs custom chain definition.\n4. zkSync Era (324) + Abstract (2741): Type 113 (EIP-712) transactions. Use viem zksync chainConfig.\n5. Linea (59144): Custom linea_estimateGas RPC method.\n6. Sei (1329): Parallelized EVM + CosmWasm interop. CoinGecko uses sei-v2 NOT sei.\n\nCOINGECKO GOTCHAS: sei-\u003esei-v2, plume-\u003eplume-network, zksync-\u003ezksync, metis-\u003emetis-andromeda, morph-\u003emorph-l2, gravity-\u003egravity-alpha, boba-\u003eboba, zora-\u003ezora-network, funkichain-\u003efunki, Superposition (capital S). BOB/Zero Network have null chain_identifier. 11 chains not on CoinGecko at all.\n\nPER-CHAIN CHECKLIST: [ ] chain-adapters [ ] caip [ ] asset-service [ ] feature-flag [ ] plugin [ ] env-config [ ] icon/logo\n\n=== RELAY SWAPPER INTEGRATION (per chain) ===\n\nFILE 1: packages/swapper/src/swappers/RelaySwapper/constant.ts\n - Add to chainIdToRelayChainId map: [newChainChainId]: \u003cviem_chain\u003e.id (or numeric chain ID)\n - Import the caip chainId (e.g., scrollChainId) from @shapeshiftoss/caip\n - Import viem chain from viem/chains (or use raw numeric ID if not in viem)\n - The reverse map relayChainIdToChainId auto-generates via invert()\n\nFILE 2: packages/swapper/src/swappers/RelaySwapper/utils/relayTokenToAssetId.ts\n - Add case CHAIN_REFERENCE.NewChainMainnet: with ASSET_REFERENCE.NewChain + ASSET_NAMESPACE.slip44\n\nNO CHANGES NEEDED (for EVM chains):\n - getRelayAssetAddress.ts (uses generic isNativeEvmAsset + DEFAULT_RELAY_EVM_TOKEN_ADDRESS)\n - getRelayDefaultUserAddress.ts (falls through to DEFAULT_RELAY_EVM_USER_ADDRESS default)\n\nUPSTREAM DEPS REQUIRED (in @shapeshiftoss packages):\n - @shapeshiftoss/caip: export newChainChainId, CHAIN_REFERENCE.NewChainMainnet, ASSET_REFERENCE.NewChain\n - @shapeshiftoss/types: KnownChainIds.NewChainMainnet\n - @shapeshiftoss/chain-adapters: NewChain chain adapter class\n - viem/chains: chain config (or custom if not in viem)\n\nRELAY API: https://api.relay.link (env: VITE_RELAY_API_URL) - no per-chain config needed\n\n=== ASSET DATA REGENERATION (per chain) ===\n\nAfter integrating each chain, run a partial assets data regen to populate the token list.\n\nFILE 1 (CREATE): scripts/generateAssetData/\u003cchainname\u003e/index.ts\n Pattern (identical for all EVM chains):\n import { \u003cchainname\u003eChainId } from '@shapeshiftoss/caip'\n import type { Asset } from '@shapeshiftoss/types'\n import { \u003cchainname\u003e, unfreeze } from '@shapeshiftoss/utils'\n import * as coingecko from '../coingecko'\n export const getAssets = async (): Promise\u003cAsset[]\u003e =\u003e {\n const assets = await coingecko.getAssets(\u003cchainname\u003eChainId)\n return [...assets, unfreeze(\u003cchainname\u003e)]\n }\n\nFILE 2 (MODIFY): scripts/generateAssetData/coingecko.ts\n - Import \u003cchainname\u003eChainId from @shapeshiftoss/caip\n - Import \u003cchainname\u003e native asset from @shapeshiftoss/utils\n - Add case in the switch: case \u003cchainname\u003eChainId: return { assetNamespace: ASSET_NAMESPACE.erc20, category: adapters.chainIdToCoingeckoAssetPlatform(chainId), explorer/explorerAddressLink/explorerTxLink from \u003cchainname\u003e utils }\n - NOTE: chains not on CoinGecko will only have the native asset (no ERC20 token list)\n\nFILE 3 (MODIFY): scripts/generateAssetData/generateAssetData.ts\n - Import: import * as \u003cchainname\u003e from './\u003cchainname\u003e'\n - Call: const \u003cchainname\u003eAssets = await \u003cchainname\u003e.getAssets()\n - Spread into unfilteredAssetData: ...\u003cchainname\u003eAssets\n\nCOMMAND: yarn generate:asset-data\n This regenerates: src/generated/asset-data.json + related-asset-index + manifest + compressed assets\n\n=== DRAFT PR CREATION (final step per chain) ===\n\nAfter all code changes are complete for a chain, open a draft PR using the repo template.\n\nBRANCH NAMING: feat/integrate-\u003cchainname\u003e-relay (e.g. feat/integrate-scroll-relay)\n\nCOMMAND:\n gh pr create --draft --title 'feat: integrate \u003cChainName\u003e (chainId: \u003cid\u003e) via Relay' --body \"$(cat \u003c\u003c'PREOF'\n ## Description\n\n Add \u003cChainName\u003e (chainId: \u003cid\u003e) as a second-class EVM chain in ShapeShift Web with Relay bridge support.\n\n **Changes:**\n - Plugin registration in src/plugins/\u003cchainname\u003e/\n - Feature flag `\u003cChainName\u003e` in preferencesSlice\n - Relay swapper chain mapping in RelaySwapper/constant.ts\n - Asset data generation via CoinGecko (`\u003ccoingecko_platform_id\u003e`)\n - Environment config for RPC URL\n\n **Chain details:**\n - TVL: \u003ctvl\u003e\n - Native token: \u003ctoken\u003e (18 decimals)\n - Architecture: \u003carch_type\u003e\n - RPC: \u003crpc_url\u003e\n - Explorer: \u003cexplorer_url\u003e\n\n ## Issue (if applicable)\n\n Part of #11902\n\n ## Risk\n\n Low risk — new chain behind feature flag (`\u003cChainName\u003e`), no existing functionality affected.\n\n \u003e New chain plugin + Relay bridge mapping. No modifications to existing chain logic.\n\n ## Testing\n\n ### Engineering\n\n 1. Enable feature flag: set `VITE_FEATURE_\u003cCHAINNAME\u003e=true` in .env\n 2. Verify chain appears in supported chains list\n 3. Verify native token balance loads (connect wallet with \u003ctoken\u003e on \u003cChainName\u003e)\n 4. Verify Relay bridge quote works: bridge ETH from Ethereum → \u003cChainName\u003e\n 5. Run `yarn lint --fix \u0026\u0026 yarn type-check` — must pass\n\n ### Operations\n\n - [x] :checkered_flag: My feature is behind a flag and doesn't require operations testing (yet)\n\n ## Screenshots (if applicable)\n\n N/A — chain integration, no UI changes.\n PREOF\n )\"\n\nNOTE: Replace all \u003cplaceholders\u003e with actual chain values from the bead description/notes.\nNOTE: Use --draft flag to create as draft PR.\nNOTE: Branch off develop (main branch is develop, not main).", + "status": "open", + "priority": 0, + "issue_type": "epic", + "owner": "14963751+NeOMakinG@users.noreply.github.com", + "created_at": "2026-02-17T10:55:02Z", + "created_by": "NeOMakinG", + "updated_at": "2026-02-17T11:21:27Z", + "external_ref": "gh-11902", + "dependency_type": "parent-child" + } + ], + "parent": "ss-dx5" + } +] diff --git a/.env b/.env index bf3e9b11fc2..c96b735e257 100644 --- a/.env +++ b/.env @@ -164,6 +164,7 @@ VITE_SCROLL_NODE_URL=https://rpc.scroll.io/ VITE_KATANA_NODE_URL=https://rpc.katana.network VITE_SONIC_NODE_URL=https://rpc.soniclabs.com VITE_UNICHAIN_NODE_URL=https://mainnet.unichain.org +VITE_BOB_NODE_URL=https://rpc.gobob.xyz VITE_THORCHAIN_NODE_URL=https://api.thorchain.shapeshift.com/lcd VITE_MAYACHAIN_NODE_URL=https://api.mayachain.shapeshift.com/lcd VITE_SOLANA_NODE_URL=https://api.solana.shapeshift.com/api/v1/jsonrpc @@ -320,6 +321,7 @@ VITE_FEATURE_NEAR=true VITE_FEATURE_KATANA=true VITE_FEATURE_SONIC=false VITE_FEATURE_UNICHAIN=false +VITE_FEATURE_BOB=false # Yield.xyz Feature Flag VITE_FEATURE_YIELD_XYZ=true diff --git a/.env.development b/.env.development index de9b1ed1dcc..030c39dcfd7 100644 --- a/.env.development +++ b/.env.development @@ -63,6 +63,7 @@ VITE_PLASMA_NODE_URL=https://rpc.plasma.to VITE_KATANA_NODE_URL=https://rpc.katana.network VITE_SONIC_NODE_URL=https://rpc.soniclabs.com VITE_UNICHAIN_NODE_URL=https://mainnet.unichain.org +VITE_BOB_NODE_URL=https://rpc.gobob.xyz VITE_HYPEREVM_NODE_URL=https://rpc.hyperliquid.xyz/evm VITE_MANTLE_NODE_URL=https://rpc.mantle.xyz VITE_INK_NODE_URL=https://ink.drpc.org @@ -112,6 +113,7 @@ VITE_FEATURE_SCROLL=true VITE_FEATURE_KATANA=true VITE_FEATURE_SONIC=true VITE_FEATURE_UNICHAIN=true +VITE_FEATURE_BOB=true VITE_FEATURE_TON=true VITE_FEATURE_STONFI_SWAP=true VITE_FEATURE_ACROSS_SWAP=true diff --git a/headers/csps/chains/bob.ts b/headers/csps/chains/bob.ts new file mode 100644 index 00000000000..cb597bdfa8c --- /dev/null +++ b/headers/csps/chains/bob.ts @@ -0,0 +1,10 @@ +import { loadEnv } from 'vite' + +import type { Csp } from '../../types' + +const mode = process.env.MODE ?? process.env.NODE_ENV ?? 'development' +const env = loadEnv(mode, process.cwd(), '') + +export const csp: Csp = { + 'connect-src': [env.VITE_BOB_NODE_URL], +} diff --git a/headers/csps/index.ts b/headers/csps/index.ts index 72f53da0854..bf00276a39f 100644 --- a/headers/csps/index.ts +++ b/headers/csps/index.ts @@ -11,6 +11,7 @@ import { csp as berachain } from './chains/berachain' import { csp as bitcoin } from './chains/bitcoin' import { csp as bitcoincash } from './chains/bitcoincash' import { csp as bnbsmartchain } from './chains/bnbsmartchain' +import { csp as bob } from './chains/bob' import { csp as cosmos } from './chains/cosmos' import { csp as cronos } from './chains/cronos' import { csp as dogecoin } from './chains/dogecoin' @@ -121,6 +122,7 @@ export const csps = [ cosmos, dogecoin, ethereum, + bob, gnosis, berachain, hyperevm, diff --git a/packages/caip/src/adapters/coingecko/generated/eip155_60808/adapter.json b/packages/caip/src/adapters/coingecko/generated/eip155_60808/adapter.json new file mode 100644 index 00000000000..bbff56db447 --- /dev/null +++ b/packages/caip/src/adapters/coingecko/generated/eip155_60808/adapter.json @@ -0,0 +1 @@ +{"eip155:60808/slip44:60":"ethereum"} diff --git a/packages/caip/src/adapters/coingecko/generated/index.ts b/packages/caip/src/adapters/coingecko/generated/index.ts index 2b71d25da81..cb39a17de99 100644 --- a/packages/caip/src/adapters/coingecko/generated/index.ts +++ b/packages/caip/src/adapters/coingecko/generated/index.ts @@ -15,6 +15,7 @@ import hyperevm from "./eip155_999/adapter.json"; import plasma from "./eip155_9745/adapter.json"; import katana from "./eip155_747474/adapter.json"; import linea from "./eip155_59144/adapter.json"; +import bob from "./eip155_60808/adapter.json"; import megaeth from "./eip155_4326/adapter.json"; import mantle from "./eip155_5000/adapter.json"; import berachain from "./eip155_80094/adapter.json"; @@ -47,6 +48,7 @@ export { gnosis, arbitrum, base, + bob, monad, hyperevm, plasma, diff --git a/packages/caip/src/adapters/coingecko/index.test.ts b/packages/caip/src/adapters/coingecko/index.test.ts index 3ca99d34862..bc4fcda8146 100644 --- a/packages/caip/src/adapters/coingecko/index.test.ts +++ b/packages/caip/src/adapters/coingecko/index.test.ts @@ -87,11 +87,18 @@ describe('adapters:coingecko', () => { assetNamespace: 'slip44', assetReference: ASSET_REFERENCE.Unichain, }) + const ethOnBob = toAssetId({ + chainNamespace, + chainReference: CHAIN_REFERENCE.BobMainnet, + assetNamespace: 'slip44', + assetReference: ASSET_REFERENCE.Bob, + }) expect(coingeckoToAssetIds('ethereum')).toEqual([ ethOnEthereum, ethOnOptimism, ethOnArbitrum, ethOnBase, + ethOnBob, ethOnLinea, ethOnMegaEth, ethOnInk, diff --git a/packages/caip/src/adapters/coingecko/index.ts b/packages/caip/src/adapters/coingecko/index.ts index b7559dab3cb..8f1bf977639 100644 --- a/packages/caip/src/adapters/coingecko/index.ts +++ b/packages/caip/src/adapters/coingecko/index.ts @@ -9,6 +9,7 @@ import { avalancheChainId, baseChainId, berachainChainId, + bobChainId, bscChainId, CHAIN_NAMESPACE, CHAIN_REFERENCE, @@ -63,6 +64,7 @@ export enum CoingeckoAssetPlatform { Mantle = 'mantle', Linea = 'linea', Unichain = 'unichain', + Bob = 'bob-network', Sonic = 'sonic', MegaEth = 'megaeth', Berachain = 'berachain', @@ -134,6 +136,8 @@ export const chainIdToCoingeckoAssetPlatform = (chainId: ChainId): string => { return CoingeckoAssetPlatform.Sonic case CHAIN_REFERENCE.UnichainMainnet: return CoingeckoAssetPlatform.Unichain + case CHAIN_REFERENCE.BobMainnet: + return CoingeckoAssetPlatform.Bob case CHAIN_REFERENCE.MegaEthMainnet: return CoingeckoAssetPlatform.MegaEth case CHAIN_REFERENCE.LineaMainnet: @@ -259,6 +263,8 @@ export const coingeckoAssetPlatformToChainId = ( return sonicChainId case CoingeckoAssetPlatform.Unichain: return unichainChainId + case CoingeckoAssetPlatform.Bob: + return bobChainId case CoingeckoAssetPlatform.MegaEth: return megaethChainId case CoingeckoAssetPlatform.Linea: diff --git a/packages/caip/src/adapters/coingecko/utils.test.ts b/packages/caip/src/adapters/coingecko/utils.test.ts index d41064cfe3f..1009755afea 100644 --- a/packages/caip/src/adapters/coingecko/utils.test.ts +++ b/packages/caip/src/adapters/coingecko/utils.test.ts @@ -197,6 +197,9 @@ describe('adapters:coingecko:utils', () => { 'eip155:57073': { 'eip155:57073/slip44:60': 'ethereum', }, + 'eip155:60808': { + 'eip155:60808/slip44:60': 'ethereum', + }, 'eip155:25': { 'eip155:25/slip44:60': 'crypto-com-chain', }, diff --git a/packages/caip/src/adapters/coingecko/utils.ts b/packages/caip/src/adapters/coingecko/utils.ts index 8af1b79e732..210f34deb36 100644 --- a/packages/caip/src/adapters/coingecko/utils.ts +++ b/packages/caip/src/adapters/coingecko/utils.ts @@ -15,6 +15,8 @@ import { bchChainId, berachainAssetId, berachainChainId, + bobAssetId, + bobChainId, bscAssetId, bscChainId, btcChainId, @@ -431,6 +433,20 @@ export const parseData = (coins: CoingeckoCoin[]): AssetMap => { } } + if (Object.keys(platforms).includes(CoingeckoAssetPlatform.Bob)) { + try { + const assetId = toAssetId({ + chainNamespace: CHAIN_NAMESPACE.Evm, + chainReference: CHAIN_REFERENCE.BobMainnet, + assetNamespace: 'erc20', + assetReference: platforms[CoingeckoAssetPlatform.Bob], + }) + prev[bobChainId][assetId] = id + } catch { + // unable to create assetId, skip token + } + } + if (Object.keys(platforms).includes(CoingeckoAssetPlatform.Starknet)) { try { const assetId = toAssetId({ @@ -497,6 +513,7 @@ export const parseData = (coins: CoingeckoCoin[]): AssetMap => { [scrollChainId]: { [scrollAssetId]: 'ethereum' }, [sonicChainId]: { [sonicAssetId]: 'sonic-3' }, [unichainChainId]: { [unichainAssetId]: 'ethereum' }, + [bobChainId]: { [bobAssetId]: 'ethereum' }, [solanaChainId]: { [solAssetId]: 'solana' }, [starknetChainId]: { [starknetAssetId]: 'starknet' }, [tronChainId]: { [tronAssetId]: 'tron' }, diff --git a/packages/caip/src/constants.ts b/packages/caip/src/constants.ts index bb6944d0962..63c0885fdd7 100644 --- a/packages/caip/src/constants.ts +++ b/packages/caip/src/constants.ts @@ -28,6 +28,7 @@ export const lineaAssetId: AssetId = 'eip155:59144/slip44:60' export const scrollAssetId: AssetId = 'eip155:534352/slip44:60' export const sonicAssetId: AssetId = 'eip155:146/slip44:60' export const unichainAssetId: AssetId = 'eip155:130/slip44:60' +export const bobAssetId: AssetId = 'eip155:60808/slip44:60' export const solAssetId: AssetId = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501' export const wrappedSolAssetId: AssetId = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:So11111111111111111111111111111111111111112' @@ -97,6 +98,7 @@ export const lineaChainId: ChainId = 'eip155:59144' export const scrollChainId: ChainId = 'eip155:534352' export const sonicChainId: ChainId = 'eip155:146' export const unichainChainId: ChainId = 'eip155:130' +export const bobChainId: ChainId = 'eip155:60808' export const cosmosChainId: ChainId = 'cosmos:cosmoshub-4' export const thorchainChainId: ChainId = 'cosmos:thorchain-1' @@ -157,6 +159,7 @@ export const CHAIN_REFERENCE = { ScrollMainnet: '534352', // https://scrollscan.com SonicMainnet: '146', // https://docs.soniclabs.com UnichainMainnet: '130', // https://docs.unichain.org + BobMainnet: '60808', // https://docs.gobob.xyz SolanaMainnet: '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', // https://namespaces.chainagnostic.org/solana/caip2 TronMainnet: '0x2b6653dc', // https://developers.tron.network/docs/networks SuiMainnet: '35834a8a', // First 8 chars of SUI mainnet genesis hash @@ -210,6 +213,7 @@ export const ASSET_REFERENCE = { Scroll: '60', // evm chain which uses ethereum derivation path as common practice Sonic: '60', // evm chain which uses ethereum derivation path as common practice Unichain: '60', // evm chain which uses ethereum derivation path as common practice + Bob: '60', // evm chain which uses ethereum derivation path as common practice Solana: '501', Tron: '195', Sui: '784', @@ -248,6 +252,7 @@ export const VALID_CHAIN_IDS: ValidChainMap = Object.freeze({ CHAIN_REFERENCE.ScrollMainnet, CHAIN_REFERENCE.SonicMainnet, CHAIN_REFERENCE.UnichainMainnet, + CHAIN_REFERENCE.BobMainnet, ], [CHAIN_NAMESPACE.CosmosSdk]: [ CHAIN_REFERENCE.CosmosHubMainnet, diff --git a/packages/chain-adapters/src/evm/EvmBaseAdapter.ts b/packages/chain-adapters/src/evm/EvmBaseAdapter.ts index e097335ba8a..07ec2427af3 100644 --- a/packages/chain-adapters/src/evm/EvmBaseAdapter.ts +++ b/packages/chain-adapters/src/evm/EvmBaseAdapter.ts @@ -12,6 +12,7 @@ import { supportsAvalanche, supportsBase, supportsBerachain, + supportsBob, supportsBSC, supportsCronos, supportsETH, @@ -104,6 +105,7 @@ export const evmChainIds = [ KnownChainIds.ScrollMainnet, KnownChainIds.SonicMainnet, KnownChainIds.UnichainMainnet, + KnownChainIds.BobMainnet, ] as const export type EvmChainAdapter = EvmBaseAdapter @@ -224,6 +226,8 @@ export abstract class EvmBaseAdapter implements IChainAdap return supportsSonic(wallet) case Number(fromChainId(KnownChainIds.UnichainMainnet).chainReference): return supportsUnichain(wallet) + case Number(fromChainId(KnownChainIds.BobMainnet).chainReference): + return supportsBob(wallet) default: return false } @@ -361,6 +365,11 @@ export abstract class EvmBaseAdapter implements IChainAdap symbol: 'ETH', explorer: 'https://uniscan.xyz', }, + [KnownChainIds.BobMainnet]: { + name: 'Ethereum', + symbol: 'ETH', + explorer: 'https://explorer.gobob.xyz', + }, }[this.chainId] try { diff --git a/packages/chain-adapters/src/evm/SecondClassEvmAdapter.ts b/packages/chain-adapters/src/evm/SecondClassEvmAdapter.ts index d76fc3143ee..2bfecf9fd04 100644 --- a/packages/chain-adapters/src/evm/SecondClassEvmAdapter.ts +++ b/packages/chain-adapters/src/evm/SecondClassEvmAdapter.ts @@ -2,6 +2,7 @@ import type { AssetId, ChainId } from '@shapeshiftoss/caip' import { ASSET_NAMESPACE, berachainChainId, + bobChainId, cronosChainId, hyperEvmChainId, mantleChainId, @@ -50,6 +51,7 @@ const WRAPPED_NATIVE_CONTRACT_BY_CHAIN_ID: Partial> = { [cronosChainId]: '0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23', [sonicChainId]: '0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38', [unichainChainId]: '0x4200000000000000000000000000000000000006', + [bobChainId]: '0x4200000000000000000000000000000000000006', } const BATCH_SIZE = 500 diff --git a/packages/chain-adapters/src/evm/bob/BobChainAdapter.ts b/packages/chain-adapters/src/evm/bob/BobChainAdapter.ts new file mode 100644 index 00000000000..a9e5fa1f6f3 --- /dev/null +++ b/packages/chain-adapters/src/evm/bob/BobChainAdapter.ts @@ -0,0 +1,57 @@ +import type { AssetId } from '@shapeshiftoss/caip' +import { ASSET_REFERENCE, bobAssetId } from '@shapeshiftoss/caip' +import type { RootBip44Params } from '@shapeshiftoss/types' +import { KnownChainIds } from '@shapeshiftoss/types' + +import { ChainAdapterDisplayName } from '../../types' +import type { TokenInfo } from '../SecondClassEvmAdapter' +import { SecondClassEvmAdapter } from '../SecondClassEvmAdapter' + +const SUPPORTED_CHAIN_IDS = [KnownChainIds.BobMainnet] +const DEFAULT_CHAIN_ID = KnownChainIds.BobMainnet + +export type ChainAdapterArgs = { + rpcUrl: string + getKnownTokens: () => TokenInfo[] +} + +export const isBobChainAdapter = (adapter: unknown): adapter is ChainAdapter => { + return (adapter as ChainAdapter).getType() === KnownChainIds.BobMainnet +} + +export class ChainAdapter extends SecondClassEvmAdapter { + public static readonly rootBip44Params: RootBip44Params = { + purpose: 44, + coinType: Number(ASSET_REFERENCE.Bob), + accountNumber: 0, + } + + constructor(args: ChainAdapterArgs) { + super({ + assetId: bobAssetId, + chainId: DEFAULT_CHAIN_ID, + rootBip44Params: ChainAdapter.rootBip44Params, + supportedChainIds: SUPPORTED_CHAIN_IDS, + rpcUrl: args.rpcUrl, + getKnownTokens: args.getKnownTokens, + }) + } + + getDisplayName() { + return ChainAdapterDisplayName.Bob + } + + getName() { + return 'BOB' + } + + getType(): KnownChainIds.BobMainnet { + return KnownChainIds.BobMainnet + } + + getFeeAssetId(): AssetId { + return this.assetId + } +} + +export type { TokenInfo } diff --git a/packages/chain-adapters/src/evm/bob/index.ts b/packages/chain-adapters/src/evm/bob/index.ts new file mode 100644 index 00000000000..574c880b3ad --- /dev/null +++ b/packages/chain-adapters/src/evm/bob/index.ts @@ -0,0 +1 @@ +export * from './BobChainAdapter' diff --git a/packages/chain-adapters/src/evm/index.ts b/packages/chain-adapters/src/evm/index.ts index 316de960290..de030ba66a1 100644 --- a/packages/chain-adapters/src/evm/index.ts +++ b/packages/chain-adapters/src/evm/index.ts @@ -26,3 +26,4 @@ export * as linea from './linea' export * as scroll from './scroll' export * as sonic from './sonic' export * as unichain from './unichain' +export * as bob from './bob' diff --git a/packages/chain-adapters/src/types.ts b/packages/chain-adapters/src/types.ts index 6821cefd5bb..95ebf8ff229 100644 --- a/packages/chain-adapters/src/types.ts +++ b/packages/chain-adapters/src/types.ts @@ -57,6 +57,7 @@ type ChainSpecificAccount = ChainSpecific< [KnownChainIds.ScrollMainnet]: evm.Account [KnownChainIds.SonicMainnet]: evm.Account [KnownChainIds.UnichainMainnet]: evm.Account + [KnownChainIds.BobMainnet]: evm.Account [KnownChainIds.BitcoinMainnet]: utxo.Account [KnownChainIds.BitcoinCashMainnet]: utxo.Account [KnownChainIds.DogecoinMainnet]: utxo.Account @@ -117,6 +118,7 @@ type ChainSpecificFeeData = ChainSpecific< [KnownChainIds.ScrollMainnet]: evm.FeeData [KnownChainIds.SonicMainnet]: evm.FeeData [KnownChainIds.UnichainMainnet]: evm.FeeData + [KnownChainIds.BobMainnet]: evm.FeeData [KnownChainIds.BitcoinMainnet]: utxo.FeeData [KnownChainIds.BitcoinCashMainnet]: utxo.FeeData [KnownChainIds.DogecoinMainnet]: utxo.FeeData @@ -210,6 +212,7 @@ export type ChainSignTx = { [KnownChainIds.ScrollMainnet]: ETHSignTx [KnownChainIds.SonicMainnet]: ETHSignTx [KnownChainIds.UnichainMainnet]: ETHSignTx + [KnownChainIds.BobMainnet]: ETHSignTx [KnownChainIds.BitcoinMainnet]: BTCSignTx [KnownChainIds.BitcoinCashMainnet]: BTCSignTx [KnownChainIds.DogecoinMainnet]: BTCSignTx @@ -275,6 +278,7 @@ export type ChainSpecificBuildTxData = ChainSpecific< [KnownChainIds.ScrollMainnet]: evm.BuildTxInput [KnownChainIds.SonicMainnet]: evm.BuildTxInput [KnownChainIds.UnichainMainnet]: evm.BuildTxInput + [KnownChainIds.BobMainnet]: evm.BuildTxInput [KnownChainIds.BitcoinMainnet]: utxo.BuildTxInput [KnownChainIds.BitcoinCashMainnet]: utxo.BuildTxInput [KnownChainIds.DogecoinMainnet]: utxo.BuildTxInput @@ -390,6 +394,7 @@ type ChainSpecificGetFeeDataInput = ChainSpecific< [KnownChainIds.ScrollMainnet]: evm.GetFeeDataInput [KnownChainIds.SonicMainnet]: evm.GetFeeDataInput [KnownChainIds.UnichainMainnet]: evm.GetFeeDataInput + [KnownChainIds.BobMainnet]: evm.GetFeeDataInput [KnownChainIds.BitcoinMainnet]: utxo.GetFeeDataInput [KnownChainIds.BitcoinCashMainnet]: utxo.GetFeeDataInput [KnownChainIds.DogecoinMainnet]: utxo.GetFeeDataInput @@ -470,6 +475,7 @@ export enum ChainAdapterDisplayName { Scroll = 'Scroll', Sonic = 'Sonic', Unichain = 'Unichain', + Bob = 'BOB', Cosmos = 'Cosmos', Bitcoin = 'Bitcoin', BitcoinCash = 'Bitcoin Cash', diff --git a/packages/contracts/src/ethersProviderSingleton.ts b/packages/contracts/src/ethersProviderSingleton.ts index dc6e9f63a36..8b3da470a0c 100644 --- a/packages/contracts/src/ethersProviderSingleton.ts +++ b/packages/contracts/src/ethersProviderSingleton.ts @@ -50,6 +50,8 @@ export const rpcUrlByChainId = (chainId: EvmChainId): string => { return process.env.VITE_SONIC_NODE_URL case KnownChainIds.UnichainMainnet: return process.env.VITE_UNICHAIN_NODE_URL + case KnownChainIds.BobMainnet: + return process.env.VITE_BOB_NODE_URL default: return assertUnreachable(chainId) } diff --git a/packages/contracts/src/viemClient.ts b/packages/contracts/src/viemClient.ts index 3bfad17b39d..99ac4eb8dab 100644 --- a/packages/contracts/src/viemClient.ts +++ b/packages/contracts/src/viemClient.ts @@ -8,6 +8,7 @@ import { avalanche, base, berachain, + bob, bsc, cronos, gnosis, @@ -165,6 +166,11 @@ export const viemUnichainClient = createPublicClient({ transport: fallback([process.env.VITE_UNICHAIN_NODE_URL].filter(Boolean).map(url => http(url))), }) as PublicClient +export const viemBobClient = createPublicClient({ + chain: bob, + transport: fallback([process.env.VITE_BOB_NODE_URL].filter(Boolean).map(url => http(url))), +}) as PublicClient + export const viemClientByChainId: Record = { [KnownChainIds.EthereumMainnet]: viemEthMainnetClient, [KnownChainIds.BnbSmartChainMainnet]: viemBscClient, @@ -187,6 +193,7 @@ export const viemClientByChainId: Record = { [KnownChainIds.ScrollMainnet]: viemScrollClient, [KnownChainIds.SonicMainnet]: viemSonicClient, [KnownChainIds.UnichainMainnet]: viemUnichainClient, + [KnownChainIds.BobMainnet]: viemBobClient, } export const viemNetworkIdByChainId: Record = { @@ -211,6 +218,7 @@ export const viemNetworkIdByChainId: Record = { [KnownChainIds.ScrollMainnet]: scroll.id, [KnownChainIds.SonicMainnet]: sonic.id, [KnownChainIds.UnichainMainnet]: unichain.id, + [KnownChainIds.BobMainnet]: bob.id, } export const viemClientByNetworkId: Record = { @@ -235,6 +243,7 @@ export const viemClientByNetworkId: Record = { [scroll.id]: viemScrollClient, [sonic.id]: viemSonicClient, [unichain.id]: viemUnichainClient, + [bob.id]: viemBobClient, } export const assertGetViemClient = (chainId: ChainId): PublicClient => { diff --git a/packages/hdwallet-coinbase/src/coinbase.ts b/packages/hdwallet-coinbase/src/coinbase.ts index 98c1072a7f7..bc492ecae82 100644 --- a/packages/hdwallet-coinbase/src/coinbase.ts +++ b/packages/hdwallet-coinbase/src/coinbase.ts @@ -111,6 +111,7 @@ export class CoinbaseHDWallet implements core.HDWallet, core.ETHWallet { readonly _supportsPlasma = false readonly _supportsKatana = false readonly _supportsSonic = false + readonly _supportsBob = false readonly _supportsHyperEvm = false readonly _supportsMantle = false readonly _supportsInk = false diff --git a/packages/hdwallet-core/src/ethereum.ts b/packages/hdwallet-core/src/ethereum.ts index d51f7245584..48e434a64b9 100644 --- a/packages/hdwallet-core/src/ethereum.ts +++ b/packages/hdwallet-core/src/ethereum.ts @@ -209,6 +209,7 @@ export interface ETHWallet extends ETHWalletInfo, HDWallet { readonly _supportsPlasma: boolean readonly _supportsKatana: boolean readonly _supportsSonic: boolean + readonly _supportsBob: boolean readonly _supportsHyperEvm: boolean readonly _supportsMantle: boolean readonly _supportsInk: boolean diff --git a/packages/hdwallet-core/src/wallet.ts b/packages/hdwallet-core/src/wallet.ts index fd38bbff0cb..987fc09db3e 100644 --- a/packages/hdwallet-core/src/wallet.ts +++ b/packages/hdwallet-core/src/wallet.ts @@ -185,6 +185,10 @@ export function supportsSonic(wallet: HDWallet): wallet is ETHWallet { return isObject(wallet) && (wallet as any)._supportsSonic } +export function supportsBob(wallet: HDWallet): wallet is ETHWallet { + return isObject(wallet) && (wallet as any)._supportsBob +} + export function supportsHyperEvm(wallet: HDWallet): wallet is ETHWallet { return isObject(wallet) && (wallet as any)._supportsHyperEvm } diff --git a/packages/hdwallet-gridplus/src/gridplus.ts b/packages/hdwallet-gridplus/src/gridplus.ts index 12c6712315e..f10bca6a248 100644 --- a/packages/hdwallet-gridplus/src/gridplus.ts +++ b/packages/hdwallet-gridplus/src/gridplus.ts @@ -364,6 +364,7 @@ export class GridPlusHDWallet readonly _supportsPlasma = true readonly _supportsKatana = false readonly _supportsSonic = false + readonly _supportsBob = false readonly _supportsHyperEvm = true readonly _supportsMantle = true readonly _supportsInk = true diff --git a/packages/hdwallet-keepkey/src/keepkey.ts b/packages/hdwallet-keepkey/src/keepkey.ts index 5e970621fdf..ab7caca5902 100644 --- a/packages/hdwallet-keepkey/src/keepkey.ts +++ b/packages/hdwallet-keepkey/src/keepkey.ts @@ -553,6 +553,7 @@ export class KeepKeyHDWallet readonly _supportsPlasma = false readonly _supportsKatana = false readonly _supportsSonic = false + readonly _supportsBob = false readonly _supportsHyperEvm = false readonly _supportsMantle = false readonly _supportsInk = false diff --git a/packages/hdwallet-ledger/src/ledger.ts b/packages/hdwallet-ledger/src/ledger.ts index 317478c3028..21e0a1093b3 100644 --- a/packages/hdwallet-ledger/src/ledger.ts +++ b/packages/hdwallet-ledger/src/ledger.ts @@ -430,6 +430,7 @@ export class LedgerHDWallet readonly _supportsPlasma = true readonly _supportsKatana = true readonly _supportsSonic = true + readonly _supportsBob = true readonly _supportsHyperEvm = true readonly _supportsMantle = true readonly _supportsInk = true diff --git a/packages/hdwallet-metamask-multichain/src/shapeshift-multichain.ts b/packages/hdwallet-metamask-multichain/src/shapeshift-multichain.ts index fa1fd2bb3da..71a08b7a251 100644 --- a/packages/hdwallet-metamask-multichain/src/shapeshift-multichain.ts +++ b/packages/hdwallet-metamask-multichain/src/shapeshift-multichain.ts @@ -280,6 +280,7 @@ export class MetaMaskMultiChainHDWallet readonly _supportsPlasma = true readonly _supportsKatana = true readonly _supportsSonic = true + readonly _supportsBob = true readonly _supportsHyperEvm = true readonly _supportsMantle = true readonly _supportsInk = true diff --git a/packages/hdwallet-native/src/ethereum.ts b/packages/hdwallet-native/src/ethereum.ts index c0e76777453..61b015f6e1a 100644 --- a/packages/hdwallet-native/src/ethereum.ts +++ b/packages/hdwallet-native/src/ethereum.ts @@ -69,6 +69,7 @@ export function MixinNativeETHWallet { assetReference: ASSET_REFERENCE.Unichain, assetNamespace: ASSET_NAMESPACE.slip44, } + case CHAIN_REFERENCE.BobMainnet: + return { + assetReference: ASSET_REFERENCE.Bob, + assetNamespace: ASSET_NAMESPACE.slip44, + } default: throw Error(`chainId '${relayToken.chainId}' not supported`) } diff --git a/packages/types/src/base.ts b/packages/types/src/base.ts index 56f2f258e13..51eec4d5660 100644 --- a/packages/types/src/base.ts +++ b/packages/types/src/base.ts @@ -35,6 +35,7 @@ export enum KnownChainIds { ScrollMainnet = 'eip155:534352', SonicMainnet = 'eip155:146', UnichainMainnet = 'eip155:130', + BobMainnet = 'eip155:60808', BitcoinMainnet = 'bip122:000000000019d6689c085ae165831e93', BitcoinCashMainnet = 'bip122:000000000000000000651ef99cb9fcbe', DogecoinMainnet = 'bip122:00000000001a91e3dace36e2be3bf030', @@ -73,6 +74,7 @@ export type EvmChainId = | KnownChainIds.ScrollMainnet | KnownChainIds.SonicMainnet | KnownChainIds.UnichainMainnet + | KnownChainIds.BobMainnet export type CosmosSdkChainId = | KnownChainIds.CosmosMainnet diff --git a/packages/types/src/zerion.ts b/packages/types/src/zerion.ts index 8a390c02383..3a10d5dea9c 100644 --- a/packages/types/src/zerion.ts +++ b/packages/types/src/zerion.ts @@ -4,6 +4,7 @@ import { avalancheChainId, baseChainId, berachainChainId, + bobChainId, bscChainId, cronosChainId, ethChainId, @@ -46,6 +47,7 @@ export const ZERION_CHAINS = [ 'cronos', 'sonic', 'unichain', + 'bob', // not yet // 'aurora', // 'fantom', @@ -75,6 +77,7 @@ export const ZERION_CHAINS_MAP: Record = { cronos: cronosChainId, sonic: sonicChainId, unichain: unichainChainId, + bob: bobChainId, } export const zerionChainIdToChainId = (chainId: ZerionChainId): ChainId | undefined => diff --git a/packages/utils/src/assetData/baseAssets.ts b/packages/utils/src/assetData/baseAssets.ts index 908ca044d1d..7dcb4527e9b 100644 --- a/packages/utils/src/assetData/baseAssets.ts +++ b/packages/utils/src/assetData/baseAssets.ts @@ -523,6 +523,23 @@ export const unichainChain: Readonly = Object.freeze({ relatedAssetKey: 'eip155:1/slip44:60', }) +export const bobChain: Readonly = Object.freeze({ + assetId: caip.bobAssetId, + chainId: caip.bobChainId, + name: 'Ethereum', + networkName: 'BOB', + symbol: 'ETH', + precision: 18, + color: '#5C6BC0', + networkColor: '#F25D00', + icon: 'https://rawcdn.githack.com/trustwallet/assets/32e51d582a890b3dd3135fe3ee7c20c2fd699a6d/blockchains/ethereum/info/logo.png', + networkIcon: 'https://assets.coingecko.com/asset_platforms/images/203/small/bob.jpeg', + explorer: 'https://explorer.gobob.xyz', + explorerAddressLink: 'https://explorer.gobob.xyz/address/', + explorerTxLink: 'https://explorer.gobob.xyz/tx/', + relatedAssetKey: 'eip155:1/slip44:60', +}) + export const solana: Readonly = Object.freeze({ assetId: caip.solAssetId, chainId: caip.solanaChainId, diff --git a/packages/utils/src/assetData/getBaseAsset.ts b/packages/utils/src/assetData/getBaseAsset.ts index 3f70872fc92..c9d30b96eff 100644 --- a/packages/utils/src/assetData/getBaseAsset.ts +++ b/packages/utils/src/assetData/getBaseAsset.ts @@ -12,6 +12,7 @@ import { bitcoin, bitcoincash, bnbsmartchain, + bobChain, cronos, dogecoin, ethereum, @@ -108,6 +109,8 @@ export const getBaseAsset = (chainId: ChainId): Readonly => { return sonic case KnownChainIds.UnichainMainnet: return unichainChain + case KnownChainIds.BobMainnet: + return bobChain case KnownChainIds.NearMainnet: return near case KnownChainIds.ZcashMainnet: diff --git a/packages/utils/src/chainIdToFeeAssetId.ts b/packages/utils/src/chainIdToFeeAssetId.ts index 913f241db9d..39f38106c90 100644 --- a/packages/utils/src/chainIdToFeeAssetId.ts +++ b/packages/utils/src/chainIdToFeeAssetId.ts @@ -5,6 +5,7 @@ import { baseAssetId, bchAssetId, berachainAssetId, + bobAssetId, bscAssetId, btcAssetId, cosmosAssetId, @@ -107,6 +108,8 @@ export const chainIdToFeeAssetId = (_chainId: ChainId): AssetId => { return sonicAssetId case KnownChainIds.UnichainMainnet: return unichainAssetId + case KnownChainIds.BobMainnet: + return bobAssetId case KnownChainIds.ZcashMainnet: return zecAssetId case KnownChainIds.NearMainnet: diff --git a/packages/utils/src/getAssetNamespaceFromChainId.ts b/packages/utils/src/getAssetNamespaceFromChainId.ts index ea41e0f5023..a34dbc5f0bc 100644 --- a/packages/utils/src/getAssetNamespaceFromChainId.ts +++ b/packages/utils/src/getAssetNamespaceFromChainId.ts @@ -35,6 +35,7 @@ export const getAssetNamespaceFromChainId = (chainId: KnownChainIds): AssetNames case KnownChainIds.ScrollMainnet: case KnownChainIds.SonicMainnet: case KnownChainIds.UnichainMainnet: + case KnownChainIds.BobMainnet: return ASSET_NAMESPACE.erc20 case KnownChainIds.StarknetMainnet: return ASSET_NAMESPACE.starknetToken diff --git a/packages/utils/src/getChainShortName.ts b/packages/utils/src/getChainShortName.ts index 96b09c37966..4e5789b4a13 100644 --- a/packages/utils/src/getChainShortName.ts +++ b/packages/utils/src/getChainShortName.ts @@ -68,6 +68,8 @@ export const getChainShortName = (chainId: KnownChainIds) => { return 'S' case KnownChainIds.UnichainMainnet: return 'UNI' + case KnownChainIds.BobMainnet: + return 'BOB' case KnownChainIds.ZcashMainnet: return 'ZEC' case KnownChainIds.NearMainnet: diff --git a/packages/utils/src/getNativeFeeAssetReference.ts b/packages/utils/src/getNativeFeeAssetReference.ts index 70d0a4ec66e..9aad0f59f30 100644 --- a/packages/utils/src/getNativeFeeAssetReference.ts +++ b/packages/utils/src/getNativeFeeAssetReference.ts @@ -66,6 +66,8 @@ export const getNativeFeeAssetReference = ( return ASSET_REFERENCE.Sonic case CHAIN_REFERENCE.UnichainMainnet: return ASSET_REFERENCE.Unichain + case CHAIN_REFERENCE.BobMainnet: + return ASSET_REFERENCE.Bob default: throw new Error(`Chain namespace ${chainNamespace} on ${chainReference} not supported.`) } diff --git a/public/generated/asset-manifest.json b/public/generated/asset-manifest.json index 08ae651fe64..258cb1360c4 100644 --- a/public/generated/asset-manifest.json +++ b/public/generated/asset-manifest.json @@ -1,4 +1,4 @@ { - "assetData": "6e05bc76", - "relatedAssetIndex": "889a8805" + "assetData": "2cea1fb8", + "relatedAssetIndex": "212243eb" } \ No newline at end of file diff --git a/public/generated/asset-manifest.json.br b/public/generated/asset-manifest.json.br index 25dfcb56e70..073c1c49b57 100644 Binary files a/public/generated/asset-manifest.json.br and b/public/generated/asset-manifest.json.br differ diff --git a/public/generated/asset-manifest.json.gz b/public/generated/asset-manifest.json.gz index 2027acfe98d..d42beb16dd5 100644 Binary files a/public/generated/asset-manifest.json.gz and b/public/generated/asset-manifest.json.gz differ diff --git a/public/generated/generatedAssetData.json b/public/generated/generatedAssetData.json index 64a350f1419..131a8f02fc8 100644 --- a/public/generated/generatedAssetData.json +++ b/public/generated/generatedAssetData.json @@ -431861,6 +431861,165 @@ "explorerAddressLink": "https://uniscan.xyz/address/", "explorerTxLink": "https://uniscan.xyz/tx/", "relatedAssetKey": "eip155:1/slip44:60" + }, + "eip155:60808/erc20:0x03c7054bcb39f7b2e5b2c7acb37583e32d70cfa3": { + "assetId": "eip155:60808/erc20:0x03c7054bcb39f7b2e5b2c7acb37583e32d70cfa3", + "chainId": "eip155:60808", + "name": "Bridged Wrapped Bitcoin BOB Network ", + "precision": 8, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/51140/large/wbtc.png?1730208114", + "symbol": "WBTC", + "explorer": "https://explorer.gobob.xyz", + "explorerAddressLink": "https://explorer.gobob.xyz/address/", + "explorerTxLink": "https://explorer.gobob.xyz/tx/", + "relatedAssetKey": "eip155:1/erc20:0x2260fac5e5542a773aa44fbcfedf7c193bc2c599" + }, + "eip155:60808/erc20:0x05d032ac25d322df992303dca074ee7392c117b9": { + "assetId": "eip155:60808/erc20:0x05d032ac25d322df992303dca074ee7392c117b9", + "chainId": "eip155:60808", + "name": "BOB Network Bridged USDT BOB Network ", + "precision": 6, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/51139/large/USDT.PNG?1730207839", + "symbol": "USDT", + "explorer": "https://explorer.gobob.xyz", + "explorerAddressLink": "https://explorer.gobob.xyz/address/", + "explorerTxLink": "https://explorer.gobob.xyz/tx/", + "relatedAssetKey": "eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7" + }, + "eip155:60808/erc20:0x236f8c0a61da474db21b693fb2ea7aab0c803894": { + "assetId": "eip155:60808/erc20:0x236f8c0a61da474db21b693fb2ea7aab0c803894", + "chainId": "eip155:60808", + "name": "Universal BTC", + "precision": 8, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/39599/large/uniBTC_200px.png?1723064455", + "symbol": "UNIBTC", + "explorer": "https://explorer.gobob.xyz", + "explorerAddressLink": "https://explorer.gobob.xyz/address/", + "explorerTxLink": "https://explorer.gobob.xyz/tx/", + "relatedAssetKey": "eip155:42161/erc20:0x6b2a01a5f79deb4c2f3c0eda7b01df456fbd726a" + }, + "eip155:60808/erc20:0x4200000000000000000000000000000000000006": { + "assetId": "eip155:60808/erc20:0x4200000000000000000000000000000000000006", + "chainId": "eip155:60808", + "name": "Bridged Wrapped Ethereum BOB Network ", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/51141/large/WETH_%281%29.png?1730208185", + "symbol": "WETH", + "explorer": "https://explorer.gobob.xyz", + "explorerAddressLink": "https://explorer.gobob.xyz/address/", + "explorerTxLink": "https://explorer.gobob.xyz/tx/", + "relatedAssetKey": "eip155:1/erc20:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + }, + "eip155:60808/erc20:0x541fd749419ca806a8bc7da8ac23d346f2df8b77": { + "assetId": "eip155:60808/erc20:0x541fd749419ca806a8bc7da8ac23d346f2df8b77", + "chainId": "eip155:60808", + "name": "Solv Protocol SolvBTC", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/36800/large/solvBTC.png?1719810684", + "symbol": "SOLVBTC", + "explorer": "https://explorer.gobob.xyz", + "explorerAddressLink": "https://explorer.gobob.xyz/address/", + "explorerTxLink": "https://explorer.gobob.xyz/tx/", + "relatedAssetKey": "eip155:42161/erc20:0x3647c54c4c2c65bc7a2d63c0da2809b399dbbdc0" + }, + "eip155:60808/erc20:0x7a087e75807f2e5143c161a817e64df6dc5eafe0": { + "assetId": "eip155:60808/erc20:0x7a087e75807f2e5143c161a817e64df6dc5eafe0", + "chainId": "eip155:60808", + "name": "XLink Bridged BTC", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/33363/large/abtc_%281%29.png?1701606956", + "symbol": "ABTC", + "explorer": "https://explorer.gobob.xyz", + "explorerAddressLink": "https://explorer.gobob.xyz/address/", + "explorerTxLink": "https://explorer.gobob.xyz/tx/", + "relatedAssetKey": null + }, + "eip155:60808/erc20:0xbba2ef945d523c4e2608c9e1214c2cc64d4fc2e2": { + "assetId": "eip155:60808/erc20:0xbba2ef945d523c4e2608c9e1214c2cc64d4fc2e2", + "chainId": "eip155:60808", + "name": "tBTC", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/11224/large/0x18084fba666a33d37592fa2633fd49a74dd93a88.png?1696511155", + "symbol": "TBTC", + "explorer": "https://explorer.gobob.xyz", + "explorerAddressLink": "https://explorer.gobob.xyz/address/", + "explorerTxLink": "https://explorer.gobob.xyz/tx/", + "relatedAssetKey": "eip155:42161/erc20:0x6c84a8f1c29108f47a79964b5fe888d4f4d0de40" + }, + "eip155:60808/erc20:0xc96de26018a54d51c097160568752c4e3bd6c364": { + "assetId": "eip155:60808/erc20:0xc96de26018a54d51c097160568752c4e3bd6c364", + "chainId": "eip155:60808", + "name": "Ignition FBTC", + "precision": 8, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/39182/large/FBTC_LOGO.png?1720850455", + "symbol": "FBTC", + "explorer": "https://explorer.gobob.xyz", + "explorerAddressLink": "https://explorer.gobob.xyz/address/", + "explorerTxLink": "https://explorer.gobob.xyz/tx/", + "relatedAssetKey": "eip155:1/erc20:0xc96de26018a54d51c097160568752c4e3bd6c364" + }, + "eip155:60808/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c": { + "assetId": "eip155:60808/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c", + "chainId": "eip155:60808", + "name": "Solv Protocol SolvBTC BBN", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/39384/large/unnamed.png?1721961640", + "symbol": "SOLVBTCBBN", + "explorer": "https://explorer.gobob.xyz", + "explorerAddressLink": "https://explorer.gobob.xyz/address/", + "explorerTxLink": "https://explorer.gobob.xyz/tx/", + "relatedAssetKey": "eip155:42161/erc20:0x346c574c56e1a4aaa8dc88cda8f7eb12b39947ab" + }, + "eip155:60808/erc20:0xe75d0fb2c24a55ca1e3f96781a2bcc7bdba058f0": { + "assetId": "eip155:60808/erc20:0xe75d0fb2c24a55ca1e3f96781a2bcc7bdba058f0", + "chainId": "eip155:60808", + "name": "BOB Network Bridged USDC E BOB Network", + "precision": 6, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/51174/large/USDC.png?1730285236", + "symbol": "USDCE", + "explorer": "https://explorer.gobob.xyz", + "explorerAddressLink": "https://explorer.gobob.xyz/address/", + "explorerTxLink": "https://explorer.gobob.xyz/tx/", + "relatedAssetKey": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" + }, + "eip155:60808/erc20:0xf3e9a5e9e8985bc56cddbe5129bc2da08db829e3": { + "assetId": "eip155:60808/erc20:0xf3e9a5e9e8985bc56cddbe5129bc2da08db829e3", + "chainId": "eip155:60808", + "name": "Hamilton UST", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/39194/large/3mxBXxM3_400x400.jpg?1720997931", + "symbol": "HUST", + "explorer": "https://explorer.gobob.xyz", + "explorerAddressLink": "https://explorer.gobob.xyz/address/", + "explorerTxLink": "https://explorer.gobob.xyz/tx/", + "relatedAssetKey": null + }, + "eip155:60808/slip44:60": { + "assetId": "eip155:60808/slip44:60", + "chainId": "eip155:60808", + "name": "Ethereum", + "networkName": "BOB", + "symbol": "ETH", + "precision": 18, + "color": "#5C6BC0", + "networkColor": "#F25D00", + "icon": "https://rawcdn.githack.com/trustwallet/assets/32e51d582a890b3dd3135fe3ee7c20c2fd699a6d/blockchains/ethereum/info/logo.png", + "networkIcon": "https://assets.coingecko.com/asset_platforms/images/203/small/bob.jpeg", + "explorer": "https://explorer.gobob.xyz", + "explorerAddressLink": "https://explorer.gobob.xyz/address/", + "explorerTxLink": "https://explorer.gobob.xyz/tx/", + "relatedAssetKey": "eip155:1/slip44:60" } }, "ids": [ @@ -462620,6 +462779,18 @@ "eip155:130/erc20:0xec4a56061d86955d0df883efb2e5791d99ea71f2", "eip155:130/erc20:0xef66491eab4bbb582c57b14778afd8dfb70d8a1a", "eip155:130/erc20:0xfdca15bd55f350a36e63c47661914d80411d2c22", - "eip155:130/slip44:60" + "eip155:130/slip44:60", + "eip155:60808/erc20:0x03c7054bcb39f7b2e5b2c7acb37583e32d70cfa3", + "eip155:60808/erc20:0x05d032ac25d322df992303dca074ee7392c117b9", + "eip155:60808/erc20:0x236f8c0a61da474db21b693fb2ea7aab0c803894", + "eip155:60808/erc20:0x4200000000000000000000000000000000000006", + "eip155:60808/erc20:0x541fd749419ca806a8bc7da8ac23d346f2df8b77", + "eip155:60808/erc20:0x7a087e75807f2e5143c161a817e64df6dc5eafe0", + "eip155:60808/erc20:0xbba2ef945d523c4e2608c9e1214c2cc64d4fc2e2", + "eip155:60808/erc20:0xc96de26018a54d51c097160568752c4e3bd6c364", + "eip155:60808/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c", + "eip155:60808/erc20:0xe75d0fb2c24a55ca1e3f96781a2bcc7bdba058f0", + "eip155:60808/erc20:0xf3e9a5e9e8985bc56cddbe5129bc2da08db829e3", + "eip155:60808/slip44:60" ] } \ No newline at end of file diff --git a/public/generated/generatedAssetData.json.br b/public/generated/generatedAssetData.json.br index c7b96643818..b5571bdddd6 100644 Binary files a/public/generated/generatedAssetData.json.br and b/public/generated/generatedAssetData.json.br differ diff --git a/public/generated/generatedAssetData.json.gz b/public/generated/generatedAssetData.json.gz index 99a42137576..94ab2077b06 100644 Binary files a/public/generated/generatedAssetData.json.gz and b/public/generated/generatedAssetData.json.gz differ diff --git a/public/generated/relatedAssetIndex.json b/public/generated/relatedAssetIndex.json index e69498425fe..6b3c472b2b2 100644 --- a/public/generated/relatedAssetIndex.json +++ b/public/generated/relatedAssetIndex.json @@ -90,7 +90,8 @@ "eip155:80094/erc20:0xc3827a4bc8224ee2d116637023b124ced6db6e90", "eip155:5000/erc20:0x93919784c523f39cacaa98ee0a9d96c3f32b593e", "eip155:146/erc20:0xc3827a4bc8224ee2d116637023b124ced6db6e90", - "eip155:130/erc20:0xd3c8da379d71a33bfee8875f87ac2748beb1d58d" + "eip155:130/erc20:0xd3c8da379d71a33bfee8875f87ac2748beb1d58d", + "eip155:60808/erc20:0x236f8c0a61da474db21b693fb2ea7aab0c803894" ], "eip155:42161/erc20:0x064f8b858c2a603e1b106a2039f5446d32dc81c1": [ "eip155:8453/erc20:0x54330d28ca3357f294334bdc454a032e7f353416", @@ -1179,7 +1180,8 @@ "eip155:42161/erc20:0x6c84a8f1c29108f47a79964b5fe888d4f4d0de40", "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:6DNSN2BJsaPFdFFc1zP37kkeNe4Usc1Sqkzr9C9vPWcU", "starknet:SN_MAIN/token:0x4daa17763b286d1e59b97c283c0b8c949994c361e426a28f743c67bdfe9a32f", - "sui:35834a8a/coin:0x77045f1b9f811a7a8fb9ebd085b5b0c55c5cb0d1520ff55f7037f89b5da9f5f1::TBTC::TBTC" + "sui:35834a8a/coin:0x77045f1b9f811a7a8fb9ebd085b5b0c55c5cb0d1520ff55f7037f89b5da9f5f1::TBTC::TBTC", + "eip155:60808/erc20:0xbba2ef945d523c4e2608c9e1214c2cc64d4fc2e2" ], "eip155:1/erc20:0x18aaa7115705e8be94bffebde57af9bfc265b998": [ "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", @@ -1416,7 +1418,8 @@ "eip155:59144/erc20:0x3aab2285ddcddad8edf438c1bab47e1a9d05a9b4", "eip155:146/erc20:0x0555e30da8f98308edb960aa94c0db47230d2b9c", "eip155:130/erc20:0x927b51f251480a681271180da4de28d44ec4afb8", - "eip155:130/erc20:0x0555e30da8f98308edb960aa94c0db47230d2b9c" + "eip155:130/erc20:0x0555e30da8f98308edb960aa94c0db47230d2b9c", + "eip155:60808/erc20:0x03c7054bcb39f7b2e5b2c7acb37583e32d70cfa3" ], "eip155:8453/erc20:0xc5fed7c8ccc75d8a72b601a66dffd7a489073f0b": [ "eip155:56/erc20:0x6ef2ffb38d64afe18ce782da280b300e358cfeaf", @@ -4757,7 +4760,8 @@ "eip155:57073/erc20:0xae4efbc7736f963982aacb17efa37fcbab924cb3", "eip155:80094/erc20:0x541fd749419ca806a8bc7da8ac23d346f2df8b77", "eip155:5000/erc20:0xa68d25fc2af7278db4bcdcaabce31814252642a9", - "eip155:146/erc20:0x541fd749419ca806a8bc7da8ac23d346f2df8b77" + "eip155:146/erc20:0x541fd749419ca806a8bc7da8ac23d346f2df8b77", + "eip155:60808/erc20:0x541fd749419ca806a8bc7da8ac23d346f2df8b77" ], "eip155:42161/erc20:0x7bbcf1b600565ae023a1806ef637af4739de3255": [ "eip155:8453/erc20:0x7bbcf1b600565ae023a1806ef637af4739de3255", @@ -6116,7 +6120,8 @@ "eip155:5000/erc20:0x09bc4e0d864854c6afb6eb9a9cdf58ac190d0df9", "eip155:25/erc20:0xc21223249ca28397b4b6541dffaecc539bff0c59", "eip155:146/erc20:0x29219dd400f2bf60e5a23d13be72b486d4038894", - "eip155:130/erc20:0x078d782b760474a361dda0af3839290b0ef57ad6" + "eip155:130/erc20:0x078d782b760474a361dda0af3839290b0ef57ad6", + "eip155:60808/erc20:0xe75d0fb2c24a55ca1e3f96781a2bcc7bdba058f0" ], "eip155:8453/erc20:0x9c632e6aaa3ea73f91554f8a3cb2ed2f29605e0c": [ "eip155:56/erc20:0x7324c7c0d95cebc73eea7e85cbaac0dbdf88a05b", @@ -7363,7 +7368,8 @@ "eip155:59144/erc20:0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f", "eip155:5000/erc20:0xdeaddeaddeaddeaddeaddeaddeaddeaddead1111", "eip155:146/erc20:0x50c42deacd8fc9773493ed674b675be577f2634b", - "eip155:130/erc20:0x4200000000000000000000000000000000000006" + "eip155:130/erc20:0x4200000000000000000000000000000000000006", + "eip155:60808/erc20:0x4200000000000000000000000000000000000006" ], "eip155:42161/erc20:0xea986d33ef8a20a96120ecc44dbdd49830192043": [ "eip155:56/erc20:0x3028b4395f98777123c7da327010c40f3c7cc4ef", @@ -7733,7 +7739,8 @@ "eip155:42161/erc20:0xc96de26018a54d51c097160568752c4e3bd6c364", "eip155:1/erc20:0xc96de26018a54d51c097160568752c4e3bd6c364", "eip155:5000/erc20:0xc96de26018a54d51c097160568752c4e3bd6c364", - "eip155:146/erc20:0xc96de26018a54d51c097160568752c4e3bd6c364" + "eip155:146/erc20:0xc96de26018a54d51c097160568752c4e3bd6c364", + "eip155:60808/erc20:0xc96de26018a54d51c097160568752c4e3bd6c364" ], "eip155:1/erc20:0xcafe001067cdef266afb7eb5a286dcfd277f3de5": [ "eip155:56/erc20:0xcafe001067cdef266afb7eb5a286dcfd277f3de5", @@ -8373,7 +8380,8 @@ "eip155:80094/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c", "eip155:59144/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c", "eip155:5000/erc20:0x1d40bafc49c37cda49f2a5427e2fb95e1e3fcf20", - "eip155:146/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c" + "eip155:146/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c", + "eip155:60808/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c" ], "eip155:42161/erc20:0x462cd9e0247b2e63831c3189ae738e5e9a5a4b64": [ "eip155:43114/erc20:0x9ceed3a7f753608372eeab300486cc7c2f38ac68", @@ -8426,7 +8434,8 @@ "eip155:146/erc20:0x6047828dc181963ba44974801ff68e538da5eaf9", "eip155:130/erc20:0x9151434b16b9763660705744891fa906f660ecc5", "eip155:130/erc20:0x1217bfe6c773eec6cc4a38b5dc45b92292b6e189", - "eip155:130/erc20:0x588ce4f028d8e7b53b687865d6a67b3a54c75518" + "eip155:130/erc20:0x588ce4f028d8e7b53b687865d6a67b3a54c75518", + "eip155:60808/erc20:0x05d032ac25d322df992303dca074ee7392c117b9" ], "eip155:1/erc20:0xdb9783ca04bbd64fe2c6d7b9503a979b3de30729": [ "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:XsAsZLF4MmsvS1sDxRMrUz7REjHfwbC9UAMXSRBqgEB", @@ -9624,7 +9633,8 @@ "eip155:57073/slip44:60", "eip155:534352/slip44:60", "eip155:59144/slip44:60", - "eip155:130/slip44:60" + "eip155:130/slip44:60", + "eip155:60808/slip44:60" ], "eip155:8453/erc20:0x22a2488fe295047ba13bd8cccdbc8361dbd8cf7c": [ "eip155:10/erc20:0x1db2466d9f5e10d7090e7152b68d62703a2245f0", diff --git a/public/generated/relatedAssetIndex.json.br b/public/generated/relatedAssetIndex.json.br index e90b992a038..9da65cfbdab 100644 Binary files a/public/generated/relatedAssetIndex.json.br and b/public/generated/relatedAssetIndex.json.br differ diff --git a/public/generated/relatedAssetIndex.json.gz b/public/generated/relatedAssetIndex.json.gz index 4a0599a8580..5f2e4653655 100644 Binary files a/public/generated/relatedAssetIndex.json.gz and b/public/generated/relatedAssetIndex.json.gz differ diff --git a/scripts/generateAssetData/bob/index.ts b/scripts/generateAssetData/bob/index.ts new file mode 100644 index 00000000000..668b7593f36 --- /dev/null +++ b/scripts/generateAssetData/bob/index.ts @@ -0,0 +1,11 @@ +import { bobChainId } from '@shapeshiftoss/caip' +import type { Asset } from '@shapeshiftoss/types' +import { bobChain, unfreeze } from '@shapeshiftoss/utils' + +import * as coingecko from '../coingecko' + +export const getAssets = async (): Promise => { + const assets = await coingecko.getAssets(bobChainId) + + return [...assets, unfreeze(bobChain)] +} diff --git a/scripts/generateAssetData/coingecko.ts b/scripts/generateAssetData/coingecko.ts index 02031e27e2a..f8aeeccd973 100644 --- a/scripts/generateAssetData/coingecko.ts +++ b/scripts/generateAssetData/coingecko.ts @@ -6,6 +6,7 @@ import { avalancheChainId, baseChainId, berachainChainId, + bobChainId, bscChainId, cronosChainId, ethChainId, @@ -38,6 +39,7 @@ import { base, berachain, bnbsmartchain, + bobChain, cronos, ethereum, gnosis, @@ -253,6 +255,14 @@ export async function getAssets(chainId: ChainId): Promise { explorerAddressLink: unichainChain.explorerAddressLink, explorerTxLink: unichainChain.explorerTxLink, } + case bobChainId: + return { + assetNamespace: ASSET_NAMESPACE.erc20, + category: adapters.chainIdToCoingeckoAssetPlatform(chainId), + explorer: bobChain.explorer, + explorerAddressLink: bobChain.explorerAddressLink, + explorerTxLink: bobChain.explorerTxLink, + } case solanaChainId: return { assetNamespace: ASSET_NAMESPACE.splToken, diff --git a/scripts/generateAssetData/generateAssetData.ts b/scripts/generateAssetData/generateAssetData.ts index 08f1119cafd..e55a2383f49 100644 --- a/scripts/generateAssetData/generateAssetData.ts +++ b/scripts/generateAssetData/generateAssetData.ts @@ -28,6 +28,7 @@ import * as avalanche from './avalanche' import * as base from './base' import * as berachain from './berachain' import * as bnbsmartchain from './bnbsmartchain' +import * as bob from './bob' import { compressGeneratedAssets } from './compressAssets' import { ASSET_DATA_PATH, GENERATED_DIR, RELATED_ASSET_INDEX_PATH } from './constants' import * as cronos from './cronos' @@ -84,6 +85,7 @@ const generateAssetData = async () => { const lineaAssets = await linea.getAssets() const cronosAssets = await cronos.getAssets() const unichainAssets = await unichain.getAssets() + const bobAssets = await bob.getAssets() const megaethAssets = await megaeth.getAssets() const berachainAssets = await berachain.getAssets() const scrollAssets = await scroll.getAssets() @@ -125,6 +127,7 @@ const generateAssetData = async () => { ...lineaAssets, ...cronosAssets, ...unichainAssets, + ...bobAssets, ...megaethAssets, ...berachainAssets, ...scrollAssets, diff --git a/scripts/generateAssetData/generateRelatedAssetIndex/generateChainRelatedAssetIndex.ts b/scripts/generateAssetData/generateRelatedAssetIndex/generateChainRelatedAssetIndex.ts index 8a725a18b65..0146c6285e3 100644 --- a/scripts/generateAssetData/generateRelatedAssetIndex/generateChainRelatedAssetIndex.ts +++ b/scripts/generateAssetData/generateRelatedAssetIndex/generateChainRelatedAssetIndex.ts @@ -3,6 +3,7 @@ import { adapters, arbitrumAssetId, baseAssetId, + bobAssetId, ethAssetId, FEE_ASSET_IDS, foxAssetId, @@ -63,6 +64,7 @@ const manualRelatedAssetIndex: Record = { megaethAssetId, scrollAssetId, unichainAssetId, + bobAssetId, ], [foxAssetId]: [foxOnArbitrumOneAssetId], [starknetAssetId]: [ diff --git a/scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts b/scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts index 63581cf9e3b..ea395544523 100644 --- a/scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts +++ b/scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts @@ -3,6 +3,7 @@ import { adapters, arbitrumAssetId, baseAssetId, + bobAssetId, ethAssetId, FEE_ASSET_IDS, foxAssetId, @@ -62,6 +63,7 @@ const manualRelatedAssetIndex: Record = { megaethAssetId, scrollAssetId, unichainAssetId, + bobAssetId, ], [foxAssetId]: [foxOnArbitrumOneAssetId], [starknetAssetId]: [ diff --git a/src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx b/src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx index bb3ff053b08..57a20b06ae2 100644 --- a/src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx +++ b/src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx @@ -1,6 +1,7 @@ import type { ChainId } from '@shapeshiftoss/caip' import { berachainAssetId, + bobAssetId, cronosAssetId, hyperEvmAssetId, inkAssetId, @@ -59,6 +60,7 @@ export const queryFn = async () => { if (enabledFlags.Cronos) assetIds.push(cronosAssetId) if (enabledFlags.Sonic) assetIds.push(sonicAssetId) if (enabledFlags.Unichain) assetIds.push(unichainAssetId) + if (enabledFlags.Bob) assetIds.push(bobAssetId) if (enabledFlags.Starknet) assetIds.push(starknetAssetId) if (enabledFlags.Tron) assetIds.push(tronAssetId) if (enabledFlags.Berachain) assetIds.push(berachainAssetId) diff --git a/src/config.ts b/src/config.ts index b15e9fa90fb..14a174337f2 100644 --- a/src/config.ts +++ b/src/config.ts @@ -72,6 +72,7 @@ const validators = { VITE_SCROLL_NODE_URL: url(), VITE_SONIC_NODE_URL: url(), VITE_UNICHAIN_NODE_URL: url(), + VITE_BOB_NODE_URL: url(), VITE_SOLANA_NODE_URL: url(), VITE_STARKNET_NODE_URL: url(), VITE_TRON_NODE_URL: url(), @@ -122,6 +123,7 @@ const validators = { VITE_FEATURE_SCROLL: bool({ default: false }), VITE_FEATURE_SONIC: bool({ default: false }), VITE_FEATURE_UNICHAIN: bool({ default: false }), + VITE_FEATURE_BOB: bool({ default: false }), VITE_FEATURE_MAYACHAIN: bool({ default: false }), VITE_FEATURE_ZCASH: bool({ default: false }), VITE_FEATURE_ZRX_SWAP: bool({ default: false }), diff --git a/src/constants/chains.ts b/src/constants/chains.ts index d23a5d7145c..9d744a317c8 100644 --- a/src/constants/chains.ts +++ b/src/constants/chains.ts @@ -18,6 +18,7 @@ export const SECOND_CLASS_CHAINS: readonly KnownChainIds[] = [ KnownChainIds.LineaMainnet, KnownChainIds.SonicMainnet, KnownChainIds.UnichainMainnet, + KnownChainIds.BobMainnet, KnownChainIds.MegaEthMainnet, KnownChainIds.BerachainMainnet, KnownChainIds.ScrollMainnet, @@ -46,6 +47,7 @@ export const knownChainIds = Object.values(KnownChainIds).filter(chainId => { if (chainId === KnownChainIds.LineaMainnet && !enabledFlags.Linea) return false if (chainId === KnownChainIds.SonicMainnet && !enabledFlags.Sonic) return false if (chainId === KnownChainIds.UnichainMainnet && !enabledFlags.Unichain) return false + if (chainId === KnownChainIds.BobMainnet && !enabledFlags.Bob) return false if (chainId === KnownChainIds.MegaEthMainnet && !enabledFlags.MegaEth) return false if (chainId === KnownChainIds.BerachainMainnet && !enabledFlags.Berachain) return false if (chainId === KnownChainIds.ScrollMainnet && !enabledFlags.Scroll) return false diff --git a/src/context/PluginProvider/PluginProvider.tsx b/src/context/PluginProvider/PluginProvider.tsx index ea226e5a0ed..fe520c6a841 100644 --- a/src/context/PluginProvider/PluginProvider.tsx +++ b/src/context/PluginProvider/PluginProvider.tsx @@ -123,6 +123,7 @@ export const PluginProvider = ({ children }: PluginProviderProps): JSX.Element = if (!featureFlags.Katana && chainId === KnownChainIds.KatanaMainnet) return false if (!featureFlags.Sonic && chainId === KnownChainIds.SonicMainnet) return false if (!featureFlags.Unichain && chainId === KnownChainIds.UnichainMainnet) return false + if (!featureFlags.Bob && chainId === KnownChainIds.BobMainnet) return false if (!featureFlags.Plasma && chainId === KnownChainIds.PlasmaMainnet) return false if (!featureFlags.Ink && chainId === KnownChainIds.InkMainnet) return false if (!featureFlags.MegaEth && chainId === KnownChainIds.MegaEthMainnet) return false diff --git a/src/hooks/useActionCenterSubscribers/useSendActionSubscriber.tsx b/src/hooks/useActionCenterSubscribers/useSendActionSubscriber.tsx index 7d9db5a886c..37b0a795410 100644 --- a/src/hooks/useActionCenterSubscribers/useSendActionSubscriber.tsx +++ b/src/hooks/useActionCenterSubscribers/useSendActionSubscriber.tsx @@ -12,6 +12,7 @@ import { getConfig } from '@/config' import { SECOND_CLASS_CHAINS } from '@/constants/chains' import { getChainAdapterManager } from '@/context/PluginProvider/chainAdapterSingleton' import { getBerachainTransactionStatus } from '@/lib/utils/berachain' +import { getBobTransactionStatus } from '@/lib/utils/bob' import { getCronosTransactionStatus } from '@/lib/utils/cronos' import { getHyperEvmTransactionStatus } from '@/lib/utils/hyperevm' import { getInkTransactionStatus } from '@/lib/utils/ink' @@ -270,6 +271,13 @@ export const useSendActionSubscriber = () => { unichainTxStatus === TxStatus.Confirmed || unichainTxStatus === TxStatus.Failed break } + case KnownChainIds.BobMainnet: { + const bobNodeUrl = getConfig().VITE_BOB_NODE_URL + const bobTxStatus = await getBobTransactionStatus(txHash, bobNodeUrl) + isConfirmed = + bobTxStatus === TxStatus.Confirmed || bobTxStatus === TxStatus.Failed + break + } case KnownChainIds.NearMainnet: { const nearTxStatus = await getNearTransactionStatus(txHash) isConfirmed = diff --git a/src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts b/src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts index 662336e7b85..08e17a23e72 100644 --- a/src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts +++ b/src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts @@ -5,6 +5,7 @@ import { baseChainId, bchChainId, berachainChainId, + bobChainId, bscChainId, btcChainId, cosmosChainId, @@ -47,6 +48,7 @@ import { supportsAvalanche, supportsBase, supportsBerachain, + supportsBob, supportsBSC, supportsBTC, supportsCosmos, @@ -165,6 +167,7 @@ export const walletSupportsChain = ({ const isHyperEvmEnabled = selectFeatureFlag(store.getState(), 'HyperEvm') const isInkEnabled = selectFeatureFlag(store.getState(), 'Ink') + const isBobEnabled = selectFeatureFlag(store.getState(), 'Bob') const isKatanaEnabled = selectFeatureFlag(store.getState(), 'Katana') const isMantleEnabled = selectFeatureFlag(store.getState(), 'Mantle') const isLineaEnabled = selectFeatureFlag(store.getState(), 'Linea') @@ -236,6 +239,8 @@ export const walletSupportsChain = ({ return isSonicEnabled && supportsSonic(wallet) case unichainChainId: return isUnichainEnabled && supportsUnichain(wallet) + case bobChainId: + return isBobEnabled && supportsBob(wallet) case cosmosChainId: return supportsCosmos(wallet) case thorchainChainId: diff --git a/src/lib/account/evm.ts b/src/lib/account/evm.ts index ed21179c5ab..ad550c16135 100644 --- a/src/lib/account/evm.ts +++ b/src/lib/account/evm.ts @@ -4,6 +4,7 @@ import { avalancheChainId, baseChainId, berachainChainId, + bobChainId, bscChainId, cronosChainId, ethChainId, @@ -31,6 +32,7 @@ import { supportsAvalanche, supportsBase, supportsBerachain, + supportsBob, supportsBSC, supportsCronos, supportsETH, @@ -151,6 +153,7 @@ export const deriveEvmAccountIdsAndMetadata: DeriveAccountIdsAndMetadata = async if (chainId === lineaChainId && !supportsLinea(wallet)) continue if (chainId === sonicChainId && !supportsSonic(wallet)) continue if (chainId === unichainChainId && !supportsUnichain(wallet)) continue + if (chainId === bobChainId && !supportsBob(wallet)) continue if ( isMetaMask(wallet) && !canAddMetaMaskAccount({ accountNumber, chainId, wallet, isSnapInstalled }) diff --git a/src/lib/asset-service/service/AssetService.ts b/src/lib/asset-service/service/AssetService.ts index 9c94b432ee6..c2184958d06 100644 --- a/src/lib/asset-service/service/AssetService.ts +++ b/src/lib/asset-service/service/AssetService.ts @@ -4,6 +4,7 @@ import { arbitrumChainId, baseChainId, berachainChainId, + bobChainId, bscChainId, cronosChainId, gnosisChainId, @@ -134,6 +135,7 @@ class _AssetService { if (!config.VITE_FEATURE_INK && asset.chainId === inkChainId) return false if (!config.VITE_FEATURE_SONIC && asset.chainId === sonicChainId) return false if (!config.VITE_FEATURE_UNICHAIN && asset.chainId === unichainChainId) return false + if (!config.VITE_FEATURE_BOB && asset.chainId === bobChainId) return false if (!config.VITE_FEATURE_MEGAETH && asset.chainId === megaethChainId) return false if (!config.VITE_FEATURE_LINEA && asset.chainId === lineaChainId) return false if (!config.VITE_FEATURE_BERACHAIN && asset.chainId === berachainChainId) return false diff --git a/src/lib/coingecko/utils.ts b/src/lib/coingecko/utils.ts index 33c094615dd..959f9ef5911 100644 --- a/src/lib/coingecko/utils.ts +++ b/src/lib/coingecko/utils.ts @@ -5,6 +5,7 @@ import { baseChainId, bchChainId, berachainChainId, + bobChainId, bscChainId, btcChainId, cosmosChainId, @@ -227,6 +228,7 @@ export const getCoingeckoSupportedChainIds = () => { ...(getConfig().VITE_FEATURE_LINEA ? [lineaChainId] : []), ...(getConfig().VITE_FEATURE_SONIC ? [sonicChainId] : []), ...(getConfig().VITE_FEATURE_UNICHAIN ? [unichainChainId] : []), + ...(getConfig().VITE_FEATURE_BOB ? [bobChainId] : []), ...(getConfig().VITE_FEATURE_TON ? [tonChainId] : []), ] } diff --git a/src/lib/market-service/coingecko/coingecko.test.ts b/src/lib/market-service/coingecko/coingecko.test.ts index 891f9f88be0..3e0dda06255 100644 --- a/src/lib/market-service/coingecko/coingecko.test.ts +++ b/src/lib/market-service/coingecko/coingecko.test.ts @@ -169,7 +169,7 @@ describe('CoinGecko market service', () => { it('can flatten multiple responses', async () => { mocks.get.mockResolvedValueOnce({ data: [eth] }).mockResolvedValue({ data: [btc] }) const result = await coinGeckoMarketService.findAll() - expect(Object.keys(result).length).toEqual(10) + expect(Object.keys(result).length).toEqual(11) }) it('can sort by market cap', async () => { @@ -193,7 +193,7 @@ describe('CoinGecko market service', () => { it('can return some results if partially rate limited', async () => { mocks.get.mockResolvedValueOnce({ status: 429 }).mockResolvedValue({ data: [eth] }) const result = await coinGeckoMarketService.findAll() - expect(Object.keys(result).length).toEqual(9) + expect(Object.keys(result).length).toEqual(10) }) it('can use default args', async () => { @@ -228,6 +228,7 @@ describe('CoinGecko market service', () => { ethOnArbitrumKey, ethOnBaseKey, ethOnLineaKey, + ethOnBobKey, ethOnMegaEthKey, ethOnInkKey, ethOnScrollKey, @@ -240,6 +241,7 @@ describe('CoinGecko market service', () => { ethOnArbitrumKey, ethOnBaseKey, ethOnLineaKey, + ethOnBobKey, ethOnMegaEthKey, ethOnInkKey, ethOnScrollKey, diff --git a/src/lib/utils/bob.ts b/src/lib/utils/bob.ts new file mode 100644 index 00000000000..58bf986ac70 --- /dev/null +++ b/src/lib/utils/bob.ts @@ -0,0 +1,43 @@ +import { bobChainId } from '@shapeshiftoss/caip' +import type { EvmChainAdapter } from '@shapeshiftoss/chain-adapters' +import { TxStatus } from '@shapeshiftoss/unchained-client' +import { JsonRpcProvider } from 'ethers' + +export const isBobChainAdapter = (adapter: unknown): adapter is EvmChainAdapter => { + if (!adapter) return false + + const maybeAdapter = adapter as EvmChainAdapter + if (typeof maybeAdapter.getChainId !== 'function') return false + + return maybeAdapter.getChainId() === bobChainId +} + +export const getBobTransactionStatus = async ( + txHash: string, + nodeUrl: string, +): Promise => { + try { + const provider = new JsonRpcProvider(nodeUrl, undefined, { + staticNetwork: true, + }) + + const receipt = await provider.getTransactionReceipt(txHash) + + if (!receipt) return TxStatus.Unknown + + switch (receipt.status) { + case 1: + return TxStatus.Confirmed + case 0: + return TxStatus.Failed + case null: + case undefined: + return TxStatus.Unknown + default: + return TxStatus.Unknown + } + } catch (error) { + console.error('[BOB] Error getting transaction status:', error) + return TxStatus.Unknown + } +} diff --git a/src/pages/Markets/components/MarketsRow.tsx b/src/pages/Markets/components/MarketsRow.tsx index 6e6038dd46f..a6ef3f5fea1 100644 --- a/src/pages/Markets/components/MarketsRow.tsx +++ b/src/pages/Markets/components/MarketsRow.tsx @@ -92,6 +92,7 @@ export const MarketsRow: React.FC = ({ const isCronosEnabled = useAppSelector(state => selectFeatureFlag(state, 'Cronos')) const isSonicEnabled = useAppSelector(state => selectFeatureFlag(state, 'Sonic')) const isUnichainEnabled = useAppSelector(state => selectFeatureFlag(state, 'Unichain')) + const isBobEnabled = useAppSelector(state => selectFeatureFlag(state, 'Bob')) const [isSmallerThanLg] = useMediaQuery(`(max-width: ${breakpoints.lg})`) const chainIds = useMemo(() => { @@ -111,6 +112,7 @@ export const MarketsRow: React.FC = ({ if (!isCronosEnabled && chainId === KnownChainIds.CronosMainnet) return false if (!isSonicEnabled && chainId === KnownChainIds.SonicMainnet) return false if (!isUnichainEnabled && chainId === KnownChainIds.UnichainMainnet) return false + if (!isBobEnabled && chainId === KnownChainIds.BobMainnet) return false return true }) }, [ @@ -128,6 +130,7 @@ export const MarketsRow: React.FC = ({ isCronosEnabled, isSonicEnabled, isUnichainEnabled, + isBobEnabled, ]) const Title = useMemo(() => { diff --git a/src/plugins/activePlugins.ts b/src/plugins/activePlugins.ts index fbf0af8694f..3cb8920fb73 100644 --- a/src/plugins/activePlugins.ts +++ b/src/plugins/activePlugins.ts @@ -5,6 +5,7 @@ import berachain from '@/plugins/berachain' import bitcoin from '@/plugins/bitcoin' import bitcoincash from '@/plugins/bitcoincash' import bnbsmartchain from '@/plugins/bnbsmartchain' +import bob from '@/plugins/bob' import cosmos from '@/plugins/cosmos' import cronos from '@/plugins/cronos' import dogecoin from '@/plugins/dogecoin' @@ -44,6 +45,7 @@ export const activePlugins = [ bitcoin, bitcoincash, bnbsmartchain, + bob, cosmos, cronos, dogecoin, diff --git a/src/plugins/bob/index.tsx b/src/plugins/bob/index.tsx new file mode 100644 index 00000000000..1ec835f3f43 --- /dev/null +++ b/src/plugins/bob/index.tsx @@ -0,0 +1,49 @@ +import { bobChainId, fromAssetId } from '@shapeshiftoss/caip' +import { bob } from '@shapeshiftoss/chain-adapters' +import { KnownChainIds } from '@shapeshiftoss/types' + +import { getConfig } from '@/config' +import { getAssetService } from '@/lib/asset-service' +import type { Plugins } from '@/plugins/types' + +// eslint-disable-next-line import/no-default-export +export default function register(): Plugins { + return [ + [ + 'bobChainAdapter', + { + name: 'bobChainAdapter', + featureFlag: ['Bob'], + providers: { + chainAdapters: [ + [ + KnownChainIds.BobMainnet, + () => { + const getKnownTokens = () => { + const assetService = getAssetService() + return assetService.assets + .filter(asset => { + const { chainId, assetNamespace } = fromAssetId(asset.assetId) + return chainId === bobChainId && assetNamespace === 'erc20' + }) + .map(asset => ({ + assetId: asset.assetId, + contractAddress: fromAssetId(asset.assetId).assetReference, + symbol: asset.symbol, + name: asset.name, + precision: asset.precision, + })) + } + + return new bob.ChainAdapter({ + rpcUrl: getConfig().VITE_BOB_NODE_URL, + getKnownTokens, + }) + }, + ], + ], + }, + }, + ], + ] +} diff --git a/src/state/migrations/index.ts b/src/state/migrations/index.ts index eb57e87a84e..ca25916304e 100644 --- a/src/state/migrations/index.ts +++ b/src/state/migrations/index.ts @@ -344,6 +344,7 @@ export const clearAssetsMigrations = { 295: clearAssets, 296: clearAssets, 297: clearAssets, + 298: clearAssets, } as unknown as Omit export const clearMarketDataMigrations = { diff --git a/src/state/slices/opportunitiesSlice/mappings.ts b/src/state/slices/opportunitiesSlice/mappings.ts index a49ca11adca..1ca2f4f6cc4 100644 --- a/src/state/slices/opportunitiesSlice/mappings.ts +++ b/src/state/slices/opportunitiesSlice/mappings.ts @@ -200,6 +200,7 @@ export const CHAIN_ID_TO_SUPPORTED_DEFI_OPPORTUNITIES: Record< [KnownChainIds.KatanaMainnet]: [], [KnownChainIds.SonicMainnet]: [], [KnownChainIds.UnichainMainnet]: [], + [KnownChainIds.BobMainnet]: [], [KnownChainIds.ZcashMainnet]: [], [KnownChainIds.NearMainnet]: [], [KnownChainIds.TonMainnet]: [], diff --git a/src/state/slices/portfolioSlice/utils/index.ts b/src/state/slices/portfolioSlice/utils/index.ts index c6615b3c48a..27d9c563755 100644 --- a/src/state/slices/portfolioSlice/utils/index.ts +++ b/src/state/slices/portfolioSlice/utils/index.ts @@ -6,6 +6,7 @@ import { baseChainId, bchChainId, berachainChainId, + bobChainId, bscChainId, btcChainId, CHAIN_NAMESPACE, @@ -54,6 +55,7 @@ import { supportsAvalanche, supportsBase, supportsBerachain, + supportsBob, supportsBSC, supportsBTC, supportsCosmos, @@ -127,6 +129,7 @@ export const accountIdToLabel = (accountId: AccountId): string => { case scrollChainId: case sonicChainId: case unichainChainId: + case bobChainId: case monadChainId: case plasmaChainId: case thorchainChainId: @@ -552,6 +555,8 @@ export const isAssetSupportedByWallet = (assetId: AssetId, wallet: HDWallet): bo return supportsSonic(wallet) case unichainChainId: return supportsUnichain(wallet) + case bobChainId: + return supportsBob(wallet) case tronChainId: return supportsTron(wallet) case nearChainId: diff --git a/src/state/slices/preferencesSlice/preferencesSlice.ts b/src/state/slices/preferencesSlice/preferencesSlice.ts index 79c4b3488b6..8e9ce563fec 100644 --- a/src/state/slices/preferencesSlice/preferencesSlice.ts +++ b/src/state/slices/preferencesSlice/preferencesSlice.ts @@ -46,6 +46,7 @@ export type FeatureFlags = { Scroll: boolean Sonic: boolean Unichain: boolean + Bob: boolean Zcash: boolean ThorSwap: boolean WalletConnectToDapps: boolean @@ -203,6 +204,7 @@ const initialState: Preferences = { Scroll: getConfig().VITE_FEATURE_SCROLL, Sonic: getConfig().VITE_FEATURE_SONIC, Unichain: getConfig().VITE_FEATURE_UNICHAIN, + Bob: getConfig().VITE_FEATURE_BOB, Zcash: getConfig().VITE_FEATURE_ZCASH, ThorSwap: getConfig().VITE_FEATURE_THOR_SWAP, WalletConnectToDappsV2: getConfig().VITE_FEATURE_WALLET_CONNECT_TO_DAPPS_V2, diff --git a/src/test/mocks/store.ts b/src/test/mocks/store.ts index d5614fb3674..e1ce2758a59 100644 --- a/src/test/mocks/store.ts +++ b/src/test/mocks/store.ts @@ -119,6 +119,7 @@ export const mockStore: ReduxState = { Scroll: false, Sonic: false, Unichain: false, + Bob: false, Zcash: false, ZrxSwap: false, ThorSwap: false, diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index e244a6378b0..a5c14b15ff3 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -194,6 +194,8 @@ interface ImportMetaEnv { readonly VITE_FEATURE_SONIC: string readonly VITE_UNICHAIN_NODE_URL: string readonly VITE_FEATURE_UNICHAIN: string + readonly VITE_BOB_NODE_URL: string + readonly VITE_FEATURE_BOB: string readonly VITE_FEATURE_NOTIFICATIONS_WEBSERVICES: string // Only present in *some* envs