手机版
扫描查看手机站
首页 > 文章 > 加密世界 > 正文

元交易合约如何实现?智能合约开发实战:元交易(Metatransaction)系列二

时间:2025-07-02 11:43:13来源:525游

引言

上文中提到,普通的 ETH 交易并不能够做到让用户无需 gas 费,需要交易中嵌套一个交易,即元交易,来实现免 gas 费。

本文将分析开源库 OpenZeppelin/openzeppelin-contracts 中的元交易合约的实现,让你能够快速入门元交易实现细节,从而能够自己对后续更多的相关技术深入探索。

前置知识概述

元交易会涉及到 ECDSA 与 EIP712 等知识,如果你是熟手,可以跳过此节内容,直接浏览具体实现分析部分。

Hash

也称哈希、散列、数字摘要。通过哈希函数,可以将长短不一的信息转化为一段长度任意但可预测的(确定性的)结果。这是一类神奇的函数,可以将一大堆信息转变成一串短的,可作为摘要的数据 “指纹”。对于一个给定的输入而言,生成的 “指纹” 始终一致。如果你的原始数据中有任何细微的改动,生成的哈希值将大不相同。以太坊中采用的是 Keccak-256 算法。

ECDSA

在密码学中,ECDSA(Elliptic Curve Digital Signature Algorithm,椭圆曲线数字签名算法)是使用椭圆曲线密码学的数字签名算法(DSA)的一个变种。

主要用于对数据(比如一个文件)创建数字签名,以便于你在不破坏它的安全性的前提下对它的真实性进行验证。可以将它想象成一个实际的签名,你可以识别部分人的签名,但是你无法在别人不知道的情况下伪造它。

你不应该将ECDSA与用来对数据进行加密的AES(高级加密标准)相混淆。ECDSA不会对数据进行加密、或阻止别人看到或访问你的数据,它可以防止的是确保数据没有被篡改。

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

元交易合约如何实现?智能合约开发实战:元交易(Metatransaction)系列二

将原始数据通过 hash 函数得到它的 hash 值后,用户 A 用自己的私钥对该 hash 值进行签名,得到 Signature(签名)。有了该签名与 hash 值,任何人都能够从中恢复出签名人的钱包地址,在这里用户 B 则恢复得到了用户 A 的钱包地址。

EIP712
Ethereum Improvement Proposals (EIPs),你可以在这里查看所有的 EIPs。EIP712 (Ethereum typed structured data hashing and signing)以太坊类型的结构化数据哈希与签名。

如果我们只关心字节字符串的话,签名数据是一个已经解决了的问题。但不幸的是,在现实世界中,我们关心的是复杂而有意义的信息,对结构化数据进行哈希是非常重要的,错误会导致系统安全属性的丢失。

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

元交易合约如何实现?智能合约开发实战:元交易(Metatransaction)系列二

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

元交易合约如何实现?智能合约开发实战:元交易(Metatransaction)系列二

元交易合约的实现

此分析针对 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 签名验证的一部分,它在部署时,将自动获取合约的地址、chainId 等信息。意味着,即便有相同的 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,而是将 data 与 from 进行 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.sender 与 msg.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 没有足够的 ETH 支付 gas 费,来铸造一个 NFT,她可以签署一个元交易,元交易的 data 是由 abi.encodeWithSignature(functionSelector, parmas...) 得到的,将该元交易递交给具有足够 ETH 的 Bob,Bob 调用元交易合约 MinimalForwarder.execute(req, signature),从而让 Alice 的元交易成功执行。

以上就是元交易合约如何实现?智能合约开发实战:元交易(Metatransaction)系列二的详细内容,更多关于元交易合约实现的资料请关其它相关文章!


