导读:本期聚焦于韦伯创作的《如何将React借贷应用迁移到Compound协议并集成Governance治理功能?》,敬请观看详情。将一个现有的React借贷应用迁移到Compound协议,并在此基础上集成Governance治理功能,是不少DeFi开发者会面临的需求。Compound作为以太坊上成熟的借贷协议,提供了cToken合约与利率模型,而其Governance模块则允许持有COMP代币的用户参与提案投票。本文围绕迁移的整体思路展开,先分析迁移前需要确认的技术依赖与钱包交互方案,再讲解如何在前端接入Compound合约的ABI与地址映射,包括供应、赎回、借入、还款等核心操作的代码实现,最后详细说明Governance的委托与投票流程如何嵌入React组件,并分享状态管理与gas优化的实践经验,帮助你顺利完成迁移。

Compound是以太坊生态中最具代表性的借贷协议之一,它允许用户向资金池供应资产赚取利息,也可以抵押资产借出其他代币。如果一个已经存在的React借贷应用还停留在中心化的账本逻辑上,迁移到Compound协议不仅能获得真实的市场利率和去中心化的清算机制,还能借助其Governance模块让社区参与协议参数的决策。本文将从依赖准备、合约交互、治理集成三个层面,完整梳理迁移过程中的关键步骤和容易踩到的坑。

如何将React借贷应用迁移到Compound协议并集成Governance治理功能?

迁移前的技术准备与依赖梳理

迁移的第一步不是直接改代码,而是梳理现有应用与Compound协议之间的差异。你需要明确现有借贷应用的核心功能有哪些:充值、提现、借入、还款、利息计算、清算逻辑。在Compound中,这些功能分别对应供应(mint)、赎回(redeem)、借入(borrow)、还款(repayBorrow),而利率计算和清算完全由链上合约自动完成,前端不再需要自己维护一套利率模型,这一点是迁移后最大的架构简化。

在依赖方面,推荐使用ethers.js作为Web3交互库,配合wagmi或者直接用ethers的BrowserProvider连接MetaMask等钱包。项目安装依赖如下:

npm install ethers @compound-finance/compound-js
# 或者使用wagmi构建更现代的hooks风格
npm install wagmi viem @tanstack/react-query

另一个必须提前确认的事项是网络环境。Compound V2部署在以太坊主网,同时也有以太坊测试网的历史部署记录。开发阶段建议先在本地用Hardhat或Anvil fork主网,这样可以用真实的合约状态进行调试,避免在测试网找不到完整的市场配置。fork主网的命令示例:

anvil --fork-url https://eth-mainnet.g.alchemy.com/v2/你的API_KEY --block-number 19000000

fork之后,你的本地节点上就有真实的cToken合约、Comptroller合约和COMP代币,前端只需要把RPC地址指向本地节点即可联调,这是迁移阶段效率最高的做法。

在React中接入Compound合约:地址映射与核心操作

Compound V2的核心合约包括Comptroller(风险控制与账户流动性)、各个市场的cToken(如cETH、cDAI)、PriceOracle(价格预言机)。前端需要维护一份地址映射表,建议单独抽出一个配置文件,按网络ID区分:

// src/config/compound.js
export const COMPOUND_ADDRESSES = {
  1: {
    comptroller: '0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B',
    comp: '0xc00e94Cb662C3520282E6f5717214004A7f26888',
    cEth: '0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5',
    cDai: '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643'
  },
  31337: {
    // 本地fork主网时使用主网地址
    comptroller: '0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B',
    comp: '0xc00e94Cb662C3520282E6f5717214004A7f26888'
  }
};

供应操作方面,Compound V2区分了ERC20和ETH两种路径。供应ERC20时需要先调用底层代币的approve授权给cToken合约,再调用mint;供应ETH则直接向cETH合约发送交易并附带value。下面是一个封装好的供应函数示例:

async function supplyErc20(signer, erc20Address, cTokenAddress, amount) {
  const erc20 = new ethers.Contract(
    erc20Address,
    ['function approve(address,uint256) returns (bool)'],
    signer
  );
  const cToken = new ethers.Contract(
    cTokenAddress,
    [
      'function mint(uint256) returns (uint256)',
      'function balanceOf(address) view returns (uint256)',
      'function exchangeRateCurrent() view returns (uint256)'
    ],
    signer
  );
  const tx1 = await erc20.approve(cTokenAddress, amount);
  await tx1.wait();
  const tx2 = await cToken.mint(amount);
  await tx2.wait();
}

借入操作前必须先调用Comptroller的enterMarkets,把抵押的cToken纳入流动性计算,否则账户没有借款额度。这个步骤很多迁移者会遗漏,导致borrow交易revert却查不到原因。借入和还款的实现如下:

