当前位置: 首页
web3.0
以太坊元交易:免Gas费智能合约开发指南

以太坊元交易:免Gas费智能合约开发指南

热心网友 时间:2025-09-08
转载

深入解析以太坊元交易:免 Gas 费的奥秘

上文提到,以太坊普通交易无法实现用户免 Gas 费,需要通过一种特殊的交易——元交易(Meta Transaction)来实现。本文将深入分析开源库 OpenZeppelin/openzeppelin-contracts 中元交易合约的实现,帮助你快速掌握元交易的细节,并为后续更深入的技术探索打下基础。

2025年主流加密货币交易所:

前置知识

元交易涉及 ECDSA 和 EIP712 等知识。如果你已经熟悉这些概念,可以直接跳到具体实现分析部分。

Hash

Hash,也称哈希、散列或数字摘要。通过哈希函数,可以将任意长度的信息转换为一段长度固定且可预测的结果。哈希函数就像一个神奇的转换器,将大量信息变成一串简短的数据 "指纹"。对于相同的输入,生成的 "指纹" 始终一致。但如果原始数据有任何细微改动,哈希值就会截然不同。以太坊采用的是 Keccak-256 算法。

ECDSA

ECDSA(Elliptic Curve Digital Signature Algorithm,椭圆曲线数字签名算法)是使用椭圆曲线密码学的数字签名算法(DSA)的变种,用于为数据(例如文件)创建数字签名,以便在不破坏安全性的前提下验证数据的真实性。可以将它想象成一个手写签名,你可以识别某些人的签名,但无法在别人不知情的情况下伪造。

ECDSA 不会对数据进行加密或阻止他人访问你的数据,而是确保数据没有被篡改。

如图所示,在以太坊中,ECDSA 用于对原始数据的 Hash 值进行签名和恢复。

\

原始数据经过哈希函数处理后得到 Hash 值。用户 A 使用私钥对 Hash 值进行签名,生成 Signature(签名)。有了签名和 Hash 值,任何人都可以从中恢复出签名人的账户地址。例如,用户 B 可以恢复得到用户 A 的账户地址。

EIP712

EIP(Ethereum Improvement Proposals)是以太坊改进提案。EIP712(Ethereum typed structured data hashing and signing)指的是以太坊类型的结构化数据哈希与签名。

签名数据是一个已经解决的问题。但在现实世界中,我们关心的是复杂且有意义的信息,对结构化数据进行哈希至关重要,否则可能导致系统安全性降低。

EIP712 旨在提高链上使用的链下消息签名的可用性。越来越多的人采用链下消息签名,因为它节省了 Gas 费,减少了区块链上的交易数量。目前的签名消息是一个不透明的十六进制字符串,显示给用户的信息很少,缺乏组成消息的项目的上下文。

\

EIP712 概述了一种编码数据及其结构的方案,允许在签名时将数据显示给用户进行验证。下图展示了用户在签署 EIP712 消息时显示的示例。

\

元交易合约的实现

此分析基于 openzeppelin-contracts v4.3.2 版本。

contract MinimalForwarder is EIP712 {
    using ECDSA for bytes32;
struct ForwardRequest {
    address from;
    address to;
    uint256 value;
    uint256 gas;
    uint256 nonce;
    bytes data;
}

constructor() EIP712("MinimalForwarder", "0.0.1") {}

}

ECDSA 是 OpenZeppelin 实现的一个 Solidity 库,它实现了从 Hash 值中恢复账户地址的方法。将其应用在 bytes32 上,就可以直接在 bytes32 上调用 recover 方法。recover 函数签名:function recover(bytes32 hash, bytes memory signature) internal pure returns (address)

ForwardRequest 结构体定义了一个交易中用于签名的基本组成成分。与以太坊交易不同的是,它没有 gasPrice,因为智能合约的执行只关心 Gas 的消耗。ForwardRequest 中的 nonce 概念与以太坊类似,都是为了避免双花攻击,但这里的 nonce 仅由智能合约维护,与普通的以太坊交易中的 nonce 无关。

构造函数中直接使用 EIP712 的构造函数进行初始化。EIP712 的构造函数签名为:constructor(string memory name, string memory version),其中 name 是合约名称,version 是合约版本,这将作为 EIP712 签名验证的一部分。部署时,它会自动获取合约的地址和链 ID 等信息。这意味着,即使有相同的 ForwardRequest 结构体数据,但合约地址或区块链网络不同,也会导致签名无效。

mapping(address => uint256) private _nonces;

