chainlink 小实战 web3 “捐助我”项目合约及前端交互——关于 《Patrick web3 course Lesson 7-8 》课程代码中文详解

这篇具有很好参考价值的文章主要介绍了chainlink 小实战 web3 “捐助我”项目合约及前端交互——关于 《Patrick web3 course Lesson 7-8 》课程代码中文详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

FundMe lesson 的 示例 本质上是一个合约上对 eth 接收和发送的演示,但这个演示增加了前端 ethers 的交互,以及对 chainlink 预言机喂价的使用。

总体来说是一些 Defi 项目上的某一块功能的缩影,不过总体来说还是挺简单的。

若不会 chainlink 喂价的使用可以去看我这篇文 《预言机chainlink的使用——喂价、VRF》。

本节内容完全参照 Patrick 在课程中的代码,其课程链接为:https://github.com/smartcontractkit/full-blockchain-solidity-course-js

一、library 解读

我们先从合约块开始讲,毕竟合约可以脱离前端而单独存在,也就是一个 api,写好后可以简单的进行测试。

1.1 library 库简单了解

其中 library 库代码如下:
//价格转化器 主要是调用 chainlink 的喂价

//价格转化器 主要是调用 chainlink 的喂价
library PriceConverter {
    //传入一个 AggregatorV3Interface 的对象,AggregatorV3Interface 是chainlink 中的 interface 对象
    function getPrice(AggregatorV3Interface priceFeed)internal view returns (uint256){
        //得到交易对价
        (, int256 answer, , , ) = priceFeed.latestRoundData();
        // 整数位单位得到每个 eth 价钱
        return uint256(answer/100000000);//answer * 10000000000
    }

    // 1000000000
    // call it get fiatConversionRate, since it assumes something about decimals
    // It wouldn't work for every aggregator
    function getConversionRate(uint256 ethAmount, AggregatorV3Interface priceFeed)internal view returns (uint256){
        //传入 AggregatorV3Interface 对消得到当前的 eth 价
        uint256 ethPrice = getPrice(priceFeed);
        //价钱单价乘上数量,由于是 wei 单位,直接除个 10 的18次方使其为总的 eth 个数价钱
        uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1000000000000000000;
        // the actual ETH/USD conversation rate, after adjusting the extra 0s.
        return ethAmountInUsd;
    }
}

虽然以上的代码已经写了注释,但是在这里还是需要解释一下。

库 PriceConverter 主要是用于对 chainlink 预言机进行价格获取,获取的交易对价格是 eth 与 usd,这个库中有俩个方法:

  • getPrice
  • getConversionRate

其中 getConversionRate 调用了 getPrice 获取价格。

1.2 getPrice 方法

getPrice 方法接收一个参数 AggregatorV3Interface 类型的 priceFeed,AggregatorV3Interface 就是我们需要获取交易对价钱的预言机类型,而 priceFeed 你可以理解为这个类型的对象(虽然说我们在传值的时候给的是地址)。

那么,接下来只需要使用 priceFeed 调用 latestRoundData 即可获得交易对价钱(不会的去看我上面提到的那篇文)。

接下来有关 eth 与 usd 交易对的价钱我跟原本的做了一下改变,本来是:

获取之后由于位数的问题(主要是接下来需要通过这个值转化为 wei,与wei 的值做对比),所以在这里乘上一个值并做返回:return uint256(answer * 10000000000);

然后我改成了 return uint256(answer/100000000); 这样就刚好得到了对应美元上的价钱(chainlink 返回价钱的单位是啥一下子忘记了,这是我测试出来的结果)。

这样就完成了一个取价的方法。

1.3 getConversionRate

