导读:本期聚焦于苏沐橙创作的《如何将React应用迁移到Swell并接入rswETH流动性质押?》,敬请观看详情。把rswETH直接当普通ERC20余额展示,是React应用迁移到Swell时最容易犯的错误。流动性质押衍生品的关键在于状态会随协议收益自动变化,前端不能只读一次余额就固化到store里。本文围绕一个已有的React质押面板,说明如何将ETH质押入口迁移到Swell协议,并把swETH再质押为rswETH。需要调整的包括钱包连接层、合约ABI加载、质押交易构造、以及余额轮询策略。文中给出基于ethers.js的实现片段,并讨论gas限制、许可签名和错误回滚。迁移后应用可以同时展示ETH、swETH、rswETH三层资产,并通过事件监听同步解锁状态,避免用户重复授权或重复质押。

迁移一个已经上线的React质押面板到Swell + rswETH,核心不是替换几个合约地址,而是把前端对质押凭证的认知从静态余额切换为动态衍生品。原先面板可能只展示用户质押的ETH数量,以及对应的奖励累计;切换到流动性质押后,用户钱包里多出swETH和rswETH两个代币,它们的余额会随着协议收益增加或汇率变化而改变。如果前端继续沿用一次性读取ERC20余额的写法,页面数据很快会失真。

如何将React应用迁移到Swell并接入rswETH流动性质押?

调整钱包连接层与合约抽象

迁移的第一步是梳理当前React应用的钱包连接层。不少现有项目使用ethers.js v5,而Swell相关的合约交互示例更偏向ethers v6或viem。虽然不强制更换库,但v6对BigInt的原生支持能减少余额处理中的精度错误。建议在迁移前将依赖升级到ethers v6,并统一使用JsonRpcProvider或BrowserProvider作为providers。如果应用已经使用了wagmi,可以考虑直接通过useReadContract读取Swell合约,但为了减少框架耦合,本文示例统一使用ethers v6。

合约抽象方面,需要把原先的Staking合约调用替换为两个核心合约:Swell Vault负责ETH到swETH的兑换,rswETH Vault负责将swETH封装为rswETH。这两个合约的地址不要硬编码在组件里,建议放入环境变量。ABI也不要用全量,只保留前端用到的函数,减少打包体积和实例化开销。下面是一个最小ABI声明示例,包含了质押和余额查询所需的核心函数。

import { ethers } from 'ethers';

const SWELL_VAULT_ABI = [
  "function deposit() external payable returns (uint256)",
  "function getSwellETHByEth(uint256 ethAmount) external view returns (uint256)"
];

const RSWETH_ABI = [
  "function deposit(uint256 swethAmount) external returns (uint256)",
  "function balanceOf(address account) external view returns (uint256)"
];

export { SWELL_VAULT_ABI, RSWETH_ABI };

状态管理也需要分层。原先可能只有一个stakedBalance字段,迁移后至少要维护ethBalance、swethBalance和rswethBalance三个独立字段。另外swETH和rswETH虽然都符合ERC20规范,但rswETH代表的是再质押凭证,解除质押可能需要异步等待期。前端要在store中区分可即时赎回的swETH和需要等待的rswETH,避免用户把两者混为一谈,也避免出现展示余额可以提现但实际仍在锁定期的误导。

实现ETH到swETH再到rswETH的质押流程

质押流程通常分为两步。第一步用户向Swell Vault发送ETH并调用deposit,交易成功后按照当前汇率获得swETH;第二步用户将swETH授权给rswETH Vault,再调用deposit把swETH封装为rswETH。这里要特别注意第一步的deposit函数是payable的,前端发起交易时必须带上value,而不能只发data。漏掉value会导致交易看似成功但实际没有存入任何ETH,或者直接触发合约回滚。

在React组件中,可以通过一个自定义Hook来封装这两个交易。初始化合约实例后,读取用户地址,再根据输入的ETH数量计算可质押金额。用户点击质押按钮时,先调用Swell Vault的deposit,并设置合理的gasLimit。因为Swell的质押合约在不同网络上的gas消耗差异较大,建议用estimateGas动态估算,再上浮20%到30%。第一步交易成功后,等待一个区块确认再读取用户swETH余额,避免因节点未同步导致余额为0。

import { ethers } from 'ethers';
import { SWELL_VAULT_ABI, RSWETH_ABI } from './abis';

const SWELL_VAULT = import.meta.env.VITE_SWELL_VAULT_ADDRESS;
const RSWETH_VAULT = import.meta.env.VITE_RSWETH_VAULT_ADDRESS;

