【科普】为什么使用defi产品前会有approve这么多余的操作?

疑惑:多余的设计?

第一次用uniswap的时候一直很疑惑为什么必须要先授权,才能再进行token交换。因为既然我都签名将token发送给uniswap 了,那不就意味着我肯定同意这笔交易了吗?所以一直感觉“授权”这样的设计很多余。

最近学习了erc20标准及相关的知识,解决了部分疑惑,现在记录下来跟大家一起学习。

概念:什么是erc20标准

Erc20是一套标准的合约规范,主要内容就是规定了合约中必须具备的变量和函数,详细内容定义在EIP20中。包括:

name:token名称

symbol:token名称简称

decimals:转账可以接受的最长小数位

totalSupply:token总量

(以上不重要,可以忽略,下面的是重点)

balanceOf:地址余额,也就是账本,记录了token分布在什么地址上。输入一个特定地址可以查询token的余额。本文中讨论的重点概念

transfer:转币的函数,平时自己转币就是调用这个函数,只能允许token的持有人调用。调用transfer后,更改balanceOf的账 本情况。本文中讨论的重点概念

approve:token的持有人授权其他人转移持有人一定数量的代币,也就是一开始uniswap那个“授权”的操作。授权情况记录在allowance中。只能允许token的持有人 调用

transferFrom:在approve后,被授权人可 以使用此函数直接转移token持有人(授权人)的token,无需持有人同意。调用transferFrom后,balanceOf的账本情况相 应更改。

allowance:记录approve情况的变量。不 太重要。

以上差不多是erc20标准的全部内容(此外还有2个event,不是重点讨论的内容)。另外不同的token合约中可以根据需要加入不同的函数,比如增加token总量、销毁等等。

一些思考和认识

(1)token余额的本质:此“币”非“真币”

以前一直以为erc20标准中会存在一个“发币”的操作,使得某个token从无到有。但在erc20标准中并无这样的函数,仅仅定义了几个变量和2个改变账本的函数。只要在智能合约中按照规范定义了上述变量和函数,一旦部署合约,以太坊浏览器就会认为这是一个erc20标准token合约,主动追踪token的transfer情况,并在以太坊浏览器 上展示。

所以大家所看到的token余额的本质就是token智能合约中变量balanceOf所记录的 账本的情况,balanceOf说某人有1个,那以太坊浏览器就会显示某人有1个,balanceOf说某人没币,那就是没币。

当然以太坊网络本身也就是一个分布式账本,站在以太坊网络外部的角度来看,ETH也不是真的“币”,不过是账本的数据。这与token的理解类似,可以理解为ETH是一个大账本,token是大账本里面的一个小账本。但如果站在以太坊网络内部的角度看,ETH是内部流通的真币,而token只是一个账本。

(2)token转移的逻辑:此“收币”非“真收币”

相信大家都转过币,无论是转ETH还是erc20的token,都要输入接收方地址。但在以太坊网络中,这2种操作的业务逻辑是完全不一样的。站在以太坊网络内部的角度来看,如果我们认为ETH是网络中真实存在的币,那么在转ETH的操作中,接收方是真实收到了币,因为此时接收方的地址位于交易数据的to字段,此时的交易逻辑是发送方真真实实地向接收方发送了ETH。

转移erc20的token情况则完全不一样,此时交易的to字段为token的合约地址,而非接收方的地址。接收方地址位于交易数据的data字段,此时的交易逻辑是发送方向token合约地址发送交易,通过传输的data数据调用transfer函数,将自己名下的token 的余额记录一部分在接收方的地址上。从这个角度来看,可以理解为接收方并非真实收到币,只是发送方更改了token的账本。

(3)为什么必须先approve授权:合约调用逻辑和erc20标准的限制导致

回到本文一开始的问题,为什么使用uniswap必须先approve。事实上,普通用 户平时的操作基本上可以分为2类,一是给普通地址转ETH,二是调用合约。我们使用uniswap显然是属于第二种情况。

