打破壁垒:如何用 Wrapped Token 解决原生资产与 ERC-20 的 “语言不通”

  • 木西
  • 发布于 1天前
  • 阅读 59

前言本文将系统梳理WrappedToken(包装代币)的核心理论与实操流程。主要内容涵盖:核心概念:深入浅出地解释包装代币是什么,以及它如何解决原生资产与ERC-20标准之间的“语言不通”痛点。应用场景:探讨包装代币在各类DeFi协议(如DEX、借贷平台)中的关

前言

本文将系统梳理 Wrapped Token(包装代币)  的核心理论与实操流程。

主要内容涵盖:

  • 核心概念:  深入浅出地解释包装代币是什么,以及它如何解决原生资产与 ERC-20 标准之间的“语言不通”痛点。
  • 应用场景:  探讨包装代币在各类 DeFi 协议(如 DEX、借贷平台)中的关键作用。
  • 风险与注意事宜:  分析使用和实现包装代币时需要警惕的潜在风险和安全细节。
  • 代码实践:  提供完整的 WETH(Wrapped ETH)智能合约实现(基于 Solidity 0.8.24 和 OpenZeppelin V5),并配套详尽的开发、测试(使用 viem 和 Hardhat)、以及潜在的部署流程代码。

通过理论结合 WETH 的直观代码运行逻辑,旨在帮助读者全面掌握包装代币的设计原理与工程实践。

一、定义与核心机制

包装代币是与原生资产 1:1 挂钩的映射资产,通过锁定原生资产(托管或智能合约锁仓)在目标链发行对应代币,赎回时销毁包装代币并释放原生资产。

  1. 核心特征

    • 1:1 价值锚定:与原生资产严格 1:1 抵押,保障价值一致。
    • 跨链 / 跨标准映射:突破原生资产的链与标准限制,如 BTC 包装为 ERC - 20 的 WBTC、ETH 包装为 ERC - 20 的 WETH。
    • 双向兑换:可随时赎回原生资产,销毁对应包装代币。
    • 发行模式:分为托管型(如 WBTC 由 BitGo 托管)和非托管型(如 Ren 协议通过智能合约自动锁定发行)。
  2. 常见示例

    • WBTC:比特币在以太坊的 ERC - 20 包装代币,主流托管型跨链资产。
    • WETH:以太坊原生 ETH 的 ERC - 20 包装代币,适配 DeFi 协议的 ERC - 20 交互需求。
    • renBTC:基于 Ren 协议的非托管型比特币跨链包装代币。

二、包装代币解决的核心问题

  1. 跨链互操作性缺失:不同区块链因共识、节点、资产标准差异,原生资产无法直接跨链流通,包装代币通过映射实现跨链价值传递,是早期跨链的主流方案。例如 BTC 无法直接在以太坊使用,WBTC 可让 BTC 进入以太坊 DeFi 生态。
  2. 原生资产与合约标准不兼容:部分公链原生资产(如 ETH)非 ERC - 20 标准,难以适配多数 DeFi 协议,包装为 ERC - 20 代币(如 WETH)可解决兼容性问题。
  3. 资产流动性割裂:原生资产多闲置在原生链,包装后能跨生态流动,激活资产流动性,提升资金使用效率。如 BTC 包装后可参与以太坊借贷、流动性挖矿等。
  4. 链性能与成本优化:利用高 TPS、低成本链的优势,如用 WBTC 在以太坊进行低成本、快速交易,同时保留 BTC 的价格敞口。

三、主要使用场景

  1. DeFi 生态核心应用

    • 借贷与抵押:作为借贷协议(如 MakerDAO、Aave)的抵押品,如 WBTC 在以太坊借贷平台获取贷款。
    • 流动性挖矿:在 DEX(如 Uniswap)提供包装代币流动性,获取手续费与奖励,如 WBTC - ETH 流动性池。
    • 去中心化交易:用于跨链资产兑换,降低跨链交易摩擦。
  2. NFT 与 Web3 生态拓展

    • NFT 支付:部分 NFT 市场仅支持 ERC - 20 代币支付,WETH 可解决 ETH 原生资产支付限制。
    • 链上资产组合:包装代币可与 NFT、其他通证组合,用于合成资产、收益聚合等协议。
  3. 跨链资产转移与价值传递

    • 跨链转账:通过包装 - 赎回流程实现资产跨链转移,比原生跨链更灵活,适配多链生态。
    • 多链资产管理:用户无需切换钱包和链,在单一链管理多链资产,提升管理效率。
  4. 中心化金融(CeFi)与交易场景

    • 交易所跨链交易对:提供包装代币交易对,丰富交易品种,如 WBTC / USDT、WETH / BTC 等。
    • 降低交易成本:在低成本链使用包装代币交易,规避原生链拥堵与高手续费问题。

