首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >通过链下签名授权实现更少 Gas 的 ERC20代币

通过链下签名授权实现更少 Gas 的 ERC20代币

作者头像
Tiny熊
发布于 2020-09-29 02:14:47
发布于 2020-09-29 02:14:47
3.5K00
代码可运行
举报
运行总次数:0
代码可运行

  • 译文出自:登链翻译计划[1]
  • 译者:Tiny熊[2]

解锁消耗到了大量的 gas

每个人都在谈论 “无gas” 的以太坊交易,因为没有人喜欢支付gas费用。但是以太坊网络的运行正是因为交易是付费的。那么,你怎么才能“无gas”交易呢? 这是什么法术?

在本文中,我将展示如何使用 “无 gas” 交易背后的模式。你会发现,尽管以太坊没有免费的午餐之类的东西,但是你可以通过有趣的方式改变 gas 成本。

通过运用本文中的知识,你的用户将节省大量 gas,享受更好的用户体验,甚至可以在你的智能合约中构建新颖的委派模式。

可是等等!还有更多!为方便起见,我将所需的所有工具都放在了此存储库[3]中。因此,现在你实现 “无 gas” 代币的障碍就突然降低了很多。

让我们开始吧。

背景

我不得不承认,即使我知道如何在智能合约中实现“无 gas”交易,但对于使它们成为可能的密码学我也知之甚少。那对我来说不是障碍,所以对你也不应该是。

据我所知,私钥用于签署发送给以太坊的交易,一些密码学魔术用于将我(签名者)识别为msg.sender。这支撑了以太坊中所有访问控制。

“无 gas” 交易背后的法宝是,我可以使用我的私钥和要执行的智能合约交易进行签名。

签名是在链下进行的,而无需花费任何 gas。然后,我可以将此签名交给其他人,以他们的名义代表我执行交易。

签名函数通常就是常规合约方法,但会使用其他签名参数进行扩展。例如,在dai.sol[4]中,我们有授权(approve)函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function approve(address usr, uint wad) external returns (bool)

我们还具有permit许可函数,该功能与approve函数相同,但是将签名作为参数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s) external

不用担心所有这些额外的参数,我们将介绍它们。你需要注意的是这两个函数都使用allowance映射执行的操作:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function approve(address usr, uint wad) external returns (bool)
{
  allowance[msg.sender][usr] = wad;}

function permit(
  address holder, address spender,
  uint256 nonce, uint256 expiry, bool allowed,
  uint8 v, bytes32 r, bytes32 s
) external {
  …
  allowance[holder][spender] = wad;}

如果使用approve,则允许spender最多使用wad个代币。

如果你给某人提供有效的签名,则该人可以调用permit以允许spender 使用你的代币。

因此,基本上,“无 gas”交易背后的模式是制作可以提供给某人的签名,以便他们可以安全地执行特殊交易。这就像授予某人执行函数的权限。

这是一种授权模式。

标准

如果你像我一样,那么你要做的第一件事就是深入研究代码。我立即注意到此注释:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// — — EIP712 niceties — -

有了这个,我钻进了兔子洞[5],却无望地迷路了。现在,我已经理解了,我可以用简单的方式来解释它。

EIP712[6]描述了如何以通用方式构建函数签名。其他EIP描述了如何将EIP712[7]应用。例如,EIP2612[8]描述了如何使用EIP712[9]的签名应用于permit函数,其功能应与ERC20代币中的approve功能相同。

如果你只想实现之前提到的签名功能,例如将签名批准添加到自己的MetaCoin,则可以阅读EIP2612[10] ,你甚至可以继承实现过的合约,并减轻生活压力。

在本文中,我们将研究dai.sol[11]中“无 gas”交易的实现。这将使事情变得清晰。dai.sol[12]实现发生在EIP2612[13]之前,会略有不同。那不会有问题。

签名组成

EIP712[14]签名的早期实现可以在dai.sol[15]源码中找到 。它允许Dai持有人通过计算链下签名并将其提供给支出者(spender)来批准转账交易,而不是自己调用approve函数。

它包含下面几个部分:

  1. 一个 DOMAIN_SEPARATOR .
  2. 一个 PERMIT_TYPEHASH .
  3. 一个 nonces 变量.
  4. 一个 permit 函数.