在 getConversionRate 方法中,主要是对传入过来的 eth 数量 与 单价 相乘,最后除 10 的 18 次方,这样就得到了捐赠者捐赠的总额 eth 的美元总价:uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1000000000000000000;,其中 uint256 ethPrice = getPrice(priceFeed); 是获取单价,priceFeed 则是你获取什么交易对的地址,在 https://docs.chain.link/docs/data-feeds/price-feeds/addresses/ 中可以查看得到:
chainlink 小实战 web3 “捐助我”项目合约及前端交互——关于 《Patrick web3 course Lesson 7-8 》课程代码中文详解

二、fundme 合约解读

2.1 状态变量

在 fundme 合约中状态变量给与 uint256 “附带方法”:using PriceConverter for uint256;,该作用是为了使 uint256 的值可以直接通过 点 “.” 运算符操作 library 方法(不会的可以去看看 library for 的用法)。

接着我改写了 最小捐赠美元数:uint256 public constant MINIMUM_USD =1;,在此我直接设置为 1 美元起就可以捐赠。

接着创建了一个可声明后再初始化的 immutable 修饰“常量”:address private immutable i_owner;,以及用于记录未提现捐助人、捐赠记录、预言机接口的状态变量,如下所示:

//把这个库给到一个 uint256类型,方便操作
using PriceConverter for uint256;

// State variables
//最少 1 个 usd
uint256 public constant MINIMUM_USD =1;
//immutable 可以在声明或构造函数中再初始化
address private immutable i_owner;
//未提现的捐赠记录捐赠人
address[] public s_funders;
//捐赠记录
mapping(address => uint256) private s_addressToAmountFunded;
// chainlink 预言机 的 interface 接口对象
AggregatorV3Interface private s_priceFeed;

2.2 方法

2.2.1 onlyOwner

接下来在代码中定义了一个 Modifiers,方便做权限设置:

// Modifiers 前置操作
modifier onlyOwner() {
    // require(msg.sender == i_owner);
    if (msg.sender != i_owner) revert FundMe__NotOwner();
    _;
}

此方法的作用是,如果不是 owner 那就不给提现,毕竟随便来个人提现就出问题了。

2.2.2 constructor 构造函数

构造函数接收一个 priceFreed,这个参数就是在部署合约时,对合约进行传入一个你用 chainlink 预言机喂价的地址,并且设置 owner 为 msg.sender:

constructor(address priceFeed) {
    s_priceFeed = AggregatorV3Interface(priceFeed);//初始化预言机
    i_owner = msg.sender;//设置 owner
}

2.2.3 fund 主要的捐赠方法

fund 方法时一个接收 eth 的方法,捐赠就是使用该方法。

在该方法中使用了 payable 使其能够接收调用方法时 附带 的 eth,并且设置了最小捐赠额。

检查捐赠额时使用了require:

require(
   //由于在上面写了 PriceConverter for uint256 所以可以进行操作 getConversionRate
   //接着传入 s_priceFeed ,判断此时的价钱是否大于 最小金额的设置 MINIMUM_USD
   msg.value.getConversionRate(s_priceFeed) >= MINIMUM_USD,
   "You need to spend more ETH!"
);

在 require 中能够直接使用 msg.value 调用 getConversionRate 的原因是在这个合约中,直接使用了 library for 的方式给与了 uint256 类型附带了 getConversionRate 方法所在的 library;此时msg.value.getConversionRate(s_priceFeed) 所得到的就是捐赠者所捐赠的金额总数,那么这个总数只需要大于我设置的最小金额数即可。

接着就直接把当前捐赠地址和金额记录在捐赠记录和未提现的数组之中:

//记录一下哪个地址捐赠了多少钱 捐赠记录
s_addressToAmountFunded[msg.sender] += msg.value;
//把当前这个人添加到 s_funders 捐赠人数组之中
s_funders.push(msg.sender);

该方法完整代码:

function fund() public payable {
   require(
       //由于在上面写了 PriceConverter for uint256 所以可以进行操作 getConversionRate
       //接着传入 s_priceFeed ,判断此时的价钱是否大于 最小金额的设置 MINIMUM_USD
       msg.value.getConversionRate(s_priceFeed) >= MINIMUM_USD,
       "You need to spend more ETH!"
   );
   // require(PriceConverter.getConversionRate(msg.value) >= MINIMUM_USD, "You need to spend more ETH!");
   //记录一下哪个地址捐赠了多少钱
   s_addressToAmountFunded[msg.sender] += msg.value;
   //把当前这个人添加到 s_funders 捐赠人数组之中
   s_funders.push(msg.sender);
}

2.2.4 withdraw 提现方法

提现方法也比较简单,直接 for 循环设置捐赠记录里面的人余额记录清空,并且清空未体现记录的数组 s_funders,该方法代码如下:

//提现方法 onlyOwner Modff
function withdraw() public onlyOwner {
    //从捐赠人数组里面进行循环
    for (uint256 funderIndex = 0;funderIndex < s_funders.length;funderIndex++) {
        //找到当前的捐赠地址
        address funder = s_funders[funderIndex];
        //设置捐赠人的 map 余额为0
        s_addressToAmountFunded[funder] = 0;
    }
    //设置捐赠人数组的值为0
    s_funders = new address[](0);
    // Transfer vs call vs Send
    // payable(msg.sender).transfer(address(this).balance);
    //调用 call 方法转账提现当前合约的全部的捐赠
    (bool success, ) = i_owner.call{value: address(this).balance}("");
    require(success);
}

循环的逻辑是循环 未提现记录的数组 s_funders,从中取到地址,给与到 s_addressToAmountFunded 对应地址的记录捐赠为 0,最后s_funders 清空,并且 使用 i_owner 调用 call 去转账当前合约的所有余额,其中 address(this).balance 就表示当前合约的所有余额,并且转账给 i_owner,这样就完成了提现操作。

2.2.5 withdraw 提现方法的改动

由于我个人不是特别理解 Patrick 为什么要这么做,所以我自己写了一个方法:

//我自己写的提现方法 主要是捐赠记录不归零
//我自己写的提现方法 主要是捐赠记录不归零
function customWithdraw()public onlyOwner{
    s_funders = new address[](0);
    (bool success, ) = i_owner.call{value: address(this).balance}("");
    require(success);
}

直接对应的删除未体现的记录数组内容,随后直接提现所有余额即可,这样也减少了状态变量的改动,并且不用 for 循环,也省 gas。

最后剩下的几个方法都是比较简单的了,在此不再赘述,完整合约代码如下(我自己添加了一些,并且为了方便直接 library 放一起了):

// SPDX-License-Identifier: MIT
// 1. Pragma
pragma solidity ^0.8.7;
// 2. Imports
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

//价格转化器 主要是调用 chainlink 的喂价
library PriceConverter {
    //传入一个 AggregatorV3Interface 的对象,AggregatorV3Interface 是chainlink 中的 interface 对象
    function getPrice(AggregatorV3Interface priceFeed)internal view returns (uint256){
        //得到交易对价
        (, int256 answer, , , ) = priceFeed.latestRoundData();
        // 整数位单位得到每个 eth 价钱
        return uint256(answer/100000000);//answer * 10000000000
    }

    // 1000000000
    // call it get fiatConversionRate, since it assumes something about decimals
    // It wouldn't work for every aggregator
    function getConversionRate(uint256 ethAmount, AggregatorV3Interface priceFeed)internal view returns (uint256){
        //传入 AggregatorV3Interface 对消得到当前的 eth 价
        uint256 ethPrice = getPrice(priceFeed);
        //价钱单价乘上数量,由于是 wei 单位,直接除个 10 的18次方使其为总的 eth 个数价钱
        uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1000000000000000000;
        // the actual ETH/USD conversation rate, after adjusting the extra 0s.
        return ethAmountInUsd;
    }
}


    // 3. Interfaces, Libraries, Contracts
    //自定义错误
    error FundMe__NotOwner();

