From d5bbc43b521ad08fb552e117a6e47d0d638f8a70 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Fri, 13 Mar 2026 05:25:46 +0800 Subject: [PATCH] feat: add AccountCenter component for user authentication, subscription, and payment management. --- frontend/components/account/AccountCenter.tsx | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/frontend/components/account/AccountCenter.tsx b/frontend/components/account/AccountCenter.tsx index b8f4fe75..f1e7d23e 100644 --- a/frontend/components/account/AccountCenter.tsx +++ b/frontend/components/account/AccountCenter.tsx @@ -81,6 +81,8 @@ type CreatedIntent = { to: string; data: string; value: string; + amount_units: string; + token_address: string; }; }; @@ -132,6 +134,27 @@ function shortAddress(address: string) { return `${text.slice(0, 8)}...${text.slice(-6)}`; } +function toPaddedHex(value: bigint) { + return value.toString(16).padStart(64, "0"); +} + +function toPaddedAddress(address: string) { + return String(address || "") + .toLowerCase() + .replace(/^0x/, "") + .padStart(64, "0"); +} + +function buildAllowanceCalldata(owner: string, spender: string) { + // allowance(address owner, address spender) + return `0xdd62ed3e${toPaddedAddress(owner)}${toPaddedAddress(spender)}`; +} + +function buildApproveCalldata(spender: string, amount: bigint) { + // approve(address spender, uint256 amount) + return `0x095ea7b3${toPaddedAddress(spender)}${toPaddedHex(amount)}`; +} + function InfoItem({ icon: Icon, label, value, status = "default" }: InfoItemProps) { return (
@@ -324,6 +347,30 @@ export function AccountCenter() { } catch {} }; + const waitForReceipt = async ( + txHash: string, + timeoutMs = 120000, + pollMs = 3000, + ) => { + const eth = window.ethereum; + if (!eth) { + throw new Error("MetaMask not found"); + } + const started = Date.now(); + while (Date.now() - started < timeoutMs) { + const receipt = (await eth.request({ + method: "eth_getTransactionReceipt", + params: [txHash], + })) as { status?: string } | null; + if (receipt && receipt.status) { + if (receipt.status === "0x1") return receipt; + throw new Error(`transaction reverted: ${txHash}`); + } + await new Promise((resolve) => setTimeout(resolve, pollMs)); + } + throw new Error(`transaction confirmation timeout: ${txHash}`); + }; + const planList = paymentConfig?.plans || []; const selectedPlan = planList.find((p) => p.plan_code === selectedPlanCode) || planList[0]; const paymentFeatureReady = Boolean(paymentConfig?.enabled && paymentConfig?.configured); @@ -448,6 +495,43 @@ export function AccountCenter() { } setLastIntentId(intentId); + const tokenAddress = String(txPayload.token_address || "").toLowerCase(); + const amountUnits = BigInt(String(txPayload.amount_units || "0")); + if (!tokenAddress.startsWith("0x") || amountUnits <= 0n) { + throw new Error("intent token/amount invalid"); + } + + const allowanceHex = (await eth.request({ + method: "eth_call", + params: [ + { + to: tokenAddress, + data: buildAllowanceCalldata(payingWallet, txPayload.to), + }, + "latest", + ], + })) as string; + const allowance = BigInt(String(allowanceHex || "0x0")); + + if (allowance < amountUnits) { + setPaymentInfo("检测到授权不足,正在发起 USDC 授权..."); + const approveHash = (await eth.request({ + method: "eth_sendTransaction", + params: [ + { + from: payingWallet, + to: tokenAddress, + data: buildApproveCalldata(txPayload.to, amountUnits), + value: "0x0", + }, + ], + })) as string; + await waitForReceipt(String(approveHash || "")); + setPaymentInfo("USDC 授权成功,正在发起支付..."); + } else { + setPaymentInfo("授权额度充足,正在发起支付..."); + } + const txHash = (await eth.request({ method: "eth_sendTransaction", params: [