function getNonce(address from) public view returns (uint256) { return _nonces[from]; }

为了避免双花攻击,在智能合约中维护 nonce 是必要的。

bytes32 private constant _TYPEHASH = keccak256("ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,bytes data)");

function verify(ForwardRequest calldata req, bytes calldata signature) public view returns (bool) { address signer = _hashTypedDataV4(keccak256(abi.encode(_TYPEHASH, req.from, req.to, req.value, req.gas, req.nonce, keccak256(req.data)))) .recover(signature); return _nonces[req.from] == req.nonce && signer == req.from; }

verify 函数可以看出,要将账户地址恢复,至少需要经过 ECDSA 的签名以及用于签名的原始数据。此处,ECDSA 签名的原始数据就是经过 ABI 编码的 keccak256(abi.encode(_TYPEHASH, req.from, req.to, req.value, req.gas, req.nonce, keccak256(req.data))),即 ForwardRequest 结构体数据的哈希值。再通过调用 ECDSA 库中的 recover 函数,传入签名,就能够恢复得到签名者的账户地址。

通过 _nonces[req.from] == req.nonce 来确保交易的调用是顺序的,且不会遭受双花攻击。signer == req.from 避免签名者与实际元交易发送者不匹配。

接下来看如何执行元交易。

function execute(ForwardRequest calldata req, bytes calldata signature)
public
payable
returns (bool, bytes memory)
{
require(verify(req, signature), "MinimalForwarder: signature does not match request");
_nonces[req.from] = req.nonce + 1;

(bool success, bytes memory returndata) = req.to.call{gas: req.gas, value: req.value}(abi.encodePacked(req.data, req.from));

// Validate that the relayer has sent enough gas for the call.
// See https://ronan.eth.link/blog/ethereum-gas-dangers/
assert(gasleft() > req.gas / 63);

return (success, returndata);

}

在使用 Address.call 方法时,根据元交易参数,指定了 call 的 Gas 和 Value 值。需要注意的是,这里并不直接将元交易的 data 字段当作 call 操作的 data,而是将 datafrom 进行 ABI 编码后一起作为 call 操作的参数。这在目标合约(也就是 req.to)中会被解析,从而得到交易的发送者,下面会详细讲解。

assert(gasleft() > req.gas / 63) 简单理解为避免中继器(代为执行元交易的人)恶意或无意地使用足够低的 Gas,使得交易执行成功,而元交易执行失败。详情可以在 ethereum gas dangers 中学习。

ERC2771

要支持元交易,仅实现元交易智能合约是不够的,因为目标合约无法知道实际的元交易 from 是谁。如果没有额外的措施,它将只能够从 msg.sender 中获取,由于在元交易合约实现中,是通过 Address.call 调用的,因此将得到的发送者是元交易合约的地址。ERC2771 则解决了该问题。

abstract contract ERC2771Context is Context

ERC2771Context 继承了 Context,而 Context 中简单封装了从 msg.sendermsg.data,以便规范这两个功能的使用,且能够让其在子合约中修改其行为。要求使用 Context 合约获取 msg 相关的数据,而不是直接使用 msg.sender 等。

abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}

function _msgData() internal view virtual returns (bytes calldata) {
    return msg.data;
}

}

ERC2771Context 修改了 Context 合约的方法。

function _msgSender() internal view virtual override returns (address sender) {
if (isTrustedForwarder(msg.sender)) {
// The assembly code is more direct than the Solidity version using abi.decode.
assembly {
sender := shr(96, calldataload(sub(calldatasize(), 20)))
}
} else {
return super._msgSender();
}
}

先通过 isTrustedForwarder(msg.sender) 验证元交易的调用方是期望的元交易合约地址。assembly 代码将上文的元交易合约中 req.to.call{...}(abi.encodePacked(req.data, req.from)) 编码进的 data 部分内容的 req.from 获取到,然后再返回该值。

元交易使用概览

让我们尝试简单使用元交易合约。要支持元交易,你所编写的合约必须继承 ERC2771Context。这里简单实现一个 NFT 合约。在部署它之前,你必须先部署元交易合约,并将元交易合约地址作为参数传递给 NFT 合约的构造函数。