这是DOMAIN_SEPARATOR,和相关变量:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
string  public constant name     = "Dai Stablecoin";
string  public constant version  = "1";
bytes32 public DOMAIN_SEPARATOR;
constructor(uint256 chainId_) public {
  ...
  DOMAIN_SEPARATOR = keccak256(abi.encode(
    keccak256(
      "EIP712Domain(string name,string version," + 
      "uint256 chainId,address verifyingContract)"
    ),
    keccak256(bytes(name)),
    keccak256(bytes(version)),
    chainId_,
    address(this)
  ));
}

DOMAIN_SEPARATOR只不过是唯一标识智能合约的哈希。它是由EIP712域(EIP712Domain)的字符串,包含代币合约的名称,版本,所在的chainId以及合约部署的地址构成。

所有这些信息都在构造函数上进行hash 运算赋值到DOMAIN_SEPARATOR变量中,该变量在创建线下签名时由持有人使用,并且在执行permit时需要匹配。这样可以确保签名仅对一个合约有效。

这是PERMIT_TYPEHASH

PERMIT_TYPEHASH 是函数名称(大写开头)和所有参数(包括类型和名称)的哈希。目的是清楚地标志签名的函数。

签名将在permit函数中处理,如果使用的PERMIT_TYPEHASH不是该特定函数的签名,它将回退交易。这样可以确保仅将签名用于预期的功能。

然后是nonces映射:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mapping (address => uint) public nonces;

该映射记录了特定持有人已使用了多少次签名。创建签名时,需要包含一个nonces值。执行permit时,所包含的nonce 值必须与该持有人到目前为止使用的签名数完全匹配。这样可以确保每个签名仅使用一次。

所有这三个条件,即PERMIT_TYPEHASHDOMAIN_SEPARATORnonce,确保每个签名仅用于预期的合约,预期的函数,并且仅使用一次。

现在,让我们看看如何在智能合约中处理签名。

permit 函数

permit是dai.sol[16]里实现的函数,允许使用签名来修改持有人的 allowance对spender授权的数量。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// --- 通过签名授权 ---
function permit(
  address holder, address spender,
  uint256 nonce, uint256 expiry, bool allowed,
  uint8 v, bytes32 r, bytes32 s
) external;

如你所见,permit有很多参数。它们是计算签名所需的所有参数,加上签名本身就是vrs

你需要用参数创建签名似乎很愚蠢,但是你确实需要。因为仅能从签名中恢复签名的地址。我们将使用所有参数和恢复的地址来确保签名有效。

首先,我们使用确保安全性所需的所有参数来计算digest。作为签名创建的一部分,holder将需要在链下计算出完全相同的digest

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bytes32 digest =
  keccak256(abi.encodePacked(
    "\x19\x01",
    DOMAIN_SEPARATOR,
    keccak256(abi.encode(
      PERMIT_TYPEHASH,
      holder,
      spender,
      nonce,
      expiry,
      allowed
    ))
  ));

使用ecrecoverv,r,s签名,我们可以恢复地址。如果它是holder的地址,我们知道所有参数都匹配DOMAIN_SEPARATORPERMIT_TYPEHASHnonceholderspenderexpiryallowed。哪怕是任何一点内容没匹配,则签名被拒绝:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
require(holder == ecrecover(digest, v, r, s), "Dai/invalid-permit");

请注意这里。签名中有许多参数,其中一些参数有点模糊,例如chainId (它是 DOMAIN_SEPARATOR 的一部分)。它们中的任何一个不匹配都会导致签名被拒绝,并带有完全相同的错误提示,这让链下调试签名很困难。

现在我们知道 holder 批准了这个函数调用。接下来,我们将证明签名没有被滥用。我们检查当前时间是否在 expiry(过期)之前,这保证了仅在特定时间内许可有效。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
require(expiry == 0 || now <= expiry, "Dai/permit-expired");

我们还会检查签名中的 nonce ,以便每个签名只能使用一次。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
require(nonce == nonces[holder]++, "Dai/invalid-nonce");

