Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .beads/ss-dx5.5.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[
{
"id": "ss-dx5.5",
"title": "Integrate Berachain (chainId: 80094, BERA, PoL L1)",
"description": "TVL: $324.3M | RPC: https://rpc.berachain.com/ | Explorer: https://beratrail.io | Token Support: All | Pattern B (RPC-only) | Proof of Liquidity consensus",
"notes": "Logo: https://assets.relay.link/icons/80094/light.png (dark: dark.png)\nCoinGecko: berachain\nArch: L1 (Proof of Liquidity)\nViem key: berachain\n⚠️ NON-ETH GAS: BERA - must configure in chainIdToFeeAssetId.ts\nChecklist: [ ] chain-adapters [ ] caip [ ] asset-service [ ] feature-flag [ ] plugin [ ] env-config [ ] icon/logo [ ] fee-asset-config\n\n--- RELAY ACTIVATION ---\n[ ] Add to chainIdToRelayChainId in packages/swapper/src/swappers/RelaySwapper/constant.ts\n[ ] Add native asset case in packages/swapper/src/swappers/RelaySwapper/utils/relayTokenToAssetId.ts\n--- ASSET DATA REGEN ---\n[ ] Create scripts/generateAssetData/\u003cchainname\u003e/index.ts\n[ ] Add case to scripts/generateAssetData/coingecko.ts\n[ ] Add import + getAssets call to scripts/generateAssetData/generateAssetData.ts\n[ ] Run: yarn generate:asset-data\n--- DRAFT PR ---\n[ ] Branch: feat/integrate-\u003cchainname\u003e-relay (off develop)\n[ ] gh pr create --draft using .github/PULL_REQUEST_TEMPLATE.md\n[ ] Fill: Description, Issue (Part of #11902), Risk (Low - behind feature flag), Testing (enable flag + verify chain + relay bridge quote), Operations (behind flag checkbox)\n[ ] yarn lint --fix \u0026\u0026 yarn type-check must pass before PR",
"status": "open",
"priority": 1,
"issue_type": "task",
"owner": "14963751+NeOMakinG@users.noreply.github.com",
"created_at": "2026-02-17T10:55:24Z",
"created_by": "NeOMakinG",
"updated_at": "2026-02-17T11:21:35Z",
"labels": [
"evm",
"non-eth-gas",
"relay",
"tier-1"
],
"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"
}
]
128 changes: 128 additions & 0 deletions .claude/skills/chain-integration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -1623,6 +1623,50 @@ if (enabledFlags.Sui) assetIds.push(suiAssetId)
**Reference PRs:**
- See how Monad, Tron, Sui, Plasma, and HyperEVM were added in the same PR

### Step 5.6: Add ETH Related Asset to Related Asset Index (CONDITIONAL - ETH-native chains only!)

**ONLY for chains where ETH is the native gas token** (e.g., Optimism, Arbitrum, Base, Katana, MegaETH).
**SKIP for chains with their own native token** (e.g., Berachain/BERA, Monad/MON, Tron/TRX, Sui/SUI).

**Why this matters:** The related asset index groups the same asset across different chains (e.g., ETH on Ethereum, ETH on Optimism, ETH on Arbitrum). When a user views ETH, they can see all the chain variants. If your chain uses ETH as its native gas token and you don't add it here, the chain's ETH won't appear as a related asset in the UI.

**File**: `scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts`

1. **Import your chain's asset ID** at the top:
```typescript
import {
adapters,
arbitrumAssetId,
baseAssetId,
ethAssetId,
FEE_ASSET_IDS,
foxAssetId,
foxOnArbitrumOneAssetId,
fromAssetId,
katanaAssetId,
megaethAssetId,
[chainLower]AssetId, // ADD THIS - only if native token IS ETH
optimismAssetId,
starknetAssetId,
} from '@shapeshiftoss/caip'
```

2. **Add to the `manualRelatedAssetIndex`** under `ethAssetId`:
```typescript
const manualRelatedAssetIndex: Record<AssetId, AssetId[]> = {
[ethAssetId]: [optimismAssetId, arbitrumAssetId, baseAssetId, katanaAssetId, megaethAssetId, [chainLower]AssetId],
// ^^^^^^^^^^^^^^^^ ADD
[foxAssetId]: [foxOnArbitrumOneAssetId],
// ...
}
```

**How to determine if this step applies:**
- ✅ **ADD** if: Chain uses ETH as native gas (user pays gas in ETH, like Optimism, Base, Arbitrum)
- ❌ **SKIP** if: Chain has its own native gas token (like BERA for Berachain, MON for Monad, TRX for Tron)

**Reference**: Look at which assetIds are already in the array - they're all L2s/chains where the native token is ETH.

---