四、风险与注意事项

  • 托管风险:托管型包装代币依赖第三方托管机构,存在资产挪用风险。
  • 智能合约风险:非托管型依赖合约安全,合约漏洞可能导致资产损失。
  • 流动性与赎回风险:极端行情下可能出现赎回延迟、流动性不足问题。

    智能合约开发、测试、部署流程

    智能合约

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

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";

/**

  • @title WrappedEther (WETH)
  • @notice 实现 ETH 与 ERC20 的 1:1 兑换,并支持 EIP-2612 Permit 离线授权。 */ contract WrappedEther is ERC20, ERC20Permit { // 兼容传统 WETH 的事件 event Deposit(address indexed dst, uint256 wad); event Withdrawal(address indexed src, uint256 wad);

    /**

    • @dev 初始化代币名称与符号,同时初始化 Permit 域分割符。 */ constructor() ERC20("Wrapped Ether", "WETH") ERC20Permit("Wrapped Ether") {}

    /**

    • @dev 存入原生 ETH。
    • 逻辑:增加合约 ETH 余额,同时为调用者铸造相同数量的 WETH 代币。 */ function deposit() public payable { _mint(msg.sender, msg.value); emit Deposit(msg.sender, msg.value); }

    /**

    • @dev 销毁 WETH 提取原生 ETH。
    • @param wad 提取的数量(单位:wei)。
    • 逻辑:销毁调用者的 WETH 代币,并将对应 ETH 发送回调用者。 */ function withdraw(uint256 wad) public { require(balanceOf(msg.sender) >= wad, "WETH: Burn amount exceeds balance"); _burn(msg.sender, wad);

      (bool success, ) = msg.sender.call{value: wad}(""); require(success, "WETH: ETH transfer failed");

      emit Withdrawal(msg.sender, wad); }

    /**

    • @dev 允许用户直接向合约地址发送 ETH 触发包装逻辑。 */ receive() external payable { deposit(); }

    /**

    • @dev 处理未定义的合约调用。 */ fallback() external payable { deposit(); }

    /**

    • @dev OpenZeppelin V5 默认 18 位精度,符合 ETH 规范。 */ function decimals() public view virtual override returns (uint8) { return 18; } }
### 测试脚本
**测试说明**:
* **WETH代币的初始化参数验证**;
* **存款逻辑测试: 原生 ETH -> WETH**;
* **提款逻辑测试: WETH -> 原生 ETH**;
* **Permit 离线签名授权测试**

import assert from "node:assert/strict"; import { describe, it, beforeEach } from "node:test"; import hre from "hardhat"; import { parseEther } from "viem";