这些检查都通过了!dai.sol[17]使spender可以使用的holder的代币数量设置为最大值(即allowance设置为最大),并触发一个事件,仅此而已。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
uint wad = allowed ? uint(-1) : 0;
allowance[holder][spender] = wad;
emit Approval(holder, spender, wad);

dai.sol[18]合约对 allowance 使用的是二分法设置(译者注:要么是最大,要么是 0), 在代码库[19],有更传统的方法。

创建链下签名

创建签名也许需要通过一些实践才可以掌握它。我们将分三步复制智能合约中permit的功能:

  1. 生成 DOMAIN_SEPARATOR
  2. 生成 digest
  3. 创建交易签名

以下函数将生成 DOMAIN_SEPARATOR。它与dai.sol[20]构造函数中的代码相同,但在JavaScript中实现,并使用ethers.js[21]keccak256defaultAbiCodertoUtfBytes,它需要代币名称和部署地址,以及chainId。假定代币版本为“1”。

以下函数将为特定的permit调用生成digest。注意,holderspendernonceexpiry作为参数传递。为了清楚起见,它还传递了一个 approve.allowed 参数,尽管你可以将其始终设置为 true,否则签名将被拒绝。从刚刚dai.sol[22]复制PERMIT_TYPEHASH

一旦我们有了digest,对其进行签名就相对容易了。我们从[digest]中删除0x前缀后,使用ethereumjs-util[23]中的ecsign。请注意,我们需要用户私钥才能执行此操作。

在代码中,我们将按以下方式调用这些函数:

请注意,对permit的调用需要重用用于创建digest的所有参数。只有在这种情况下,签名才有效。

还要注意的是,此代码段中仅有的两个交易是由user2调用的。user1holder,是创建digest并签名的用户。但是,user1并没有花费任何gas。

user1将签名提供给user2,后者使用它来执行user1授权的 permittransferFrom

user1的角度来看,这是一次“无 gas”交易, 他没有花一分钱。

结论

本文介绍了如何使用“无Gas”交易,阐明了“无Gas”实际上意味着将Gas成本转移给其他人。为此,我们需要一个智能合约中的功能,该功能可以处理预先签署的交易,并且需要进行大量的数据检验以确保一切安全。

但是,使用此模式有很多好处,因此,它被广泛使用。签名允许将交易 gas 成本从用户转移到服务提供商,从而在许多情况下消除了相当大的障碍。它还允许实现更高级的委派模式,通常会对UX进行相当大的改进。

已为您提供入门代码库[24],请使用它。


本翻译由 Cell Network[25] 赞助支持。

原文链接:https://hackernoon.com/how-to-code-gas-less-tokens-on-ethereum-43u3ew4

作者:@albertocuestacanada[26]


参考资料

[1]

登链翻译计划: https://github.com/lbc-team/Pioneer

[2]

Tiny熊: https://learnblockchain.cn/people/15

[3]

此存储库: https://github.com/albertocuestacanada/ERC20Permit?ref=learnblockchain.cn

[4]

dai.sol: https://github.com/makerdao/dss/blob/master/src/dai.sol?ref=learnblockchain.cn

[5]

钻进了兔子洞: https://learnblockchain.cn/docs/eips/eip-712.html

[6]

EIP712: https://learnblockchain.cn/docs/eips/eip-712.html

[7]

EIP712: https://learnblockchain.cn/docs/eips/eip-712.html

[8]

EIP2612: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2612.md?ref=learnblockchain.cn

[9]

EIP712: https://learnblockchain.cn/docs/eips/eip-712.html

[10]

EIP2612: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2612.md?ref=learnblockchain.cn

[11]

dai.sol: https://github.com/makerdao/dss/blob/master/src/dai.sol?ref=learnblockchain.cn

[12]

dai.sol: https://github.com/makerdao/dss/blob/master/src/dai.sol?ref=learnblockchain.cn

[13]

EIP2612: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2612.md?ref=learnblockchain.cn

[14]

EIP712: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md?ref=learnblockchain.cn

[15]

dai.sol: https://github.com/makerdao/dss/blob/master/src/dai.sol

[16]

dai.sol: https://github.com/makerdao/dss/blob/master/src/dai.sol?ref=learnblockchain.cn

[17]