/**@title A sample Funding Contract
 * @author Patrick Collins
 * @notice This contract is for creating a sample funding contract
 * @dev This implements price feeds as our library
 */
contract FundMe {
    // Type Declarations 
    //把这个库给到一个 uint256类型,方便操作
    using PriceConverter for uint256;

    // State variables
    //最少 1 个 usd
    uint256 public constant MINIMUM_USD =1;
    //immutable 可以在声明或构造函数中再初始化
    address private immutable i_owner;
    //未提现的捐赠记录捐赠人
    address[] public s_funders;
    //捐赠记录
    mapping(address => uint256) private s_addressToAmountFunded;
    // chainlink 预言机 的 interface 接口对象
    AggregatorV3Interface private s_priceFeed;

    // Events (we have none!)

    // Modifiers 前置操作
    modifier onlyOwner() {
        // require(msg.sender == i_owner);
        if (msg.sender != i_owner) revert FundMe__NotOwner();
        _;
    }

    // Functions Order:
     constructor
     receive
     fallback
     external
     public
     internal
     private
     view / pure

    constructor(address priceFeed) {
        s_priceFeed = AggregatorV3Interface(priceFeed);//初始化预言机
        i_owner = msg.sender;//设置 owner
    }

    /// @notice Funds our contract based on the ETH/USD price
    //根据以太币/美元价格为我们的合同提供资金
    function fund() public payable {
        require(
            //由于在上面写了 PriceConverter for uint256 所以可以进行操作 getConversionRate
            //接着传入 s_priceFeed ,判断此时的价钱是否大于 最小金额的设置 MINIMUM_USD
            msg.value.getConversionRate(s_priceFeed) >= MINIMUM_USD,
            "You need to spend more ETH!"
        );
        // require(PriceConverter.getConversionRate(msg.value) >= MINIMUM_USD, "You need to spend more ETH!");
        //记录一下哪个地址捐赠了多少钱
        s_addressToAmountFunded[msg.sender] += msg.value;
        //把当前这个人添加到 s_funders 捐赠人数组之中
        s_funders.push(msg.sender);
    }

    //提现方法 onlyOwner Modff
    function withdraw() public onlyOwner {
        //从捐赠人数组里面进行循环
        for (uint256 funderIndex = 0;funderIndex < s_funders.length;funderIndex++) {
            //找到当前的捐赠地址
            address funder = s_funders[funderIndex];
            //设置捐赠人的 map 余额为0
            s_addressToAmountFunded[funder] = 0;
        }
        //设置捐赠人数组的值为0
        s_funders = new address[](0);
        // Transfer vs call vs Send
        // payable(msg.sender).transfer(address(this).balance);
        //调用 call 方法转账提现当前合约的全部的捐赠
        (bool success, ) = i_owner.call{value: address(this).balance}("");
        require(success);
    }

    //这个便宜的提现方法,没有用到过多的状态遍历
    function cheaperWithdraw() public onlyOwner {
        address[] memory funders = s_funders;//这里是 memory
        // mappings can't be in memory, sorry!
        for (uint256 funderIndex = 0;funderIndex < funders.length;funderIndex++) {
            address funder = funders[funderIndex];
            s_addressToAmountFunded[funder] = 0;
        }
        s_funders = new address[](0);
        // payable(msg.sender).transfer(address(this).balance);
        (bool success, ) = i_owner.call{value: address(this).balance}("");
        require(success);
    }

    //我自己写的提现方法 主要是捐赠记录不归零
    function customWithdraw()public onlyOwner{
        s_funders = new address[](0);
        (bool success, ) = i_owner.call{value: address(this).balance}("");
        require(success);
    }

    /** @notice Gets the amount that an address has funded
     *  @param fundingAddress the address of the funder
     *  @return the amount funded
     */
     //获取捐助人的钱
    function getAddressToAmountFunded(address fundingAddress)public view returns (uint256){
        return s_addressToAmountFunded[fundingAddress];
    }
    //chainlink 预言机的版本
    function getVersion() public view returns (uint256) {
        return s_priceFeed.version();
    }

    //查看
    function getFunder(uint256 index) public view returns (address) {
        return s_funders[index];
    }
    //owner
    function getOwner() public view returns (address) {
        return i_owner;
    }
    //返回 AggregatorV3Interface 预言机对象
    function getPriceFeed() public view returns (AggregatorV3Interface) {
        return s_priceFeed;
    }
    //返回当前 eth/usd价钱
    function getETHUSEDPrice() public view returns(int){
        (
            /*uint80 roundID*/,
            int price,
            /*uint startedAt*/,
            /*uint timeStamp*/,
            /*uint80 answeredInRound*/
        )=s_priceFeed.latestRoundData();
        return price/100000000;
    }
}

