React Native DApp 开发全栈实战·从 0 到 1 系列(兑换-合约部分)

前言

本文借助 Solidity 0.8、OpenZeppelin 与 Chainlink 喂价,构建一套 链上即时汇率结算、链下可信价格驱动 的微型兑换系统。本文将带你完成:

  1. 部署可铸造 ERC-20(BoykayuriToken,BTK)
  2. 部署 Chainlink 风格喂价合约(MockV3Aggregator),本地即可模拟 ETH/USD = 2000 的实时价格
  3. 部署 SwapToken ------ 接收 ETH、按市价折算 USD、并立即向用户发放等值 BTK
  4. 使用 Hardhat 本地网络 + hardhat-deploy 插件一键启动,5 条指令完成编译、测试、部署全流程 无需前端,无需真实 LINK,即可体验 "价格输入 → 汇率计算 → 代币闪兑" 的完整闭环,为后续接入主网喂价、多币种池子、流动性挖矿奠定可复用的脚手架。

智能合约

代币合约
ini 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract SwapToken is Ownable {
    AggregatorV3Interface internal priceFeed;
    IERC20 public token;
    uint public constant TOKENS_PER_USD = 1000; // 1 USD = 1000 MTK

    constructor(address _priceFeed, address _token) Ownable(msg.sender) {
        priceFeed = AggregatorV3Interface(_priceFeed);
        token = IERC20(_token);
    }

    function swap() public payable {
        uint usd = getEthInUsd(msg.value);
        uint amount = usd * TOKENS_PER_USD;
        require(token.balanceOf(address(this)) >= amount, "Not enough liquidity");
        token.transfer(msg.sender, amount);
    }

    function getEthInUsd(uint ethAmount) public view returns (uint) {
        (, int price, , , ) = priceFeed.latestRoundData(); // price in 8 decimals
        uint ethUsd = (ethAmount * uint(price)) / 1e18; // ETH amount in USD (8 decimals)
        return ethUsd / 1e8; // return USD amount
    }

    receive() external payable {}
}
喂价合约
ini 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

contract MockV3Aggregator is AggregatorV3Interface {
    uint256 public constant versionvar = 4;

    uint8 public decimalsvar;
    int256 public latestAnswer;
    uint256 public latestTimestamp;
    uint256 public latestRound;
    mapping(uint256 => int256) public getAnswer;
    mapping(uint256 => uint256) public getTimestamp;
    mapping(uint256 => uint256) private getStartedAt;
    string private descriptionvar;

    constructor(
        uint8 _decimals,
        string memory _description,
        int256 _initialAnswer
    ) {
        decimalsvar = _decimals;
        descriptionvar = _description;
        updateAnswer(_initialAnswer);
    }

    function updateAnswer(int256 _answer) public {
        latestAnswer = _answer;
        latestTimestamp = block.timestamp;
        latestRound++;
        getAnswer[latestRound] = _answer;
        getTimestamp[latestRound] = block.timestamp;
        getStartedAt[latestRound] = block.timestamp;
    }

    function getRoundData(uint80 _roundId)
        external
        view
        override
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        )
    {
        return (
            _roundId,
            getAnswer[_roundId],
            getStartedAt[_roundId],
            getTimestamp[_roundId],
            _roundId
        );
    }

    function latestRoundData()
        external
        view
        override
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        )
    {
        return (
            uint80(latestRound),
            latestAnswer,
            getStartedAt[latestRound],
            latestTimestamp,
            uint80(latestRound)
        );
    }

    function decimals() external view override returns (uint8) {
        return decimalsvar;
    }

    function description() external view override returns (string memory) {
        return descriptionvar;
    }

    function version() external  pure override returns (uint256) {
        return versionvar;
    }
}
兑换合约
ini 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract SwapToken is Ownable {
    AggregatorV3Interface internal priceFeed;
    IERC20 public token;
    uint public constant TOKENS_PER_USD = 1000; // 1 USD = 1000 MTK

    constructor(address _priceFeed, address _token) Ownable(msg.sender) {
        priceFeed = AggregatorV3Interface(_priceFeed);
        token = IERC20(_token);
    }

    function swap() public payable {
        uint usd = getEthInUsd(msg.value);
        uint amount = usd * TOKENS_PER_USD;
        require(token.balanceOf(address(this)) >= amount, "Not enough liquidity");
        token.transfer(msg.sender, amount);
    }

    function getEthInUsd(uint ethAmount) public view returns (uint) {
        (, int price, , , ) = priceFeed.latestRoundData(); // price in 8 decimals
        uint ethUsd = (ethAmount * uint(price)) / 1e18; // ETH amount in USD (8 decimals)
        return ethUsd / 1e8; // return USD amount
    }

    receive() external payable {}
}
编译指令:npx hardhat compile

