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)是用户 ...
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 还有一个优点,相...
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)是用户 ...
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 还有一个优点,相...
Smart Contract Developer

Subscribe to xyyme.eth

Subscribe to xyyme.eth
Share Dialog
Share Dialog
<100 subscribers
<100 subscribers
目前NFT白名单校验主要有两种方式
Merkel Tree
签名验证
其中签名验证的详细介绍在这篇文章。
这篇文章我们主要来介绍 Merkle Tree 的实现方法。关于其原理,我们这里就不再多赘述了,不太了解的朋友可以 Google 一下,今天主要讨论一下实现细节。
一般要实现 Merkle Tree 校验,主要有两部分的工作:
后台根据白名单地址生成一个 Merkle Tree,包括 Merkle Root。管理员将生成的 Merkle Root 设置到合约中
前端(或者后端)根据当前的用户,生成一个 Merkle Proof。将 Proof 作为参数传入合约中,与 msg.sender 和之前设置的 Merkle Root 进行校验
这里的校验逻辑可以直接使用 Openzeppelin 的现成合约库,可以看到,需要的参数有:
proof(由链下生成传入,每个用户的 proof 都不同)
root(链下生成好之后设置为状态变量,所有用户均相同)
leaf(由用户的地址生成)
function verify(
bytes32[] memory proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
目前NFT白名单校验主要有两种方式
Merkel Tree
签名验证
其中签名验证的详细介绍在这篇文章。
这篇文章我们主要来介绍 Merkle Tree 的实现方法。关于其原理,我们这里就不再多赘述了,不太了解的朋友可以 Google 一下,今天主要讨论一下实现细节。
一般要实现 Merkle Tree 校验,主要有两部分的工作:
后台根据白名单地址生成一个 Merkle Tree,包括 Merkle Root。管理员将生成的 Merkle Root 设置到合约中
前端(或者后端)根据当前的用户,生成一个 Merkle Proof。将 Proof 作为参数传入合约中,与 msg.sender 和之前设置的 Merkle Root 进行校验
这里的校验逻辑可以直接使用 Openzeppelin 的现成合约库,可以看到,需要的参数有:
proof(由链下生成传入,每个用户的 proof 都不同)
root(链下生成好之后设置为状态变量,所有用户均相同)
leaf(由用户的地址生成)
function verify(
bytes32[] memory proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
那么如何调用 verify 方法呢:
function verify(address addr, bytes32[] calldata _merkleProof) public view returns (bool) {
bytes32 leaf = keccak256(abi.encode(addr));
return MerkleProof.verify(_merkleProof, merkleRoot, leaf);
}
通过 encode 编码之后再进行 hash 运算,便可以生成 Merkle Tree 的一个 leaf,这个 leaf 便是 verify 方法的参数 leaf。注意这里我们是将用户地址作为参数传给合约,实际开发时应该直接使用 msg.sender 作为原始参数,这是为了保证安全(否则不是白名单的地址就可以传递白名单地址来给自己 mint)。
verify 方法会返回一个 boolean 值,代表白名单校验是否成功。
下面来说说链下的工作,前面说过,链下要根据白名单数据生成 Merkle Tree,我们这里使用 Javascript 来完成相关工作。
首先需要安装两个依赖包:
npm install --save keccak256 merkletreejs
实例代码如下:
const { MerkleTree } = require('merkletreejs');
const keccak256 = require('keccak256');
// 白名单地址,这里采用了硬编码,实际开发应该从数据库读取
// 这里我们随机生成几个地址,作为白名单示例
let whitelistAddresses = [
"0x978DCD67B155b3dBecd221Ec0D193f6fa7d3B8c2",
"0x41fed4790A6137083fac595e00090b2D01d012b6",
"0xFbC43c738d17F4d43627B8675A8cdC691A603BB3",
"0xBD925b9Fab6Eb9f713238Cc688A91a7f5c7Ff4c8",
"0xc6c74C251aa41FCB0De4c55fb751eec04f66774A"
]
// 计算 leaf 叶子结点的数据
const leafNodes = whitelistAddresses.map(addr => keccak256(addr));
// 生成 Merkle Tree
const merkleTree = new MerkleTree(leafNodes, keccak256, { sortPairs: true });
// 获取 Merkle Root
const rootHash = merkleTree.getRoot();
// 打印查看 Merkle Tree 全部数据
console.log('Whitelist Merkle Tree\n', merkleTree.toString());
// 打印查看 Root 数据,需要设置到合约中
console.log("Root Hash: ", rootHash.toString('hex'));
// 选择一个白名单地址进行校验
const claimingAddress = keccak256("0xc6c74C251aa41FCB0De4c55fb751eec04f66774A");
// 计算这个地址的 Merkle Proof,注意这就是我们要传给合约的参数 Proof
const hexProof = merkleTree.getHexProof(claimingAddress);
console.log(hexProof);
// 校验
console.log(merkleTree.verify(hexProof, claimingAddress, rootHash));
通过这段代码,我们就可以生成 Merkle Root,以及用户地址对应的 Merkle Proof
一般来说,合约中还会包括一个设置 Root 的方法,便于在合约部署之后,将 Root 设置进去:
function setRoot(bytes32 _root) external onlyOwner {
root = _root;
}
Merkle Tree 白名单校验的代码逻辑就是这些。一般来说,整个白名单业务顺序如下:
根据所有白名单地址计算出 Merkle Root,并设置到合约中
前端会根据用户地址请求后端获取对应 Merkle Proof
将 Proof 传入合约进行校验
那么如何调用 verify 方法呢:
function verify(address addr, bytes32[] calldata _merkleProof) public view returns (bool) {
bytes32 leaf = keccak256(abi.encode(addr));
return MerkleProof.verify(_merkleProof, merkleRoot, leaf);
}
通过 encode 编码之后再进行 hash 运算,便可以生成 Merkle Tree 的一个 leaf,这个 leaf 便是 verify 方法的参数 leaf。注意这里我们是将用户地址作为参数传给合约,实际开发时应该直接使用 msg.sender 作为原始参数,这是为了保证安全(否则不是白名单的地址就可以传递白名单地址来给自己 mint)。
verify 方法会返回一个 boolean 值,代表白名单校验是否成功。
下面来说说链下的工作,前面说过,链下要根据白名单数据生成 Merkle Tree,我们这里使用 Javascript 来完成相关工作。
首先需要安装两个依赖包:
npm install --save keccak256 merkletreejs
实例代码如下:
const { MerkleTree } = require('merkletreejs');
const keccak256 = require('keccak256');
// 白名单地址,这里采用了硬编码,实际开发应该从数据库读取
// 这里我们随机生成几个地址,作为白名单示例
let whitelistAddresses = [
"0x978DCD67B155b3dBecd221Ec0D193f6fa7d3B8c2",
"0x41fed4790A6137083fac595e00090b2D01d012b6",
"0xFbC43c738d17F4d43627B8675A8cdC691A603BB3",
"0xBD925b9Fab6Eb9f713238Cc688A91a7f5c7Ff4c8",
"0xc6c74C251aa41FCB0De4c55fb751eec04f66774A"
]
// 计算 leaf 叶子结点的数据
const leafNodes = whitelistAddresses.map(addr => keccak256(addr));
// 生成 Merkle Tree
const merkleTree = new MerkleTree(leafNodes, keccak256, { sortPairs: true });
// 获取 Merkle Root
const rootHash = merkleTree.getRoot();
// 打印查看 Merkle Tree 全部数据
console.log('Whitelist Merkle Tree\n', merkleTree.toString());
// 打印查看 Root 数据,需要设置到合约中
console.log("Root Hash: ", rootHash.toString('hex'));
// 选择一个白名单地址进行校验
const claimingAddress = keccak256("0xc6c74C251aa41FCB0De4c55fb751eec04f66774A");
// 计算这个地址的 Merkle Proof,注意这就是我们要传给合约的参数 Proof
const hexProof = merkleTree.getHexProof(claimingAddress);
console.log(hexProof);
// 校验
console.log(merkleTree.verify(hexProof, claimingAddress, rootHash));
通过这段代码,我们就可以生成 Merkle Root,以及用户地址对应的 Merkle Proof
一般来说,合约中还会包括一个设置 Root 的方法,便于在合约部署之后,将 Root 设置进去:
function setRoot(bytes32 _root) external onlyOwner {
root = _root;
}
Merkle Tree 白名单校验的代码逻辑就是这些。一般来说,整个白名单业务顺序如下:
根据所有白名单地址计算出 Merkle Root,并设置到合约中
前端会根据用户地址请求后端获取对应 Merkle Proof
将 Proof 传入合约进行校验
No activity yet