EIP-712 使用详解
之前的文章我们介绍过如何对数据进行签名,利用签名技术我们可以实现一些功能例如白名单校验等。但是这种签名技术的应用场景比较简单,一般就是给一串字符串,或者一串哈希签名,如果我们想为更复杂的数据签名就无法实现了。 EIP-712 的出现就是为了解决这个问题,利用 EIP-712,我们可以对更大的数据集,例如对结构体进行签名。那么这种签名格式有什么实际的应用场景呢。使用过 Uniswap,PancakeSwap 等 DEX 的朋友应该有印象,在移除 LP 流动性的时候,我们需要先签名,然后再发送一笔交易移除流动性。正常情况下,其实应该我们先调用 LP 代币的授权方法,授权 DEX 合约可以转移我们的 LP,然后再去移除流动性。而这种二合一的实现正是应用了 EIP-712。它帮助我们仅仅签名一次,就可以将两步交易合并为一步交易,从而节省 Gas 费用。这篇文章我们就来看看 EIP-712 到底是怎么使用的。基本结构EIP712Domain顾名思义,是一个与域相关的结构体,总共包含五个字段:name,合约或者协议的名称version,合约的版本chainId,合约部署的链 Id,一般使用 ...
流动性挖矿-合约原理详解
流动性挖矿应该是上个牛市最火热的内容,基本上整个 DeFi 都是在围绕着流动性挖矿展开的,今天我们就来看看它到底是什么以及合约代码层面是怎么实现的。流动性挖矿简介首先我们先从用户的角度来理解一下流动性挖矿是什么,实际上就是用户通过在合约中质押一个 token 从而赚取另一个 token 的过程。例如,SushiSwap 最初推出的 DEX 流动性挖矿,用户可以通过将 SushiSwap 的 LP token 质押到合约中赚取 Sushi token。那么这个奖励具体是怎么发放以及如何实现的呢?我们今天就来研究一下这部分内容。 先来看几个例子: 一:假设有一个流动性挖矿的合约,可以质押 A token 赚取 B token。它在 0 秒时开始活动,每秒奖励 R 个 B token。此时有用户 Alice 在第 3 秒时质押了 2 个 A token,并且之后没有其他人参与,在第 8 秒时取出 token,图示:那么他在此时获得的收益就是:5R = (2 / 2) * (8 - 3) * R 其中,第一个 2 是用户 A 质押的数量,第二个 2 是合约中质押的总量,(8-3)是用户 ...
ERC-6551 详解
最近 ERC-6551 的热度很高,我们就来聊聊 ERC-6551 到底是什么。这篇文章我想从它的原理,代码实现,以及应用案例来讲解 6551。相信看完这篇文章后大家应该会对 6551 有一个比较全面的认识。什么是 ERC-6551简单来说,6551 是一个为 NFT 创建钱包的标准。这是什么意思呢? 我们考虑一个场景,假设在游戏中,我的地址 A 拥有一个角色 Bob,这个角色它本身是一个 ERC721 的 NFT,同时它的身上也有许多道具,例如帽子,鞋子,武器等,也可能拥有一些资产例如金元宝等。这些资产本身也是 ERC20,ERC721 等类型的 token。这些道具和资产在游戏逻辑中是属于我的角色 Bob 的,但是在实际的底层合约实现中,其实它们都是属于我的地址 A 的。如果我想将我的角色 Bob 出售给别人,我就需要分别将 Bob 还有它所拥有的所有资产都一一转账给买家。这在逻辑和操作上都不是很合理。 6551 标准的目的就是为角色 Bob 创建一个钱包,使得它所拥有的道具都是属于角色 Bob 的,这样看起来就合理多了。我们可以看看官方 EIP 里给到的示例图:ERC-65...
Smart Contract Developer
EIP-712 使用详解
之前的文章我们介绍过如何对数据进行签名,利用签名技术我们可以实现一些功能例如白名单校验等。但是这种签名技术的应用场景比较简单,一般就是给一串字符串,或者一串哈希签名,如果我们想为更复杂的数据签名就无法实现了。 EIP-712 的出现就是为了解决这个问题,利用 EIP-712,我们可以对更大的数据集,例如对结构体进行签名。那么这种签名格式有什么实际的应用场景呢。使用过 Uniswap,PancakeSwap 等 DEX 的朋友应该有印象,在移除 LP 流动性的时候,我们需要先签名,然后再发送一笔交易移除流动性。正常情况下,其实应该我们先调用 LP 代币的授权方法,授权 DEX 合约可以转移我们的 LP,然后再去移除流动性。而这种二合一的实现正是应用了 EIP-712。它帮助我们仅仅签名一次,就可以将两步交易合并为一步交易,从而节省 Gas 费用。这篇文章我们就来看看 EIP-712 到底是怎么使用的。基本结构EIP712Domain顾名思义,是一个与域相关的结构体,总共包含五个字段:name,合约或者协议的名称version,合约的版本chainId,合约部署的链 Id,一般使用 ...
流动性挖矿-合约原理详解
流动性挖矿应该是上个牛市最火热的内容,基本上整个 DeFi 都是在围绕着流动性挖矿展开的,今天我们就来看看它到底是什么以及合约代码层面是怎么实现的。流动性挖矿简介首先我们先从用户的角度来理解一下流动性挖矿是什么,实际上就是用户通过在合约中质押一个 token 从而赚取另一个 token 的过程。例如,SushiSwap 最初推出的 DEX 流动性挖矿,用户可以通过将 SushiSwap 的 LP token 质押到合约中赚取 Sushi token。那么这个奖励具体是怎么发放以及如何实现的呢?我们今天就来研究一下这部分内容。 先来看几个例子: 一:假设有一个流动性挖矿的合约,可以质押 A token 赚取 B token。它在 0 秒时开始活动,每秒奖励 R 个 B token。此时有用户 Alice 在第 3 秒时质押了 2 个 A token,并且之后没有其他人参与,在第 8 秒时取出 token,图示:那么他在此时获得的收益就是:5R = (2 / 2) * (8 - 3) * R 其中,第一个 2 是用户 A 质押的数量,第二个 2 是合约中质押的总量,(8-3)是用户 ...
ERC-6551 详解
最近 ERC-6551 的热度很高,我们就来聊聊 ERC-6551 到底是什么。这篇文章我想从它的原理,代码实现,以及应用案例来讲解 6551。相信看完这篇文章后大家应该会对 6551 有一个比较全面的认识。什么是 ERC-6551简单来说,6551 是一个为 NFT 创建钱包的标准。这是什么意思呢? 我们考虑一个场景,假设在游戏中,我的地址 A 拥有一个角色 Bob,这个角色它本身是一个 ERC721 的 NFT,同时它的身上也有许多道具,例如帽子,鞋子,武器等,也可能拥有一些资产例如金元宝等。这些资产本身也是 ERC20,ERC721 等类型的 token。这些道具和资产在游戏逻辑中是属于我的角色 Bob 的,但是在实际的底层合约实现中,其实它们都是属于我的地址 A 的。如果我想将我的角色 Bob 出售给别人,我就需要分别将 Bob 还有它所拥有的所有资产都一一转账给买家。这在逻辑和操作上都不是很合理。 6551 标准的目的就是为角色 Bob 创建一个钱包,使得它所拥有的道具都是属于角色 Bob 的,这样看起来就合理多了。我们可以看看官方 EIP 里给到的示例图:ERC-65...
Share Dialog
Share Dialog
Smart Contract Developer