dai.sol: https://github.com/makerdao/dss/blob/master/src/dai.sol?ref=learnblockchain.cn

[18]

dai.sol: https://github.com/makerdao/dss/blob/master/src/dai.sol?ref=learnblockchain.cn

[19]

代码库: https://github.com/albertocuestacanada/ERC20Permit?ref=learnblockchain.cn

[20]

dai.sol: https://github.com/makerdao/dss/blob/master/src/dai.sol?ref=learnblockchain.cn

[21]

ethers.js: https://learnblockchain.cn/docs/ethers.js/

[22]

dai.sol: https://github.com/makerdao/dss/blob/master/src/dai.sol?ref=learnblockchain.cn

[23]

digest]中删除0x前缀后,使用[ethereumjs-util: https://github.com/ethereumjs/ethereumjs-util?ref=learnblockchain.cn

[24]

代码库: https://github.com/albertocuestacanada/ERC20Permit?ref=learnblockchain.cn

[25]

Cell Network: https://www.cellnetwork.io/?utm_souce=learnblockchain

[26]

@albertocuestacanada: https://hackernoon.com/u/albertocuestacanada

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 深入浅出区块链技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【知识】无GAS以太坊交易实现原理及源码
每个人都在讨论无gas以太坊交易,因为没有人喜欢支付gas费用。但是以太坊网络能够精准地运转恰恰是因为交易需要手续费。那么如何实现无gas交易呢?让我们一起学习无gas以太坊交易的魔法!
辉哥
2021/11/24
1.9K0
【知识】无GAS以太坊交易实现原理及源码
Uniswap V2 源码学习 (四). 签名和路由
上次我们在研究 router 合约的时候, 有一个 removeLiquidityWithPermit 函数[2], 今天讲讲它和 Pair 的 permit 方法
Tiny熊
2022/05/25
1.4K0
Uniswap Permit2 - 高效、一致和安全的授权
前几天,Uniswap Labs 发布了两个新的智能合约 Permit2 和 Universal Router :
Tiny熊
2023/01/09
1.9K0
Uniswap Permit2 - 高效、一致和安全的授权
[译]按照EIP-712规范签名完成委托和投票
Compound[4]的治理体系是由发放给用户的COMP代币[5]来驱动的。COMP代币持有者拥有与持有量1:1的投票权。投票权利可以委托给任意一个地址,让其去给提案投票。
Tiny熊
2020/08/18
2K0
创建并部署ERC20代币
第一个标准由 Fabian Vogelsteller 于 2015 年 11 月以 ethereum request for Comments(ERC)引入,它被自动分配到 GitHub 第 20 个议题,所以叫“ERC20 代币”。目前绝大多数代币都基于 ERC20 标准。ERC20 后来变成了以太坊改进提案 20(EIP-20),但是大部分仍然使用它最初的名字,ERC20。
Tiny熊
2022/04/08
1.5K0
创建并部署ERC20代币
应用EIP712
以太坊钱包如MetaMask[2]都支持EIP712[3] —— 类型结构化消息签名[4]标准,让钱包可以结构化和可读的格式在签名提示中显示数据。EIP712 在安全性和可用性方面向前迈进了一大步,因为用户不再需要对难以理解的十六进制字符串签名(这是一种令人困惑、不安全的做法)。
Tiny熊
2022/04/11
2.2K0
应用EIP712
【以太坊通证标准】ERC20系列,ERC721系列,ERC865
【本文目标】 通过本文学习,了解ERC定义及知名ERC协议标准,如ERC20以及ERC223、ERC621,ERC827协议,ERC721以及 ERC875,ERC1155,ERC998协议,ERC 865等描述和定义。
辉哥
2018/08/10
1.8K0
【以太坊通证标准】ERC20系列,ERC721系列,ERC865
WETH10 - 更高效的 WETH
玩过 DEFI 的应该都知道,很多项目是通过 WETH 把以太币代币化[1],再接入到 ERC20 为主的 DEFI 生态中。
Tiny熊
2020/11/03
1.7K0
WETH10 - 更高效的 WETH
ERC777 功能型代币(通证)最佳实践
想必很多同学都已经使用过ERC20 创建过代币[1],或许已经被老板要求在ERC20代币上实现一些附加功能搞的焦头烂额,如果还有选择,一定要选择 ERC777 。
Tiny熊
2019/09/30
1.3K0
ERC-777标准规范
在经典的ERC-20场景中,如果用户想要授权给第三方账户或者智能合约进行转账操作,那么需要通过两个事务来完成整个转账的操作,在这里需要注意的是在授权是需要指定对应的amount数量,那么当每次进行授权转账时都需要进行一次查询或者让A用户再次授权给B用户:
Al1ex
2021/07/21
1.2K0
ERC-777标准规范
在经典的ERC-20场景中,如果用户想要授权给第三方账户或者智能合约进行转账操作,那么需要通过两个事务来完成整个转账的操作,在这里需要注意的是在授权是需要指定对应的amount数量,那么当每次进行授权转账时都需要进行一次查询或者让A用户再次授权给B用户:
Al1ex
2021/07/16
1.7K0
ERC-777标准规范
paradigm ctf 2022 - Hint finance
这是一个主网的 fork,三个 underlying token 分别是:PNT,SAND,AMP;其中,PNT 和 AMP 都是 777 token,在 transfer 之前和之后都有 callback 回掉。而 SAND token 是一个 ERC20 Token。可以访问这个链接[2]查看具体的 777 标准。简单概括,该标准要求 ERC777 token 的发送方和接受方要到 EIP-1820 这个地址上进行注册,注册时,需要调用setInterfaceImplementer方法,传入需要注册的 key 和 address;
Tiny熊
2022/11/07
1.5K0
paradigm ctf 2022 - Hint finance
UniswapV2协议解析
本篇文章主要对Uniswap V2协议的工作原理、项目构成、源码实现等部分进行详细解读。
Al1ex
2021/07/21
3.7K2
UniswapV2协议解析
安全的处理 ERC20 转账(解决非标准 ERC20 问题)
你可能认为在 ERC-20 调用几个函数非常简单,对吗?很不幸,不是的。有些事情我们必须要考虑,而且还可能出现一些很常见的问题。
Tiny熊
2021/10/13
2K0
在Substrate链上跑Solidity ERC20智能合约
本实践案例中,我们首先会搭建和启动一条substrate链,再通过MetaMask这款著名的以太坊钱包浏览器插件,通过自定义RPC的方式,接入我们搭建好的substrate链。
Tiny熊
2020/07/24
1.4K0
BSC智能链挖矿dapp系统开发智能合约技术指南
币安智能链(Binance Smart Chain,简称 BSC )是一条以太坊虚拟机兼容,与币安链并行的区块链,是加密资产行业顶尖项目的测试和前沿探索。
开发v_hkkf5566
2022/10/25
1.4K0
HCTF2018智能合约两则Writeup
这次比赛为了顺应潮流,HCTF出了3道智能合约的题目,其中1道是逆向,2道是智能合约的代码审计题目。
LoRexxar
2023/02/21
4130
HCTF2018智能合约两则Writeup
通过CREATE2获得合约地址:解决交易所充值账号问题
CREATE2 是以太坊在2019年2月28号的君士坦丁堡(Constantinople)硬分叉[1]中引入 的一个新操作码。根据EIP1014[2]CREATE2操作码引入,主要是用于状态通道,然而,我们也可以用于解决其他问题。
Tiny熊
2020/07/28
2.9K0
ERC-20标准规范
ERC-20为以太坊智能合约提供了一套编写规范,而IERC-20则规定了一个Token需要实现的基本接口,本篇文章将对此进行解读。
Al1ex
2021/03/23
2.5K0
ERC-20标准规范
dapp安全总结与典型安全事件
以太坊以及 EVM 的诞生使得 Dapp这种新的业务形态成为可能。总的来说,EVM 实现了一个全局的状态机,为所有的 Dapp提供了统一的状态空间;实现了图灵完备,并抽象出了账户模型,账户之间可以相互调用,使得不同的应用可以无缝组合,展现了 Dapp的独特魅力。
Tiny熊
2022/11/07
5550
dapp安全总结与典型安全事件
相关推荐
【知识】无GAS以太坊交易实现原理及源码
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档