2024 年,活跃区块链超过 100 条。用户资产分散在以太坊(主网)、Arbitrum(便宜)、Base(社交)、Solana(高性能)。多链 DApp 不是"可选项",是默认配置。
14.6.1 链切换与链无关设计
用户看到的 vs 前端看到的
graph LR
subgraph UX["用户视角"]
U1[使用 DApp]
U2["需要跨链(如 Arbitrum → Base)]"
U3[期待"品牌感知"]
end
subgraph Internal["前端实现"]
I1[检测当前链]
I2[调用 wallet_switchEthereumChain]
I3[重新初始化 provider]
I4[加载该链合约地址映射]
I5[查询该链状态]
end
U1 --> I1
U2 --> I2 --> I3 --> I4 --> I5
style UX fill:#e8f5e9
style Internal fill:#e3f2fd
链配置管理
typescript
/**
* 多链配置管理(TypeScript 骨架)
*/
interface ChainConfig {
id: number;
name: string;
rpcUrl: string;
currency: { name: string; symbol: string; decimals: number };
explorer: string;
contracts: { [name: string]: string }; // 已部署合约地址映射
isTestnet: boolean;
}
const chains: Record<string, ChainConfig> = {
mainnet: {
id: 1,
name: "Ethereum",
rpcUrl: "https://eth-mainnet.g.alchemy.com/v2/...",
currency: { name: "Ether", symbol: "ETH", decimals: 18 },
explorer: "https://etherscan.io",
contracts: {
token: "0x6B1754...", // DAI on mainnet
aavePool: "0x87870B...",
},
isTestnet: false,
},
arbitrum: {
id: 42161,
name: "Arbitrum One",
rpcUrl: "https://arb-mainnet.g.alchemy.com/v2/...",
currency: { name: "Ether", symbol: "ETH", decimals: 18 },
explorer: "https://arbiscan.io",
contracts: {
token: "0xDA1000...", // DAI on Arbitrum
aavePool: "0x794a61...",
},
isTestnet: false,
},
base: {
id: 8453,
name: "Base",
rpcUrl: "https://base-mainnet.g.alchemy.com/v2/...",
currency: { name: "Ether", symbol: "ETH", decimals: 18 },
explorer: "https://basescan.org",
contracts: {
token: "0x50c572...", // DAI on Base
},
isTestnet: false,
},
sepolia: {
id: 11155111,
name: "Sepolia",
rpcUrl: "https://eth-sepolia.g.alchemy.com/v2/...",
currency: { name: "SepoliaETH", symbol: "ETH", decimals: 18 },
explorer: "https://sepolia.etherscan.io",
contracts: {
token: "0x123abc...",
},
isTestnet: true,
},
};
// 切换链
async function switchChain(targetChainId: number, wallet: any) {
try {
await wallet.request({
method: "wallet_switchEthereumChain",
params: [{ chainId: "0x" + targetChainId.toString(16) }], // 0x1, 0xa4b1 (42161)
});
} catch (error: any) {
// 如果链未安装,需要先添加
if (error.code === 4902) {
const chain = Object.values(chains).find(c => c.id === targetChainId);
await wallet.request({
method: "wallet_addEthereumChain",
params: [{
chainId: "0x" + targetChainId.toString(16),
chainName: chain?.name,
rpcUrls: [chain?.rpcUrl],
nativeCurrency: chain?.currency,
blockExplorerUrls: [chain?.explorer],
}],
});
}
}
}
console.log("多链配置已定义");14.6.2 跨链桥的原理
跨链桥不是把 ETH"移动"到另一链——原生资产不能离开其原生链。跨链桥通过锁定 + 铸造机制解决:
sequenceDiagram
participant User as 用户
participant Bridge as 跨链桥合约
participant Oracle as 验证网络 / 预言机
participant Target as 目标链合约
User ->> Bridge: 锁定 100 ETH (源链)
Bridge ->> Bridge: 冻结 100 ETH 在合约中
Bridge -->> Oracle: 证明事件(Merkle proof / 多重签名)
Oracle -->> Target: 验证 + 签名
Target ->> Target: 铸造 100 wETH (目标链)
Target -->> User: 在目标链上使用影子资产
Note over Target: "wETH" 是"包装 ETH",<br/>不是原生 ETH!<br/>赎回时销毁 wETH → 释放锁定的原生 ETH
主要跨链桥方案
| 桥 | 技术 | 去中心化程度 | 速度 | 费用 |
|---|---|---|---|---|
| Wormhole | 多重签名验证者 | 中 | 快 | 中 |
| LayerZero | 预言机 + 中继器 | 中 | 极快 | 低 |
| Stargate (LayerZero) | 统一流动性池 | 中 | 极快 | 低 |
| Hop Protocol | 流动性网络 | 较高 | 分钟级 | 低 |
| Across | UMA 乐观验证 | 中 | 快 | 中 |
| 官方桥(如 Arbitrum Bridge) | 欺诈证明 / 有效性证明 | 高 | 慢(天级退出) | 高(L1 gas) |
14.6.3 前端跨链交互模式
状态等待 UX
跨链交易需要时间:消息从源链传播到目标链可能是几秒到几十分钟。前端 UX 必须管理这个等待状态。
typescript
/**
* 跨链交易状态追踪
*/
interface CrossChainTx {
id: string;
sourceChain: number; // 源链 ID
targetChain: number; // 目标链 ID
status: "sending" | "waiting_attestation" | "delivered" | "completed" | "failed";
sourceTxHash: string; // 源链交易哈希
targetTxHash?: string; // 目标链执行哈希(有延迟)
estimatedTime: number; // 秒
progress: number; // 0-100%
}
class CrossChainTracker {
private txs: Map<string, CrossChainTx> = new Map();
private listeners = new Set<(tx: CrossChainTx) => void>();
// 用户发起桥接
async initiateBridge(from: number, to: number, amount: bigint, token: string): Promise<string> {
const txId = crypto.randomUUID(); // 简化 ID
this.txs.set(txId, {
id: txId,
sourceChain: from,
targetChain: to,
status: "sending",
sourceTxHash: "",
estimatedTime: 120, // 2 分钟估计
progress: 0,
});
// 简化流程模拟
setTimeout(() => this.updateStatus(txId, "waiting_attestation", 10), 2000);
setTimeout(() => this.updateStatus(txId, "delivered", 80), 60000);
setTimeout(() => this.updateStatus(txId, "completed", 100), 120000);
return txId;
}
private updateStatus(id: string, status: CrossChainTx["status"], progress: number) {
const tx = this.txs.get(id);
if (!tx) return;
tx.status = status;
tx.progress = progress;
for (const cb of this.listeners) cb({ ...tx });
}
subscribe(txId: string, cb: (tx: CrossChainTx) => void) {
this.listeners.add(cb);
return () => this.listeners.delete(cb);
}
getStatusText(tx: CrossChainTx): string {
const map: Record<string, string> = {
sending: "正在发送源链交易...",
waiting_attestation: "等待目标链验证...",
delivered: "已到达目标链,确认中...",
completed: "✅ 完成!",
failed: "❌ 失败",
};
return map[tx.status];
}
}
// LayerZero SDK 风格的调用层
interface LZSendParam {
dstEid: number; // 目标端点 ID (Endpoint ID)
to: string; // 目标地址
amount: bigint;
minAmount: bigint; // 防止滑点
extraOptions: string; // 额外选项(gas 空投等)
composeMsg: string; // 可携带额外消息
oftCmd: string; // 命令
}
// 现代跨链桥 SDK 简化高层调用:
// send(srcChain, dstChain, token, amount, recipient)
// 抽象掉 Wormhole VAA / LayerZero 消息包 / 中继器验证 等底层
console.log("跨链架构:锁定-证明-铸造/释放");14.6.4 桥的风险
跨链桥是历史漏洞重灾区——锁定的巨大 TVL 使其成为攻击首选:
| 事件 | 损失 | 原因 |
|---|---|---|
| Wormhole(2022) | $320M | 验证者签名被绕过 |
| Ronin(2022) | $622M | 私钥泄露(社工) |
| Nomad(2022) | $190M | 初始化错误 |
| Multichain(2023) | $126M | 多签私钥控制问题 |
启示:跨链桥选择时,验证机制的去中心化程度比"品牌知名度"更重要。
> ← 14.5 去中心化存储 | 前往 → ch14-summary(本章总结) |*
评论
0评论加载中…