export function useSwellStake(signer, account) {
  const [step, setStep] = useState('idle');
  const [txHash, setTxHash] = useState('');

  async function stakeEth(ethAmount) {
    if (!signer || !account) return;
    setStep('depositing');
    try {
      const vault = new ethers.Contract(SWELL_VAULT, SWELL_VAULT_ABI, signer);
      const tx = await vault.deposit({ value: ethers.parseEther(ethAmount) });
      setTxHash(tx.hash);
      await tx.wait();
      setStep('sweth_received');
    } catch (error) {
      setStep('error');
      throw error;
    }
  }

  async function restakeSweth(swethAmount) {
    const sweth = new ethers.Contract(SWELL_VAULT, SWELL_VAULT_ABI, signer);
    const rsweth = new ethers.Contract(RSWETH_VAULT, RSWETH_ABI, signer);
    // 注意spender必须是rswETH Vault地址
    const allowance = await sweth.allowance(account, RSWETH_VAULT);
    if (allowance < ethers.parseEther(swethAmount)) {
      const approveTx = await sweth.approve(RSWETH_VAULT, ethers.MaxUint256);
      await approveTx.wait();
    }
    const tx = await rsweth.deposit(ethers.parseEther(swethAmount));
    await tx.wait();
  }

  return { stakeEth, restakeSweth, step, txHash };
}

第二步涉及ERC20授权。如果前端检测到用户对rswETH Vault的allowance不足,需要先发起approve交易。这里一个常见误区是把approve的spender写成Swell Vault,实际应该写rswETH Vault地址。完成approve后再调用rswETH Vault的deposit存入swETH。两步之间要显示明确的进度提示,不能只显示全局loading,否则用户无法判断当前是在等待ETH存款确认,还是在等待授权交易完成。

余额同步、事件监听与汇率展示

迁移到流动性质押后,余额更新策略必须改变。swETH余额在用户质押后会立即变化,但rswETH的兑换率会随着restaking收益持续增长。前端如果只在交易后手动刷新一次余额,无法反映账龄收益。因此需要设置合理的轮询间隔,同时监听ERC20的Transfer事件来触发刷新。这样当用户收到额外rswETH铸造时,页面能自动更新,而不需要用户手动刷新。

可以使用ethers的contract.on监听用户地址相关的Transfer事件。因为很多rswETH实现会在用户余额增加时发送Transfer事件,即使是非转账的收益累积也可能通过内部铸造完成。监听时要注意过滤掉历史事件,只处理最新区块。轮询方面,推荐每15秒至30秒读取一次余额,不必过于频繁。对于Layer2网络,可以利用更低延迟的RPC来缩短轮询间隔。

import { ethers } from 'ethers';

export function watchRswethBalance(contract, account, callback) {
  const filter = contract.filters.Transfer(null, account);
  contract.on(filter, (from, to, value, event) => {
    if (to.toLowerCase() === account.toLowerCase()) {
      callback(value);
    }
  });
}

展示汇率时,不要把1 swETH等同于1 ETH直接写死。Swell的汇率会变化,应该从合约读取getSwellETHByEth或类似的视图函数。rswETH与swETH之间的汇率也要单独获取。前端可以维护一个汇率缓存,在用户打开页面和余额更新时刷新。如果用户只质押少量ETH,展示时注意保留足够的小数位,避免因四舍五入产生误解。尤其在计算预期rswETH数量时,应使用BigInt运算而不是JavaScript的Number类型。

错误处理与安全边界

迁移后的React应用要处理更多合约错误。用户拒绝签名或交易、gas费高于预期、合约暂停、网络切换等场景都要有对应的UI提示。捕获错误时不能只弹出一个error.message,因为RPC返回的原始错误可能包含十六进制数据。建议解析常见的revert原因,例如用户取消时错误码是ACTION_REJECTED或消息包含user rejected。前端应该把这类情况与合约实际失败区分开,避免把用户主动取消展示成系统级故障。

在代码中,可以用try/catch包住交易调用,并针对错误码做分支。如果错误是用户拒绝,则只提示交易已取消,不显示失败状态。如果是其他错误,可以保留错误详情并展示区块浏览器链接,但正文中不提供可点击链接。安全方面,绝对不要在React代码中硬编码私钥或助记词,所有签名必须通过钱包插件完成。对于高金额质押,还可以增加交易预览和二次确认步骤。

try {
  const tx = await vault.deposit({ value: amount });
  await tx.wait();
} catch (error) {
  if (error.code === 'ACTION_REJECTED') {
    setMessage('交易已取消');
  } else if (error.message && error.message.indexOf('user rejected') !== -1) {
    setMessage('用户拒绝了签名');
  } else {
    setMessage('交易失败,请检查gas或网络');
  }
}

迁移完成后,建议增加一个只读模式,在用户未连接钱包时也能看到Swell当前的ETH/swETH汇率和rswETH总锁仓量等公开数据。这样既能方便用户决策,也能降低迁移后空状态带来的困惑。最后把交易相关按钮统一加上防重复点击、gas提示和交易哈希展示,整个应用对流动性质押衍生品的支持才算完整。通过分层管理资产状态、动态同步余额、细化错误反馈,React应用能够稳定承载Swell与rswETH的质押流程。

ReactSwellrswETH修改时间:2026-09-25 09:18:15

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