describe("WrappedEther Permit Test", async function () { // 1. 在 describe 顶级通过异步连接获取 viem 和 helpers // 这是 Hardhat v3 的标准写法 const { viem, networkHelpers } = await hre.network.connect(); let owner: any, otherAccount: any; let publicClient: any; // let testClient: any; let WrappedEther: any; let TokenLocker: any; let deployerAddress: 0x${string}; // const lockTime = 3600n;

beforeEach(async function () {
    // 使用从 connect() 拿到的 viem 实例
    publicClient = await viem.getPublicClient();
    // testClient = await viem.getTestClient();

    [owner, otherAccount] = await viem.getWalletClients();
    deployerAddress = owner.account.address;

    // 部署合约
    WrappedEther = await viem.deployContract("WrappedEther", []);
    console.log("WrappedEther地址:",WrappedEther.address);
});
// 测试用例1:初始化参数验证
it("初始化参数验证", async function () {
    console.log(await WrappedEther.read.name());
    console.log(await WrappedEther.read.symbol());
    console.log(await WrappedEther.read.decimals());
    console.log(await WrappedEther.read.totalSupply());
    console.log(await WrappedEther.read.balanceOf([deployerAddress]));
});
it("存款逻辑测试: 原生 ETH -> WETH", async function () {
    const depositAmount = parseEther("2.5");

    // 记录操作前的 ETH 余额
    const ethBalanceBefore = await publicClient.getBalance({ address: deployerAddress });
    console.log("操作前 ETH 余额:", ethBalanceBefore);
    // 执行存款
    const hash = await WrappedEther.write.deposit({ 
        value: depositAmount,
        account: owner.account 
    });
    console.log("存款交易哈希:", hash);

    // 验证 WETH 余额
    const wethBalance = await WrappedEther.read.balanceOf([deployerAddress]);
    console.log("操作后 WETH 余额:", wethBalance);
    assert.equal(wethBalance, depositAmount, "WETH 余额应等于存款金额");

    // 验证合约内 ETH 锁定数量
    const contractEthBalance = await publicClient.getBalance({ address: WrappedEther.address });
    console.log("合约内 ETH 余额:", contractEthBalance);
    assert.equal(contractEthBalance, depositAmount, "合约持有的 ETH 应等于总存款数量");
});
it("提款逻辑测试: WETH -> 原生 ETH", async function () {
    const amount = parseEther("1.0");
    // 记录操作前的 ETH 余额
    const ethBalanceBefore = await publicClient.getBalance({ address: deployerAddress });
    console.log("操作前 ETH 余额:", ethBalanceBefore);
    // 1. 先存入 ETH
    await WrappedEther.write.deposit({ value: amount, account: owner.account });

    // 2. 执行提款
    await WrappedEther.write.withdraw([amount], { account: owner.account });

    // 3. 验证余额销毁
    const wethBalance = await WrappedEther.read.balanceOf([deployerAddress]);
    console.log("操作后 WETH 余额:", wethBalance);
    assert.equal(wethBalance, 0n, "提款后 WETH 余额应为 0");
    // 4. 验证合约 ETH 减少
    const contractEthBalance = await publicClient.getBalance({ address: WrappedEther.address });
    console.log("操作后合约内 ETH 余额:", contractEthBalance);
    assert.equal(contractEthBalance, 0n, "提款后合约内不应留存 ETH");
});
it("Permit 离线签名授权测试", async function () {
    const value = parseEther("1");
     const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600); // 1小时后过期
     // 1. 获取当前 Nonce 和 ChainId
    const nonce = await WrappedEther.read.nonces([owner.account.address]);
    const chainId = BigInt(await publicClient.getChainId());
    // 2. 构造 EIP-712 签名数据 (Viem 核心逻辑)
    const domain = {
    name: "Wrapped Ether",
    version: "1",
    chainId: chainId,
    verifyingContract: WrappedEther.address,
    };
    const types = {
        Permit: [
            { name: "owner", type: "address" },
            { name: "spender", type: "address" },
            { name: "value", type: "uint256" },
            { name: "nonce", type: "uint256" },
            { name: "deadline", type: "uint256" },
        ],
        };
        // 3. 生成签名
const signature = await owner.signTypedData({
  account: owner.account,
  domain,
  types,
  primaryType: "Permit",
  message: {
    owner: owner.account.address,
    spender: otherAccount.account.address,
    value: value,
    nonce: nonce,
    deadline: deadline,
  },
});

// 4. 拆解签名 (r, s, v)
const { r, s, v } = parseSignature(signature);
// 5. 提交 permit 交易 (由 otherAccount 提交,实现无 gas 授权)
await WrappedEther.write.permit([
  owner.account.address,
  otherAccount.account.address,
  value,
  deadline,
  Number(v),
  r,
  s,
]);

// 6. 验证 allowance
const allowance = await WrappedEther.read.allowance([owner.account.address, otherAccount.account.address]);
console.log(allowance,value);
assert.equal(allowance, value);
});

}); // 辅助函数:解析签名 function parseSignature(signature: 0x${string}) { const r = 0x${signature.substring(2, 66)} as 0x${string}; const s = 0x${signature.substring(66, 130)} as 0x${string}; const v = BigInt(0x${signature.substring(130, 132)}); return { r, s, v }; }

### 部署脚本

// scripts/deploy.js import { network, artifacts } from "hardhat"; async function main() { // 连接网络 const { viem } = await network.connect({ network: network.name });//指定网络进行链接

// 获取客户端 const [deployer] = await viem.getWalletClients(); const publicClient = await viem.getPublicClient();

const deployerAddress = deployer.account.address; console.log("部署者的地址:", deployerAddress); // 加载合约 const WETHArtifact = await artifacts.readArtifact("WrappedEther");

// 部署(构造函数参数:recipient, initialOwner) const hash = await deployer.deployContract({ abi: WETHArtifact.abi,//获取abi bytecode: WETHArtifact.bytecode,//硬编码 args: [],//process.env.RECIPIENT, process.env.OWNER });

// 等待确认并打印地址 const tokenReceipt = await publicClient.waitForTransactionReceipt({ hash }); console.log("合约地址:", tokenReceipt.contractAddress);

}

main().catch(console.error);


# 结语
至此,我们已经完成了从**理论梳理**到**代码实战**的完整闭环。

本文不仅深入探讨了包装代币(Wrapped Token)的**核心原理**、**解决痛点**及**安全风险**,更基于最新的 **Solidity 0.8.24** 与 **OpenZeppelin V5** 标准,手把手实现了一个具备 **Permit 离线签名**功能的生产级 **WETH 合约**。通过配套的 **Viem 测试脚本**,我们直观地验证了资产包装、提取及授权的运行逻辑。

无论你是想理解 DeFi 底层资产的演进,还是寻找可落地的工程实践指南,希望这份全景式的梳理能为你提供清晰的参考。
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
木西
木西
0x5D5C...2dD7
江湖只有他的大名,没有他的介绍。