async function enterMarketAndBorrow(signer, comptroller, cTokenCollateral, cTokenBorrow, amount) {
  const comptrollerContract = new ethers.Contract(
    comptroller,
    ['function enterMarkets(address[]) returns (uint256[])'],
    signer
  );
  const tx = await comptrollerContract.enterMarkets([cTokenCollateral]);
  await tx.wait();

  const cToken = new ethers.Contract(
    cTokenBorrow,
    ['function borrow(uint256) returns (uint256)'],
    signer
  );
  const tx2 = await cToken.borrow(amount);
  await tx2.wait();
}

状态管理上,建议用自定义hooks把链上数据查询和写操作分开。例如写一个useAccountLiquidityhook,轮询Comptroller的getAccountLiquidity返回账户可用借款额度,写一个useMarketMetadatahook读取每个市场的供应利率、借款利率和抵押因子。这样组件层只消费hooks返回的数据,切换到wagmi或重构时影响面也更小。

集成Governance:COMP委托与投票的前端实现

Compound的治理体系围绕COMP代币展开,核心流程是:持有者将投票权委托(delegate)给自己或他人,社区成员创建提案(propose),在延迟期后进入投票窗口,得票超过法定门槛且支持票多于反对票的提案进入时间锁(Timelock)排队执行。前端集成Governance主要涉及三块功能:投票权委托、提案列表展示、投票操作。

委托操作是参与治理的前提,注意COMP合约的委托有三种:delegate委托给指定地址,delegateBySig支持链下签名委托。前端实现一个简单的委托组件:

async function delegateVotes(signer, compAddress, delegatee) {
  const comp = new ethers.Contract(
    compAddress,
    [
      'function delegates(address) view returns (address)',
      'function delegate(address)',
      'function getCurrentVotes(address) view returns (uint96)'
    ],
    signer
  );
  const current = await comp.delegates(await signer.getAddress());
  if (current.toLowerCase() === delegatee.toLowerCase()) {
    console.log('已经委托给该地址');
    return;
  }
  const tx = await comp.delegate(delegatee);
  await tx.wait();
}

提案的读取需要遍历Governor Bravo合约的事件。通过ProposalCreated事件可以拿到提案ID、提案人、目标和调用数据,再结合state方法判断提案处于Pending、Active、Succeeded还是Executed阶段:

async function loadProposals(provider, governorAddress) {
  const governor = new ethers.Contract(
    governorAddress,
    [
      'event ProposalCreated(uint256,address,address[],uint256[],string[],bytes[],uint256,uint256,string)',
      'function state(uint256) view returns (uint8)',
      'function castVote(uint256,uint8)'
    ],
    provider
  );
  const filter = governor.filters.ProposalCreated();
  const events = await governor.queryFilter(filter, 18000000);
  return Promise.all(events.map(async (e) => ({
    id: e.args.proposalId,
    proposer: e.args.proposer,
    description: e.args.description,
    state: await governor.state(e.args.proposalId)
  })));
}

投票操作调用castVote,参数中1表示支持,0表示反对。需要注意的是,投票权快照在提案创建时已经确定,提案创建后才委托的投票权对当前提案无效,UI层面应该根据用户的快照票数给出提示。另外Governor Bravo的投票期只有几天,前端要做好倒计时展示,用proposal.endBlock结合出块时间估算截止时间。

迁移后的优化建议与常见问题

迁移完成后,性能和用户体验上还有几个值得投入的点。第一,合约调用合并:页面加载时如果逐个请求市场数据,几十次RPC往返会让首屏很慢,建议用Multicall3合约把多个读请求打包成一次调用,配合React Query的缓存策略,可以把首屏数据加载时间压缩到原来的三分之一左右。

第二,错误提示要针对链上revert做翻译。Compound合约的错误都通过返回码表达而不是revert字符串,例如mint返回非零值就代表失败,前端应根据返回码映射出可读的提示,比如抵押因子不足、市场未上市等,而不是给用户抛一个原始错误。第三,gas估算失败时给出预检,在提交交易前先调用callStatic模拟执行,能提前暴露大部分问题,避免用户白白支付失败的gas费。

最后是治理数据的链下索引。如果应用需要展示历史投票记录、提案讨论等,纯靠RPC查询事件会很吃力,可以考虑用The Graph subgraph或者自建索引服务,把Governance事件落库后通过GraphQL提供给前端。这样治理页面的加载体验会有质的提升,也为后续做提案分析、代表画像等高级功能打下基础。整个迁移过程本质上是把应用从自建账本转向消费链上协议状态,前端的职责更多变成合约状态的呈现与交易编排,这也是DeFi前端开发的主流范式。

ReactCompound协议去中心化治理修改时间:2026-09-03 17:57:11

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260903/49718.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。