## Phase 6: Ledger Support (Optional)
Expand Down Expand Up @@ -1671,6 +1715,90 @@ export const availableLedgerAppAssetIds = [

## Phase 7: Testing & Validation

### Step 7.0: Fix CoinGecko Adapter Tests (REQUIRED - ALWAYS FAILS AFTER CHAIN INTEGRATION!)

**CRITICAL**: After adding a new chain, TWO test files in `packages/caip/src/adapters/coingecko/` will almost always fail. Fix them BEFORE running the full test suite.

**IMPORTANT**: Always run `yarn build:packages` FIRST so TypeScript can resolve workspace package exports. Without this, type-check shows false errors like `'"@shapeshiftoss/caip"' has no exported member named '[chainLower]ChainId'`.

#### Test File 1: `packages/caip/src/adapters/coingecko/utils.test.ts`

The `parseData` test at line ~100 has a hardcoded expected output with every chain's entry. When you add a new chain to `parseData()` in `utils.ts`, you MUST add the matching expected entry:

```typescript
// Add your chain's expected entry to the `expected` object in the test
'eip155:[CHAIN_ID]': {
'eip155:[CHAIN_ID]/slip44:60': '[coingecko-native-id]',
},
```

**How to find the CoinGecko native ID**: Look at what you set in the `parseData()` function's initial data (the object at the bottom of `parseData()` that maps `chainId` → `{ assetId: coingeckoId }`).

#### Test File 2: `packages/caip/src/adapters/coingecko/index.test.ts`

**ONLY for ETH-native chains** (where CoinGecko maps the native asset to `'ethereum'`):

The `coingeckoToAssetIds('ethereum')` test expects a specific list of all chain asset IDs that map to `'ethereum'` in CoinGecko. Add your chain:

```typescript
const ethOn[ChainName] = toAssetId({
chainNamespace,
chainReference: CHAIN_REFERENCE.[ChainName]Mainnet,
assetNamespace: 'slip44',
assetReference: ASSET_REFERENCE.[ChainName],
})
expect(coingeckoToAssetIds('ethereum')).toEqual([
ethOnEthereum,
ethOnOptimism,
ethOnArbitrum,
ethOnBase,
ethOnMegaEth,
ethOn[ChainName], // ADD THIS
])
```

**Skip this** if your chain's native asset has its own CoinGecko ID (e.g., 'mantle', 'monad', 'berachain-bera').

#### Test File 3: `packages/chain-adapters/src/evm/EvmBaseAdapter.ts` (targetNetwork)

**CRITICAL for EVM chains**: The `targetNetwork` object in `EvmBaseAdapter.ts` (around line ~230) maps chain IDs to network display info for `ethSwitchChain`. If you add your chain to `evmChainIds` but forget `targetNetwork`, the build will fail with:

```
error TS2339: Property 'eip155:XXX' does not exist on type...
error TS18048: 'targetNetwork' is possibly 'undefined'.
```

**Fix**: Add your chain's entry:
```typescript
[KnownChainIds.[ChainName]Mainnet]: {
name: '[ChainName]',
symbol: '[SYMBOL]',
explorer: 'https://[explorer-url]',
},
```

#### Verification Commands

```bash
# 1. Build packages first (REQUIRED before type-check)
yarn build:packages

# 2. Run CAIP tests specifically
yarn vitest run packages/caip/ --reporter=verbose

# 3. Type-check
yarn type-check

# 4. Lint
yarn lint --fix
```

**Common patterns by chain native token**:
| Native Token | CoinGecko ID | Update index.test.ts? | Update utils.test.ts? |
|---|---|---|---|
| ETH (L2s like Base, Optimism, Ink) | `'ethereum'` | YES - add to `coingeckoToAssetIds('ethereum')` | YES - add chain entry |
| Own token (BERA, MON, MNT, CRO) | `'berachain-bera'`, `'monad'`, `'mantle'`, `'cronos'` | NO | YES - add chain entry |

### Step 7.1: Type Check

```bash
Expand Down
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,8 @@ VITE_HYPEREVM_NODE_URL=https://rpc.hyperliquid.xyz/evm
VITE_FEATURE_HYPEREVM=true
VITE_FEATURE_INK=false
VITE_FEATURE_MEGAETH=false
VITE_BERACHAIN_NODE_URL=https://rpc.berachain.com
VITE_FEATURE_BERACHAIN=false
VITE_FEATURE_SCROLL=false
VITE_FEATURE_NEAR=true
VITE_FEATURE_KATANA=true
Expand Down
2 changes: 2 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ VITE_KATANA_NODE_URL=https://rpc.katana.network
VITE_HYPEREVM_NODE_URL=https://rpc.hyperliquid.xyz/evm
VITE_INK_NODE_URL=https://ink.drpc.org
VITE_MEGAETH_NODE_URL=https://mainnet.megaeth.com/rpc
VITE_BERACHAIN_NODE_URL=https://rpc.berachain.com
VITE_SCROLL_NODE_URL=https://rpc.scroll.io/
# Swap me back to 9R as-needed
# VITE_THORCHAIN_NODE_URL=https://thornode.ninerealms.com
Expand Down Expand Up @@ -98,6 +99,7 @@ VITE_FEATURE_WC_DIRECT_CONNECTION=true
VITE_FEATURE_CETUS_SWAP=true
VITE_FEATURE_INK=true
VITE_FEATURE_MEGAETH=true
VITE_FEATURE_BERACHAIN=true
VITE_FEATURE_SCROLL=true
VITE_FEATURE_KATANA=true
VITE_FEATURE_ACROSS_SWAP=true
Expand Down
10 changes: 10 additions & 0 deletions headers/csps/chains/berachain.ts
Original file line number Diff line number Diff line change
@@ -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_BERACHAIN_NODE_URL],
}
2 changes: 2 additions & 0 deletions headers/csps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { csp as chainflip } from './chainflip'
import { csp as arbitrum } from './chains/arbitrum'
import { csp as avalanche } from './chains/avalanche'
import { csp as baseChain } from './chains/base'
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'
Expand Down Expand Up @@ -116,6 +117,7 @@ export const csps = [
dogecoin,
ethereum,
gnosis,
berachain,
hyperevm,
ink,
katana,
Expand Down
Loading
Loading