三、前端解读

这里需要对 metamask 操作,我写了另外一篇放在外面了,放一起内容太多不好看,地址是:https://i1bit.blog.csdn.net/article/details/127349452

3.1一般操作

前端代码主要解释 js 代码,html 内容是很基础的,所以就不再赘述。

在 js 代码中,首先引入对应的 ethers 和一个合约地址:

import { ethers } from "ethers";
//合约地址
export const contractAddress = "0xe65d94905f5BFaa0Ec382e8652d4E39E41E83205";

接着是 abi,abi 太长在此不在贴出,不过在此介绍另外一种使用函数签名的方式编写 abi(只需要将 函数声明放过来就ok了,构造函数若没有参数则不需要添加,用啥接口就添加啥 abi):

//函数签名的方式 编写abi
export const abi = [
    "constructor(address priceFeed)",
    "function fund() public payable",
    "function withdraw() public onlyOwner",
    "function customWithdraw()public onlyOwner",
];

接着获取 html 代码中的所有元素:

//获取元素对象
const connectButton = document.getElementById("connectButton")
const withdrawButton = document.getElementById("withdrawButton")
const fundButton = document.getElementById("fundButton")
const balanceButton = document.getElementById("balanceButton")

再接着为此绑定事件:

//绑定事件
connectButton.onclick = connect
withdrawButton.onclick = withdraw
fundButton.onclick = fund
balanceButton.onclick = getBalance

3.2 connect 链接钱包

接着是 connect 方法,connect 方法中 if (typeof window.ethereum !== "undefined") 是判断当前浏览器是否安装了 metamask 插件,对 metamask 的操作可以参考 metamask 文档:https://docs.metamask.io/guide/#why-metamask

接着是一个 try catch 的异常检测:

try {
   //连接账户
   await ethereum.request({ method: "eth_requestAccounts" });
} catch (error) {
   console.log(error);
}

其中代码 await ethereum.request({ method: "eth_requestAccounts" }); 为通过 ethereum.request 对metamask 进行链接,在一般情况下若 webpage 没有执行链接 metamask,将会显示 未连接状态:
chainlink 小实战 web3 “捐助我”项目合约及前端交互——关于 《Patrick web3 course Lesson 7-8 》课程代码中文详解
接着更改提示,更改提示完毕后再请求一下 eth_accounts 获得当前选择账户的地址:

//更改提示
connectButton.innerHTML = "Connected";
//得到当前用户的 address
const accounts = await ethereum.request({ method: "eth_accounts" });
console.log(accounts);

3.3 提现 withdraw

withdraw 方法我们主要看判断 metamask 后的代码,因为大致都与 connect 相同的。

首先查看:

const provider = new ethers.providers.Web3Provider(window.ethereum);

此时 ethers.providers.Web3Provider 表示直接链接现有的 web3 网络,此时指定的是 window.ethereum,你可以理解为 metamask 所提供的网络。