消逝的光芒荧光蘑菇怎么获得(消逝的光芒荧光蘑菇有什么用) 王者荣耀单字id怎么改(王者荣耀单字id改空白名复制) 如何理解区块链转账?用 imToken 进行转账有什么优势? Filecoin是如何存储数据的?Filecoin的价值体现和未来前景分析 第五人格演员实机操作介绍(第五人格戏剧演员) 绅士向宅男手游有哪些(绅士向手游大全) 有什么传奇手游不花自己的钱充值(什么传奇手游不充钱能玩) OKCoin交易平台App下载可靠么?OKCoin官网登录入口 虚拟币模拟交易app有哪些软件?2025虚拟币交易app排行榜 三款经典的回合制游戏(回合制游戏排行榜前十,你都知道吗?) 比特币交易在中国合法吗?比特币交易所下载官网app dnf高级炉岩碳有什么作用(dnf高级炉岩炭) 金铲铲之战蛮王主C阵容怎么玩 蛮王主C阵容攻略 方舟如何把霸王龙变成精英霸王龙(方舟手游霸王龙怎么变成精英霸王龙) 和平精英怎么掷骰子(和平精英怎么掷骰子) F2Pool鱼池还能挖吗?F2Pool-全球领先的比特币矿池 不氪金的卡牌回合制游戏有哪些(不氪金回合制手游排行榜) 王者荣耀升到15级要多久(王者荣耀升到15级需要多长时间?) 端游cf生化头上血条怎么弄的(cf生化显示血条的角色) 怎么买狗狗币和屎币?如何购买柴犬币shib和狗狗币doge? 普通人区块链怎么赚钱?2025最火的区块链项目大全 手机模拟炒币软件哪个好用?2025最新虚拟币模拟交易app下载安装 币安交易所绑定邮箱/手机安全设置操作图解教程 BTSE是什么交易所?BTSE交易所最新介绍 比特币信托基金是什么?比特币信托基金怎么运作的? CoinEx交易所究竟怎么样?CoinEx交易所靠谱吗? 大型多人在线网游有哪些(大型多人在线游戏排行) 梦幻西游手游熔炼多少次必满(梦幻西游手游熔炼多少次才能熔炼满) 区块链扩容是什么意思?区块链扩容的方式有哪些? 原神满足沙拉配方怎么获得(原神满足沙拉做好后送哪) 第五人格紫皮卡怎么分解(第五人格紫皮卡怎么用?) 数字货币交易平台有哪些?2022最新区块链数字货币交易平台排名前十名 王者荣耀保存的视频在哪看(王者荣耀保存的视频在哪查看) 王者荣耀真我杯是什么赛事(真我是什么游戏) 如何做自媒体才能赚钱?自媒体赚钱月入8000方法大揭秘 传奇霸业手游非R玩家攻略(传奇霸业手游怎么样) 王者荣耀画面设置怎么调最流畅(王者荣耀画质设置手感更好) DeFi挖矿SUN教程,使用比特派钱包bitpie参与SUN挖矿(波场系)教程 帝国战纪酒馆如何抽英雄(帝国战纪精英招募) 原神怎么查充值记录(原神如何查充值记录) 币圈集体崩盘,这两天到底发生了什么? 问道手游李靖怎么杀(问道手游李伞) STX币是什么币?STX币怎么样? 炒币的人后来都怎么样了?中国靠比特币发财的人现状 冰原守卫者地牢通关攻略(冰原守卫者手游攻略) 地下城堡3魂之诗沃夫加藏身在哪(地下城堡3沃尔加) 电脑上u盘打不开是怎么回事?竟然提示需要格式化怎么办 Exmo交易所如何提现?Exmo充值提现、充币提币使用教程 消逝的光芒2集市保险箱密码是多少(消逝的光芒2彩蛋) 和平精英巅峰赛一周可以打几次(和平精英巅峰赛一周最多升两个段位吗) MX币是什么币?MX币投资前景点评分析 蜀门手游青城技能怎么循环(蜀门手游青城后期穿什么属性装备) 全栈开发next.js 与strapi 视频课程内容总结5 同为女性向游戏(以闪亮之名是什么类型的游戏) 币安怎么交易和提现?币安交易和提现新手教程 手机版有什么攻城守城游戏(攻城类手机单机游戏) 诛仙手游玉兔几点刷新(诛仙手游玉兔怎么抓) 消逝的光芒2帧数怎么调(消逝的光芒2怎么全屏) 梦幻西游160武器有哪些(梦幻西游160武器大全带图) 欧易合约怎么玩?欧易okx合约交易做多做空操作教程 csgo跳过热身指令是什么(csgi跳过热身) 火币网币币账户划转法币账户操作流程指南 dnf灵魂之源怎么获得(dnf灵魂之源怎么获得技能) wnxm币总量多少?WNXM币发行总量和流通量介绍 炒币平台APP哪个最好?币圈目前最好的炒币软件2025排名 宝可梦传说阿尔宙斯头目会刷新吗(宝可梦阿尔宙斯在哪抓) 创造与魔法中怎么获得青青猪(创造与魔法哪里刷青青猪几率大) FIL币今日多少人民币一枚?FIL币价格今日行情实时行情历史走势 雷达币今日最新价格,雷达币价格实时走势K线图历史趋势 新年推荐四款看电视直播软件(看电视直播的软件有哪些?) 宝可梦剑盾锐利之爪能给谁进化(神奇宝贝剑盾锐利之爪) 元气骑士新版武士刀怎么玩(元气骑士武器大全刀) 蜀门手游武尊技能加点怎么加(蜀门武尊怎么玩) 魔兽世界直升110划算吗(魔兽世界直升120划算吗) 目前最受欢迎的国产仙侠手游(国内仙侠手游排行) 火币交易所还可靠吗?三个头部数字货币交易所比较下,哪家更靠谱? 腾讯微云怎么解散共享组 腾讯微云解散共享组步骤 王者荣耀怎么全部恢复默认(王者荣耀怎么全部恢复默认设置方法) 宝可梦传说阿尔宙斯闪光有什么区别(阿尔宙斯gf) 王者荣耀怎么隐藏贵族(王者荣耀怎么隐藏贵族标志2022) 方舟怎么让龙跟随(方舟怎么让龙跟随别的龙飞行) 洛克王国燃料棒怎么得(洛克王国燃烧棒怎么快速得) 和平精英人物离屏幕太近怎么设置(和平精英人物离手机太近怎么调) VB GLOBAL交易所怎么样?VB交易所安全合法吗? 王者荣耀遇见飞天是谁的皮肤(王者荣耀遇见飞天返场多少钱) mana币前景怎么样?mana币是哪个国家的 科普:闪电网络常见问题及解决方法汇总 什么软件上的游戏全是内置菜单(打开内置游戏) SPA是什么币种?SPA币前景和未来价值分析 2024炒币平台app哪个最好 虚拟币哪个app最好 欧易合约手续费怎么算的?欧易交易所合约手续费明细 视频剪辑软件选哪一款好?来看看这5款 Chia奇亚科普:Chia奇亚为什么突然爆火? 知道qq号怎么查王者荣耀昵称(知道qq号怎么知道王者名字) V神最新思考:两种途径解决PoS共识机制下的MEV问题 欧易活期挖矿可以一直挖吗?欧易挖矿怎么样? 王者荣耀怎么复制键位给另一个号(王者荣耀怎么把这个号的键位复制到另一个号?) 比特币交易网Btctrade官网介绍?btc期货交易平台Btctrade数字货币交易app下载 消逝的光芒2瞄准键怎么按(消逝的光芒2操作) 手机挖以太坊用什么软件?手机上最火3个挖矿软件

热门文章

推荐专题

更多>>

游戏推荐

更多>>