年轻人的第二个智能合约:V我50

下面,我们将一起在 Remix 上编写、部署和测试年轻人的第二个智能合约,该合约实现了V我50的功能:

  • 其它人可以给合约转账;
  • 可以查询每个人的转账金额;
  • 合约创建人可以提取所有金额;

合约概览

合约分为两个部分,一个是合约的主体,另外一个是合约主体调用的库。

合约主体

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.24;

import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import {PriceConverter} from "./PriceConverter.sol";

error NotOwner();

contract V50ToMe {
    using PriceConverter for uint256;

    mapping(address => uint256) public addressToAmountFunded;
    address[] public funders;

    address public /* immutable */ i_owner;
    uint256 public constant MINIMUM_CNY = 50 * 10 ** 18;
    
    constructor() {
        i_owner = msg.sender;
    }

    function V50() public payable {
        require(msg.value.getConversionRate() >= MINIMUM_CNY, "You need to spend more ETH!");
        addressToAmountFunded[msg.sender] += msg.value;
        funders.push(msg.sender);
    }
    
    function getVersion() public view returns (uint256){
        AggregatorV3Interface priceFeed = AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306);
        return priceFeed.version();
    }

    function getEthPrinceInCNY(uint256 ethAmount) public view returns (uint256){
        return ethAmount.getConversionRate();
    }
    
    modifier onlyOwner {
        if (msg.sender != i_owner) revert NotOwner();
        _;
    }
    
    function withdraw() public onlyOwner {
        for (uint256 funderIndex=0; funderIndex < funders.length; funderIndex++){
            address funder = funders[funderIndex];
            addressToAmountFunded[funder] = 0;
        }
        funders = new address[](0);
  
        (bool callSuccess, ) = payable(msg.sender).call{value: address(this).balance}("");
        require(callSuccess, "Call failed");
    }

    fallback() external payable {
        V50();
    }

    receive() external payable {
        V50();
    }

}

调用的库

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

library PriceConverter {

    uint256 public constant USD_TO_CNY_RATE = 7.22861955 * 10 ** 18;

    function getPrice() internal view returns (uint256) {
        // Sepolia ETH / USD Address
        // https://docs.chain.link/data-feeds/price-feeds/addresses
        AggregatorV3Interface priceFeed = AggregatorV3Interface(
            0x694AA1769357215DE4FAC081bf1f309aDC325306
        );
        (, int256 answer, , , ) = priceFeed.latestRoundData();
        // ETH/USD rate in 18 digit
        return uint256(answer * 10000000000);
    }

    function getConversionRate(
        uint256 ethAmount
    ) internal view returns (uint256) {
        uint256 ethPrice = getPrice();
        uint256 ethAmountInUsd = (ethPrice * ethAmount * USD_TO_CNY_RATE) / 1000000000000000000 / 10**18;
        // the actual ETH/USD conversion rate, after adjusting the extra 0s.
        return ethAmountInUsd;
    }
}

代码详解

合约主体

首先,我们来看合约主体部分。这段Solidity智能合约实现了一个简单的众筹合约,使用Chainlink预言机获取以太坊的实时价格,并以此来确定用户发送的ETH是否等值于或超过50人民币(CNY)的最低限额。下面是合约的详细解释:

合约声明和导入

  • pragma solidity ^0.8.24; 指定了合约兼容的Solidity编译器版本。
  • 通过**import语句引入了Chainlink的AggregatorV3Interface接口和一个名为PriceConverter**的本地库,后者用于将ETH转换为等值的CNY金额。

状态变量和错误声明

  • **addressToAmountFunded**映射记录了每个资助者地址及其资助的金额(以ETH计)。
  • **funders**数组存储了所有资助者的地址。
  • i_owner变量存储了合约的所有者地址。注释中提到的/* immutable */是被注释掉的,意味着在这个版本中i_owner不是不可变的,但实际上,将其设置为不可变(immutable)是个好做法,因为它在构造函数中被赋值且之后不会改变。
  • **MINIMUM_CNY**常量定义了资助的最低人民币金额,这里设定为50 CNY,考虑到以太坊和人民币的换算比例,此值需根据实时汇率进行ETH转换。

构造函数

  • 在部署时,构造函数会设置**i_owner**为部署合约的地址。