接着通过 await provider.send('eth_requestAccounts', []); 发送一个请求链接的请求,也就是代码 await ethereum.request({ method: "eth_requestAccounts" });

此时你可能想,为什么这里直接用 provider 就可以了?我们可以直接看源码,右键到实现:
chainlink 小实战 web3 “捐助我”项目合约及前端交互——关于 《Patrick web3 course Lesson 7-8 》课程代码中文详解

此时找到了 send 方法:
chainlink 小实战 web3 “捐助我”项目合约及前端交互——关于 《Patrick web3 course Lesson 7-8 》课程代码中文详解
此时发现这个 方法属于 Web3Provider 类中,并且查看构造函数:
chainlink 小实战 web3 “捐助我”项目合约及前端交互——关于 《Patrick web3 course Lesson 7-8 》课程代码中文详解
之前我们有使用 Web3Provider 指定一个 web3 网络,指定的是 metamask 所提供的网络,之前的 window.ethereum 也表示 metamask 的网络,这点我们要明白。

接着查看使用 send 后调用的是 jsonRpcFetchFunc 方法:
chainlink 小实战 web3 “捐助我”项目合约及前端交互——关于 《Patrick web3 course Lesson 7-8 》课程代码中文详解
此时看这里就知道了这个 jsonRpcFetchFunc 就是 window.ethereum :
chainlink 小实战 web3 “捐助我”项目合约及前端交互——关于 《Patrick web3 course Lesson 7-8 》课程代码中文详解
那么当然可以这样写了:await provider.send('eth_requestAccounts', []);

接着调用 provider.getSigner(); 获取当前选中的账户并且当做操作合约的账户:

//当前登录的账户(只有一个被选择)
const signer = provider.getSigner();
//当做钱包用
const contract = new ethers.Contract(contractAddress, abi, signer);

最后我们直接通过这个 contract 合约的“对象”操作自己的方法提现即可(此时使用的是我自己写的 customWithdraw):

try {
    //调用合约方法提现
    const transactionResponse = await contract.customWithdraw();
    await listenForTransactionMine(transactionResponse, provider);
    // await transactionResponse.wait(1)
} catch (error) {
    console.log(error);
}

接着剩下的其他代码都很类似了,也都是获取账户,然后调用合约,本质上没什么区别,所以就不再赘述了。

3.4 判断是否确认完毕 listenForTransactionMine

接下来查看 listenForTransactionMine 的 Promise内的代码。

provider.once 主要是等待一次,监听交易是否完成,原型如下(找了半天 once 接口,都没查到,去文档也没看见,竟然眼花了):

once(eventName: EventType, listener: Listener)

代码如下:

provider.once(transactionResponse.hash, (transactionReceipt) => {
    console.log(
        `Completed with ${transactionReceipt.confirmations} confirmations. `
    )
    resolve();
});

此时等待交易完成,transactionResponse.hash 得到了那么就结束 once。

once 文档说明为:https://docs.ethers.io/v5/api/providers/provider/

带注释的说明代码如下(abi 我改掉了,建议用原版的):文章来源地址https://www.toymoban.com/news/detail-432214.html

import { ethers } from "ethers";

//合约地址
export const contractAddress = "0xe65d94905f5BFaa0Ec382e8652d4E39E41E83205";
//函数签名的方式 编写abi
export const abi = [
    "constructor(address priceFeed)",
    "function fund() public payable",
    "function withdraw() public onlyOwner",
    "function customWithdraw()public onlyOwner",
];

//获取元素对象
const connectButton = document.getElementById("connectButton")
const withdrawButton = document.getElementById("withdrawButton")
const fundButton = document.getElementById("fundButton")
const balanceButton = document.getElementById("balanceButton")
//绑定事件
connectButton.onclick = connect
withdrawButton.onclick = withdraw
fundButton.onclick = fund
balanceButton.onclick = getBalance
//链接 metamask
async function connect() {
    //判断是否安装 metamask
    if (typeof window.ethereum !== "undefined") {
        try {
            //连接账户
            await ethereum.request({ method: "eth_requestAccounts" });
        } catch (error) {
            console.log(error);
        }
        //更改提示
        connectButton.innerHTML = "Connected";
        //得到当前用户的 address
        const accounts = await ethereum.request({ method: "eth_accounts" });
        console.log(accounts);
    } else {
        connectButton.innerHTML = "Please install MetaMask";
    }
}

