diff --git a/.beads/ss-dx5.16.json b/.beads/ss-dx5.16.json new file mode 100644 index 00000000000..a7c83e7cf8a --- /dev/null +++ b/.beads/ss-dx5.16.json @@ -0,0 +1,39 @@ +[ + { + "id": "ss-dx5.16", + "title": "Integrate Story (chainId: 1514, IP, L1)", + "description": "TVL: $42.4M | RPC: https://mainnet.storyrpc.io | Explorer: https://storyscan.xyz | Token Support: All | Pattern B (RPC-only) | IP blockchain", + "notes": "\nLogo: https://assets.relay.link/icons/1514/light.png (dark: dark.png)\nCoinGecko platform: story\nCoinGecko native ID: story-2 (⚠️ NOT \"story\"!)\nArch: L1 (IP/Intellectual Property blockchain)\nNative token: IP (18 decimals)\nSLIP44: 60 (Ethereum)\nETH-native: NO — IP gas token\nViem: ✅ story (id: 1514) — import { story } from \"viem/chains\"\nAcross: ❌ NOT SUPPORTED\nZerion ID: \"story\" (standard lowercase)\n\n--- PHASE 1 LEARNED CHECKLIST (MANDATORY) ---\n[ ] adapter.json: Create packages/caip/src/adapters/coingecko/generated/eip155_1514/adapter.json → {\"eip155:1514/slip44:60\":\"story-2\"}\n[ ] Wire adapter.json: Import+export from packages/caip/src/adapters/coingecko/generated/index.ts\n[ ] getCoingeckoSupportedChainIds: Add storyChainId (feature-flagged) in src/lib/coingecko/utils.ts\n[ ] Zerion: Add \"story\" to ZERION_CHAINS + ZERION_CHAINS_MAP in packages/types/src/zerion.ts\n[ ] Across: ❌ SKIP — not supported\n[ ] ETH test ordering: ❌ SKIP — not ETH-native\n[ ] Market-service counts: ❌ SKIP — not ETH-native\n[ ] relatedAssetKey: null (NOT eip155:1/slip44:60)\n\n--- RELAY ACTIVATION ---\n[ ] Add [storyChainId]: story.id to chainIdToRelayChainId\n[ ] Add native asset case in relayTokenToAssetId.ts\n--- ASSET DATA REGEN ---\n[ ] Create scripts/generateAssetData/story/index.ts\n[ ] Add case to scripts/generateAssetData/coingecko.ts\n[ ] Add import + getAssets call to generateAssetData.ts\n--- DRAFT PR ---\n[ ] Branch: feat/integrate-story-relay (off develop)\n[ ] gh pr create --draft\n[ ] yarn lint --fix \u0026\u0026 yarn type-check must pass\n", + "status": "open", + "priority": 2, + "issue_type": "task", + "owner": "14963751+NeOMakinG@users.noreply.github.com", + "created_at": "2026-02-17T10:56:01Z", + "created_by": "NeOMakinG", + "updated_at": "2026-02-17T22:43:23Z", + "labels": [ + "evm", + "non-eth-gas", + "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 c926554048d..48069edd296 100644 --- a/.env +++ b/.env @@ -162,6 +162,7 @@ VITE_MEGAETH_NODE_URL=https://mainnet.megaeth.com/rpc VITE_LINEA_NODE_URL=https://rpc.linea.build VITE_SCROLL_NODE_URL=https://rpc.scroll.io/ VITE_KATANA_NODE_URL=https://rpc.katana.network +VITE_STORY_NODE_URL=https://mainnet.storyrpc.io VITE_ZKSYNC_ERA_NODE_URL=https://mainnet.era.zksync.io VITE_BLAST_NODE_URL=https://rpc.blast.io VITE_WORLDCHAIN_NODE_URL=https://worldchain-mainnet.g.alchemy.com/public @@ -326,6 +327,7 @@ VITE_FEATURE_BERACHAIN=false VITE_FEATURE_SCROLL=false VITE_FEATURE_NEAR=true VITE_FEATURE_KATANA=true +VITE_FEATURE_STORY=false VITE_FEATURE_ZK_SYNC_ERA=false VITE_FEATURE_BLAST=false VITE_FEATURE_HEMI=false diff --git a/.env.development b/.env.development index 514c4e7fbe3..e9648847a9d 100644 --- a/.env.development +++ b/.env.development @@ -61,6 +61,7 @@ VITE_BASE_NODE_URL=https://dev-api.base.shapeshift.com/api/v1/jsonrpc VITE_MONAD_NODE_URL=https://rpc.monad.xyz VITE_PLASMA_NODE_URL=https://rpc.plasma.to VITE_KATANA_NODE_URL=https://rpc.katana.network +VITE_STORY_NODE_URL=https://mainnet.storyrpc.io VITE_ZKSYNC_ERA_NODE_URL=https://mainnet.era.zksync.io VITE_BLAST_NODE_URL=https://rpc.blast.io VITE_WORLDCHAIN_NODE_URL=https://worldchain-mainnet.g.alchemy.com/public @@ -117,6 +118,7 @@ VITE_FEATURE_LINEA=true VITE_FEATURE_BERACHAIN=true VITE_FEATURE_SCROLL=true VITE_FEATURE_KATANA=true +VITE_FEATURE_STORY=true VITE_FEATURE_ZK_SYNC_ERA=true VITE_FEATURE_BLAST=true VITE_FEATURE_WORLDCHAIN=true diff --git a/headers/csps/chains/story.ts b/headers/csps/chains/story.ts new file mode 100644 index 00000000000..7f8235b7ae5 --- /dev/null +++ b/headers/csps/chains/story.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_STORY_NODE_URL], +} diff --git a/headers/csps/index.ts b/headers/csps/index.ts index 969757560b8..86592af0881 100644 --- a/headers/csps/index.ts +++ b/headers/csps/index.ts @@ -38,6 +38,7 @@ import { csp as solana } from './chains/solana' import { csp as soneium } from './chains/soneium' import { csp as sonic } from './chains/sonic' import { csp as starknet } from './chains/starknet' +import { csp as story } from './chains/story' import { csp as sui } from './chains/sui' import { csp as thorchain } from './chains/thorchain' import { csp as ton } from './chains/ton' @@ -159,6 +160,7 @@ export const csps = [ monad, near, plasma, + story, worldchain, sonic, trustwallet, diff --git a/packages/caip/src/adapters/coingecko/generated/eip155_1514/adapter.json b/packages/caip/src/adapters/coingecko/generated/eip155_1514/adapter.json new file mode 100644 index 00000000000..3e6fcb456c0 --- /dev/null +++ b/packages/caip/src/adapters/coingecko/generated/eip155_1514/adapter.json @@ -0,0 +1 @@ +{"eip155:1514/slip44:60":"story-2"} \ No newline at end of file diff --git a/packages/caip/src/adapters/coingecko/generated/index.ts b/packages/caip/src/adapters/coingecko/generated/index.ts index f2d67e687b9..3de5de28f54 100644 --- a/packages/caip/src/adapters/coingecko/generated/index.ts +++ b/packages/caip/src/adapters/coingecko/generated/index.ts @@ -21,6 +21,7 @@ import linea from "./eip155_59144/adapter.json"; import bob from "./eip155_60808/adapter.json"; import mode from "./eip155_34443/adapter.json"; import megaeth from "./eip155_4326/adapter.json"; +import story from "./eip155_1514/adapter.json"; import mantle from "./eip155_5000/adapter.json"; import berachain from "./eip155_80094/adapter.json"; import ink from "./eip155_57073/adapter.json"; @@ -65,6 +66,7 @@ export { linea, mode, megaeth, + story, mantle, berachain, ink, diff --git a/packages/caip/src/adapters/coingecko/index.ts b/packages/caip/src/adapters/coingecko/index.ts index 75828d12571..e8dbb373545 100644 --- a/packages/caip/src/adapters/coingecko/index.ts +++ b/packages/caip/src/adapters/coingecko/index.ts @@ -37,6 +37,7 @@ import { soneiumChainId, sonicChainId, starknetChainId, + storyChainId, suiChainId, thorchainChainId, tonChainId, @@ -67,6 +68,7 @@ export enum CoingeckoAssetPlatform { Plasma = 'plasma', Ink = 'ink', Katana = 'katana', + Story = 'story', ZkSyncEra = 'zksync', Blast = 'blast', WorldChain = 'world-chain', @@ -142,6 +144,8 @@ export const chainIdToCoingeckoAssetPlatform = (chainId: ChainId): string => { return CoingeckoAssetPlatform.Plasma case CHAIN_REFERENCE.KatanaMainnet: return CoingeckoAssetPlatform.Katana + case CHAIN_REFERENCE.StoryMainnet: + return CoingeckoAssetPlatform.Story case CHAIN_REFERENCE.ZkSyncEraMainnet: return CoingeckoAssetPlatform.ZkSyncEra case CHAIN_REFERENCE.BlastMainnet: @@ -283,6 +287,8 @@ export const coingeckoAssetPlatformToChainId = ( return worldChainChainId case CoingeckoAssetPlatform.Katana: return katanaChainId + case CoingeckoAssetPlatform.Story: + return storyChainId case CoingeckoAssetPlatform.Mantle: return mantleChainId case CoingeckoAssetPlatform.Sonic: diff --git a/packages/caip/src/adapters/coingecko/utils.test.ts b/packages/caip/src/adapters/coingecko/utils.test.ts index 765df7363df..8500f844bc6 100644 --- a/packages/caip/src/adapters/coingecko/utils.test.ts +++ b/packages/caip/src/adapters/coingecko/utils.test.ts @@ -191,6 +191,9 @@ describe('adapters:coingecko:utils', () => { 'eip155:747474': { 'eip155:747474/slip44:60': 'katana', }, + 'eip155:1514': { + 'eip155:1514/slip44:60': 'story-2', + }, 'eip155:324': { 'eip155:324/slip44:60': 'ethereum', }, diff --git a/packages/caip/src/adapters/coingecko/utils.ts b/packages/caip/src/adapters/coingecko/utils.ts index 5a0bc633ba4..25bd0521dba 100644 --- a/packages/caip/src/adapters/coingecko/utils.ts +++ b/packages/caip/src/adapters/coingecko/utils.ts @@ -70,6 +70,8 @@ import { sonicChainId, starknetAssetId, starknetChainId, + storyAssetId, + storyChainId, suiAssetId, suiChainId, thorchainChainId, @@ -431,6 +433,20 @@ export const parseData = (coins: CoingeckoCoin[]): AssetMap => { } } + if (Object.keys(platforms).includes(CoingeckoAssetPlatform.Story)) { + try { + const assetId = toAssetId({ + chainNamespace: CHAIN_NAMESPACE.Evm, + chainReference: CHAIN_REFERENCE.StoryMainnet, + assetNamespace: 'erc20', + assetReference: platforms[CoingeckoAssetPlatform.Story], + }) + prev[storyChainId][assetId] = id + } catch { + // unable to create assetId, skip token + } + } + if (Object.keys(platforms).includes(CoingeckoAssetPlatform.ZkSyncEra)) { try { const assetId = toAssetId({ @@ -606,6 +622,7 @@ export const parseData = (coins: CoingeckoCoin[]): AssetMap => { [berachainChainId]: { [berachainAssetId]: 'berachain-bera' }, [cronosChainId]: { [cronosAssetId]: 'crypto-com-chain' }, [katanaChainId]: { [katanaAssetId]: 'katana' }, + [storyChainId]: { [storyAssetId]: 'story-2' }, [zkSyncEraChainId]: { [zkSyncEraAssetId]: 'ethereum' }, [blastChainId]: { [blastAssetId]: 'ethereum' }, [worldChainChainId]: { [worldChainAssetId]: 'ethereum' }, diff --git a/packages/caip/src/constants.ts b/packages/caip/src/constants.ts index ea530f5aab5..be170f5e8e8 100644 --- a/packages/caip/src/constants.ts +++ b/packages/caip/src/constants.ts @@ -25,6 +25,7 @@ export const worldChainAssetId: AssetId = 'eip155:480/slip44:60' export const berachainAssetId: AssetId = 'eip155:80094/slip44:60' export const cronosAssetId: AssetId = 'eip155:25/slip44:60' export const katanaAssetId: AssetId = 'eip155:747474/slip44:60' +export const storyAssetId: AssetId = 'eip155:1514/slip44:60' export const zkSyncEraAssetId: AssetId = 'eip155:324/slip44:60' export const blastAssetId: AssetId = 'eip155:81457/slip44:60' export const hemiAssetId: AssetId = 'eip155:43111/slip44:60' @@ -101,6 +102,7 @@ export const worldChainChainId: ChainId = 'eip155:480' export const berachainChainId: ChainId = 'eip155:80094' export const cronosChainId: ChainId = 'eip155:25' export const katanaChainId: ChainId = 'eip155:747474' +export const storyChainId: ChainId = 'eip155:1514' export const zkSyncEraChainId: ChainId = 'eip155:324' export const blastChainId: ChainId = 'eip155:81457' export const hemiChainId: ChainId = 'eip155:43111' @@ -168,6 +170,7 @@ export const CHAIN_REFERENCE = { BerachainMainnet: '80094', CronosMainnet: '25', KatanaMainnet: '747474', // https://docs.katana.network + StoryMainnet: '1514', // https://docs.story.foundation ZkSyncEraMainnet: '324', // https://docs.zksync.io BlastMainnet: '81457', // https://docs.blast.io HemiMainnet: '43111', // https://docs.hemi.xyz @@ -227,6 +230,7 @@ export const ASSET_REFERENCE = { Berachain: '60', // evm chain which uses ethereum derivation path as common practice Cronos: '60', // evm chain which uses ethereum derivation path as common practice Katana: '60', // evm chain which uses ethereum derivation path as common practice + Story: '60', // evm chain which uses ethereum derivation path as common practice ZkSyncEra: '60', // evm chain which uses ethereum derivation path as common practice Blast: '60', // evm chain which uses ethereum derivation path as common practice WorldChain: '60', // evm chain which uses ethereum derivation path as common practice @@ -272,6 +276,7 @@ export const VALID_CHAIN_IDS: ValidChainMap = Object.freeze({ CHAIN_REFERENCE.BerachainMainnet, CHAIN_REFERENCE.CronosMainnet, CHAIN_REFERENCE.KatanaMainnet, + CHAIN_REFERENCE.StoryMainnet, CHAIN_REFERENCE.ZkSyncEraMainnet, CHAIN_REFERENCE.BlastMainnet, CHAIN_REFERENCE.WorldChainMainnet, @@ -340,6 +345,7 @@ export const FEE_ASSET_IDS = [ mantleAssetId, inkAssetId, megaethAssetId, + storyAssetId, zkSyncEraAssetId, blastAssetId, hemiAssetId, diff --git a/packages/chain-adapters/src/evm/EvmBaseAdapter.ts b/packages/chain-adapters/src/evm/EvmBaseAdapter.ts index 81f3ad7fefd..149a6dccaef 100644 --- a/packages/chain-adapters/src/evm/EvmBaseAdapter.ts +++ b/packages/chain-adapters/src/evm/EvmBaseAdapter.ts @@ -33,6 +33,7 @@ import { supportsScroll, supportsSoneium, supportsSonic, + supportsStory, supportsUnichain, supportsWorldChain, supportsZkSyncEra, @@ -107,6 +108,7 @@ export const evmChainIds = [ KnownChainIds.BerachainMainnet, KnownChainIds.CronosMainnet, KnownChainIds.KatanaMainnet, + KnownChainIds.StoryMainnet, KnownChainIds.ZkSyncEraMainnet, KnownChainIds.BlastMainnet, KnownChainIds.WorldChainMainnet, @@ -230,6 +232,8 @@ export abstract class EvmBaseAdapter implements IChainAdap return supportsCronos(wallet) case Number(fromChainId(KnownChainIds.KatanaMainnet).chainReference): return supportsKatana(wallet) + case Number(fromChainId(KnownChainIds.StoryMainnet).chainReference): + return supportsStory(wallet) case Number(fromChainId(KnownChainIds.ZkSyncEraMainnet).chainReference): return supportsZkSyncEra(wallet) case Number(fromChainId(KnownChainIds.BlastMainnet).chainReference): @@ -369,6 +373,11 @@ export abstract class EvmBaseAdapter implements IChainAdap symbol: 'ETH', explorer: 'https://katanascan.com', }, + [KnownChainIds.StoryMainnet]: { + name: 'Story', + symbol: 'IP', + explorer: 'https://storyscan.xyz', + }, [KnownChainIds.ZkSyncEraMainnet]: { name: 'Ether', symbol: 'ETH', diff --git a/packages/chain-adapters/src/evm/SecondClassEvmAdapter.ts b/packages/chain-adapters/src/evm/SecondClassEvmAdapter.ts index ae310eb511f..33151c9f3fa 100644 --- a/packages/chain-adapters/src/evm/SecondClassEvmAdapter.ts +++ b/packages/chain-adapters/src/evm/SecondClassEvmAdapter.ts @@ -11,6 +11,7 @@ import { modeChainId, soneiumChainId, sonicChainId, + storyChainId, toAssetId, unichainChainId, worldChainChainId, @@ -64,6 +65,7 @@ const WRAPPED_NATIVE_CONTRACT_BY_CHAIN_ID: Partial> = { [worldChainChainId]: '0x4200000000000000000000000000000000000006', [blastChainId]: '0x4300000000000000000000000000000000000004', [zkSyncEraChainId]: '0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91', + [storyChainId]: '0x1514000000000000000000000000000000000000', } const BATCH_SIZE = 500 @@ -484,7 +486,6 @@ export abstract class SecondClassEvmAdapter extends EvmBas value: log.args.value.toString(), }) } - // Fallback: WETH9 Withdrawal(address indexed src, uint256 wad) event // On OP Stack L2s and other chains, WETH.withdraw() emits Withdrawal instead of Transfer-to-zero if (internalTxs.length === 0) { diff --git a/packages/chain-adapters/src/evm/index.ts b/packages/chain-adapters/src/evm/index.ts index c06d57fbabc..43c12f26deb 100644 --- a/packages/chain-adapters/src/evm/index.ts +++ b/packages/chain-adapters/src/evm/index.ts @@ -22,6 +22,7 @@ export * as megaeth from './megaeth' export * as berachain from './berachain' export * as cronos from './cronos' export * as katana from './katana' +export * as story from './story' export * as zksyncera from './zksyncera' export * as blast from './blast' export * as worldchain from './worldchain' diff --git a/packages/chain-adapters/src/evm/story/StoryChainAdapter.ts b/packages/chain-adapters/src/evm/story/StoryChainAdapter.ts new file mode 100644 index 00000000000..a466237ece4 --- /dev/null +++ b/packages/chain-adapters/src/evm/story/StoryChainAdapter.ts @@ -0,0 +1,57 @@ +import type { AssetId } from '@shapeshiftoss/caip' +import { ASSET_REFERENCE, storyAssetId } 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.StoryMainnet] +const DEFAULT_CHAIN_ID = KnownChainIds.StoryMainnet + +export type ChainAdapterArgs = { + rpcUrl: string + getKnownTokens: () => TokenInfo[] +} + +export const isStoryChainAdapter = (adapter: unknown): adapter is ChainAdapter => { + return (adapter as ChainAdapter).getType() === KnownChainIds.StoryMainnet +} + +export class ChainAdapter extends SecondClassEvmAdapter { + public static readonly rootBip44Params: RootBip44Params = { + purpose: 44, + coinType: Number(ASSET_REFERENCE.Story), + accountNumber: 0, + } + + constructor(args: ChainAdapterArgs) { + super({ + assetId: storyAssetId, + chainId: DEFAULT_CHAIN_ID, + rootBip44Params: ChainAdapter.rootBip44Params, + supportedChainIds: SUPPORTED_CHAIN_IDS, + rpcUrl: args.rpcUrl, + getKnownTokens: args.getKnownTokens, + }) + } + + getDisplayName() { + return ChainAdapterDisplayName.Story + } + + getName() { + return 'Story' + } + + getType(): KnownChainIds.StoryMainnet { + return KnownChainIds.StoryMainnet + } + + getFeeAssetId(): AssetId { + return this.assetId + } +} + +export type { TokenInfo } diff --git a/packages/chain-adapters/src/evm/story/index.ts b/packages/chain-adapters/src/evm/story/index.ts new file mode 100644 index 00000000000..6b7052c8eaf --- /dev/null +++ b/packages/chain-adapters/src/evm/story/index.ts @@ -0,0 +1 @@ +export * from './StoryChainAdapter' diff --git a/packages/chain-adapters/src/types.ts b/packages/chain-adapters/src/types.ts index e84bd40584d..534cae45bb2 100644 --- a/packages/chain-adapters/src/types.ts +++ b/packages/chain-adapters/src/types.ts @@ -53,6 +53,7 @@ type ChainSpecificAccount = ChainSpecific< [KnownChainIds.BerachainMainnet]: evm.Account [KnownChainIds.CronosMainnet]: evm.Account [KnownChainIds.KatanaMainnet]: evm.Account + [KnownChainIds.StoryMainnet]: evm.Account [KnownChainIds.ZkSyncEraMainnet]: evm.Account [KnownChainIds.BlastMainnet]: evm.Account [KnownChainIds.WorldChainMainnet]: evm.Account @@ -120,6 +121,7 @@ type ChainSpecificFeeData = ChainSpecific< [KnownChainIds.BerachainMainnet]: evm.FeeData [KnownChainIds.CronosMainnet]: evm.FeeData [KnownChainIds.KatanaMainnet]: evm.FeeData + [KnownChainIds.StoryMainnet]: evm.FeeData [KnownChainIds.ZkSyncEraMainnet]: evm.FeeData [KnownChainIds.BlastMainnet]: evm.FeeData [KnownChainIds.WorldChainMainnet]: evm.FeeData @@ -220,6 +222,7 @@ export type ChainSignTx = { [KnownChainIds.BerachainMainnet]: ETHSignTx [KnownChainIds.CronosMainnet]: ETHSignTx [KnownChainIds.KatanaMainnet]: ETHSignTx + [KnownChainIds.StoryMainnet]: ETHSignTx [KnownChainIds.ZkSyncEraMainnet]: ETHSignTx [KnownChainIds.BlastMainnet]: ETHSignTx [KnownChainIds.WorldChainMainnet]: ETHSignTx @@ -292,6 +295,7 @@ export type ChainSpecificBuildTxData = ChainSpecific< [KnownChainIds.BerachainMainnet]: evm.BuildTxInput [KnownChainIds.CronosMainnet]: evm.BuildTxInput [KnownChainIds.KatanaMainnet]: evm.BuildTxInput + [KnownChainIds.StoryMainnet]: evm.BuildTxInput [KnownChainIds.ZkSyncEraMainnet]: evm.BuildTxInput [KnownChainIds.BlastMainnet]: evm.BuildTxInput [KnownChainIds.WorldChainMainnet]: evm.BuildTxInput @@ -414,6 +418,7 @@ type ChainSpecificGetFeeDataInput = ChainSpecific< [KnownChainIds.BerachainMainnet]: evm.GetFeeDataInput [KnownChainIds.CronosMainnet]: evm.GetFeeDataInput [KnownChainIds.KatanaMainnet]: evm.GetFeeDataInput + [KnownChainIds.StoryMainnet]: evm.GetFeeDataInput [KnownChainIds.ZkSyncEraMainnet]: evm.GetFeeDataInput [KnownChainIds.BlastMainnet]: evm.GetFeeDataInput [KnownChainIds.WorldChainMainnet]: evm.GetFeeDataInput @@ -501,6 +506,7 @@ export enum ChainAdapterDisplayName { Berachain = 'Berachain', Cronos = 'Cronos', Katana = 'Katana', + Story = 'Story', ZkSyncEra = 'zkSync Era', Blast = 'Blast', WorldChain = 'World Chain', diff --git a/packages/contracts/src/ethersProviderSingleton.ts b/packages/contracts/src/ethersProviderSingleton.ts index 9481a869c2f..5f2122841cb 100644 --- a/packages/contracts/src/ethersProviderSingleton.ts +++ b/packages/contracts/src/ethersProviderSingleton.ts @@ -42,6 +42,8 @@ export const rpcUrlByChainId = (chainId: EvmChainId): string => { return process.env.VITE_CRONOS_NODE_URL case KnownChainIds.KatanaMainnet: return process.env.VITE_KATANA_NODE_URL + case KnownChainIds.StoryMainnet: + return process.env.VITE_STORY_NODE_URL case KnownChainIds.ZkSyncEraMainnet: return process.env.VITE_ZKSYNC_ERA_NODE_URL case KnownChainIds.BlastMainnet: diff --git a/packages/contracts/src/viemClient.ts b/packages/contracts/src/viemClient.ts index cb6744580cd..54bcbccfa42 100644 --- a/packages/contracts/src/viemClient.ts +++ b/packages/contracts/src/viemClient.ts @@ -28,6 +28,7 @@ import { scroll, soneium, sonic, + story, unichain, worldchain, zksync, @@ -157,6 +158,11 @@ export const viemKatanaClient = createPublicClient({ transport: fallback([process.env.VITE_KATANA_NODE_URL].filter(Boolean).map(url => http(url))), }) as PublicClient +export const viemStoryClient = createPublicClient({ + chain: story, + transport: fallback([process.env.VITE_STORY_NODE_URL].filter(Boolean).map(url => http(url))), +}) as PublicClient + export const viemZkSyncEraClient = createPublicClient({ chain: zksync, transport: fallback([process.env.VITE_ZKSYNC_ERA_NODE_URL].filter(Boolean).map(url => http(url))), @@ -225,6 +231,7 @@ export const viemClientByChainId: Record = { [KnownChainIds.BerachainMainnet]: viemBerachainClient, [KnownChainIds.CronosMainnet]: viemCronosClient, [KnownChainIds.KatanaMainnet]: viemKatanaClient, + [KnownChainIds.StoryMainnet]: viemStoryClient, [KnownChainIds.ZkSyncEraMainnet]: viemZkSyncEraClient, [KnownChainIds.BlastMainnet]: viemBlastClient, [KnownChainIds.WorldChainMainnet]: viemWorldChainClient, @@ -256,6 +263,7 @@ export const viemNetworkIdByChainId: Record = { [KnownChainIds.BerachainMainnet]: berachain.id, [KnownChainIds.CronosMainnet]: cronos.id, [KnownChainIds.KatanaMainnet]: katana.id, + [KnownChainIds.StoryMainnet]: story.id, [KnownChainIds.ZkSyncEraMainnet]: zksync.id, [KnownChainIds.BlastMainnet]: blast.id, [KnownChainIds.WorldChainMainnet]: worldchain.id, @@ -287,6 +295,7 @@ export const viemClientByNetworkId: Record = { [berachain.id]: viemBerachainClient, [cronos.id]: viemCronosClient, [katana.id]: viemKatanaClient, + [story.id]: viemStoryClient, [zksync.id]: viemZkSyncEraClient, [blast.id]: viemBlastClient, [worldchain.id]: viemWorldChainClient, diff --git a/packages/hdwallet-coinbase/src/coinbase.ts b/packages/hdwallet-coinbase/src/coinbase.ts index d4324648a5b..b82dc62aac1 100644 --- a/packages/hdwallet-coinbase/src/coinbase.ts +++ b/packages/hdwallet-coinbase/src/coinbase.ts @@ -110,6 +110,7 @@ export class CoinbaseHDWallet implements core.HDWallet, core.ETHWallet { readonly _supportsMonad = false readonly _supportsPlasma = false readonly _supportsKatana = false + readonly _supportsStory = false readonly _supportsSonic = false readonly _supportsBob = false readonly _supportsMode = false diff --git a/packages/hdwallet-core/src/ethereum.ts b/packages/hdwallet-core/src/ethereum.ts index febf4e39450..71a2b77fab0 100644 --- a/packages/hdwallet-core/src/ethereum.ts +++ b/packages/hdwallet-core/src/ethereum.ts @@ -208,6 +208,7 @@ export interface ETHWallet extends ETHWalletInfo, HDWallet { readonly _supportsMonad: boolean readonly _supportsPlasma: boolean readonly _supportsKatana: boolean + readonly _supportsStory: boolean readonly _supportsSonic: boolean readonly _supportsBob: boolean readonly _supportsMode: boolean diff --git a/packages/hdwallet-core/src/wallet.ts b/packages/hdwallet-core/src/wallet.ts index d63aaef4abc..28409a9f13e 100644 --- a/packages/hdwallet-core/src/wallet.ts +++ b/packages/hdwallet-core/src/wallet.ts @@ -181,6 +181,10 @@ export function supportsKatana(wallet: HDWallet): wallet is ETHWallet { return isObject(wallet) && (wallet as any)._supportsKatana } +export function supportsStory(wallet: HDWallet): wallet is ETHWallet { + return isObject(wallet) && (wallet as any)._supportsStory +} + export function supportsSonic(wallet: HDWallet): wallet is ETHWallet { return isObject(wallet) && (wallet as any)._supportsSonic } diff --git a/packages/hdwallet-gridplus/src/gridplus.ts b/packages/hdwallet-gridplus/src/gridplus.ts index d49f41c0ac1..d60834e1500 100644 --- a/packages/hdwallet-gridplus/src/gridplus.ts +++ b/packages/hdwallet-gridplus/src/gridplus.ts @@ -363,6 +363,7 @@ export class GridPlusHDWallet readonly _supportsMonad = true readonly _supportsPlasma = true readonly _supportsKatana = false + readonly _supportsStory = false readonly _supportsSonic = false readonly _supportsBob = false readonly _supportsMode = false diff --git a/packages/hdwallet-keepkey/src/keepkey.ts b/packages/hdwallet-keepkey/src/keepkey.ts index 577523aec2e..28ac87127e1 100644 --- a/packages/hdwallet-keepkey/src/keepkey.ts +++ b/packages/hdwallet-keepkey/src/keepkey.ts @@ -552,6 +552,7 @@ export class KeepKeyHDWallet readonly _supportsMonad = false readonly _supportsPlasma = false readonly _supportsKatana = false + readonly _supportsStory = false readonly _supportsSonic = false readonly _supportsBob = false readonly _supportsMode = false diff --git a/packages/hdwallet-ledger/src/ledger.ts b/packages/hdwallet-ledger/src/ledger.ts index 59e4c3db40f..c3d30e720e1 100644 --- a/packages/hdwallet-ledger/src/ledger.ts +++ b/packages/hdwallet-ledger/src/ledger.ts @@ -429,6 +429,7 @@ export class LedgerHDWallet readonly _supportsMonad = true readonly _supportsPlasma = true readonly _supportsKatana = true + readonly _supportsStory = true readonly _supportsSonic = true readonly _supportsBob = true readonly _supportsMode = true diff --git a/packages/hdwallet-metamask-multichain/src/shapeshift-multichain.ts b/packages/hdwallet-metamask-multichain/src/shapeshift-multichain.ts index ef4b9928d3e..791b597e369 100644 --- a/packages/hdwallet-metamask-multichain/src/shapeshift-multichain.ts +++ b/packages/hdwallet-metamask-multichain/src/shapeshift-multichain.ts @@ -91,14 +91,11 @@ export class MetaMaskMultiChainHDWalletInfo implements core.HDWalletInfo, core.E case 'Atom': return core.cosmosDescribePath(msg.path) - case 'Ethereum': return core.describeETHPath(msg.path) - case 'Rune': case 'Thorchain': return core.thorchainDescribePath(msg.path) - default: throw new Error('Unsupported path') } @@ -279,6 +276,7 @@ export class MetaMaskMultiChainHDWallet readonly _supportsMonad = true readonly _supportsPlasma = true readonly _supportsKatana = true + readonly _supportsStory = true readonly _supportsSonic = true readonly _supportsBob = true readonly _supportsMode = true diff --git a/packages/hdwallet-native/src/ethereum.ts b/packages/hdwallet-native/src/ethereum.ts index 92daf77f05d..c9a2d59f7b5 100644 --- a/packages/hdwallet-native/src/ethereum.ts +++ b/packages/hdwallet-native/src/ethereum.ts @@ -68,6 +68,7 @@ export function MixinNativeETHWallet { assetReference: ASSET_REFERENCE.Katana, assetNamespace: ASSET_NAMESPACE.slip44, } + case CHAIN_REFERENCE.StoryMainnet: + return { + assetReference: ASSET_REFERENCE.Story, + assetNamespace: ASSET_NAMESPACE.slip44, + } case CHAIN_REFERENCE.ZkSyncEraMainnet: return { assetReference: ASSET_REFERENCE.ZkSyncEra, diff --git a/packages/types/src/base.ts b/packages/types/src/base.ts index 6934b986395..66891141c1a 100644 --- a/packages/types/src/base.ts +++ b/packages/types/src/base.ts @@ -31,6 +31,7 @@ export enum KnownChainIds { BerachainMainnet = 'eip155:80094', CronosMainnet = 'eip155:25', KatanaMainnet = 'eip155:747474', + StoryMainnet = 'eip155:1514', ZkSyncEraMainnet = 'eip155:324', BlastMainnet = 'eip155:81457', WorldChainMainnet = 'eip155:480', @@ -76,6 +77,7 @@ export type EvmChainId = | KnownChainIds.BerachainMainnet | KnownChainIds.CronosMainnet | KnownChainIds.KatanaMainnet + | KnownChainIds.StoryMainnet | KnownChainIds.ZkSyncEraMainnet | KnownChainIds.BlastMainnet | KnownChainIds.WorldChainMainnet diff --git a/packages/types/src/zerion.ts b/packages/types/src/zerion.ts index f0157acc3a4..e19f55a21b4 100644 --- a/packages/types/src/zerion.ts +++ b/packages/types/src/zerion.ts @@ -23,6 +23,7 @@ import { solanaChainId, soneiumChainId, sonicChainId, + storyChainId, suiChainId, tronChainId, unichainChainId, @@ -47,6 +48,7 @@ export const ZERION_CHAINS = [ 'plasma', 'world-chain', 'katana', + 'story', 'zksync-era', 'blast', 'hemi', @@ -83,6 +85,7 @@ export const ZERION_CHAINS_MAP: Record = { plasma: plasmaChainId, 'world-chain': worldChainChainId, katana: katanaChainId, + story: storyChainId, 'zksync-era': zkSyncEraChainId, blast: blastChainId, hemi: hemiChainId, diff --git a/packages/utils/src/assetData/baseAssets.ts b/packages/utils/src/assetData/baseAssets.ts index 04011ae781a..9ebaa49ed09 100644 --- a/packages/utils/src/assetData/baseAssets.ts +++ b/packages/utils/src/assetData/baseAssets.ts @@ -472,6 +472,23 @@ export const katana: Readonly = Object.freeze({ relatedAssetKey: 'eip155:1/slip44:60', }) +export const story: Readonly = Object.freeze({ + assetId: caip.storyAssetId, + chainId: caip.storyChainId, + name: 'Story', + networkName: 'Story', + symbol: 'IP', + precision: 18, + color: '#EA580C', + networkColor: '#EA580C', + icon: 'https://assets.coingecko.com/coins/images/51994/large/story-2.png', + networkIcon: 'https://assets.coingecko.com/coins/images/51994/small/story-2.png', + explorer: 'https://storyscan.xyz', + explorerAddressLink: 'https://storyscan.xyz/address/', + explorerTxLink: 'https://storyscan.xyz/tx/', + relatedAssetKey: null, +}) + export const zkSyncEra: Readonly = Object.freeze({ assetId: caip.zkSyncEraAssetId, chainId: caip.zkSyncEraChainId, diff --git a/packages/utils/src/assetData/getBaseAsset.ts b/packages/utils/src/assetData/getBaseAsset.ts index 88cc544fa9e..efda1012c64 100644 --- a/packages/utils/src/assetData/getBaseAsset.ts +++ b/packages/utils/src/assetData/getBaseAsset.ts @@ -38,6 +38,7 @@ import { soneium, sonic, starknet, + story, sui, thorchain, ton, @@ -107,6 +108,8 @@ export const getBaseAsset = (chainId: ChainId): Readonly => { return cronos case KnownChainIds.KatanaMainnet: return katana + case KnownChainIds.StoryMainnet: + return story case KnownChainIds.ZkSyncEraMainnet: return zkSyncEra case KnownChainIds.BlastMainnet: diff --git a/packages/utils/src/chainIdToFeeAssetId.ts b/packages/utils/src/chainIdToFeeAssetId.ts index 5eb3f895c90..e5f38929a75 100644 --- a/packages/utils/src/chainIdToFeeAssetId.ts +++ b/packages/utils/src/chainIdToFeeAssetId.ts @@ -34,6 +34,7 @@ import { soneiumAssetId, sonicAssetId, starknetAssetId, + storyAssetId, suiAssetId, thorchainAssetId, tonAssetId, @@ -106,6 +107,8 @@ export const chainIdToFeeAssetId = (_chainId: ChainId): AssetId => { return cronosAssetId case KnownChainIds.KatanaMainnet: return katanaAssetId + case KnownChainIds.StoryMainnet: + return storyAssetId case KnownChainIds.ZkSyncEraMainnet: return zkSyncEraAssetId case KnownChainIds.BlastMainnet: diff --git a/packages/utils/src/getAssetNamespaceFromChainId.ts b/packages/utils/src/getAssetNamespaceFromChainId.ts index 24e9142e1b4..eb51f42a8c0 100644 --- a/packages/utils/src/getAssetNamespaceFromChainId.ts +++ b/packages/utils/src/getAssetNamespaceFromChainId.ts @@ -31,6 +31,7 @@ export const getAssetNamespaceFromChainId = (chainId: KnownChainIds): AssetNames case KnownChainIds.BerachainMainnet: case KnownChainIds.CronosMainnet: case KnownChainIds.KatanaMainnet: + case KnownChainIds.StoryMainnet: case KnownChainIds.ZkSyncEraMainnet: case KnownChainIds.BlastMainnet: case KnownChainIds.WorldChainMainnet: diff --git a/packages/utils/src/getChainShortName.ts b/packages/utils/src/getChainShortName.ts index e9a3a236a28..11335a6d999 100644 --- a/packages/utils/src/getChainShortName.ts +++ b/packages/utils/src/getChainShortName.ts @@ -60,6 +60,8 @@ export const getChainShortName = (chainId: KnownChainIds) => { return 'CRO' case KnownChainIds.KatanaMainnet: return 'KAT' + case KnownChainIds.StoryMainnet: + return 'STORY' case KnownChainIds.ZkSyncEraMainnet: return 'ZKS' case KnownChainIds.BlastMainnet: diff --git a/packages/utils/src/getNativeFeeAssetReference.ts b/packages/utils/src/getNativeFeeAssetReference.ts index 78031986601..facfae08cf3 100644 --- a/packages/utils/src/getNativeFeeAssetReference.ts +++ b/packages/utils/src/getNativeFeeAssetReference.ts @@ -58,6 +58,8 @@ export const getNativeFeeAssetReference = ( return ASSET_REFERENCE.Cronos case CHAIN_REFERENCE.KatanaMainnet: return ASSET_REFERENCE.Katana + case CHAIN_REFERENCE.StoryMainnet: + return ASSET_REFERENCE.Story case CHAIN_REFERENCE.ZkSyncEraMainnet: return ASSET_REFERENCE.ZkSyncEra case CHAIN_REFERENCE.BlastMainnet: diff --git a/public/generated/asset-manifest.json b/public/generated/asset-manifest.json index c84fae7277a..920c799c58a 100644 --- a/public/generated/asset-manifest.json +++ b/public/generated/asset-manifest.json @@ -1,4 +1,4 @@ { - "assetData": "949760af", - "relatedAssetIndex": "632b7e0d" + "assetData": "de860198", + "relatedAssetIndex": "f3a40758" } \ No newline at end of file diff --git a/public/generated/asset-manifest.json.br b/public/generated/asset-manifest.json.br index 2d7979d8bb0..696a3bcbe2a 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 93d79b6d653..22fd185ab05 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 b004b00c7c9..b1734e32512 100644 --- a/public/generated/generatedAssetData.json +++ b/public/generated/generatedAssetData.json @@ -436549,6 +436549,191 @@ "explorerAddressLink": "https://explorer.zksync.io/address/", "explorerTxLink": "https://explorer.zksync.io/tx/", "relatedAssetKey": "eip155:1/slip44:60" + }, + "eip155:1514/erc20:0x1514000000000000000000000000000000000000": { + "assetId": "eip155:1514/erc20:0x1514000000000000000000000000000000000000", + "chainId": "eip155:1514", + "name": "Wrapped IP", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/54378/large/image.png?1739437755", + "symbol": "WIP", + "explorer": "https://storyscan.xyz", + "explorerAddressLink": "https://storyscan.xyz/address/", + "explorerTxLink": "https://storyscan.xyz/tx/", + "relatedAssetKey": null + }, + "eip155:1514/erc20:0x25f9c9715d1d700a50b2a9a06d80fe9f98ccb549": { + "assetId": "eip155:1514/erc20:0x25f9c9715d1d700a50b2a9a06d80fe9f98ccb549", + "chainId": "eip155:1514", + "name": "BENJAMIN", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/54990/large/benjaminn.png?1743059951", + "symbol": "BENJI", + "explorer": "https://storyscan.xyz", + "explorerAddressLink": "https://storyscan.xyz/address/", + "explorerTxLink": "https://storyscan.xyz/tx/", + "relatedAssetKey": null + }, + "eip155:1514/erc20:0x543374350269cce6651358769512875faa4cccff": { + "assetId": "eip155:1514/erc20:0x543374350269cce6651358769512875faa4cccff", + "chainId": "eip155:1514", + "name": "IPPY", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/68997/large/0x543374350269cce6651358769512875faa4cccff.png?1757225559", + "symbol": "IPPY", + "explorer": "https://storyscan.xyz", + "explorerAddressLink": "https://storyscan.xyz/address/", + "explorerTxLink": "https://storyscan.xyz/tx/", + "relatedAssetKey": null + }, + "eip155:1514/erc20:0x5a212776066b81e449fe74396cce368dc4b14043": { + "assetId": "eip155:1514/erc20:0x5a212776066b81e449fe74396cce368dc4b14043", + "chainId": "eip155:1514", + "name": "WhatTheFreg", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/66146/large/sidebar_head.png?1748465639", + "symbol": "WTF", + "explorer": "https://storyscan.xyz", + "explorerAddressLink": "https://storyscan.xyz/address/", + "explorerTxLink": "https://storyscan.xyz/tx/", + "relatedAssetKey": null + }, + "eip155:1514/erc20:0x693c7acf65e52c71bafe555bc22d69cb7f8a78a2": { + "assetId": "eip155:1514/erc20:0x693c7acf65e52c71bafe555bc22d69cb7f8a78a2", + "chainId": "eip155:1514", + "name": "Evil Larry", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/68054/large/1000440957.jpg?1754637180", + "symbol": "LARRY", + "explorer": "https://storyscan.xyz", + "explorerAddressLink": "https://storyscan.xyz/address/", + "explorerTxLink": "https://storyscan.xyz/tx/", + "relatedAssetKey": null + }, + "eip155:1514/erc20:0xa074aca68eeb6c831a78c84606f3cb44f9e547f5": { + "assetId": "eip155:1514/erc20:0xa074aca68eeb6c831a78c84606f3cb44f9e547f5", + "chainId": "eip155:1514", + "name": "Louie", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/68839/large/2025-08-31_10.56.07.jpg?1756755652", + "symbol": "LOUIE", + "explorer": "https://storyscan.xyz", + "explorerAddressLink": "https://storyscan.xyz/address/", + "explorerTxLink": "https://storyscan.xyz/tx/", + "relatedAssetKey": null + }, + "eip155:1514/erc20:0xa4f5c615f72ddeb2220471694fff1c0c3de051e1": { + "assetId": "eip155:1514/erc20:0xa4f5c615f72ddeb2220471694fff1c0c3de051e1", + "chainId": "eip155:1514", + "name": "LYRA", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/53984/large/WhatsApp_Image_2025-01-22_at_23.29.41.jpeg?1737876448", + "symbol": "LYRA", + "explorer": "https://storyscan.xyz", + "explorerAddressLink": "https://storyscan.xyz/address/", + "explorerTxLink": "https://storyscan.xyz/tx/", + "relatedAssetKey": null + }, + "eip155:1514/erc20:0xb5461c1fd0312cd4bf037058f8a391e6a42f9639": { + "assetId": "eip155:1514/erc20:0xb5461c1fd0312cd4bf037058f8a391e6a42f9639", + "chainId": "eip155:1514", + "name": "Staked Aria Premier Launch", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/67815/large/APL_logo.jpg?1753940903", + "symbol": "STAPL", + "explorer": "https://storyscan.xyz", + "explorerAddressLink": "https://storyscan.xyz/address/", + "explorerTxLink": "https://storyscan.xyz/tx/", + "relatedAssetKey": null + }, + "eip155:1514/erc20:0xbab93b7ad7fe8692a878b95a8e689423437cc500": { + "assetId": "eip155:1514/erc20:0xbab93b7ad7fe8692a878b95a8e689423437cc500", + "chainId": "eip155:1514", + "name": "Stargate Bridged WETH", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/69466/large/weth_2.jpg?1758701596", + "symbol": "WETH", + "explorer": "https://storyscan.xyz", + "explorerAddressLink": "https://storyscan.xyz/address/", + "explorerTxLink": "https://storyscan.xyz/tx/", + "relatedAssetKey": "eip155:1/erc20:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + }, + "eip155:1514/erc20:0xc9cbbd8f211300dd0e7a3933b7aeedac6f61dd52": { + "assetId": "eip155:1514/erc20:0xc9cbbd8f211300dd0e7a3933b7aeedac6f61dd52", + "chainId": "eip155:1514", + "name": "Aria", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/70388/large/logo-abberation-token-white.png?1762454816", + "symbol": "ARIAIP", + "explorer": "https://storyscan.xyz", + "explorerAddressLink": "https://storyscan.xyz/address/", + "explorerTxLink": "https://storyscan.xyz/tx/", + "relatedAssetKey": null + }, + "eip155:1514/erc20:0xd07faed671decf3c5a6cc038dad97c8efdb507c0": { + "assetId": "eip155:1514/erc20:0xd07faed671decf3c5a6cc038dad97c8efdb507c0", + "chainId": "eip155:1514", + "name": "Staked IP", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/69438/large/stip.png?1758595832", + "symbol": "STIP", + "explorer": "https://storyscan.xyz", + "explorerAddressLink": "https://storyscan.xyz/address/", + "explorerTxLink": "https://storyscan.xyz/tx/", + "relatedAssetKey": null + }, + "eip155:1514/erc20:0xf1815bd50389c46847f0bda824ec8da914045d14": { + "assetId": "eip155:1514/erc20:0xf1815bd50389c46847f0bda824ec8da914045d14", + "chainId": "eip155:1514", + "name": "Stargate Bridged USDC", + "precision": 6, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/69316/large/usdc.jpg?1758186473", + "symbol": "USDC.E", + "explorer": "https://storyscan.xyz", + "explorerAddressLink": "https://storyscan.xyz/address/", + "explorerTxLink": "https://storyscan.xyz/tx/", + "relatedAssetKey": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" + }, + "eip155:1514/erc20:0xfe82012ecce57a188e5f9f3fc1cb2d335c58f1f5": { + "assetId": "eip155:1514/erc20:0xfe82012ecce57a188e5f9f3fc1cb2d335c58f1f5", + "chainId": "eip155:1514", + "name": "Aria Premier Launch", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/66927/large/download.png?1751093687", + "symbol": "APL", + "explorer": "https://storyscan.xyz", + "explorerAddressLink": "https://storyscan.xyz/address/", + "explorerTxLink": "https://storyscan.xyz/tx/", + "relatedAssetKey": null + }, + "eip155:1514/slip44:60": { + "assetId": "eip155:1514/slip44:60", + "chainId": "eip155:1514", + "name": "Story", + "networkName": "Story", + "symbol": "IP", + "precision": 18, + "color": "#EA580C", + "networkColor": "#EA580C", + "icon": "https://assets.coingecko.com/coins/images/51994/large/story-2.png", + "networkIcon": "https://assets.coingecko.com/coins/images/51994/small/story-2.png", + "explorer": "https://storyscan.xyz", + "explorerAddressLink": "https://storyscan.xyz/address/", + "explorerTxLink": "https://storyscan.xyz/tx/", + "relatedAssetKey": null } }, "ids": [ @@ -467667,6 +467852,20 @@ "eip155:324/erc20:0xfb3fccdd38a96c3f769c62e578323f22c3bfcbec", "eip155:324/erc20:0xfb4c64c144c2bd0e7f2a06da7d6aac32d8cb2514", "eip155:324/erc20:0xfeafde23fb2364896c297c3fa6628f04601a035b", - "eip155:324/slip44:60" + "eip155:324/slip44:60", + "eip155:1514/erc20:0x1514000000000000000000000000000000000000", + "eip155:1514/erc20:0x25f9c9715d1d700a50b2a9a06d80fe9f98ccb549", + "eip155:1514/erc20:0x543374350269cce6651358769512875faa4cccff", + "eip155:1514/erc20:0x5a212776066b81e449fe74396cce368dc4b14043", + "eip155:1514/erc20:0x693c7acf65e52c71bafe555bc22d69cb7f8a78a2", + "eip155:1514/erc20:0xa074aca68eeb6c831a78c84606f3cb44f9e547f5", + "eip155:1514/erc20:0xa4f5c615f72ddeb2220471694fff1c0c3de051e1", + "eip155:1514/erc20:0xb5461c1fd0312cd4bf037058f8a391e6a42f9639", + "eip155:1514/erc20:0xbab93b7ad7fe8692a878b95a8e689423437cc500", + "eip155:1514/erc20:0xc9cbbd8f211300dd0e7a3933b7aeedac6f61dd52", + "eip155:1514/erc20:0xd07faed671decf3c5a6cc038dad97c8efdb507c0", + "eip155:1514/erc20:0xf1815bd50389c46847f0bda824ec8da914045d14", + "eip155:1514/erc20:0xfe82012ecce57a188e5f9f3fc1cb2d335c58f1f5", + "eip155:1514/slip44:60" ] } \ No newline at end of file diff --git a/public/generated/generatedAssetData.json.br b/public/generated/generatedAssetData.json.br index 2d5950b6e77..bfacdac574a 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 ad14e7e9e48..e18b40d3883 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 ed3f3348403..db93282764f 100644 --- a/public/generated/relatedAssetIndex.json +++ b/public/generated/relatedAssetIndex.json @@ -6227,7 +6227,8 @@ "eip155:480/erc20:0x79a02482a880bce3f13e09da970dc34db4cd24d1", "eip155:81457/erc20:0xeb466342c4d449bc9f53a865d5cb90586f405215", "eip155:324/erc20:0x3355df6d4c9c3035724fd0e3914de96a5a83aaf4", - "eip155:324/erc20:0x1d17cbcf0d6d143135ae902365d2e5e2a16538d4" + "eip155:324/erc20:0x1d17cbcf0d6d143135ae902365d2e5e2a16538d4", + "eip155:1514/erc20:0xf1815bd50389c46847f0bda824ec8da914045d14" ], "eip155:8453/erc20:0x9c632e6aaa3ea73f91554f8a3cb2ed2f29605e0c": [ "eip155:56/erc20:0x7324c7c0d95cebc73eea7e85cbaac0dbdf88a05b", @@ -7490,7 +7491,8 @@ "eip155:34443/erc20:0x4200000000000000000000000000000000000006", "eip155:1868/erc20:0x4200000000000000000000000000000000000006", "eip155:43111/erc20:0x4200000000000000000000000000000000000006", - "eip155:81457/erc20:0x4300000000000000000000000000000000000004" + "eip155:81457/erc20:0x4300000000000000000000000000000000000004", + "eip155:1514/erc20:0xbab93b7ad7fe8692a878b95a8e689423437cc500" ], "eip155:42161/erc20:0xea986d33ef8a20a96120ecc44dbdd49830192043": [ "eip155:56/erc20:0x3028b4395f98777123c7da327010c40f3c7cc4ef", diff --git a/public/generated/relatedAssetIndex.json.br b/public/generated/relatedAssetIndex.json.br index f9b6cca2949..a8c787e4a65 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 7032ea448e8..57a9851c9d2 100644 Binary files a/public/generated/relatedAssetIndex.json.gz and b/public/generated/relatedAssetIndex.json.gz differ diff --git a/scripts/generateAssetData/coingecko.ts b/scripts/generateAssetData/coingecko.ts index dd51c4e2d32..01b30f88813 100644 --- a/scripts/generateAssetData/coingecko.ts +++ b/scripts/generateAssetData/coingecko.ts @@ -30,6 +30,7 @@ import { soneiumChainId, sonicChainId, starknetChainId, + storyChainId, suiChainId, toAssetId, tonChainId, @@ -67,6 +68,7 @@ import { solana, soneium, sonic, + story, sui, ton, tron, @@ -251,6 +253,14 @@ export async function getAssets(chainId: ChainId): Promise { explorerAddressLink: katana.explorerAddressLink, explorerTxLink: katana.explorerTxLink, } + case storyChainId: + return { + assetNamespace: ASSET_NAMESPACE.erc20, + category: adapters.chainIdToCoingeckoAssetPlatform(chainId), + explorer: story.explorer, + explorerAddressLink: story.explorerAddressLink, + explorerTxLink: story.explorerTxLink, + } case zkSyncEraChainId: return { assetNamespace: ASSET_NAMESPACE.erc20, diff --git a/scripts/generateAssetData/generateAssetData.ts b/scripts/generateAssetData/generateAssetData.ts index 304068ae33c..39b26cb9c85 100644 --- a/scripts/generateAssetData/generateAssetData.ts +++ b/scripts/generateAssetData/generateAssetData.ts @@ -55,6 +55,7 @@ import * as solana from './solana' import * as soneium from './soneium' import * as sonic from './sonic' import * as starknet from './starknet' +import * as story from './story' import * as sui from './sui' import * as tonModule from './ton' import * as tronModule from './tron' @@ -100,6 +101,7 @@ const generateAssetData = async () => { const scrollAssets = await scroll.getAssets() const modeAssets = await mode.getAssets() const plasmaAssets = await plasma.getAssets() + const storyAssets = await story.getAssets() const worldchainAssets = await worldchainModule.getAssets() const sonicAssets = await sonic.getAssets() const soneiumAssets = await soneium.getAssets() @@ -148,6 +150,7 @@ const generateAssetData = async () => { ...scrollAssets, ...modeAssets, ...plasmaAssets, + ...storyAssets, ...worldchainAssets, ...sonicAssets, ...soneiumAssets, diff --git a/scripts/generateAssetData/generateRelatedAssetIndex/generateChainRelatedAssetIndex.ts b/scripts/generateAssetData/generateRelatedAssetIndex/generateChainRelatedAssetIndex.ts index 80450089252..893fbc5347b 100644 --- a/scripts/generateAssetData/generateRelatedAssetIndex/generateChainRelatedAssetIndex.ts +++ b/scripts/generateAssetData/generateRelatedAssetIndex/generateChainRelatedAssetIndex.ts @@ -84,7 +84,7 @@ const manualRelatedAssetIndex: Record = { 'eip155:1/erc20:0xca14007eff0db1f8135f4c25b34de49ab0d42766', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:HsRpHQn6VbyMs5b5j5SV6xQ2VvpvvCCzu19GjytVSCoz', ], - // Native stablecoins not tagged as bridged by CoinGecko + // Native stablecoins on Linea + Mantle - CoinGecko doesn't tag these as bridged categories 'eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48': [ 'eip155:59144/erc20:0x176211869ca2b568f2a7d4ee941e073a821ee1ff', 'eip155:5000/erc20:0x09bc4e0d864854c6afb6eb9a9cdf58ac190d0df9', diff --git a/scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts b/scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts index 413d90c187d..cbd9eea6409 100644 --- a/scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts +++ b/scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts @@ -64,19 +64,19 @@ const manualRelatedAssetIndex: Record = { optimismAssetId, arbitrumAssetId, baseAssetId, + katanaAssetId, + megaethAssetId, + zkSyncEraAssetId, blastAssetId, + worldChainAssetId, hemiAssetId, inkAssetId, - katanaAssetId, lineaAssetId, - megaethAssetId, scrollAssetId, - worldChainAssetId, unichainAssetId, bobAssetId, modeAssetId, soneiumAssetId, - zkSyncEraAssetId, ], [foxAssetId]: [foxOnArbitrumOneAssetId], [starknetAssetId]: [ diff --git a/scripts/generateAssetData/story/index.ts b/scripts/generateAssetData/story/index.ts new file mode 100644 index 00000000000..9906db6766d --- /dev/null +++ b/scripts/generateAssetData/story/index.ts @@ -0,0 +1,11 @@ +import { storyChainId } from '@shapeshiftoss/caip' +import type { Asset } from '@shapeshiftoss/types' +import { story, unfreeze } from '@shapeshiftoss/utils' + +import * as coingecko from '../coingecko' + +export const getAssets = async (): Promise => { + const assets = await coingecko.getAssets(storyChainId) + + return [...assets, unfreeze(story)] +} diff --git a/src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx b/src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx index 31071b1b7d2..b8e1a3638fc 100644 --- a/src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx +++ b/src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx @@ -20,6 +20,7 @@ import { soneiumAssetId, sonicAssetId, starknetAssetId, + storyAssetId, suiAssetId, thorchainAssetId, tronAssetId, @@ -62,6 +63,7 @@ export const queryFn = async () => { if (enabledFlags.Ink) assetIds.push(inkAssetId) if (enabledFlags.Scroll) assetIds.push(scrollAssetId) if (enabledFlags.Katana) assetIds.push(katanaAssetId) + if (enabledFlags.Story) assetIds.push(storyAssetId) if (enabledFlags.ZkSyncEra) assetIds.push(zkSyncEraAssetId) if (enabledFlags.Blast) assetIds.push(blastAssetId) if (enabledFlags.WorldChain) assetIds.push(worldChainAssetId) diff --git a/src/config.ts b/src/config.ts index c35f88e2d74..0529b3c1f6f 100644 --- a/src/config.ts +++ b/src/config.ts @@ -68,6 +68,7 @@ const validators = { VITE_BERACHAIN_NODE_URL: url(), VITE_CRONOS_NODE_URL: str(), VITE_KATANA_NODE_URL: url(), + VITE_STORY_NODE_URL: url(), VITE_ZKSYNC_ERA_NODE_URL: url(), VITE_BLAST_NODE_URL: url(), VITE_WORLDCHAIN_NODE_URL: url(), @@ -125,6 +126,7 @@ const validators = { VITE_FEATURE_BERACHAIN: bool({ default: false }), VITE_FEATURE_CRONOS: bool({ default: false }), VITE_FEATURE_KATANA: bool({ default: false }), + VITE_FEATURE_STORY: bool({ default: false }), VITE_FEATURE_ZK_SYNC_ERA: bool({ default: false }), VITE_FEATURE_BLAST: bool({ default: false }), VITE_FEATURE_WORLDCHAIN: bool({ default: false }), diff --git a/src/constants/chains.ts b/src/constants/chains.ts index 5278804d1f6..cfed7dc6373 100644 --- a/src/constants/chains.ts +++ b/src/constants/chains.ts @@ -13,6 +13,7 @@ export const SECOND_CLASS_CHAINS: readonly KnownChainIds[] = [ KnownChainIds.HyperEvmMainnet, KnownChainIds.PlasmaMainnet, KnownChainIds.KatanaMainnet, + KnownChainIds.StoryMainnet, KnownChainIds.WorldChainMainnet, KnownChainIds.MantleMainnet, KnownChainIds.InkMainnet, @@ -48,6 +49,7 @@ export const knownChainIds = Object.values(KnownChainIds).filter(chainId => { if (chainId === KnownChainIds.HyperEvmMainnet && !enabledFlags.HyperEvm) return false if (chainId === KnownChainIds.PlasmaMainnet && !enabledFlags.Plasma) return false if (chainId === KnownChainIds.KatanaMainnet && !enabledFlags.Katana) return false + if (chainId === KnownChainIds.StoryMainnet && !enabledFlags.Story) return false if (chainId === KnownChainIds.WorldChainMainnet && !enabledFlags.WorldChain) return false if (chainId === KnownChainIds.MantleMainnet && !enabledFlags.Mantle) return false if (chainId === KnownChainIds.InkMainnet && !enabledFlags.Ink) return false diff --git a/src/context/PluginProvider/PluginProvider.tsx b/src/context/PluginProvider/PluginProvider.tsx index 7809b8e842e..8a00e11106b 100644 --- a/src/context/PluginProvider/PluginProvider.tsx +++ b/src/context/PluginProvider/PluginProvider.tsx @@ -121,6 +121,7 @@ export const PluginProvider = ({ children }: PluginProviderProps): JSX.Element = if (!featureFlags.Monad && chainId === KnownChainIds.MonadMainnet) return false if (!featureFlags.HyperEvm && chainId === KnownChainIds.HyperEvmMainnet) return false if (!featureFlags.Katana && chainId === KnownChainIds.KatanaMainnet) return false + if (!featureFlags.Story && chainId === KnownChainIds.StoryMainnet) return false if (!featureFlags.WorldChain && chainId === KnownChainIds.WorldChainMainnet) return false if (!featureFlags.Sonic && chainId === KnownChainIds.SonicMainnet) return false if (!featureFlags.Unichain && chainId === KnownChainIds.UnichainMainnet) return false diff --git a/src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts b/src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts index 2617fb04a04..5cc41c1785d 100644 --- a/src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts +++ b/src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts @@ -34,6 +34,7 @@ import { soneiumChainId, sonicChainId, starknetChainId, + storyChainId, suiChainId, thorchainChainId, tonChainId, @@ -80,6 +81,7 @@ import { supportsSoneium, supportsSonic, supportsStarknet, + supportsStory, supportsSui, supportsThorchain, supportsTron, @@ -184,6 +186,7 @@ export const walletSupportsChain = ({ const isInkEnabled = selectFeatureFlag(store.getState(), 'Ink') const isBobEnabled = selectFeatureFlag(store.getState(), 'Bob') const isKatanaEnabled = selectFeatureFlag(store.getState(), 'Katana') + const isStoryEnabled = selectFeatureFlag(store.getState(), 'Story') const isMantleEnabled = selectFeatureFlag(store.getState(), 'Mantle') const isLineaEnabled = selectFeatureFlag(store.getState(), 'Linea') const isCronosEnabled = selectFeatureFlag(store.getState(), 'Cronos') @@ -247,6 +250,8 @@ export const walletSupportsChain = ({ return isPlasmaEnabled && supportsPlasma(wallet) case katanaChainId: return isKatanaEnabled && supportsKatana(wallet) + case storyChainId: + return isStoryEnabled && supportsStory(wallet) case zkSyncEraChainId: return isZkSyncEraEnabled && supportsZkSyncEra(wallet) case blastChainId: diff --git a/src/lib/account/evm.ts b/src/lib/account/evm.ts index eb513e8535f..d8f4e031c64 100644 --- a/src/lib/account/evm.ts +++ b/src/lib/account/evm.ts @@ -26,6 +26,7 @@ import { scrollChainId, soneiumChainId, sonicChainId, + storyChainId, toAccountId, unichainChainId, worldChainChainId, @@ -59,6 +60,7 @@ import { supportsScroll, supportsSoneium, supportsSonic, + supportsStory, supportsUnichain, supportsWorldChain, supportsZkSyncEra, @@ -162,6 +164,7 @@ export const deriveEvmAccountIdsAndMetadata: DeriveAccountIdsAndMetadata = async if (chainId === scrollChainId && !supportsScroll(wallet)) continue if (chainId === cronosChainId && !supportsCronos(wallet)) continue if (chainId === katanaChainId && !supportsKatana(wallet)) continue + if (chainId === storyChainId && !supportsStory(wallet)) continue if (chainId === zkSyncEraChainId && !supportsZkSyncEra(wallet)) continue if (chainId === blastChainId && !supportsBlast(wallet)) continue if (chainId === worldChainChainId && !supportsWorldChain(wallet)) continue diff --git a/src/lib/asset-service/service/AssetService.ts b/src/lib/asset-service/service/AssetService.ts index 7e94dc7cf06..f6bf704ffe4 100644 --- a/src/lib/asset-service/service/AssetService.ts +++ b/src/lib/asset-service/service/AssetService.ts @@ -27,6 +27,7 @@ import { soneiumChainId, sonicChainId, starknetChainId, + storyChainId, suiChainId, tonChainId, tronChainId, @@ -137,6 +138,7 @@ class _AssetService { if (!config.VITE_FEATURE_MONAD && asset.chainId === monadChainId) return false if (!config.VITE_FEATURE_HYPEREVM && asset.chainId === hyperEvmChainId) return false if (!config.VITE_FEATURE_KATANA && asset.chainId === katanaChainId) return false + if (!config.VITE_FEATURE_STORY && asset.chainId === storyChainId) return false if (!config.VITE_FEATURE_ZK_SYNC_ERA && asset.chainId === zkSyncEraChainId) return false if (!config.VITE_FEATURE_BLAST && asset.chainId === blastChainId) return false if (!config.VITE_FEATURE_WORLDCHAIN && asset.chainId === worldChainChainId) return false diff --git a/src/lib/coingecko/utils.ts b/src/lib/coingecko/utils.ts index 7289a7a87e6..f701dbbaf4d 100644 --- a/src/lib/coingecko/utils.ts +++ b/src/lib/coingecko/utils.ts @@ -28,6 +28,7 @@ import { soneiumChainId, sonicChainId, starknetChainId, + storyChainId, suiChainId, thorchainChainId, toAssetId, @@ -230,6 +231,7 @@ export const getCoingeckoSupportedChainIds = () => { ...(getConfig().VITE_FEATURE_INK ? [inkChainId] : []), ...(getConfig().VITE_FEATURE_CRONOS ? [cronosChainId] : []), ...(getConfig().VITE_FEATURE_KATANA ? [katanaChainId] : []), + ...(getConfig().VITE_FEATURE_STORY ? [storyChainId] : []), ...(getConfig().VITE_FEATURE_ZK_SYNC_ERA ? [zkSyncEraChainId] : []), ...(getConfig().VITE_FEATURE_BLAST ? [blastChainId] : []), ...(getConfig().VITE_FEATURE_WORLDCHAIN ? [worldChainChainId] : []), diff --git a/src/pages/Markets/components/MarketsRow.tsx b/src/pages/Markets/components/MarketsRow.tsx index a247d7ed5ba..c23cdf9bb29 100644 --- a/src/pages/Markets/components/MarketsRow.tsx +++ b/src/pages/Markets/components/MarketsRow.tsx @@ -89,6 +89,7 @@ export const MarketsRow: React.FC = ({ const isLineaEnabled = useAppSelector(state => selectFeatureFlag(state, 'Linea')) const isScrollEnabled = useAppSelector(state => selectFeatureFlag(state, 'Scroll')) const isKatanaEnabled = useAppSelector(state => selectFeatureFlag(state, 'Katana')) + const isStoryEnabled = useAppSelector(state => selectFeatureFlag(state, 'Story')) const isZkSyncEraEnabled = useAppSelector(state => selectFeatureFlag(state, 'ZkSyncEra')) const isBlastEnabled = useAppSelector(state => selectFeatureFlag(state, 'Blast')) const isWorldChainEnabled = useAppSelector(state => selectFeatureFlag(state, 'WorldChain')) @@ -114,6 +115,7 @@ export const MarketsRow: React.FC = ({ if (!isLineaEnabled && chainId === KnownChainIds.LineaMainnet) return false if (!isScrollEnabled && chainId === KnownChainIds.ScrollMainnet) return false if (!isKatanaEnabled && chainId === KnownChainIds.KatanaMainnet) return false + if (!isStoryEnabled && chainId === KnownChainIds.StoryMainnet) return false if (!isZkSyncEraEnabled && chainId === KnownChainIds.ZkSyncEraMainnet) return false if (!isBlastEnabled && chainId === KnownChainIds.BlastMainnet) return false if (!isWorldChainEnabled && chainId === KnownChainIds.WorldChainMainnet) return false @@ -139,6 +141,7 @@ export const MarketsRow: React.FC = ({ isLineaEnabled, isScrollEnabled, isKatanaEnabled, + isStoryEnabled, isZkSyncEraEnabled, isBlastEnabled, isWorldChainEnabled, diff --git a/src/plugins/activePlugins.ts b/src/plugins/activePlugins.ts index 3fea3e1f000..8eadcf2225b 100644 --- a/src/plugins/activePlugins.ts +++ b/src/plugins/activePlugins.ts @@ -33,6 +33,7 @@ import solana from '@/plugins/solana' import soneium from '@/plugins/soneium' import sonic from '@/plugins/sonic' import starknet from '@/plugins/starknet' +import story from '@/plugins/story' import sui from '@/plugins/sui' import thorchain from '@/plugins/thorchain' import ton from '@/plugins/ton' @@ -80,6 +81,7 @@ export const activePlugins = [ solana, sonic, starknet, + story, sui, thorchain, ton, diff --git a/src/plugins/story/index.tsx b/src/plugins/story/index.tsx new file mode 100644 index 00000000000..79973437fd5 --- /dev/null +++ b/src/plugins/story/index.tsx @@ -0,0 +1,49 @@ +import { fromAssetId, storyChainId } from '@shapeshiftoss/caip' +import { story } 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 [ + [ + 'storyChainAdapter', + { + name: 'storyChainAdapter', + featureFlag: ['Story'], + providers: { + chainAdapters: [ + [ + KnownChainIds.StoryMainnet, + () => { + const getKnownTokens = () => { + const assetService = getAssetService() + return assetService.assets + .filter(asset => { + const { chainId, assetNamespace } = fromAssetId(asset.assetId) + return chainId === storyChainId && assetNamespace === 'erc20' + }) + .map(asset => ({ + assetId: asset.assetId, + contractAddress: fromAssetId(asset.assetId).assetReference, + symbol: asset.symbol, + name: asset.name, + precision: asset.precision, + })) + } + + return new story.ChainAdapter({ + rpcUrl: getConfig().VITE_STORY_NODE_URL, + getKnownTokens, + }) + }, + ], + ], + }, + }, + ], + ] +} diff --git a/src/state/migrations/index.ts b/src/state/migrations/index.ts index 11ff6767d18..c9b6f425e37 100644 --- a/src/state/migrations/index.ts +++ b/src/state/migrations/index.ts @@ -349,6 +349,7 @@ export const clearAssetsMigrations = { 300: clearAssets, 301: clearAssets, 302: clearAssets, + 303: 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 420cffd2b1d..5724b285fe3 100644 --- a/src/state/slices/opportunitiesSlice/mappings.ts +++ b/src/state/slices/opportunitiesSlice/mappings.ts @@ -198,6 +198,7 @@ export const CHAIN_ID_TO_SUPPORTED_DEFI_OPPORTUNITIES: Record< [KnownChainIds.ScrollMainnet]: [], [KnownChainIds.CronosMainnet]: [], [KnownChainIds.KatanaMainnet]: [], + [KnownChainIds.StoryMainnet]: [], [KnownChainIds.WorldChainMainnet]: [], [KnownChainIds.SonicMainnet]: [], [KnownChainIds.UnichainMainnet]: [], diff --git a/src/state/slices/portfolioSlice/utils/index.ts b/src/state/slices/portfolioSlice/utils/index.ts index c21c50cdca7..9a9acb671d9 100644 --- a/src/state/slices/portfolioSlice/utils/index.ts +++ b/src/state/slices/portfolioSlice/utils/index.ts @@ -40,6 +40,7 @@ import { soneiumChainId, sonicChainId, starknetChainId, + storyChainId, suiChainId, thorchainChainId, toAccountId, @@ -87,6 +88,7 @@ import { supportsSoneium, supportsSonic, supportsStarknet, + supportsStory, supportsSui, supportsThorchain, supportsTron, @@ -141,6 +143,7 @@ export const accountIdToLabel = (accountId: AccountId): string => { case lineaChainId: case cronosChainId: case katanaChainId: + case storyChainId: case worldChainChainId: case scrollChainId: case sonicChainId: @@ -563,6 +566,8 @@ export const isAssetSupportedByWallet = (assetId: AssetId, wallet: HDWallet): bo return supportsPlasma(wallet) case katanaChainId: return supportsKatana(wallet) + case storyChainId: + return supportsStory(wallet) case zkSyncEraChainId: return supportsZkSyncEra(wallet) case blastChainId: diff --git a/src/state/slices/preferencesSlice/preferencesSlice.ts b/src/state/slices/preferencesSlice/preferencesSlice.ts index e5482b45f68..120886eca21 100644 --- a/src/state/slices/preferencesSlice/preferencesSlice.ts +++ b/src/state/slices/preferencesSlice/preferencesSlice.ts @@ -42,6 +42,7 @@ export type FeatureFlags = { Berachain: boolean Cronos: boolean Katana: boolean + Story: boolean ZkSyncEra: boolean Blast: boolean WorldChain: boolean @@ -206,6 +207,7 @@ const initialState: Preferences = { Berachain: getConfig().VITE_FEATURE_BERACHAIN, Cronos: getConfig().VITE_FEATURE_CRONOS, Katana: getConfig().VITE_FEATURE_KATANA, + Story: getConfig().VITE_FEATURE_STORY, ZkSyncEra: getConfig().VITE_FEATURE_ZK_SYNC_ERA, Blast: getConfig().VITE_FEATURE_BLAST, WorldChain: getConfig().VITE_FEATURE_WORLDCHAIN, diff --git a/src/test/mocks/store.ts b/src/test/mocks/store.ts index 2ce5dd76c91..e36a1bdafd9 100644 --- a/src/test/mocks/store.ts +++ b/src/test/mocks/store.ts @@ -115,6 +115,7 @@ export const mockStore: ReduxState = { Berachain: false, Cronos: false, Katana: false, + Story: false, ZkSyncEra: false, Blast: false, WorldChain: false, diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 1e9043b3970..b6b519bb505 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -66,6 +66,8 @@ interface ImportMetaEnv { readonly VITE_MANTLE_NODE_URL: string readonly VITE_FEATURE_INK: string readonly VITE_INK_NODE_URL: string + readonly VITE_FEATURE_STORY: string + readonly VITE_STORY_NODE_URL: string readonly VITE_FEATURE_ZK_SYNC_ERA: string readonly VITE_ZKSYNC_ERA_NODE_URL: string