Install the "chain-integration" agent skill from https://github.com/shapeshift/web/tree/develop/.claude/skills/chain-integration into .claude/skills/chain-integration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "chain-integration", then confirm the skill loads.
Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
Type this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
skills CLI
$ npx skills add shapeshift/web --skill chain-integration -a codex
Project install goes to .agents/skills/; add -g for ~/.codex/skills/.
Install the "chain-integration" agent skill from https://github.com/shapeshift/web/tree/develop/.claude/skills/chain-integration into .agents/skills/chain-integration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "chain-integration", then confirm the skill loads.
Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
skills CLI
$ npx skills add shapeshift/web --skill chain-integration -a cursor
Project install goes to .agents/skills/; add -g for ~/.cursor/skills/.
Install the "chain-integration" agent skill from https://github.com/shapeshift/web/tree/develop/.claude/skills/chain-integration into .cursor/skills/chain-integration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "chain-integration", then confirm the skill loads.
Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
skills CLI
$ npx skills add shapeshift/web --skill chain-integration -a gemini-cli
Project install goes to .agents/skills/; add -g for ~/.gemini/skills/.
Install the "chain-integration" agent skill from https://github.com/shapeshift/web/tree/develop/.claude/skills/chain-integration into .gemini/skills/chain-integration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "chain-integration", then confirm the skill loads.
Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
Installs for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
skills CLI
$ npx skills add shapeshift/web --skill chain-integration -a github-copilot
Project install goes to .agents/skills/; add -g for ~/.copilot/skills/.
Install the "chain-integration" agent skill from https://github.com/shapeshift/web/tree/develop/.claude/skills/chain-integration into .github/skills/chain-integration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "chain-integration", then confirm the skill loads.
GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
skills CLI
$ npx skills add shapeshift/web --skill chain-integration -a opencode
OpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
Install the "chain-integration" agent skill from https://github.com/shapeshift/web/tree/develop/.claude/skills/chain-integration into .opencode/skills/chain-integration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "chain-integration", then confirm the skill loads.
OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
Facts
Skill name
chain-integration
GitHub stars
206
Token cost
~20k tokens
SKILL.md length
5,396 words
Files
1
Skills in repo
7
Repo updated
First seen
Licence
MIT
At a glance
Integrate a new blockchain as a second-class citizen in ShapeShift Web.
Works in 9 steps: Deep Research & Information Gathering → HDWallet Native Support → Web Chain Adapter (Poor Man's Approach) → …
Wants to add basic support for a new blockchain
SKILL.md covers When This Skill Activates, Critical Understanding, Phase 0: Deep Research &… and Integration Path Decision, plus 2 more sections
Calls pnpm, git and gh; reaches github.com; needs ZERION_API_KEY
What it does
Chain Integration is an agent skill from shapeshift/web. Integrate a new blockchain as a second-class citizen in ShapeShift Web. HDWallet packages live in the monorepo under packages/hdwallet-. Covers everything from HDWallet native/Ledger support to Web chain adapter, asset generation, and feature flags. Activates when user wants to add basic support for a new blockchain.
Its SKILL.md is about 20k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Development, covering Game assets and audio, Monorepo tooling and Smart contracts. The licence is MIT.
Read from SKILL.md and the folder at commit 52aebb2. It shows what the files ask for, not the result of running them.
Tool permissions
Pre-approves these tools, so the agent can use them without asking each time:
Read
Write
Edit
Grep
Glob
Bash
From allowed-tools in the SKILL.md frontmatter.
Runs code
Shell commands in SKILL.md call:
pnpm
git
gh
From the folder's file list and the shell code blocks in SKILL.md.
Network
Hosts in commands or code, which the agent is likely to contact:
github.com
Also links to:
docs.relay.link
ledger.com
0x.org
docs.cow.fi
docs.thorchain.org
chainlist.org
docs.1inch.io
From URLs in SKILL.md, links to its own repository left out.
Credentials
Names these keys or tokens, usually read from environment variables:
ZERION_API_KEY
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Context cost
Chain Integration loads about 20k tokens when it runs. Until then it costs about 84 tokens; SKILL.md has 5,396 words of instructions outside code blocks.
Always· name and description, kept in context so the agent knows when to use it
~84
When it runs· the whole SKILL.md, loaded when a task matches
~20k
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
Safety
Auto-check: notes
The automated check noted patterns worth knowing about, such as sudo or a known installer.
NoteMentions a .env fileSKILL.md:1126
**File**: `.env`
NoteMentions a .env fileSKILL.md:1133
**File**: `.env.development`
NoteMentions a .env fileSKILL.md:1140
**File**: `.env.production`
NoteMentions a .env fileSKILL.md:2312
- [ ] `.env`
NoteMentions a .env fileSKILL.md:2313
- [ ] `.env.development`
NoteMentions a .env fileSKILL.md:2314
- [ ] `.env.production`
NotePre-approves every shell command (allowed-tools: Bash)SKILL.md
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
Download SKILL.mdSave it as .claude/skills/chain-integration/SKILL.md (or your agent's skills folder).
name
chain-integration
description
Integrate a new blockchain as a second-class citizen in ShapeShift Web. HDWallet packages live in the monorepo under packages/hdwallet-*. Covers everything from HDWallet native/Ledger support to Web chain adapter, asset generation, and feature flags. Activates when user wants to add basic support for a new blockchain.
allowed-tools
Read, Write, Edit, Grep, Glob, Bash
Second-class EVM chain? If this is a second-class EVM chain integration,
disregard the rest of this skill. Load and follow the contract at
.claude/contracts/second-class-evm-chain.md instead - it contains the
complete, authoritative checklist of every integration point required.
Use the contract as your build todo list, checking off items as you go.
Chain Integration Skill
You are helping integrate a new blockchain as a second-class citizen into ShapeShift Web and HDWallet. This means basic support (native asset send/receive, account derivation, swaps to/from the chain) using the "poor man's" approach similar to Monad, Tron, and Sui - public RPC, no microservices, minimal features.
When This Skill Activates
Use this skill when the user wants to:
"Add support for [ChainName]"
"Integrate [ChainName] as second-class citizen"
"Implement basic [ChainName] support"
"Add [ChainName] with native wallet only"
Critical Understanding
Second-Class Citizen Pattern
Recent examples: Monad (EVM), Tron (UTXO-like), Sui (non-EVM)
EVM chains (like Monad): 90% less code! Just add to EVM chains list. Auto-supported by all EVM wallets.
Non-EVM chains (like Tron, Sui): Need full custom implementation with crypto adapters.
Step 0.2: Interactive Information Gathering
Use the AskUserQuestion tool with the Claude inquiry UI to gather information.
Question 1 - Chain Architecture (MOST IMPORTANT):
Does the user know if this is an EVM-compatible chain?
Options:
1. "Yes, it's EVM-compatible" → Proceed with EVM integration path (much simpler!)
2. "No, it's a custom blockchain" → Proceed with non-EVM integration path
3. "Not sure - can you research it?" → Perform web research (search for EVM compatibility indicators)
Context: EVM-compatible chains like Monad require minimal code changes (just add to supported chains list). Non-EVM chains like Tron/Sui require full custom crypto adapters.
Question 2 - RPC Endpoint:
Do you have a public RPC endpoint URL?
Options:
1. "Yes, here's the URL: [input]" → Use provided URL
2. "No, can you find one?" → Search ChainList.org, official docs, and GitHub for public RPC
3. "Need both HTTP and WebSocket" → Search for both endpoint types
Context: We need a reliable public RPC for the poor man's chain adapter. WebSocket is optional but nice for real-time updates.
Question 3 - SLIP44 Coin Type:
Do you know the SLIP44 coin type (BIP44 derivation path)?
Options:
1. "Yes, it's [number]" → Use provided coin type
2. "No, can you look it up?" → Search SLIP44 registry: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
3. "Use the same as Ethereum (60)" → Common for EVM chains
Context: This determines the BIP44 derivation path: m/44'/[TYPE]'/0'/0/0
Step 0.3: Structured Information Collection
After determining chain type (EVM or non-EVM), collect remaining details:
Use AskUserQuestion to ask:
For ALL chains:
Chain Basic Info
Chain name (exact capitalization, e.g., "Monad", "Tron", "Sui")
SLIP44 coin type (from Step 0.2 above)
Chain ID (numeric or string, e.g., "1" for Ethereum, "monad-1", etc.)
Documentation Links
Official website URL
Developer documentation URL
Block explorer URL
GitHub repository (if available)
Asset Information
Native asset symbol (e.g., MON, TRX, SUI)
Native asset name (e.g., "Monad", "Tron", "Sui")
Decimals/precision (usually 18 for EVM, varies for others)
CoinGecko ID (search: "coingecko [chainname]" or ask user)
For EVM chains only:
EVM-Specific Info
Network/Chain ID (numeric, e.g., 41454 for Monad)
Token standard: ERC20 (always)
Block explorer API (etherscan-like)?
Any non-standard behavior vs Ethereum?
For non-EVM chains only:
Chain Architecture Details
Transaction structure/format (link to docs)
Signing algorithm (secp256k1, ed25519, etc.)
Address format (base58, bech32, hex, etc.)
Official SDK (npm package name if available)
Token standard name (e.g., "TRC20", "SUI Coin", "SPL")
✅ Basic asset information (symbol, name, decimals)
Pro Tips:
For EVM chains: Integration is 10x easier. You mostly just add constants.
For non-EVM: Budget extra time for crypto adapter implementation.
Missing RPC? Check ChainList.org, official Discord, or GitHub repos.
Missing SLIP44? Check if it's in SLIP-0044 registry or propose one.
Can't find CoinGecko ID? Search their API or website directly.
Integration Path Decision
Based on Phase 0 research, choose your path:
Path A: EVM Chain Integration (SIMPLE)
Examples: Monad, Base, Arbitrum, Optimism
Characteristics:
✅ Uses Ethereum Virtual Machine
✅ Solidity smart contracts
✅ ERC20 token standard
✅ Web3/ethers.js compatible
✅ Auto-supported by MetaMask, Ledger Ethereum app
What you'll do:
HDWallet: Just add chain ID to EVM chains list (~10 lines of code)
Web: Extend EvmBaseAdapter (~100 lines)
Everything else: Add constants and config
Time estimate: 2-4 hours for basic integration
Path B: Non-EVM Chain Integration (COMPLEX)
Examples: Tron, Sui, Cosmos, Solana
Characteristics:
❌ Custom virtual machine (not EVM)
❌ Custom smart contract language
❌ Custom token standard
❌ Custom transaction format
❌ Requires chain-specific crypto implementation
What you'll do:
HDWallet: Implement full chain module with crypto adapters (~500-1000 lines)
Web: Implement full IChainAdapter interface (~500-1000 lines)
Everything else: Add constants and config
Time estimate: 1-2 days for basic integration
Phase 1: HDWallet Native Support
Working Directory: Same monorepo — hdwallet packages are at packages/hdwallet-*
Step 1.0: Choose Implementation Strategy
If EVM chain: Continue with Step 1.2-EVM below (MINIMAL hdwallet work - ~30 minutes)
If non-EVM chain: Continue with Step 1.1 below (COMPLEX - 1-2 days)
⚡ EVM Chains: Minimal HDWallet Work Required
For EVM-compatible chains (like Monad, HyperEVM, Base), you need MINIMAL changes to hdwallet:
What EVM chains DON'T need:
❌ No new core interfaces (TronWallet, SuiWallet, etc.)
❌ No crypto adapters (address derivation, signing)
❌ No wallet mixins
✅ Use existing Ethereum crypto (secp256k1, Keccak256)
What EVM chains DO need:
✅ Wallet support flags (_supportsChainName: boolean)
✅ Support function (supportsChainName())
✅ Set flags on all wallet implementations (~14 files)
✅ Build and verify with pnpm run hdwallet:build
Why? Each wallet type (Native, Ledger, MetaMask, etc.) needs to explicitly declare support for the chain, even though the crypto is identical. This enables wallet-specific gating in the UI.
Time estimate: 30 minutes for hdwallet changes (vs 1-2 days for non-EVM)
Step 1.1: Research HDWallet Patterns (Non-EVM Only)
Examine existing implementations to understand patterns:
For non-EVM chains (like Tron, Sui):
bash
# In the monorepo
cat packages/hdwallet-core/src/tron.ts
cat packages/hdwallet-native/src/tron.ts
cat packages/hdwallet-native/src/crypto/isolation/adapters/tron.ts
Key pattern: Need new core interfaces, native implementation, and crypto adapters for signing.
File: packages/hdwallet-core/src/ethereum.ts
Add your chain to supported EVM chains:
typescript
// Find the list of supported chain IDs and add yours
export const SUPPORTED_EVM_CHAINS = [
1, // Ethereum
10, // Optimism
// ... other chains
41454, // Add your chain ID here (example: Monad)
]
File: packages/hdwallet-core/src/utils.ts
Register SLIP44 if not using Ethereum's (60):
typescript
// If your chain uses a different SLIP44 than Ethereum
{ slip44: YOUR_SLIP44, symbol: 'SYMBOL', name: 'ChainName' }
That's it for hdwallet! EVM chains don't need crypto adapters. Skip to Step 1.6 (Version Bump).
Step 1.2-EVM: EVM Chain HDWallet Support (MINIMAL WORK - ~30 minutes)
For EVM chains only (like Monad, HyperEVM). Follow these PRs as reference:
export function supportsMonad(wallet: HDWallet): wallet is ETHWallet {
return isObject(wallet) && (wallet as any)._supportsMonad;
}
export function supports[ChainName](wallet: HDWallet): wallet is ETHWallet {
return isObject(wallet) && (wallet as any)._supports[ChainName];
}
Set flags on ALL wallet implementations (~12 files):
For second-class EVM chains (HyperEVM, Monad, Plasma):
Set readonly _supports[ChainName] = true on:
packages/hdwallet-native/src/ethereum.ts
packages/hdwallet-metamask-multichain/src/shapeshift-multichain.ts (uses standard EVM cryptography)
packages/hdwallet-ledger/src/ledger.ts (uses Ethereum app, supports all EVM chains)
packages/hdwallet-trezor/src/trezor.ts (uses Ethereum app, supports all EVM chains)
packages/hdwallet-walletconnectv2/src/walletconnectv2.ts (chain-agnostic, supports all EVM chains)
Set readonly _supports[ChainName] = false on:
packages/hdwallet-coinbase/src/coinbase.ts
packages/hdwallet-gridplus/src/gridplus.ts
packages/hdwallet-keepkey/src/keepkey.ts
packages/hdwallet-keplr/src/keplr.ts
packages/hdwallet-phantom/src/phantom.ts
packages/hdwallet-vultisig/src/vultisig.ts
For non-EVM chains:
Set readonly _supports[ChainName] = true for Native only:
See SuiChainAdapter.ts or TronChainAdapter.ts for complete examples.
Export:
typescript
// In packages/chain-adapters/src/[adaptertype]/[chainname]/index.ts
export * from './[ChainName]ChainAdapter'
export * from './types'
// In packages/chain-adapters/src/[adaptertype]/index.ts
export * as [chainLower] from './[chainname]'
// In packages/chain-adapters/src/index.ts
export * from './[adaptertype]'
Step 3.2a: Implement parseTx (Iterative Approach)
CRITICAL: The parseTx() method parses transaction data after broadcast. This determines:
Whether the transaction shows in history (if applicable)
Execution price calculation for swaps
Transfer display (from/to/value)
Reference Implementations (use these as patterns):
EVM chains: SecondClassEvmAdapter.parseTx() - handles ERC-20 Transfer events automatically
Sui: SuiChainAdapter.parseTx() - parses SUI native and coin transfers
User testing reveals issues - User tests sends/swaps and reports:
"Native send works but tokens don't show"
"Swap execution price is wrong"
Provides RPC response from debugger
Refine with actual RPC response - User provides debugger scope:
typescript
// Example: User provides RPC response showing token events in logs
// You then add token parsing logic based on actual data structure
private parseTokenTransfers(result: RpcResult, pubkey: string): Transfer[] {
const transfers: Transfer[] = []
// Parse token events from logs/events
for (const event of result.events || []) {
if (event.type === 'token_transfer') {
// Token-specific parsing based on actual RPC structure
}
}
return transfers
}
// NEAR pattern - EVENT_JSON logs
for (const log of receipt.outcome.logs) {
if (!log.startsWith('EVENT_JSON:')) continue
const event = JSON.parse(log.slice('EVENT_JSON:'.length))
if (event.standard === 'nep141' && event.event === 'ft_transfer') {
// Parse transfer from event.data
}
}
// Sui pattern - coin type from object changes
for (const change of result.objectChanges) {
if (change.type === 'mutated' && change.objectType.includes('::coin::Coin<')) {
// Extract coin type and amount
}
}
// Tron pattern - TRC20 logs
for (const log of result.log || []) {
if (log.topics[0] === TRC20_TRANSFER_TOPIC) {
// Decode TRC20 transfer
}
}
pubkey vs account ID gotcha:
Some chains pass pubkey as hex public key
But logs/events use account addresses (e.g., alice.near, base58, etc.)
May need to convert: const accountId = pubKeyToAddress(pubkey)
When to ask user for debugger scope:
Initial naive implementation doesn't catch tokens
Swap execution prices are wrong
Internal transfers missing
Example request to user:
"The parseTx implementation needs refinement for token transfers. Can you:
Make a token send/swap
Set a breakpoint in parseTx()
Share the result variable from the RPC response
This will help me see the actual data structure for token events."
Add your chain to the isAssetSupportedByWallet function around line 367:
typescript
// 1. Import your chain ID at the top
import {
// ... existing imports
[chainLower]ChainId,
} from '@shapeshiftoss/caip'
// 2. Import the support function from hdwallet-core
import {
// ... existing imports
supports[ChainName],
} from '@shapeshiftoss/hdwallet-core'
// 3. Add case to the switch statement in isAssetSupportedByWallet
export const isAssetSupportedByWallet = (assetId: AssetId, wallet: HDWallet): boolean => {
if (!assetId) return false
const { chainId } = fromAssetId(assetId)
switch (chainId) {
// ... existing cases
case [chainLower]ChainId:
return supports[ChainName](wallet)
// ... rest of cases
default:
return false
}
}
Why this matters: This function determines if a wallet can use a particular asset. Without it, assets for your chain won't appear in wallet UIs even if everything else is configured correctly!
Example: For HyperEVM, add:
typescript
case hyperEvmChainId:
return supportsHyperEvm(wallet)
Phase 4: Web Plugin & Feature Flags
Step 4.1: Create Plugin
File: src/plugins/[chainname]/index.tsx
typescript
import { [chainLower]ChainId } from '@shapeshiftoss/caip'
import { [chainLower] } from '@shapeshiftoss/chain-adapters'
import { KnownChainIds } from '@shapeshiftoss/types'
import { getConfig } from '@/config'
import type { Plugins } from '@/plugins/types'
export default function register(): Plugins {
return [
[
'[chainLower]ChainAdapter',
{
name: '[chainLower]ChainAdapter',
featureFlag: ['[ChainName]'],
providers: {
chainAdapters: [
[
KnownChainIds.[ChainName]Mainnet,
() => {
return new [chainLower].ChainAdapter({
rpcUrl: getConfig().VITE_[CHAIN]_NODE_URL,
// Add other config as needed
})
},
],
],
},
},
],
]
}
Register plugin:
typescript
// In src/plugins/activePlugins.ts
import [chainLower] from './[chainname]'
export const activePlugins = [
// ...
[chainLower],
]
Gate in provider:
typescript
// In src/context/PluginProvider/PluginProvider.tsx
// Add feature flag check for your chain
// For EVM chains, add to the EVM switch (inside chainNamespace Evm case)
case CHAIN_REFERENCE.[ChainName]Mainnet:
return CoingeckoAssetPlatform.[ChainName]
case CoingeckoAssetPlatform.[ChainName]:
return [chainLower]ChainId
NOTE: This reverse mapping requires importing [chainLower]ChainId from ../../constants. Only import what is used — chainIdToCoingeckoAssetPlatform uses CHAIN_REFERENCE not chainId constants.
Touchpoint 4 — Add chainId to buildByChainId loop in COINGECKO_ASSET_PLATFORM_TO_CHAIN_ID_MAP (~line 280-310):
typescript
// Import chainId + assetId from constants at top of file
import { [chainLower]AssetId, [chainLower]ChainId, ... } from '../../constants'
// Add to the switch/if chain inside the buildByChainId loop
prev[[chainLower]ChainId][assetId] = id
Touchpoint 5 — Add native asset to COINGECKO_NATIVE_ASSET_PLATFORM_TO_CHAIN_ID_MAP (~line 370-390):
// Add example asset from your chain to test fixtures
const [chainLower]UsdcAssetId: AssetId = 'eip155:[CHAIN_ID]/erc20:[USDC_ADDRESS]'
// Update test expectations to include your chain's asset
Add native asset case in the relayTokenToAssetId function:
typescript
// Add to the switch statement for native assets (around line 100+)
case CHAIN_REFERENCE.[ChainName]Mainnet:
return {
assetReference: ASSET_REFERENCE.[ChainName],
assetNamespace: ASSET_NAMESPACE.slip44,
}
IMPORTANT: Make sure ALL chains that are in the chainIdToRelayChainId mapping in constant.ts have a corresponding case in the switch statement in relayTokenToAssetId.ts. Missing cases will cause runtime errors like chainId 'XX' not supported.
IMPORTANT: Asset generation requires a Zerion API key for related asset indexing.
Ask user for Zerion API key using AskUserQuestion:
Question: Do you have a Zerion API key to run asset generation?
Options:
"Yes, here it is" → User provides key (NEVER store in VCS!)
"No, skip for now" → Skip asset generation, user can run manually later
Context: Asset generation fetches token metadata and requires a Zerion API key.
The key is passed via environment variable and should NEVER be committed to VCS.
Ask user how they want to run generation using AskUserQuestion:
Question: How do you want to run the asset generation pipeline?
Options:
"I'll run it myself" → Copy command to clipboard (echo | pbcopy), user runs it, better visibility of progress
"Claude runs it" → Claude runs all steps in background. ⚠️ WARNING: May take 5-10 minutes with limited visibility. You'll see less progress output.
Context: Asset generation has 5 steps (caip-adapters, color-map, asset-data, tradable-asset-map, thor-longtail).
Running manually gives full visibility of progress (you'll see "chain_id: hyperevm" tokens being processed).
Claude running it is hands-off but you won't see detailed progress, and it may appear stuck for several minutes while processing thousands of tokens.
Run generation scripts ONE AT A TIME (better visibility than generate:all):
bash
# Step 1: Generate CoinGecko CAIP adapters (JSON mappings from our code)
pnpm run generate:caip-adapters
# ✓ Generates packages/caip/src/adapters/coingecko/generated/eip155_999/adapter.json
# ✓ Takes ~10 seconds
# Step 2: Generate color map (picks up new assets)
pnpm run generate:color-map
# ✓ Updates scripts/generateAssetData/color-map.json
# ✓ Takes ~5 seconds
# Step 3: Generate asset data (fetches tokens from CoinGecko)
ZERION_API_KEY=<user-provided-key> pnpm run generate:asset-data
# ✓ Fetches all HyperEVM ERC20 tokens from CoinGecko platform 'hyperevm'
# ✓ Updates src/assets/generated/
# ✓ Takes 2-5 minutes - YOU SHOULD SEE:
# - "Total Portals tokens fetched for ethereum: XXXX"
# - "Total Portals tokens fetched for base: XXXX"
# - "chain_id": "hyperevm" appearing in output (means HyperEVM tokens found!)
# - "Generated CoinGecko AssetId adapter data."
# - "Asset data generated successfully"
# Step 4: Generate tradable asset map (for swapper support)
pnpm run generate:tradable-asset-map
# ✓ Generates src/lib/swapper/constants.ts mappings
# ✓ Takes ~10 seconds
# Step 5: Generate Thor longtail tokens (Thor-specific, optional for most chains)
pnpm run generate:thor-longtail-tokens
# ✓ Updates Thor longtail token list
# ✓ Takes ~5 seconds
Why step-by-step is better than generate:all:
✅ See exactly which step is running
✅ Catch errors immediately
✅ See progress output (like "chain_id": "hyperevm" tokens being processed)
✅ Can skip irrelevant steps (e.g., thor-longtail for non-Thor chains)
⚠️ CRITICAL: NEVER commit the Zerion API key. Only use it in the command line.
Step 5.4: Research & Add Swapper Support
IMPORTANT: After assets are generated, check which swappers support your new chain!
Step 5.4a: Ask User About Swapper Support
Use AskUserQuestion to determine swapper support:
Which swappers support [ChainName]?
Options:
1. "I know which swappers support it" → User provides list
2. "Research it for me" → AI will search swapper docs
3. "Skip for now" → Can add swapper support later
Context: Different DEX aggregators support different chains. We need to add your chain to each swapper that supports it so users can trade.
Step 5.4b: Research Common Swapper Support (if needed)
If user chooses "Research it for me", check these sources:
// Add native asset case in switch statement (around line 124):
case CHAIN_REFERENCE.PlasmaMainnet:
return {
assetReference: ASSET_REFERENCE.Plasma,
assetNamespace: ASSET_NAMESPACE.slip44,
}
Step 5.4d: Add Other Swapper Support (As Needed)
Follow similar patterns for other swappers (CowSwap, 0x, etc.) - see swapper-integration skill for detailed guidance.
Reference: Plasma added to Relay swapper for swap support
Step 5.5: Add Native Asset to Popular Assets
CRITICAL: Second-class citizen chains are not in CoinGecko's top 100 by market cap, so they won't appear in the popular assets list by default. This causes the native asset to be missing when users filter by that chain.
// Add import at the top
import {
hyperEvmAssetId,
mayachainAssetId,
monadAssetId,
plasmaAssetId, // example for Plasma
[chainLower]AssetId, // Add your chain's asset ID
thorchainAssetId,
tronAssetId,
suiAssetId,
} from '@shapeshiftoss/caip'
// Add to the queryFn, after the mayachain check (around line 37)
// add second-class citizen chains to popular assets for discoverability
if (enabledFlags.HyperEvm) assetIds.push(hyperEvmAssetId)
if (enabledFlags.Monad) assetIds.push(monadAssetId)
if (enabledFlags.Plasma) assetIds.push(plasmaAssetId)
if (enabledFlags.[ChainName]) assetIds.push([chainLower]AssetId) // Add your chain
if (enabledFlags.Tron) assetIds.push(tronAssetId)
if (enabledFlags.Sui) assetIds.push(suiAssetId)
Why this is needed:
Popular assets are fetched from CoinGecko's top 100 by market cap
New/small chains aren't in the top 100
Without this, when filtering by your chain, only tokens appear (via relatedAssetIds)
The native asset won't show up, which is confusing for users
Example: Searching "monad" in MetaMask (doesn't support Monad) shows Monad tokens but not MON itself
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.
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'
Add to the manualRelatedAssetIndex under ethAssetId:
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 pnpm run 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:
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'.
Everything is in the same monorepo now — hdwallet packages are workspace packages under packages/hdwallet-*.
bash
# Commit everything together
git add -A
git commit -m "feat: implement [chainname]
- Add [ChainName] hdwallet core interfaces and native wallet support
- Add [ChainName] chain adapter with poor man's RPC
- Support native asset sends/receives
- Add account derivation
- Add feature flag VITE_FEATURE_[CHAIN]
- Add asset generation for [chain] from CoinGecko
- Wire transaction status polling
- Add [chain] plugin with feature flag gating
Behind feature flag for now."
Step 8.2: Open PR
bash
git push origin HEAD
# Open PR to develop
gh pr create --title "feat: implement [chainname]" \
--body "Adds basic [ChainName] support as second-class citizen..."
Problem: Invalid addresses accepted or valid ones rejected
Solution: Use chain-specific validation (checksumming for EVM, base58 for Tron, etc.)
Example: Tron address parsing issues (#11229)
Gotcha 3: Transaction Broadcasting
Problem: Signed transactions fail to broadcast
Solution: Check serialization format matches chain expectations
Example: Ensure proper hex encoding, network byte for Tron, etc.
Gotcha 4: Bundle Size
Problem: Build size explodes after adding chain SDK
Solution: Extract large dependencies to separate chunk
Example: Sui SDK needed code splitting (#11238 comments)
Gotcha 5: Minimum Trade Amounts
Problem: Small swaps fail without clear error
Solution: Add minimum amount validation in swapper
Example: Tron tokens need minimum amounts (#11253)
Gotcha 6: Token Grouping
Problem: Tokens don't group with related assets in UI
Solution: Check asset namespace and ID generation
Example: Tron/Sui tokens grouping issues (#11252)
Gotcha 7: Ledger App Mismatch
Problem: Ledger transactions fail with unclear error
Solution: Verify correct Ledger app is mapped
Example: Use Ethereum app for EVM chains, not chain-specific app
Gotcha 8: Missing walletSupportsChain Case (CRITICAL - BLOCKS ACCOUNT DISCOVERY!)
Problem: Assets appear but no accounts are derived/discovered for the chain
Symptoms:
Chain adapter is registered
Assets show up in asset list
But wallet shows no accounts for the chain
Logs show account derivation never runs for chainId
Root Cause: Missing case in walletSupportsChain() switch statement
Solution: Add your chain to the switch statement in src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts:
typescript
// 1. Import chain ID
import { [chainLower]ChainId } from '@shapeshiftoss/caip'
// 2. Import support function from hdwallet-core
import { supports[ChainName] } from '@shapeshiftoss/hdwallet-core'
// 3. Add to switch statement (around line 186+)
case [chainLower]ChainId:
return supports[ChainName](wallet)
Example: HyperEVM was missing this - caused account discovery to skip it entirely
Why it matters: useDiscoverAccounts filters chains using walletSupportsChain(). If it returns false, the chain is never passed to deriveAccountIdsAndMetadata() → no accounts!
Reference: Same issue as Plasma PR #11361 but for wallet support instead of feature flag
Gotcha 9: Feature Flag Not Working
Problem: Chain doesn't appear even with flag enabled
Solution: Check ALL places flags are checked:
Plugin registration (featureFlag array)
PluginProvider gating (add chainId filter)
Asset service filtering
Constants array (SECOND_CLASS_CHAINS)
Gotcha 10: Balance Updates
Problem: Balances don't update after transactions
Solution: Implement polling in tx status subscriber
Example: Add chain case in useSendActionSubscriber
Problem: pnpm run generate:asset-data fails with "no coingecko token support for chainId"
Solution: Add your chain case to scripts/generateAssetData/coingecko.tsFiles to update:
Import [chainLower]ChainId from caip
Import [chainLower] base asset from utils
Add case in switch statement with assetNamespace, category, explorer links
Example: See HyperEVM case (line ~143) for pattern
Gotcha 13: Zerion API Key Required
Problem: Asset generation fails with "Missing Zerion API key"
Solution: Get key from user via AskUserQuestion, pass as env var
Command: ZERION_API_KEY=<key> pnpm run generate:allCRITICAL: NEVER commit the Zerion API key to VCS!
Example: Always pass key via command line only
Gotcha 14: AssetService Missing Feature Flag Filter
Problem: Assets for your chain appear even when feature flag is disabled
Solution: Add feature flag filter to AssetService
File: src/lib/asset-service/service/AssetService.tsCode: if (!config.VITE_FEATURE_[CHAIN] && asset.chainId === [chainLower]ChainId) return falseExample: See line ~53 for Monad/Tron/Sui pattern
Reference: Fixed in PR #11241 (Monad) - was initially forgotten
Gotcha 15: Missing from evmChainIds Array (EVM Chains Only)
Problem: TypeScript errors "Type 'KnownChainIds.[Chain]Mainnet' is not assignable to type EvmChainId"
Solution: Add your chain to the evmChainIds array in EvmBaseAdapter
Files to update:
Add to evmChainIds array: KnownChainIds.[Chain]Mainnet
Add to targetNetwork object (line ~210): network name, symbol, explorer
Example: HyperEVM added at lines 81 and 262-266
Why: The array defines which chains are EVM-compatible for type checking
Why: TypeScript uses these to determine chain-specific data structures
CRITICAL: Missing even ONE of these causes cryptic type errors! All 4 are required for ALL chains (EVM and non-EVM).
Gotcha 17: Missing accountIdToLabel Case (BLOCKS ADDRESS DISPLAY!)
Problem: Addresses don't display in:
Account import UI (shows blank address in table)
Send flow "from" address row (shows empty from address)
Account dropdowns throughout the app
Root Cause: Missing chainId case in accountIdToLabel() function
File: src/state/slices/portfolioSlice/utils/index.ts (around line 80-125)
Solution:
Add chainId import: import { [chainLower]ChainId } from '@shapeshiftoss/caip'
Add case to switch statement: case [chainLower]ChainId:
Place it with other EVM chains (before thorchainChainId)
Example:
typescript
case baseChainId:
case hyperEvmChainId: // ← ADD THIS
case monadChainId:
case plasmaChainId:
Why: This function converts accountId to human-readable label. Without the case, it hits the default and returns '' (empty string), causing blank addresses everywhere in the UI.
Note: This affects ALL wallet types (Native, Ledger, Trezor, MetaMask), not just one wallet.
Gotcha 18: Missing getNativeFeeAssetReference Case (RUNTIME CRASH!)
Problem: App crashes with error:
Error: Chain namespace [chain] on mainnet not supported.
at getNativeFeeAssetReference.ts:XX:XX
Root Cause: Missing chainNamespace case in getNativeFeeAssetReference() function
File: packages/utils/src/getNativeFeeAssetReference.ts
Solution:
Add case to the switch statement:
typescript
case CHAIN_NAMESPACE.[ChainName]:
switch (chainReference) {
case CHAIN_REFERENCE.[ChainName]Mainnet:
return ASSET_REFERENCE.[ChainName]
default:
throw new Error(`Chain namespace ${chainNamespace} on ${chainReference} not supported.`)
}
Why: This function maps chainId to the native fee asset reference. Without it, any selector that needs the fee asset for your chain will throw and crash the app.
Note: This is called early in the app initialization, so it will crash immediately when the app tries to load accounts for your chain.
Gotcha 19: Missing accountToPortfolio Case (ACCOUNT NOT VISIBLE!)
Problem: Account discovery succeeds (getAccount returns valid data) but account doesn't appear in the UI anywhere.
Root Cause: Missing or placeholder implementation in accountToPortfolio() function
File: src/state/slices/portfolioSlice/utils/index.ts
Symptom: You can verify getAccount is returning correct data with console.log, but the account just doesn't show up in the portfolio UI.
Solution:
Find the switch statement for chainNamespace and implement your chain's case:
Why: This function converts chain adapter account data into the Redux portfolio state structure. Without it, the account data is fetched but never stored, making the account invisible.
But transaction is a native transfer to self, not a token transfer
Token balance unchanged, only gas spent
Root Cause: isToken() in packages/utils/src/index.ts doesn't include the new chain's token namespace.
Why it matters: contractAddressOrUndefined(assetId) uses isToken() to determine if an asset is a token. If isToken() returns false, contractAddressOrUndefined() returns undefined, and the chain adapter builds a native transfer instead of a token transfer.
Non-EVM chains: MUST add their token namespace to isToken():
typescript
// packages/utils/src/index.ts
export const isToken = (assetId: AssetId) => {
switch (fromAssetId(assetId).assetNamespace) {
case ASSET_NAMESPACE.erc20:
case ASSET_NAMESPACE.erc721:
case ASSET_NAMESPACE.erc1155:
case ASSET_NAMESPACE.splToken: // Solana
case ASSET_NAMESPACE.trc20: // Tron
case ASSET_NAMESPACE.suiCoin: // Sui
case ASSET_NAMESPACE.nep141: // NEAR <-- ADD YOUR NAMESPACE HERE
return true
default:
return false
}
}
Symptom: User sends token, tx succeeds, but token balance unchanged. Tx on block explorer shows native transfer to self.
Debug: Add logs in Send flow:
typescript
console.log('[Send] assetId:', asset.assetId)
console.log('[Send] contractAddress:', contractAddressOrUndefined(asset.assetId))
// If contractAddress is undefined for a token, isToken() is missing the namespace
But new chain throws TypeError: coin when deriving address
Error trace shows translateCoinAndMethod hitting default: throw new TypeError("coin")
Root Cause: For non-EVM chains, translateCoinAndMethod in Ledger WebHID/WebUSB transport needs a case for the new coin type.
Files to update:
packages/hdwallet-ledger-webhid/src/transport.ts
packages/hdwallet-ledger-webusb/src/transport.ts
Solution: Add import and case for the new chain's Ledger app:
typescript
// 1. Add import at top
import Near from "@ledgerhq/hw-app-near";
// 2. Add case in translateCoinAndMethod switch
case "Near": {
const near = new Near(transport as Transport);
const methodInstance = near[method as LedgerTransportMethodName<"Near">].bind(near);
return methodInstance as LedgerTransportMethod<T, U>;
}
Prerequisites:
@ledgerhq/hw-app-[chainname] must exist as npm package
Chain must be in LedgerTransportCoinType union in packages/hdwallet-ledger/src/transport.ts
EVM chains: Not affected - they use the Ethereum app via case "Eth".
Checklist for new non-EVM Ledger chain:
Add to LedgerTransportCoinType in hdwallet-ledger/src/transport.ts
Add to LedgerTransportMethodMap type mapping in same file
Add import + case in hdwallet-ledger-webhid/src/transport.ts
Add import + case in hdwallet-ledger-webusb/src/transport.ts
Chain Integration next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
Safely plan and execute dependency maintenance for JavaScript/TypeScript (npm, pnpm) and GitHub Actions, including npm lockfiles, pnpm workspaces, catalogs, overrides, SHA-pinned action versions…
Operating manual for the Lineth Stack quickstart — the Docker-Compose dev/demo stack at docs/getting-started/lineth-stack in the lineth-monorepo that boots a local Linea/Lineth L2 with Sepolia or…
A skill your agent uses when building the Nango monorepo or verifying TypeScript compilation - covers build commands, project references, common tsc errors, and package dependency order
Run a quality benchmark of the /translate skill by selecting stratified test keys, capturing ground truth, translating, judging with sub-agents, and compiling a regression report.
Integrate a new blockchain as a second-class citizen in ShapeShift Web. Chain Integration is an agent skill from shapeshift/web. Integrate a new blockchain as a second-class citizen in ShapeShift Web.
When should I use Chain Integration?
Chain Integration fits situations like: wants to add basic support for a new blockchain; tasks that involve Game assets and audio; tasks that involve Monorepo tooling.
How do I install Chain Integration in Claude Code?
Run `npx skills add shapeshift/web --skill chain-integration -a claude-code`. Or copy the skill folder (.claude/skills/chain-integration in shapeshift/web) into .claude/skills/chain-integration in your project. Claude Code loads it when a task matches its description.
How do I install Chain Integration in Codex?
Run `npx skills add shapeshift/web --skill chain-integration -a codex`. Or copy the skill folder (.claude/skills/chain-integration in shapeshift/web) into .agents/skills/chain-integration in your project. Codex loads it when a task matches its description.
Can I use Chain Integration in Cursor, Gemini CLI or GitHub Copilot?
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add shapeshift/web --skill chain-integration -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/chain-integration, .gemini/skills/chain-integration, .github/skills/chain-integration and .opencode/skills/chain-integration in your project.
What does Chain Integration need to run?
Going by SKILL.md and its folder, Chain Integration needs the command-line tools its instructions call (pnpm, git and gh) and credentials named ZERION_API_KEY. Its frontmatter pre-approves these tools: Read, Write, Edit, Grep, Glob, Bash.
Does Chain Integration access the network?
SKILL.md names 8 domains. In commands or code: github.com; the agent is likely to contact it when it follows the instructions. As links in the text: docs.relay.link, ledger.com, 0x.org, docs.cow.fi, docs.thorchain.org, chainlist.org and docs.1inch.io. This is read from the text; nothing was executed.
Is Chain Integration safe to install?
Our automated static check of SKILL.md found notes only (mentions a .env file; pre-approves every shell command (allowed-tools: bash)), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.
What licence does Chain Integration use?
Chain Integration is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
How many tokens does Chain Integration use?
About 20k tokens (SKILL.md is roughly 79k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
What are the alternatives to Chain Integration?
Skills that share tags, products or a category with Chain Integration: Linea Dependency Maintenance (Consensys-Incorporated/linea-attestation-registry, 177 stars), Create Vechain Dapp (vechain/x-app-template, 450 stars), Lineth Quickstart (LFDT-Lineth/lineth-monorepo, 126 stars) and Building And Verifying (NangoHQ/nango, 13k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
Who maintains Chain Integration?
shapeshift (a GitHub organization) maintains it in shapeshift/web, which has 206 GitHub stars. The repository holds 7 skills in this directory. The repository was last updated on October 7, 2026.
Source: shapeshift/web on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.