// SPDX-License-Identifier: GPL3.0
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "@openzeppelin/contracts/metatx/ERC2771Context.sol"; import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract NFT is ERC2771Context, ERC721 { using SafeMath for uint256;

uint256 private _currentTokenId = 0;

constructor(string memory name, string memory symbol, address trustedForwarder)
    ERC721(name, symbol)
    ERC2771Context(trustedForwarder)
{}

function safeMint() public virtual {
    safeMint("");
}

function safeMint(bytes memory _data) internal virtual {
    uint256 tokenId = _getNextTokenId();
    _incrementTokenId();
    _safeMint(_msgSender(), tokenId, _data);
}

function getCurrTokenId() public virtual view returns (uint256) {
    return _currentTokenId;
}

/**
 * @dev calculates the next token ID based on value of _currentTokenId
 * @return uint256 for the next token ID
 */
function _getNextTokenId() internal virtual view returns (uint256) {
    return _currentTokenId.add(1);
}

/**
 * @dev increments the value of _currentTokenId
 */
function _incrementTokenId() internal virtual {
    _currentTokenId++;
}

function _msgSender()
    internal
    view
    virtual
    override(Context, ERC2771Context)
    returns (address)
{
    return ERC2771Context._msgSender();
}

function _msgData()
    internal
    view
    virtual
    override(Context, ERC2771Context)
    returns (bytes calldata)
{
    return ERC2771Context._msgData();
}

}

在这个示例中,如果 Alice 没有足够的以太币支付 Gas 费来铸造一个 NFT,她可以签署一个元交易,元交易的 data 是由 abi.encodeWithSignature(functionSelector, parmas...) 得到的。然后,将该元交易递交给具有足够以太币的 Bob,Bob 调用元交易合约 MinimalForwarder.execute(req, signature),从而让 Alice 的元交易成功执行。

来源:https://www.jb51.net/blockchain/802369.html

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
什么是Rollux?$SYS代币经济学怎么样?核心作用是什么?

什么是Rollux?$SYS代币经济学怎么样?核心作用是什么?

Rollux与SYS代币:如何融合比特币安全与以太坊智能? 在Layer 2扩容方案竞相涌现的今天,Rollux及其原生代币$SYS提出了一个独特构想:能否将比特币无与伦比的安全性与以太坊蓬勃发展的智能合约生态合二为一?答案是肯定的。作为Syscoin生态基于OP Stack构建的Layer 2解决

时间:2026-04-06 14:54
FIL币值得投资吗?它与传统云存储服务有何不同?

FIL币值得投资吗?它与传统云存储服务有何不同?

FIL币:去中心化存储的价值内核与市场现实 说起Filecoin网络的原生代币FIL,它的核心角色很明确:为整个去中心化的存储市场提供一套激励与支付的运转血液。这个网络干了一件挺有意思的事——它用区块链技术,把全球各地闲置的硬盘空间给盘活了,整合成一个庞大的数据存储与检索市场。截至2026年3月的数

时间:2026-04-06 14:53
什么是Hyperliquid L1?HYPE币在质押和治理中起什么作用?

什么是Hyperliquid L1?HYPE币在质押和治理中起什么作用?

Hyperliquid L1:一个为极速交易而生的专用区块链 在追求中心化交易所的速度与去中心化金融的透明自主之间,市场一直在寻找一个完美的平衡点。Hyperliquid L1的出现,正是对这一核心挑战的直接回应。它并非又一个通用的公链,而是一个从底层开始,就为高速金融应用——尤其是衍生品交易——量

时间:2026-04-06 14:52
比特币测试长期支撑,多头背离酝酿筑底信号?

比特币测试长期支撑,多头背离酝酿筑底信号?

比特币回踩关键支撑区,这轮调整的“黄金坑”出现了吗? 近期,全球知名投资机构富达的全球宏观经济总监 Jurrien Timmer 在社交媒体分享的一张技术图表,引发了市场的广泛关注。图表清晰揭示,比特币价格正在 6 5 万至 7 万美元的核心区间内持续震荡。在经历了一场从 12 6 万美元高位回落至

时间:2026-04-05 18:03
ATOM币和Cosmos生态链是什么关系?ATOM币的核心作用是什么?

ATOM币和Cosmos生态链是什么关系?ATOM币的核心作用是什么?

从“区块链互联网”到价值引擎:深度解析Cosmos生态与ATOM的价值逻辑 在波谲云诡的加密世界中,Cosmos生态以其“区块链互联网”的宏大构想独树一帜。这并非停留在白皮书上的蓝图,而是通过Hub与Zone架构及革命性的IBC协议,构建出的一个繁荣的异构区块链网络。作为这个网络的核心枢纽—Cosm

时间:2026-04-05 14:53
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 日榜
  • 周榜
  • 月榜
热门教程
更多
  • 游戏攻略
  • 安卓教程
  • 苹果教程
  • 电脑教程