前端调用智能合约withdraw函数无响应问题排查
问题定位
取款无响应的核心错误就在你标注的handleWithdrawSubmit逻辑里,附带还有2个逻辑bug会导致功能不符合预期:
- 核心调用错误:合约的
withdraw是nonpayable类型函数,接收uint256 amount作为唯一入参(即取款金额),调用时不允许向合约转ETH。但你当前写法contract.withdraw({ value: ethValue })是把取款金额当成了交易附带发送的ETH值,没有作为函数参数传入,属于参数不匹配+违规向非payable函数转ETH,会直接触发EVM调用错误,交易根本不会被钱包弹出确认,自然没有任何响应。 - 余额查询逻辑错误:当前代码查的是合约地址的总锁仓ETH,不是当前连接用户的个人存款,和合约里
balances映射的记账逻辑完全不对应。 - 生命周期写法错误:
useEffect没有传依赖数组,组件每次重渲染都会重复触发钱包连接、余额查询,进而导致无限重渲染。
修复代码
1. 修正取款函数调用
把取款金额作为函数入参传入,不要传value字段:
const handleWithdrawSubmit = async (e) => { e.preventDefault() const ethValue = ethers.utils.parseEther(withdrawValue) // 按合约ABI定义,第一个参数传取款金额,nonpayable函数不需要传value const withdrawTx = await contract.withdraw(ethValue) await withdrawTx.wait() // 取款后更新当前用户余额 const userAddress = await signer.getAddress() const latestBalance = await contract.returnBalance(userAddress) setBalance(ethers.utils.formatEther(latestBalance)) }
2. 修正余额查询与useEffect逻辑
给useEffect加空依赖数组,仅在组件首次挂载时执行连接钱包、首次查余额逻辑,同时把余额查询改成读取当前用户的个人存款:
useEffect(() => { const connectWallet = async () => { await provider.send("eth_requestAccounts", []); } const getUserBalance = async () => { const userAddress = await signer.getAddress() const userBalance = await contract.returnBalance(userAddress) setBalance(ethers.utils.formatEther(userBalance)); } connectWallet().catch(console.error); getUserBalance().catch(console.error); // 空依赖数组,仅组件挂载时执行一次 }, [])
3. 修正存款成功后的余额更新逻辑
同样改成读取当前用户个人存款,不要读合约总余额:
const handleDepositSubmit = async (e) => { e.preventDefault() const ethValue = ethers.utils.parseEther(depositValue) const depositTx = await contract.deposit({ value: ethValue }) await depositTx.wait() const userAddress = await signer.getAddress() const latestBalance = await contract.returnBalance(userAddress) setBalance(ethers.utils.formatEther(latestBalance)); }
可选优化
- 所有合约交互逻辑外层包
try/catch,捕获用户拒绝签名、链上报错等异常,给用户弹出明确提示 - 交易等待过程中给按钮加禁用状态,避免用户重复点击提交交易
- 可以监听链上区块变化,自动刷新用户余额,不需要等用户操作后才更新
内容的提问来源于stack exchange,提问作者Arthur
相关产品推荐
相关产品推荐

