阅读本文的读者,应该都对于区块链的基本原理和架构有些概念,如果没有,推荐您先去 Baidu 搜寻 区块链、比特币、以太坊这些关键字了解一下。我们将在这些基础概念之上,来谈区块链的技术知识。本文并不会谈到太深的技术钻研,而是希望以深入浅出的方式让每一个想要搞懂区块链技术的人都能在读完后说出:原来如此!
为什么发送 ERC20 Token 给别人需要两次交易?
许多人可能对此感到疑问,透过本篇文章相信能充分为您解惑,阐明发送 ERC20 Token 背后的技术解析。

发送以太币
发送以太币时其实挺符合我们的直觉,就是把钱发给别人,别人收到这样。然而发送 ERC20 代币却似乎不是这么单纯,为什么呢?

ERC-20 代币的本质
ERC20 代币只是用 ERC20 之智能合约底下的记忆体来储存记录各个地址持有数量的帐本。既然对象是个智能合约,个人帐户(Externally Owned Account, EOA)与 合约帐户(Contract Account)的不同,由于智能合约没有主观意识,动作皆需要由 EOA 发交易来触发。因此当合约地址收到 ETH 外的其它代币时,它并不会知道。因为更动的是「另一个智能合约底下的记忆体」!

我们实际来看一个范例:
假设我想要透过去中心化交易所 Uniswap 将我持有的 82 颗 cDAI 兑换成 0.0092 颗 ETH,在技术上是如何做到的呢?
需备知识:cDAI 是我将 DAI 存入 Compound 放贷后,Compound 发回给我的 ERC20 Token。所以 cDAI 是由 Compound 管理的 cDAI 智能合约底下之记忆体(帐本)来记录追踪的。

我的 Token Swap 需求在常规逻辑上是:
我把我的 cDAI token 发送给 Uniswap,Uniswap 把 ETH 发送给我。
不过事情并非这么单纯。由于 Uniswap 是去中心化,由智能合约来运作的。因此当 Uniswap 收到了 cDAI token 时,它不像人一样可以点开 Metamask 去查看是否有收到,确认有收到后再把 ETH 发送给我,因此需要用特殊的做法来实现这样的流程。
我们就直接开门见山讲答案
用比喻来说明的话,做法是:
我先到 银行(ERC20 Token 的智能合约)开启 授权,
允许 对方(第三方智能合约)能够到我的户头
提取 钱(ERC20 Token),并设定「能够提取的额度」。
接着我开给对方一张「写上金额的支票」,
让对方拿着支票到 银行 中提领我帐户中的 钱。
因为对象(智能合约)是一段代码,如此做法方能 让智能合约确认提取到 Token 并执行后续的合约逻辑操作。
因此,回到区块链的世界,必须透过以下两个交易步骤来进行:
Tx1: Approve(授权)
我必须先发一笔交易到「Compound cDAI Token 智能合约」(银行),
授权「Uniswap 的 cDAI to ETH 交易对智能合约」(对方)
可以到「Compound 的 cDAI Token 智能合约」(银行)
提取我的「cDAI Token」(钱)。
Tx2: Swap Token(兑换)
随后,我再发送一笔「将 cDAI 兑换为 ETH」的交易(支票)
到「Uniswap 的 cDAI to ETH 交易对智能合约」(对方)触发,
让「Uniswap 的 cDAI to ETH 交易对智能合约」(对方)
至「Compound 的 cDAI Token 智能合约」(银行)
提领我的 82 颗「cDAI Token」(钱)。
「Uniswap 的 cDAI to ETH 交易对智能合约」(对方)提领到 Token(钱)后再将 0.0092 颗 ETH 发送给我。
如下方示意图所示:

接着我们带大家实际走一次操作流程以搞懂背后技术逻辑。
Tx1: Approve(授权)
首先,当我们来到 Uniswap 交易所,选好 Input 与 Output 的加密货币种类并在 Input 输入欲兑换的数量,便可以在我欲兑换成 ETH 的 cDAI Token 旁边看到「Unlock」的按钮。这就是要求我进行「Tx1:授权」的操作。
点选「Unlock」按钮后,Metamask 便会跳出「批准」的交易来让我签署。一但签署后,我便批准了 Uniswap 能够到 Compound 提领我的 cDAI 的权限。
这笔交易被确认后,我们便可以凭 Txn Hash 到 Etherscan 上找到这笔交易。可以发现这笔交易是发给「Compound 的 cDAI ERC20 Token 智慧合约」。
点进去这笔交易后,便可以看到呼叫的正是 approve 的 function。我同意了「Uniswap 的 ETH-cDAI 交易对」合约地址能够来提领我的 cDAI,而授权的提领金额量为「最大」。
可能会有些人好奇为什么不把 amount 设成我要兑换的量:82 就好。原因是为了便利于未来提领操作时不用再次授权,通常呼叫智能合约的 approve 函式时都会预设为开启最大值,这样未来再次需要提领时就不用再次进行 approve 的操作。
然而要注意的部分是:针对「可能存在漏洞或不被信任的智能合约」,若是仍按照预设将权限开至最大值,则将面临 Token 遭到窃取的风险。
在完成「Tx1:Approve 授权」后,便可以看到「Unlock」的按钮消失,并且「Swap」按钮已能够点选。
Tx2: Swap Token
接着便能够来进行将我的 cDAI 兑换成 ETH 的 Swap Token 动作。
同样透过 Metamask 来签署完成这笔交易后,我们到 Etherscan 上追纵这笔交易:能够发现这笔交易是从我的钱包发送给「Uniswap 的 ETH-cDAI 交易对 智能合约」。

点进去这笔交易后可以看到在这笔交易中执行了两件事:
将 82 cDAI 从「Compound 的 cDAI 智能合约」转至 「Uniswap 的 ETH-cDAI 交易对合约地址」中。
将 0.0092 ETH 从「Uniswap 的 ETH-cDAI 交易对合约地址」中转给我。

接着我们可以在「Uniswap 的 ETH-cDAI 交易对合约地址」的
「Internal Txns」中看到 0.0092 ETH 转移至我的钱包地址;以及
在「Erc20 Token Txns」中看到 82 cDAI 从「Compound 的 cDAI 智能合约」转移至「Uniswap 的 ETH-cDAI 交易对合约地址」中。
同时,这笔交易在「Compound 的 cDAI 智能合约」中也可以查看到 82 cDAI 被转移出的记录。
结论
发送 ETH 与 发送 ERC20 Token 给智能合约有本质上的差异。ETH 是 push-based,单纯把 ETH 发过去就可以。ERC20 Token 的发送则是 pull-based,让合约去主动提取 Token。
必须先发送一笔交易至 ERC20 合约进行 授权(Approve),允许某合约地址能够至 ERC20 智能合约提取我持有的 ERC20 Token。
再发送一笔交易来触发合约地址进行 提取 ERC20 Token 与后续的逻辑操作。
至此,相信读者都已充分理解发送 ERC20 Token 给智能合约背后的技术逻辑。