功能函数

  • V50:允许用户发送ETH作为资助。通过调用**PriceConverter库的getConversionRate**函数(这个函数应该是将ETH转换为CNY的金额),判断发送的ETH是否满足最小50 CNY的要求。满足条件的话,用户的地址和发送的金额会被记录。
  • getVersion:调用Chainlink预言机以获取其版本信息,这是一个验证预言机是否成功接入的辅助函数。
  • getEthPrinceInCNY:接受一个以太坊金额(ethAmount),返回其等值的人民币金额。实际上这个函数名字可能有误,应该是**getEthPriceInCNY**。
  • onlyOwner:一个修饰符,用于限制某些函数只能由合约的所有者调用。
  • withdraw:允许合约的所有者提取合约中的所有ETH。在提取之前,它会重置所有资助者的资助金额记录,并清空**funders**数组。

收款函数

  • fallbackreceive:这两个特殊的函数允许合约接收ETH,并在收到ETH时自动调用**V50**函数来处理资助逻辑。

主要逻辑和流程

  1. 用户调用**V50**函数发送ETH,函数内部检查发送的ETH是否达到了50 CNY的最低要求。
  2. 如果达到要求,用户的地址和资助金额会被记录;否则,交易将被拒绝。
  3. 合约所有者可以随时调用**withdraw**函数,提取合约中累积的所有ETH。
  4. 任何发送到合约地址的ETH(不通过特定函数调用)都会触发**fallbackreceive函数,进而调用V50**来处理资助逻辑。

调用的库

这段Solidity代码定义了一个名为**PriceConverter**的库,该库提供了与Chainlink预言机交互的功能,用于获取以太坊(ETH)相对于美元(USD)的实时价格,并将特定数量的ETH转换成等值的人民币(CNY)金额。这里是详细的解释:

状态变量

  • **USD_TO_CNY_RATE是一个常量,代表美元兑换成人民币的汇率。这里假设1美元等于7.22861955人民币,并且这个比率被乘以了10**18**以适应Solidity的无小数点数值处理方式。这意味着所有的货币值都假定为拥有18位小数,这是以太坊中的标准做法。

函数

getPrice

  • 这个函数调用Chainlink预言机来获取当前ETH相对于USD的市场汇率。
  • 它首先实例化一个**AggregatorV3Interface对象,使用的是Sepolia测试网络上的ETH/USD价格预言机地址。通过这个对象,函数调用latestRoundData**方法来获取最新的汇率数据。
  • 返回的汇率**answer被乘以10000000000**(10亿),这是为了将其从Chainlink的标准格式(通常是8位小数)转换为18位小数的格式,以匹配Solidity和以太坊智能合约中常用的货币单位处理方式。

getConversionRate

  • 这个函数接受一个以ETH为单位的金额(ethAmount),并返回该金额按当前ETH/USD汇率和USD/CNY汇率转换后的CNY价值。
  • 首先,它调用**getPrice**函数来获取当前的ETH/USD汇率。
  • 然后,它使用这个汇率和传入的ETH金额,以及**USD_TO_CNY_RATE**常量来计算ETH对应的CNY价值。
  • 计算时,首先将ETH价格乘以ETH金额,再乘以USD到CNY的转换率,最后将结果除以**10**18两次。第一次除法是为了抵消ETH金额乘以ETH价格带来的额外的10**18倍数,第二次除法是为了抵消将USD转换为CNY带来的10**18**倍数。

这个库通过Chainlink预言机提供的ETH/USD汇率,以及一个固定的USD/CNY汇率,计算出一个给定量的ETH等于多少CNY。这个过程涉及到将链上获取的实时数据(ETH/USD汇率)与链下定义的静态数据(USD/CNY汇率)相结合,以实现跨货币的价值转换。

USD/CNY 的汇率在测试网络 Sepolia 上是没有查询的接口的,因此**USD_TO_CNY_RATE**在这个库中是硬编码的,这意味着如果实际汇率发生变化,合约代码需要进行相应的更新和重新部署以反映这一变化。在实际应用中,可能需要定期更新这一汇率,或者通过某种机制动态获取它,以保证转换的准确性。

本文由博客一文多发平台 OpenWrite 发布!

  • 18
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值