测试合约

javascript 复制代码
const { ethers } = require("hardhat");
const { expect } = require("chai");

describe("SwapToken", function () {
  let SwapToken, MockToken, MockV3Aggregator;
  let owner, user;

  beforeEach(async () => {
    [owner, user] = await ethers.getSigners();
    await deployments.fixture(["MockV3Aggregator","token","SwapToken"]);
    const MockTokenAddress = await deployments.get("MyToken");          // 存入资产        // 奖励代币(USDC)
        const MockV3AggregatorAddress = await deployments.get("MockV3Aggregator");
        const SwapTokenAddress = await deployments.get("SwapToken");
        
        MockToken = await ethers.getContractAt("MyToken", MockTokenAddress.address);
        MockV3Aggregator = await ethers.getContractAt("MockV3Aggregator", MockV3AggregatorAddress.address);
        SwapToken = await ethers.getContractAt("SwapToken", SwapTokenAddress.address);
        // 给SwapToken合约铸造资产
        await MockToken.mint(await SwapToken.getAddress(), ethers.parseEther("1000000"));
        console.log('name',await MockToken.name())
        console.log("symbol",await MockToken.symbol())
        console.log(await MockV3Aggregator.latestAnswer())
   
  });

  it("Should swap ETH for MTK", async function () {
    const ethAmount = ethers.parseEther("1"); // 1 ETH = 2000 USD = 2,000,000 MTK
    await SwapToken.connect(user).swap({ value: ethAmount });

    const balance = await MockToken.balanceOf(user.address);
    console.log(balance)
    expect(balance).to.equal(2000 * 1000); // 2,000,000 MTK
  });
});
测试指令:npx hardhat test ./test/xxxx.js

部署合约

javascript 复制代码
module.exports = async  ({getNamedAccounts,deployments})=>{
    const getNamedAccount = (await getNamedAccounts()).firstAccount;
    const secondAccount= (await getNamedAccounts()).secondAccount;
    console.log('secondAccount',secondAccount)
    const {deploy,log} = deployments;
    const MyAsset  = await deployments.get("MyToken");
    //执行MockV3Aggregator部署合约
  const MockV3Aggregator=await deploy("MockV3Aggregator",{
        from:getNamedAccount,
        args: [8,"ETH/USDC", 200000000000],//参数
        log: true,
    })
  console.log("MockV3Aggregator合约地址:", MockV3Aggregator.address);
    const SwapToken=await deploy("SwapToken",{
        from:getNamedAccount,
        args: [MockV3Aggregator.address,MyAsset.address],//参数 喂价,资产地址
        log: true,
    })
    // await hre.run("verify:verify", {
    //     address: TokenC.address,
    //     constructorArguments: [TokenName, TokenSymbol],
    //     });
    console.log('SwapToken 兑换合约地址',SwapToken.address)
}
module.exports.tags = ["all", "SwapToken"];
部署指令:npx hardhat deploy --tags token,MockV3Aggregator,SwapToken

总结

通过本文,我们完成了:

  • 价格层:MockV3Aggregator 遵循 Chainlink 接口,可无缝替换为主网喂价
  • 资产层 :ERC-20 代币自带 mintOwnable,方便快速补充流动性
  • 兑换层 :SwapToken 利用 latestRoundData() 实时计算 ETH→USD→BTK 数量,全程链上可查
  • 脚本层:Hardhat 脚本化部署 + 测试固件,保证"一键重置、秒级回滚",让迭代安全又高效

后续优化:

  • 把 Mock 喂价替换为 ETH/USD 主网聚合器
  • 引入 Uniswap V2 风格的流动性池,实现双向兑换
相关推荐
CodingBrother1 天前
ABI解析智能合约
区块链·智能合约
.刻舟求剑.1 天前
solidity得高级语法3
区块链·solidity·语法笔记
许强0xq1 天前
Ethernaut Level 1: Fallback - 回退函数权限提升攻击
区块链·solidity·foundry·ethernaut
全干engineer1 天前
区块链web3项目实战-Truffle petshop
web3·区块链
Armonia生态1 天前
Armonia Mall超级数字生态WEB3商城的引领者
web3·armonia-mall
alex1002 天前
【一天一个Web3概念】Web3.0赛道分析:新一轮技术浪潮下的机遇与挑战
web3
.刻舟求剑.3 天前
solidity的高阶语法4
区块链·solidity·语法笔记
Web3_Daisy3 天前
克隆代币 + 捆绑开盘:多链环境下的低成本发币玩法
人工智能·web3·区块链·比特币
11年老程序猿在线搬砖3 天前
DAPP智能合约系统:技术解析与实现指南
区块链·智能合约·dapp