Book

 · about 1 month ago

To Base Chain

To Base Chain

To Base Chain

import { Controller } from "@hotwired/stimulus" import { wrapFetchWithPayment } from "x402-fetch" import { createWalletClient, custom } from "viem" import { baseSepolia, base } from "viem/chains" // 同时导入 mainnet,方便以后切换

export default class extends Controller { static targets = ["status", "post", "button"] static values = { url: String, // 可选:通过 data-attribute 指定目标链,默认 sepolia chain: String // "sepolia" 或 "mainnet" }

// 统一获取目标链配置 get targetChain() { const chain = this.chainValue || "sepolia" return chain === "mainnet" ? base : baseSepolia }

async pay() { this.buttonTarget.disabled = true this.buttonTarget.textContent = "连接中..."

if (!window.ethereum) {
this.statusTarget.textContent = "错误:请安装 MetaMask"
this.resetButton()
return
}

try {
// 1. 请求账户
await window.ethereum.request({ method: "eth_requestAccounts" })
const address = window.ethereum.selectedAddress

// 2. 检查并切换链
await this.ensureChain()

// 3. 创建 walletClient(现在一定是正确的链)
const walletClient = createWalletClient({
account: address,
chain: this.targetChain,
transport: custom(window.ethereum),
})

const fetchWithPay = wrapFetchWithPayment(fetch, walletClient)

this.statusTarget.textContent = `已连接: ${address.slice(0, 6)}...${address.slice(-4)} (Base ${this.chainValue || "Sepolia"})`

// 4. 请求带支付的接口
const response = await fetchWithPay(this.urlValue, {
method: "GET",
headers: { "Accept": "application/json, text/html" }
})

if (!response.ok) throw new Error(`HTTP ${response.status}`)
const html = await response.text()
this.postTarget.innerHTML = html

} catch (error) {
console.error(error)
this.statusTarget.textContent = `失败: ${error.message}`
} finally {
this.resetButton()
}


}

// ---------- 链切换核心逻辑 ---------- async ensureChain() { const targetChain = this.targetChain const currentChainId = await window.ethereum.request({ method: "eth_chainId" })

if (currentChainId === targetChain.id) {
// 已经在目标链,直接返回
return
}

this.statusTarget.textContent = `请切换到 Base ${this.chainValue || "Sepolia"}...`

try {
// 先尝试切换
await window.ethereum.request({
method: "wallet_switchEthereumChain",
params: [{ chainId: targetChain.id }],
})
} catch (switchError) {
// 如果链还没添加(错误码 4902),自动添加
if (switchError.code === 4902 || switchError.code === -32603) {
await this.addBaseChain(targetChain)
// 添加成功后再切换
await window.ethereum.request({
method: "wallet_switchEthereumChain",
params: [{ chainId: targetChain.id }],
})
} else {
throw switchError
}
}


}

// 添加 Base 链(Sepolia / Mainnet) async addBaseChain(chain) { const chainInfo = { chainId: chain.id, chainName: chain.name, nativeCurrency: chain.nativeCurrency, rpcUrls: chain.rpcUrls.default.http, blockExplorerUrls: [chain.blockExplorers.default.url], }

try {
await window.ethereum.request({
method: "wallet_addEthereumChain",
params: [chainInfo],
})
this.statusTarget.textContent = `已添加 ${chain.name}`
} catch (addError) {
throw new Error("用户拒绝添加 Base 链")
}


}

resetButton() { this.buttonTarget.disabled = false this.buttonTarget.textContent = "连接钱包并获取数据" } }