本文介绍的代币化的以太币 WETH10 ,实现在更少的 gas 下,支持更多的特性,如:离线授权、交易链、闪电铸币。 基于WETH10的 DEFI 生态一定会生出有趣的组合交易。
当前使用最广泛的WETH实现是[WETH9](https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code),有兴趣的可以点击链接查看实现。
WETH9上线至今快有3年了,现在社区小伙伴实现了升级版本:[WETH10](https://github.com/WETH10/WETH10),已经部署在 [Kovan 测试网 ](https://kovan.etherscan.io/address/0xD25f374A2d7d40566b006eC21D82b9655865F941)
2020/03/08 更新:
WETH10主网已经上线:https://cn.etherscan.com/address/0xf4bb2e28688e89fcce3c0580d37d36a7672e8a9f

## WETH10 新特性
WETH10和 WETH9 一样实现和以太币的包裹,实现WETH与以太币的1:1互换
### 存款:Ether -> WETH
具体是调用`deposit`存入以太币(或发送到合约),可接收到WETH:
“`js
receive() external payable {
require(msg.sender != address(this));
_balanceOf[msg.sender] += msg.value;
emit Transfer(address(0), msg.sender, msg.value);
}
function deposit() external payable {
_balanceOf[msg.sender] += msg.value;
emit Transfer(address(0), msg.sender, msg.value);
}
“`
以上实现和WETH9基本一致,WETH10 还可以为指定的账号存款,调用 `depositTo` :
“`js
function depositTo(address to) external payable {
require(to != address(this), “!recipient”);
_balanceOf[to] += msg.value;
emit Transfer(address(0), to, msg.value);
}
“`
WETH10 还实现了存款加调用 `depositToAndCall`,通过`depositToAndCall`可以把资金存入合约,并立即调用合约自定义的实现`onTokenTransfer`,以往使用WETH9可能需要多笔交易才能完成的工作现在只需要一笔交易完成。
`depositToAndCall` 实现如下:
“`js
function depositToAndCall(address to, bytes calldata data) external payable returns (bool success) {
require(to != address(this), “!recipient”);
_balanceOf[to] += msg.value;
emit Transfer(address(0), to, msg.value);
ERC677Receiver(to).onTokenTransfer(msg.sender, msg.value, data);
return true;
}
“`
`depositToAndCall` 的第 2 个参数 data 是传递给目标函数的参数。
目标函数的调用使用 [ERC677 标准](https://github.com/ethereum/EIPs/issues/677)。
### 取款 WETH -> Ether
取款通过调用 `withdraw` 来完成,`withdraw` 方法如下:
“`
function withdraw(uint256 value) external {
require(_balanceOf[msg.sender] >= value, “!balance”);
_balanceOf[msg.sender] -= value;
(bool success, ) = msg.sender.call{value: value}(“”);
require(success, “!withdraw”);
emit Transfer(msg.sender, address(0), value);
}
“`
以上实现和WETH9基本一致,WETH10 还可以取款到指定的账号,调用`withdrawTo`:
“`js
function withdrawTo(address to, uint256 value) external {
require(_balanceOf[msg.sender] >= value, “!balance”);
_balanceOf[msg.sender] -= value;
(bool success, ) = to.call{value: value}(“”);
require(success, “!withdraw”);
emit Transfer(msg.sender, address(0), value);
}
“`
另外还有`withdrawFrom`函数,用来从其他的账号以取款到指定的账号, 当然需要资金的所有者提前`approve`, `withdrawFrom` 实现如下:
“`js
function withdrawFrom(address from, address to, uint256 value) external {
require(_balanceOf[from] >= value, “!balance”);
if (from != msg.sender) {
uint256 allow = allowance[from][msg.sender];
if (allow != type(uint256).max) {
require(allow >= value, “!allowance”);
allowance[from][msg.sender] -= value;
emit Approval(from, msg.sender, allow – value);
}
}
_balanceOf[from] -= value;
(bool success, ) = to.call{value: value}(“”);
require(success, “!withdraw”);
emit Transfer(from, address(0), value);
}
“`
仔细查看以上代码, 你还可以看到授权量的优化,如果授权量是最大值` type(uint256).max` 时,将不再减少授权量,从而降低 gas 成本。这个优化也同样应用到了 `transferFrom` 中。
## 离线授权
升级的WETH10, 实现了离线授权功能(实现[EIP2612](https://eips.ethereum.org/EIPS/eip-2612)),可以参考文章:[通过链下签名授权实现更少 Gas 的 ERC20代币](https://learnblockchain.cn/article/1496),方便合约合并授权与转账交易,减少 gas 消耗。
## 调用链
在前面 `depositAndCall` 函数已经提到过,在用户执行存款时,可以立即在用户定义的合约中执行一笔调用, 这样存款和合约调用发生在一次交易中。
升级的WETH10 也支持在转账时(使用 `transferAndCall` 转账),同样支持在接收者地址上调用onTokenTransfer,`transferAndCall` 实现如下:
“`js
function transferAndCall(address to, uint value, bytes calldata data) external returns (bool success) {
require(_balanceOf[msg.sender] >= value, “!balance”);
require(_balanceOf[to] + value >= value, “overflow”);
_balanceOf[msg.sender] -= value;
_balanceOf[to] += value;
emit Transfer(msg.sender, to, value);
ERC677Receiver(to).onTokenTransfer(msg.sender, value, data);
return true;
}
“`
## 闪电铸币
WETH10合约允许通过`flashMint` 来增发任意数量的WETH,而不用存入真实的以太币,只需要在交易结束前将其销毁掉。
`flashMint`函数会在调用者地址上调用`executeOnFlashMint`,`flashMint` 还支持接收一个额外的`bytes`参数传递给`executeOnFlashMint`,`flashMint` 的实现如下:
“`js
function flashMint(uint256 value, bytes calldata data) external {
_balanceOf[msg.sender] += value;
require(_balanceOf[msg.sender] >= value, “overflow”);
emit Transfer(address(0), msg.sender, value);
FlashMinterLike(msg.sender).executeOnFlashMint(value, data);
require(_balanceOf[msg.sender] >= value, “!balance”);
_balanceOf[msg.sender] -= value;
emit Transfer(msg.sender, address(0), value);
}
“`
预感 `flashMint` 会产生出很多闪电代新玩法。
玩过DEFI的应该都知道,很多项目是通过WETH把以太币代币化,再接入到ERC20为主的DEFI生态中。
当前使用最广泛的WETH实现是WETH9,有兴趣的可以点击链接查看实现。
WETH9上线至今快有3年了,现在社区小伙伴实现了升级版本:WETH10,已经部署在 Kovan 测试网
2020/03/08 更新: WETH10主网已经上线:https://cn.etherscan.com/address/0xf4bb2e28688e89fcce3c0580d37d36a7672e8a9f
WETH10 新特性
WETH10和 WETH9 一样实现和以太币的包裹,实现WETH与以太币的1:1互换
存款:Ether -> WETH
具体是调用deposit
存入以太币(或发送到合约),可接收到WETH:
receive() external payable {
require(msg.sender != address(this));
_balanceOf[msg.sender] += msg.value;
emit Transfer(address(0), msg.sender, msg.value);
}
function deposit() external payable {
_balanceOf[msg.sender] += msg.value;
emit Transfer(address(0), msg.sender, msg.value);
}
以上实现和WETH9基本一致,WETH10 还可以为指定的账号存款,调用 depositTo
:
function depositTo(address to) external payable {
require(to != address(this), "!recipient");
_balanceOf[to] += msg.value;
emit Transfer(address(0), to, msg.value);
}
WETH10 还实现了存款加调用 depositToAndCall
,通过depositToAndCall
可以把资金存入合约,并立即调用合约自定义的实现onTokenTransfer
,以往使用WETH9可能需要多笔交易才能完成的工作现在只需要一笔交易完成。
depositToAndCall
实现如下:
function depositToAndCall(address to, bytes calldata data) external payable returns (bool success) {
require(to != address(this), "!recipient");
_balanceOf[to] += msg.value;
emit Transfer(address(0), to, msg.value);
ERC677Receiver(to).onTokenTransfer(msg.sender, msg.value, data);
return true;
}
depositToAndCall
的第 2 个参数 data 是传递给目标函数的参数。 目标函数的调用使用 ERC677 标准。
取款 WETH -> Ether
取款通过调用 withdraw
来完成,withdraw
方法如下:
function withdraw(uint256 value) external {
require(_balanceOf[msg.sender] >= value, "!balance");
_balanceOf[msg.sender] -= value;
(bool success, ) = msg.sender.call{value: value}("");
require(success, "!withdraw");
emit Transfer(msg.sender, address(0), value);
}
以上实现和WETH9基本一致,WETH10 还可以取款到指定的账号,调用withdrawTo
:
function withdrawTo(address to, uint256 value) external {
require(_balanceOf[msg.sender] >= value, "!balance");
_balanceOf[msg.sender] -= value;
(bool success, ) = to.call{value: value}("");
require(success, "!withdraw");
emit Transfer(msg.sender, address(0), value);
}
另外还有withdrawFrom
函数,用来从其他的账号以取款到指定的账号, 当然需要资金的所有者提前approve
, withdrawFrom
实现如下:
function withdrawFrom(address from, address to, uint256 value) external {
require(_balanceOf[from] >= value, "!balance");
if (from != msg.sender) {
uint256 allow = allowance[from][msg.sender];
if (allow != type(uint256).max) {
require(allow >= value, "!allowance");
allowance[from][msg.sender] -= value;
emit Approval(from, msg.sender, allow - value);
}
}
_balanceOf[from] -= value;
(bool success, ) = to.call{value: value}("");
require(success, "!withdraw");
emit Transfer(from, address(0), value);
}
仔细查看以上代码, 你还可以看到授权量的优化,如果授权量是最大值type(uint256).max
时,将不再减少授权量,从而降低 gas 成本。这个优化也同样应用到了 transferFrom
中。
离线授权
升级的WETH10, 实现了离线授权功能(实现EIP2612),可以参考文章:通过链下签名授权实现更少 Gas 的 ERC20代币,方便合约合并授权与转账交易,减少 gas 消耗。
调用链
在前面 depositAndCall
函数已经提到过,在用户执行存款时,可以立即在用户定义的合约中执行一笔调用, 这样存款和合约调用发生在一次交易中。
升级的WETH10 也支持在转账时(使用 transferAndCall
转账),同样支持在接收者地址上调用onTokenTransfer,transferAndCall
实现如下:
function transferAndCall(address to, uint value, bytes calldata data) external returns (bool success) {
require(_balanceOf[msg.sender] >= value, "!balance");
require(_balanceOf[to] + value >= value, "overflow");
_balanceOf[msg.sender] -= value;
_balanceOf[to] += value;
emit Transfer(msg.sender, to, value);
ERC677Receiver(to).onTokenTransfer(msg.sender, value, data);
return true;
}
闪电铸币
WETH10合约允许通过flashMint
来增发任意数量的WETH,而不用存入真实的以太币,只需要在交易结束前将其销毁掉。
flashMint
函数会在调用者地址上调用executeOnFlashMint
,flashMint
还支持接收一个额外的bytes
参数传递给executeOnFlashMint
,flashMint
的实现如下:
function flashMint(uint256 value, bytes calldata data) external {
_balanceOf[msg.sender] += value;
require(_balanceOf[msg.sender] >= value, "overflow");
emit Transfer(address(0), msg.sender, value);
FlashMinterLike(msg.sender).executeOnFlashMint(value, data);
require(_balanceOf[msg.sender] >= value, "!balance");
_balanceOf[msg.sender] -= value;
emit Transfer(msg.sender, address(0), value);
}
预感 flashMint
会产生出很多闪电代新玩法。
本文参与区块链开发网写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。
- 发表于 2020-10-29 17:21
- 阅读 ( 5315 )
- 学分 ( 239 )
- 分类:以太坊