Subscribe to xyyme.eth

Subscribe to xyyme.eth
CREATE2 是一个可以在合约中创建合约的操作码。我们先来举个例子看看它能干什么:

这段代码是 Uniswap v2-core 里面的工厂合约代码,使用 create2 操作码创建了 pair 合约,返回值是 pair 的地址,这样就可以逻辑中直接使用其地址进行接下来的操作。
那么 create2 到底是怎么使用呢,根据官方 EIP 文档,create2 一共接收四个参数,分别是:
endowment(创建合约时往合约中打的 ETH 数量)
memory_start(代码在内存中的起始位置,一般固定为 add(bytecode, 0x20) )
memory_length(代码长度,一般固定为 mload(bytecode) )
salt(随机数盐)
这里要注意的是第一个参数如果大于 0 的话,需要待部署合约的构造方法带有 payable。随机数盐是由用户自定,须为 bytes32 格式,例如在上面 Uniswap 的例子中,salt 为:
bytes32 salt = keccak256(abi.encodePacked(token0, token1));
create2 还有一个优点,相较于以前的 new 创建合约方法,可以在合约未创建之前就能够计算出合约的地址。我们之前在使用 new 创建新合约的时候,必须在取到合约对象之后,再取其 address 才能获取地址。而使用 create2,就可以这样提前计算出地址(参考):
// salt 为部署合约时使用的随机数盐
// bytecode 为合约的字节码哈希(keccak256)
// deployer 为部署合约的地址(在A合约中部署B合约,则此处为A)
function computeAddress(
bytes32 salt,
bytes32 bytecodeHash,
address deployer
) internal pure returns (address) {
bytes32 _data = keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash));
return address(uint160(uint256(_data)));
}
能够提前计算出合约地址这一点,就会给我们许多想象力。比如我们可以让合约地址变成靓号(例如前缀地址是 0x000,0x666,0x888),原因就是 salt 是我们自己定义的,那么就可以像 POW 挖矿那样,不断找寻随机数,以达到目的。这里我们就不再对这个话题过多深入,感兴趣的同学可以看看 这里 或者 这里。
接下来我们就尝试一下使用 create2 部署合约,方便的是,OpenZeppelin 库中就有现成的模版可以供我们使用,先来看看它是怎么实现的:

注意到注释中对于参数的要求:
bytecode 必须非空
对于相同的 bytecode,salt 不能重复(否则就计算出重复的地址)
部署合约中必须有 amount 数量的 ETH(如果 amount 大于 0 的话)
如果 amount 非 0,则待部署合约必须有 payable 修饰符
逻辑还是比较简单的,我们在使用的时候直接套用模版就行,来实操一下:
pragma solidity 0.8.10;
import "@openzeppelin/contracts/utils/Create2.sol";
contract Factory {
event Deployed(address addr);
// 计算合约地址的方法
function getAddress() public view returns (address) {
return
Create2.computeAddress(
keccak256("Here is salt"),
keccak256(
abi.encodePacked(type(Template).creationCode, abi.encode(3))
)
);
}
// 部署合约
function deploy() public {
address addr = Create2.deploy(
0,
keccak256("Here is salt"),
abi.encodePacked(type(Template).creationCode, abi.encode(3))
);
emit Deployed(addr);
}
}
contract Template {
uint256 public a;
constructor(uint256 _a) {
a = _a;
}
}
我们使用 keccak256("Here is salt") 为盐,当然这里可以使用任何 bytes32 类型的数据。
有一点需要注意的是,对于构造函数有参数的情况,需要将参数编码并拼接在合约字节码后面作为完整的字节码传入:
abi.encodePacked(type(Template).creationCode, abi.encode(3))
我们部署一下,先调用 getAddress 计算合约地址:

然后再调用 deploy 部署合约,在事件中查看部署的地址为:

验证地址确实相同。
这里还有一点需要说一下的是,如果要在 EtherScan 中上传代码,是需要将上面的所有合约,也就是 Factory 和 Template,包括 import 的合约都需要上传,仅仅上传 Template 是无法成功的,这里当时卡了我很长时间,最后试了试全部粘贴才能成功。
由于对于相同的合约、参数、盐,create2 计算所得的合约地址都是相同的,因此我们就可以通过 create2 与 selfdestruct 相结合,在同一个地址上多次部署合约。我在这篇文章中有详细介绍。
本文只介绍了 create2 的使用方法,其实它的应用场景还有很多,比如:
感兴趣的同学可以多看看学习学习。
CREATE2 是一个可以在合约中创建合约的操作码。我们先来举个例子看看它能干什么:

这段代码是 Uniswap v2-core 里面的工厂合约代码,使用 create2 操作码创建了 pair 合约,返回值是 pair 的地址,这样就可以逻辑中直接使用其地址进行接下来的操作。
那么 create2 到底是怎么使用呢,根据官方 EIP 文档,create2 一共接收四个参数,分别是:
endowment(创建合约时往合约中打的 ETH 数量)
memory_start(代码在内存中的起始位置,一般固定为 add(bytecode, 0x20) )
memory_length(代码长度,一般固定为 mload(bytecode) )
salt(随机数盐)
这里要注意的是第一个参数如果大于 0 的话,需要待部署合约的构造方法带有 payable。随机数盐是由用户自定,须为 bytes32 格式,例如在上面 Uniswap 的例子中,salt 为:
bytes32 salt = keccak256(abi.encodePacked(token0, token1));
create2 还有一个优点,相较于以前的 new 创建合约方法,可以在合约未创建之前就能够计算出合约的地址。我们之前在使用 new 创建新合约的时候,必须在取到合约对象之后,再取其 address 才能获取地址。而使用 create2,就可以这样提前计算出地址(参考):
// salt 为部署合约时使用的随机数盐
// bytecode 为合约的字节码哈希(keccak256)
// deployer 为部署合约的地址(在A合约中部署B合约,则此处为A)
function computeAddress(
bytes32 salt,
bytes32 bytecodeHash,
address deployer
) internal pure returns (address) {
bytes32 _data = keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash));
return address(uint160(uint256(_data)));
}
能够提前计算出合约地址这一点,就会给我们许多想象力。比如我们可以让合约地址变成靓号(例如前缀地址是 0x000,0x666,0x888),原因就是 salt 是我们自己定义的,那么就可以像 POW 挖矿那样,不断找寻随机数,以达到目的。这里我们就不再对这个话题过多深入,感兴趣的同学可以看看 这里 或者 这里。
接下来我们就尝试一下使用 create2 部署合约,方便的是,OpenZeppelin 库中就有现成的模版可以供我们使用,先来看看它是怎么实现的:

注意到注释中对于参数的要求:
bytecode 必须非空
对于相同的 bytecode,salt 不能重复(否则就计算出重复的地址)
部署合约中必须有 amount 数量的 ETH(如果 amount 大于 0 的话)
如果 amount 非 0,则待部署合约必须有 payable 修饰符
逻辑还是比较简单的,我们在使用的时候直接套用模版就行,来实操一下:
pragma solidity 0.8.10;
import "@openzeppelin/contracts/utils/Create2.sol";
contract Factory {
event Deployed(address addr);
// 计算合约地址的方法
function getAddress() public view returns (address) {
return
Create2.computeAddress(
keccak256("Here is salt"),
keccak256(
abi.encodePacked(type(Template).creationCode, abi.encode(3))
)
);
}
// 部署合约
function deploy() public {
address addr = Create2.deploy(
0,
keccak256("Here is salt"),
abi.encodePacked(type(Template).creationCode, abi.encode(3))
);
emit Deployed(addr);
}
}
contract Template {
uint256 public a;
constructor(uint256 _a) {
a = _a;
}
}
我们使用 keccak256("Here is salt") 为盐,当然这里可以使用任何 bytes32 类型的数据。
有一点需要注意的是,对于构造函数有参数的情况,需要将参数编码并拼接在合约字节码后面作为完整的字节码传入:
abi.encodePacked(type(Template).creationCode, abi.encode(3))
我们部署一下,先调用 getAddress 计算合约地址:

然后再调用 deploy 部署合约,在事件中查看部署的地址为:

验证地址确实相同。
这里还有一点需要说一下的是,如果要在 EtherScan 中上传代码,是需要将上面的所有合约,也就是 Factory 和 Template,包括 import 的合约都需要上传,仅仅上传 Template 是无法成功的,这里当时卡了我很长时间,最后试了试全部粘贴才能成功。
由于对于相同的合约、参数、盐,create2 计算所得的合约地址都是相同的,因此我们就可以通过 create2 与 selfdestruct 相结合,在同一个地址上多次部署合约。我在这篇文章中有详细介绍。
本文只介绍了 create2 的使用方法,其实它的应用场景还有很多,比如:
感兴趣的同学可以多看看学习学习。
<100 subscribers
<100 subscribers
No activity yet