//调用合约的提现方法
async function withdraw() {
    console.log(`Withdrawing...`);
    if (typeof window.ethereum !== "undefined") {
        //ethers.providers.Web3Provider 连接到现有的 web3 网络提供者
        const provider = new ethers.providers.Web3Provider(window.ethereum);
        //向web3 网络发送 eth_requestAccounts api 请求
        await provider.send('eth_requestAccounts', []);
        //当前登录的账户(只有一个被选择)
        const signer = provider.getSigner();
        //当做钱包用
        const contract = new ethers.Contract(contractAddress, abi, signer);

        try {
            //调用合约方法提现
            const transactionResponse = await contract.customWithdraw();
            await listenForTransactionMine(transactionResponse, provider);
            // await transactionResponse.wait(1)
        } catch (error) {
            console.log(error);
        }
    } else {
        withdrawButton.innerHTML = "Please install MetaMask";
    }
}

//捐赠
async function fund() {
    const ethAmount = document.getElementById("ethAmount").value;

    console.log(`Funding with ${ethAmount}...`);
    if (typeof window.ethereum !== "undefined") {

        const provider = new ethers.providers.Web3Provider(window.ethereum);
        const signer = provider.getSigner();

        const contract = new ethers.Contract(contractAddress, abi, signer);

        try {
            //传入捐赠的 eth
            const transactionResponse = await contract.fund({
                value: ethers.utils.parseEther(ethAmount),
            })
            await listenForTransactionMine(transactionResponse, provider);
        } catch (error) {
            console.log(error);
        }
    } else {
        fundButton.innerHTML = "Please install MetaMask";
    }
}

async function getBalance() {
    if (typeof window.ethereum !== "undefined") {
        const provider = new ethers.providers.Web3Provider(window.ethereum);
        try {
            const balance = await provider.getBalance(contractAddress);
            console.log(ethers.utils.formatEther(balance));
        } catch (error) {
            console.log(error);
        }
    } else {
        balanceButton.innerHTML = "Please install MetaMask";
    }
}

function listenForTransactionMine(transactionResponse, provider) {
    console.log(`Mining ${transactionResponse.hash}`);
    return new Promise((resolve, reject) => {
        try {
            provider.once(transactionResponse.hash, (transactionReceipt) => {
                console.log(
                    `Completed with ${transactionReceipt.confirmations} confirmations. `
                )
                resolve();
            });
        } catch (error) {
            reject(error);
        }
    })
}

