You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决DApp开发中‘Provider not available’运行时错误?

问题描述

DApp开发新手,按Infura教程用Truffle+React搭建DApp,本地服务器启动正常,但点击交互按钮时触发错误:Provider not available. Use .setProvideror.provider= to initialize the provider,无法与Storage.sol合约交互。此前相同步骤可正常运行,怀疑Web3.js方法过时,修改web3.givenProvider无效,求代码修改方案及问题原因。

相关代码

Storage.sol

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.2 <0.9.0;
contract Storage {
    string text;
    function store(string memory a) public {
        text = a;
    }
    function retrieve() public view returns (string memory ){
        return text;
    }
}

App.js(原代码)

import React, { useState } from "react";
import { Storage } from "./abi/abi";
import Web3 from "web3";
import "./App.css";

// Access our wallet inside of our dapp
const web3 = new Web3(Web3.givenProvider);
// Contract address of the deployed smart contract
const contractAddress = "0x088213adCC8F391Ae23961084A0bb888F09D669a";
const storageContract = new web3.eth.Contract(Storage, contractAddress);

function App() {
  // Hold variables that will interact with our contract and frontend
  const [number, setUint] = useState(0);
  const [getNumber, setGet] = useState("0");

  const numberSet = async (t) => {
    t.preventDefault();
    const accounts = await window.ethereum.enable();
    const account = accounts[0];
    // Get permission to access user funds to pay for gas fees
    const gas = await storageContract.methods.store(number).estimateGas();
    const post = await storageContract.methods.store(number).send({
      from: account,
      gas,
    });
  };

  const numberGet = async (t) => {
    t.preventDefault();
    const post = await storageContract.methods.retrieve().call();
    setGet(post);
  };

  return (
    <div className="main">
      <div className="card">
        <form className="form" onSubmit={numberSet}>
          <label>
            Set your uint256:
            <input
              className="input"
              type="text"
              name="name"
              onChange={(t) => setUint(t.target.value)}
            />
          </label>
          <button className="button" type="submit" value="Confirm">
            Confirm
          </button>
        </form>
        <br />
        <button className="button" onClick={numberGet} type="button">
          Get your uint256
        </button>
        {getNumber}
      </div>
    </div>
  );
}

export default App;

truffle-config.js

require("dotenv").config();
const HDWalletProvider = require("@truffle/hdwallet-provider");
const { INFURA_API_KEY, MNEMONIC } = process.env;

module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 8545,
      network_id: "*",
    },
    sepolia: {
      provider: () => new HDWalletProvider(MNEMONIC, INFURA_API_KEY),
      network_id: "11155111",
      gas: 8465030,
    },
  },
  compilers: {
    solc: {
      version: '0.8.2',
      parser: 'solcjs'
    }
  }
};
问题原因
  1. 初始化时机错误:在组件外部直接初始化Web3和合约时,Web3.givenProvider可能尚未注入(比如MetaMask还未完成连接),导致Web3实例无有效provider,合约实例也未绑定可用provider。
  2. 过时API使用:window.ethereum.enable()是Web3.js v0.x的旧API,v1.x及以上版本已废弃,需改用标准的eth_requestAccounts请求方法。
  3. 静态实例未动态更新:即使后续连接钱包,初始化阶段创建的合约实例不会自动更新provider,调用合约方法时仍无可用provider。
代码修改方案

修改后的App.js

import React, { useState, useEffect } from "react";
import { Storage } from "./abi/abi";
import Web3 from "web3";
import "./App.css";

function App() {
  // 状态管理Web3实例、合约实例、当前账户、输入值、读取结果
  const [web3, setWeb3] = useState(null);
  const [storageContract, setStorageContract] = useState(null);
  const [account, setAccount] = useState(null);
  const [inputValue, setInputValue] = useState("");
  const [getNumber, setGet] = useState("0");

  // 组件挂载后初始化Web3与合约
  useEffect(() => {
    const initWeb3 = async () => {
      if (!window.ethereum) {
        alert("请安装MetaMask或其他以太坊钱包");
        return;
      }

      try {
        // 请求账户访问权限(替代旧的enable())
        const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
        setAccount(accounts[0]);

        // 初始化Web3实例
        const web3Instance = new Web3(window.ethereum);
        setWeb3(web3Instance);

        // 绑定provider初始化合约实例
        const contractAddress = "0x088213adCC8F391Ae23961084A0bb888F09D669a";
        const contractInstance = new web3Instance.eth.Contract(Storage, contractAddress);
        setStorageContract(contractInstance);

        // 监听账户切换事件,自动更新当前账户
        window.ethereum.on('accountsChanged', (newAccounts) => {
          setAccount(newAccounts[0]);
        });
      } catch (error) {
        console.error("Web3初始化失败:", error);
      }
    };

    initWeb3();
  }, []);

  // 写入合约方法
  const numberSet = async (e) => {
    e.preventDefault();
    if (!storageContract || !account) return;

    try {
      const gas = await storageContract.methods.store(inputValue).estimateGas({ from: account });
      await storageContract.methods.store(inputValue).send({
        from: account,
        gas,
      });
      alert("数据存储成功");
    } catch (error) {
      console.error("存储失败:", error);
      alert("存储失败,请检查钱包连接或输入内容");
    }
  };

  // 读取合约方法
  const numberGet = async () => {
    if (!storageContract) return;

    try {
      const result = await storageContract.methods.retrieve().call();
      setGet(result);
    } catch (error) {
      console.error("读取失败:", error);
    }
  };

  return (
    <div className="main">
      <div className="card">
        {account ? (
          <>
            <p>当前连接账户: {account}</p>
            <form className="form" onSubmit={numberSet}>
              <label>
                输入存储内容:
                <input
                  className="input"
                  type="text"
                  name="content"
                  value={inputValue}
                  onChange={(e) => setInputValue(e.target.value)}
                />
              </label>
              <button className="button" type="submit">
                确认存储
              </button>
            </form>
            <br />
            <button className="button" onClick={numberGet}>
              获取存储内容
            </button>
            <p>存储的内容: {getNumber}</p>
          </>
        ) : (
          <p>请连接以太坊钱包...</p>
        )}
      </div>
    </div>
  );
}

export default App;

修改说明

  • 延迟初始化:通过useEffect在组件挂载后执行Web3初始化,确保钱包提供者已注入。
  • 替换过时API:用window.ethereum.request({ method: 'eth_requestAccounts' })替代废弃的enable()方法。
  • 状态化实例:将Web3、合约、账户存入React状态,保证实例动态更新。
  • 增加错误处理:添加初始化和合约调用的错误捕获,提升用户体验。
  • 监听账户切换:绑定accountsChanged事件,自动更新当前连接账户。

内容的提问来源于stack exchange,提问作者Antony Ashwin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 07:53:15