使用uniswap交易token A为token B的逻辑 基本上可以理解为:用户调用uniswap合约的swap函数(交易的to地址为uniswap合 约地址),在swap函数中,由uniswap合 约调用token A合约的transferFrom函数, 将原来属于用户的部分token A的余额记录在uniswap合约地址上,然后uniswap合约 再调用tokenB的transfer函数,将原来属于 uniswap合约的部分tokenB的余额记录在用 户A上,完成一次swap的操作。(此过程的概括并不是太严谨,实际上会涉及更多合约的调用)

从上述过程可知:1、用户调用的合约是uniswap,而不是直接调用token合约;2、在交换过程中是由uniswap调用token A合 约,因为开始时token A记账在用户名下,uniswap合约不能直接调用token A的 transfer函数,因此只能让用户先approve,才能调用token A的transferFrom 函数。

(哦!原来swap的过程并不是用户将tokenA发送给uniswap合约,这样只会导致代币丢失;原来approve是erc20标准里面规定的,并不是uniswap搞出来的多余的操作)

延伸

问题一:swap前真的无论如何也必须要approve吗?

答案是否定的。目前需要approve是因为EIP20规定的函数比较简单,而绝大部分token都是采用这个标准,受合约调用逻辑和标准所限,只能采取先approve的方式。而EIP712和EIP2612则对原来的标准进行了扩充,增加了一个名为permit函数,目的是解决只有erc20而没有eth的持币人与以太坊网络交互的问题。允许用户线下签好名然后发给第三方,让第三方帮用户做approve的操作。

上述解释大概有点难懂,实际可以理解为用户可以先线下签名(不需要花费gas),然后将签名数据连同下一次的合约调用数据一起发给目标合约(如uniswap),实现只花一次gas,就完成approve和另外的transferFrom操作。

事实上,uniswap的lp的token合约就包含 这个函数。细心的朋友可能会留意到,在我们操作移除流动性时,不需要单独对lp的token进行一次approve,只需要先签名一次(不花费gas),然后就可以直接移除。因此如果token合约中使用了上述提案标准,可以免去一次单独的approve。

问题二:是否还有其他更好的方法?

个人认为,既然有函数可以通过预先签名进行approve,那么应该也可以实现一个函数通过预先签名直接进行transfer,感觉理论上应该可以实现,毕竟在我看来approve的操作始终有点多余。由于知识所限,目前没掌握相关资料。

上述的设想需要改变现行的token标准。另一个想法是在现行的token标准下,改变uniswap的合约的调用方式,使用delegatecall,在不改变msg.sender的情况 下调用token的transfer方法,或许可以直接 跳过approve。

如果能跳过approve的操作,还能避免授权恶意合约导致token丢失的情况。(但可能带来了另外的安全问题?)

总之,以后应该会有更多更好的方法改善这个需要apporve的体验。

额外的废话

1.显然,approve给恶意合约,会导致token 丢失。所以丢失token有2种可能,一是approve的问题,二是私钥丢失,通过查看具体的交易记录可以判断。而丢失ETH就说明肯定丢失了私钥。

2.如果使用ETH兑换为其他token,无需先approve,兑换机制不一样,ETH也没 approve这一功能。

3.上文提到只要我们部署了ERC20标准的token合约,以太坊浏览器就会主动识别,这种说法可能不太严谨,应该是在第一次调用transfer或者approve的时候(具体不 是很确定),才会被识别为ERC20的token,此时会有一个专门的token页面显示详细数据。

4.事实上,并不需要严格按照EIP20的标准去写token合约,也有可能会被识别为erc20的token,比如将transfer的名称改为 transfer123,也会识别为erc20的token。 (可能识别的是event?,在测试网中是这种情况,主网不确定)

5.EIP20的status是final,而EIP712和EIP2612均为Review,所以后者没有被广泛采用。

水平有限,如果说错,不必当真。

本文属于“币圈趣味科普”系列之一:

1、【科普】为什么使用defi产品前会有approve这么多余的操作?

2、就Ronin被盗聊聊“假如你是黑客怎么隐藏身份和洗币”

3、科普比特币合约,永续合约和比特币爆仓

请给本文评分
[总: 1个 平均: 5星]
滚动至顶部