前言
基于《 React Native DApp 开发全栈实战·从 0 到 1 系列(收益聚合器-合约部分)》,本文进入“前端交互”环节,把已经部署在 Hardhat Local 上的 多授权代币 + 收益聚合器 搬到浏览器里:用 MetaMask 点火,用 ethers.js 操纵,完成 存、取、份额、喂价、权限 五大核心场景的实时调用。
前期准备
- hardhat启动网络节点:npx hardhat node
- 合约编译:npx hardhat compile 生成对应的xxx.json用获取abi等相关信息
- 合约部署:npx hardhat deploy --tags token3,token4,MockV3Aggregator,YieldAggregator 获取合约地址(资产代币、奖励代币、喂价和收益聚合器合约地址)
- 节点的私钥导入钱包:用来与合约交互时支付对应的gas费
核心代码
-
公共代码
import { abi as MockV3AggregatorABI } from '@/abi/MockV3Aggregator.json';
import { abi as MyTokenABI1 } from '@/abi/MyToken1.json';
import { abi as MyTokenABI3 } from '@/abi/MyToken3.json';
import { abi as YieldAggregatorABI } from '@/abi/YieldAggregator.json';
import * as ethers from 'ethers';
-
场景1(首次存入正确铸造份额=》二次存入按比例铸造份额 =》提取后份额与资产减少)
const aggregatorFn=async ()=>{
const provider = new ethers.providers.Web3Provider(window.ethereum);
await provider.send('eth_requestAccounts', []); // 唤起钱包
const signer = await provider.getSigner();
const userAddr = await signer.getAddress();//当前用户地址
const YieldAggregatorAddress="0x0E801D84Fa97b50751Dbf25036d067dCf18858bF"//聚合器地址
const MockV3AggregatorAddress="0x998abeb3E57409262aE5b751f60747921B33613E"//MockV3Aggregator地址
const MyAssetAddress="0x4826533B4897376654Bb4d4AD88B7faFD0C98528" //资产
const MyAwardAddress="0x99bbA657f2BbC93c02D617f8bA121cB8Fc104Acf"//奖励
const YieldAggregatorContract = new ethers.Contract(YieldAggregatorAddress, YieldAggregatorABI, signer);
const MockV3AggregatorContract = new ethers.Contract(MockV3AggregatorAddress, MockV3AggregatorABI, signer);
const MyAssetContract = new ethers.Contract(MyAssetAddress, MyTokenABI1, signer);//资产
const MyAwardContract = new ethers.Contract(MyAwardAddress, MyTokenABI3, signer);//奖励
console.log(YieldAggregatorContract,MockV3AggregatorContract,MyAssetContract,MyAwardContract)
const DEPOSIT_AMOUNT = ethers.utils.parseUnits("100", 18); // 100 个 token(18 位)
const WITHDRAW_SHARES = ethers.utils.parseUnits("30", 18); // 100 个 token(18 位)
/* ---------- 1. mint + approve ---------- */
// 如果 asset 是 ERC20 且用户余额不足,我们先给他 mint(测试 token 才有)
const mintTx = await MyAssetContract.mint(userAddr, DEPOSIT_AMOUNT);
await mintTx.wait();
const approveTx = await MyAssetContract.approve(YieldAggregatorAddress, DEPOSIT_AMOUNT);
await approveTx.wait();
/* ---------- 2. 首次 deposit ---------- */
const depositTx = await YieldAggregatorContract.deposit(DEPOSIT_AMOUNT);
await depositTx.wait();
/* ---------- 3. 打印结果 ---------- */
const userShares = await YieldAggregatorContract.shares(userAddr);
const totalShares= await YieldAggregatorContract.totalShares();
const totalAssets= await YieldAggregatorContract.totalAssetsDeposited();
console.log("首次存入后用户份额 :", ethers.utils.formatUnits(userShares, 18));
console.log("首次存入后份额总量 :", ethers.utils.formatUnits(totalShares, 18));
console.log("首次存入后资产总量 :", ethers.utils.formatUnits(totalAssets, 18));
//提取资产
const withdrawTx = await YieldAggregatorContract.withdraw(WITHDRAW_SHARES);
await withdrawTx.wait();
//验证
const userSharesAfter = await YieldAggregatorContract.shares(userAddr);
const totalSharesAfter= await YieldAggregatorContract.totalShares();
const totalAssetsAfter= await YieldAggregatorContract.totalAssetsDeposited();
console.log("提取资产后用户份额 :", ethers.utils.formatUnits(userSharesAfter, 18));
console.log("提取资产后份额总量 :", ethers.utils.formatUnits(totalSharesAfter, 18));
console.log("提取资产后资产总量 :", ethers.utils.formatUnits(totalAssetsAfter, 18));
}
-
场景2(无法提取超过自身份额、 rescue 只能 owner 调用 、getUserAssetValue 计算正确)
-
场景3 获取喂价
const ethPrice=await YieldAggregatorContract.getETHPrice()
console.log("ethPrice:",ethers.utils.formatUnits(ethPrice,8))
效果图
总结
-
一条命令跑通全流程
npx hardhat node→compile→deploy --tags→ 私钥导入钱包,30 秒完成链端准备。 -
三大场景一次封装
- 首次存入 1:1 铸造份额
- 二次存入按比例增发
- 提取后份额 & 资产同步减少
外加 超额提取 revert、owner rescue、实时 ETH 价格 等边界校验,全部在前端用Contract.method+wait()实现,与单元测试断言一一对应。
-
直接可迁移到生产
代码已抽离公共signer/contract初始化逻辑,只需把地址、ABI 换成正式网络,即可无缝接入 React / React Native 项目;UI 层只需包裹按钮与 Loading,即可完成「链上收益聚合器」的完整交互闭环。
至此,合约端 + 前端双端闭环全部打通,恭喜你拥有了一套可扩展、可复用、可上架的 DeFi 迷你收益聚合器 DApp!