到了这里,关于chainlink 小实战 web3 “捐助我”项目合约及前端交互——关于 《Patrick web3 course Lesson 7-8 》课程代码中文详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • WEB3 创建React前端Dapp环境并整合solidity项目,融合项目结构便捷前端拿取合约 Abi

    好 各位 经过我们上文 WEB3 solidity 带着大家编写测试代码 操作订单 创建/取消/填充操作 我们自己写了一个测试订单业务的脚本 没想到运行的还挺好的 那么 今天开始 我们就可以开始操作我们前端 Dapp 的一个操作了 在整个过程中 确实是没有我们后端的操作 或者说 我们自己就

    2024年02月07日
    浏览(63)
  • 区块链合约交互 web3

    遵循ERC20规则, 钱包里可以有很多种类型的token:USDT,DFI,DFA 1.合约交互之前需要先判断是否仍允许purchasePool合约从账户(钱包里的账户) 中提取的ERC20dfaContract 这本合约里(DFA)的金额 await this.contract.ERC20dfaContract.methods.allowance(this.address,this.addressMap.AcceleratePool).call(); ERC20df

    2024年02月07日
    浏览(63)
  • python 之 web3 与智能合约的交互、编译等使用

    一、背景 web3.py是一个用于与以太坊交互的 Python 库。 它常见于去中心化应用程序 (dapps)中,帮助发送交易、与智能合约交互、读取块数据以及各种其他用例。 最初的 API 源自Web3.js Javascript API,但后来不断发展以满足 Python 开发人员的需求和物质享受。 本人在合约审计于模糊

    2024年02月11日
    浏览(36)
  • 使用web3.js连接Metamask钱包并完成合约交互

    Web3.js是一个用于与以太坊网络进行交互的JavaScript库。Metamask是一个以太坊钱包插件,可以在浏览器中创建和管理以太坊账户。本文将介绍如何使用web3.js库连接Metamask钱包,并完成与智能合约的交互。 Web3.js是一个用于与以太坊网络进行交互的JavaScript库。它提供了一些方法和功

    2024年03月16日
    浏览(52)
  • Java与智能合约交互(Web3j)- write函数

    说在前头 Web3是一种新兴的网络概念,由于某些原因导致我们能够接触到的相关技术知识实在有限,每当我遇见技术瓶颈总是不能找到充足的资料,这也让我萌生了填补这片空白知识的冲动。 “Hello Web3” 这个专栏会尽力将我掌握的web3 知识分享给大家。如果分享的知识能帮助

    2023年04月08日
    浏览(73)
  • 【区块链】走进web3的世界-合约交互中的异常/边界处理

    在以太坊智能合约中,异常处理是一个非常重要的问题,因为任何一个函数调用都有可能导致异常。常见的异常包括函数调用失败、无效参数、内部错误等。         在 Solidity 中,可以使用 require、assert 和 revert 等来处理异常。这些可以用于检查输入参

    2024年02月09日
    浏览(43)
  • 使用nodejs和web3js实现链接metamask钱包并实现合约交互

    在以太坊区块链上,metamask钱包是一个非常常用的钱包,用以管理以太币和其他以太坊资产。同时,它也是一个重要的以太坊智能合约交互工具。在本文中,我们将介绍如何使用nodejs和web3js实现链接metamask钱包并实现合约交互。 在开始之前,首先需要安装NodeJS和Web3JS。 NodeJS是

    2024年02月04日
    浏览(90)
  • Web3 React项目Dapp获取智能合约对象

    上文Web3 整理React项目 导入Web3 并获取区块链信息中,我们在react搭建的dapp中简单拿到了我们区块链中的账号授权信息 那 我们继续 先终端运行 将ganache环境起起来 然后 我们运行 dapp 拿到授权列表 回到上文结束的一个状态 然后 我们发布一下自己的智能合约 在我们的项目终端

    2024年02月06日
    浏览(51)
  • 【区块链 | 预言机】从零开始使用Chainlink预言机(2)- 智能合约中使用更安全的随机数-代码实战

    Chainlink最近推出一款革命性的产品,VRF—Verifiable Random Function可验证随机数,给智能合约带来了真正安全的随机数。本文我们就来介绍一下如何在智能合约中使用VRF吧。 我们先简要介绍一下Chainlink VFR的工作流程。 首先,智能合约应用,也就是我们的Dapp,需要先发起一个获取

    2024年02月02日
    浏览(52)
  • 给Web3应用新增区块链数据(Web3项目一实战之六)

    当您在浏览首页以卡片形式来展示区块链列表数据(Web3项目一实战之五)时,不禁发问,既然首页读取了 区块链 列表数据,那么,那些列表数据总得在哪个页面进行添加进去吧!绝不会凭空冒出这许多数据的。 没错,如您所想,也正是您所猜测的那般, DApp 与传统 App 一样

    2024年02月05日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包