cypto is the future!

Uniswap V2 交易所部署详解
在DeFi世界中,Dex(去中心化交易所)是最核心的一块,Uniswap是整个Dex的龙头,SushiSwap,PancakeSwap 等都是参考,甚至完全fork了Uniswap的整个产品逻辑和代码,也因此好多人也想部署快速部署一个属于自己的Uniswap,个人觉得也是很有必要的,通过自己从零搭建,一步一步完成,在这个过程可以清楚知道Uniswap整个项目工程的结构和产品逻辑,将来也能站在巨人的肩膀上,开发出自己的DeFi乐高! 前置说明:本次部署是参考Uniswap V2的线上版本,同时结合催眠大师的教程总结来的。本次部署的一些前置工作还需要自己提前准备,比如github账号,钱包,测试网代币水龙头,准备部署账户等部署流程步骤:从浏览器中下载合约源码准备部署账户使用remix编译部署合约部署前端代码安装依赖库修改路由地址将代码部署到Github Pages生成自定义的token自定义token导入Uniswap交易所正文 从浏览器中下载合约源码 这次用的是线上版本 工厂合约 路由合约2 路由合约1是可选的,部署的流程是先部署工厂合约,然后将工厂合约的地址复制给路由合约2的构造...
DeFi交易龙头之Uniswap:V2(核心合约-配对合约)
前面介绍了工厂合约,我们知道配对合约其实是需要工厂合约来进行部署的。从代码上来看,配对合约是继承ERC20合约,那么配对合约实际上就是一个遵守Erc20合约的token. 代码解析类型方法增强从上图看,首先是讲SafeMath和UQ112*112 的库方法给到了对应类型上。为什么要赋予uint224呢,这是因为在solidity中没有非整形的类型,但是实际上token的数量会出现小数位,使用库UQ112**112去模拟浮点数。 2. 常量16行中定义了一个最小流动性,在白皮书的3.4中初始化流动性代币供应这节会讲到,结论就是通过保证最小数量的流动性份额,会大大增加上述攻击的成本。具体的原理我会单独在开一篇章节进行讲解。 18行的selector的常量值是transfer(address, uint256)字符串哈希值的前4个字节,这个用于直接使用call方法调用token的转账方法。 22行工厂地址:因为pair合约是通过工厂合约进行部署的,所有会有一个变量专门去存放工厂合约地址。 23到27行主要是token地址和储备量地址相关。主要是存放两个token的地址,便于调用。储备量...

Uniswap V2 交易所部署详解
在DeFi世界中,Dex(去中心化交易所)是最核心的一块,Uniswap是整个Dex的龙头,SushiSwap,PancakeSwap 等都是参考,甚至完全fork了Uniswap的整个产品逻辑和代码,也因此好多人也想部署快速部署一个属于自己的Uniswap,个人觉得也是很有必要的,通过自己从零搭建,一步一步完成,在这个过程可以清楚知道Uniswap整个项目工程的结构和产品逻辑,将来也能站在巨人的肩膀上,开发出自己的DeFi乐高! 前置说明:本次部署是参考Uniswap V2的线上版本,同时结合催眠大师的教程总结来的。本次部署的一些前置工作还需要自己提前准备,比如github账号,钱包,测试网代币水龙头,准备部署账户等部署流程步骤:从浏览器中下载合约源码准备部署账户使用remix编译部署合约部署前端代码安装依赖库修改路由地址将代码部署到Github Pages生成自定义的token自定义token导入Uniswap交易所正文 从浏览器中下载合约源码 这次用的是线上版本 工厂合约 路由合约2 路由合约1是可选的,部署的流程是先部署工厂合约,然后将工厂合约的地址复制给路由合约2的构造...
DeFi交易龙头之Uniswap:V2(核心合约-配对合约)
前面介绍了工厂合约,我们知道配对合约其实是需要工厂合约来进行部署的。从代码上来看,配对合约是继承ERC20合约,那么配对合约实际上就是一个遵守Erc20合约的token. 代码解析类型方法增强从上图看,首先是讲SafeMath和UQ112*112 的库方法给到了对应类型上。为什么要赋予uint224呢,这是因为在solidity中没有非整形的类型,但是实际上token的数量会出现小数位,使用库UQ112**112去模拟浮点数。 2. 常量16行中定义了一个最小流动性,在白皮书的3.4中初始化流动性代币供应这节会讲到,结论就是通过保证最小数量的流动性份额,会大大增加上述攻击的成本。具体的原理我会单独在开一篇章节进行讲解。 18行的selector的常量值是transfer(address, uint256)字符串哈希值的前4个字节,这个用于直接使用call方法调用token的转账方法。 22行工厂地址:因为pair合约是通过工厂合约进行部署的,所有会有一个变量专门去存放工厂合约地址。 23到27行主要是token地址和储备量地址相关。主要是存放两个token的地址,便于调用。储备量...
cypto is the future!

Subscribe to cyptoJune

Subscribe to cyptoJune
Share Dialog
Share Dialog
<100 subscribers
<100 subscribers


简介
白名单是推广NFT项目和奖励早期进入及热情参与者的好方法,有很多方法可以实现白名单发放的机制,每种方法都有自己的优势和劣势。今天就来介绍用Merkle Tree发放白名单。一份拥有800个地址的白名单,更新一次所需的gas fee在市场活跃期,很容易超过1Eth,那么用Merkel Tree的结构结合Hash函数的特性,就能很容易的在节省gas的前提下,进行白名单的发放。
流程逻辑
根据所有白名单地址计算出 Merkle Root,并设置到合约中
前端会根据用户地址请求后端获取对应 Merkle Proof
将 Proof 传入合约进行校验
具体逻辑细节
我们知道,链上空间的存储Storage和计算是很费gas的,不必要的链上存储和计算尽量避免,尽可能通过链下的方式来实现,并通过前后端的配合实现必要信息的校验和计算。
根据所有白名单地址计算出 Merkle Root,并设置到合约中
在实际的开发中,可以通过javascript库merkletree.js根据白名单来链下生成Merkle Tree,包括Merkle Root,然后合约管理员将生成的 Merkle Root 设置到合约中。
一般通过构造器函数或者设置外部函数进行部署时放置:
constructor(string memory name, string memory symbol, bytes32 merkleroot)
ERC721(name, symbol)
{
root = merkleroot;
}
function setRoot(bytes32 _root) external onlyOwner {
root = _root;
}
前端会根据用户地址请求后端获取对应 Merkle Proof
前端(或者后端)根据当前的用户,生成一个 Merkle Proof。将 Proof 作为参数传入合约中,与 msg.sender(实际开发中用msg.sender作为参数进行传入是正确的范式,因为这样保证了安全,不然不是白名单的地址就可以传递白名单地址来给自己 mint) 和之前设置的 Merkle Root 进行校验。
根据步骤一,我们可以通过npm包管理工具install merkletree.js,代码大概如下:
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));
其中,claimingAddress 地址通过hash之后,通过merkleTree.getHexProof就可以拿到对应地址的Merkle Proof
// 选择一个白名单地址进行校验
const claimingAddress = keccak256("0xc6c74C251aa41FCB0De4c55fb751eec04f66774A");
// 计算这个地址的 Merkle Proof,注意这就是我们要传给合约的参数 Proof
const hexProof = merkleTree.getHexProof(claimingAddress);
console.log(hexProof);
将 Proof 传入合约进行校验
合约校验的逻辑如下:

假设如果我们想校验当前地址和L1是否相等,那么通过提取Hash 0-1,Hash 1,以及被测试的地址。然后,按照hash规则,将hash的输出与Top Hash(默克尔Merkle root)进行比较。如果结果相同,那么可以确保L1等于输入地址,也即白名单地址。那么其他节点,其实是不需要知道的,从而可以将其他不相关的节点都可以从链上拿下来,减少链上存储空间的使用,从而达到节省gas的目的。
// 校验
console.log(merkleTree.verify(hexProof, claimingAddress, rootHash));
通过merkleTree.verify函数从而校验是否是白名单!
总结
通过Merkle Tree方法校验白名单: 优点是:经济效益高(对项目方来说),易于验证 缺点是:对用户来说,铸造 gas 成本的略大,以及每次希望改变白名单时,都需要重新设 置Merkle Tree 根Root.
例子
一个简单的利用MerkleTree合约来发放NFT白名单: MerkleTree合约继承了ERC721标准,并利用了MerkleProof库
contract MerkleTree is ERC721 {
bytes32 immutable public root; // Merkle树的根
mapping(address => bool) public mintedAddress; // 记录已经mint的地址
// 构造函数,初始化NFT合集的名称、代号、Merkle树的根
constructor(string memory name, string memory symbol, bytes32 merkleroot)
ERC721(name, symbol)
{
root = merkleroot;
}
// 利用Merkle树验证地址并完成mint
function mint(address account, uint256 tokenId, bytes32[] calldata proof)
external
{
require(_verify(_leaf(account), proof), "Invalid merkle proof"); // Merkle检验通过
require(!mintedAddress[account], "Already minted!"); // 地址没有mint过
_mint(account, tokenId); // mint
mintedAddress[account] = true; // 记录mint过的地址
}
// 计算Merkle树叶子的哈希值
function _leaf(address account)
internal pure returns (bytes32)
{
return keccak256(abi.encodePacked(account));
}
// Merkle树验证,调用MerkleProof库的verify()函数
function _verify(bytes32 leaf, bytes32[] memory proof)
internal view returns (bool)
{
return MerkleProof.verify(proof, root, leaf);
}
}
解释:
状态变量
root存储了Merkle Tree的根,部署合约的时候赋值
mintedAddress是一个mapping,记录了已经mint过的地址,某地址mint成功后进行赋值
函数
构造函数初始化NFT的名称和代号,还有Merkle Tree的root
mint()函数接受地址address,tokenId和proof三个参数。首先验证address是否在白名单中,验证通过则把序号为tokenId的NFT铸造给该地址,并将它记录到mintedAddress。此过程中调用了_leaf()和_verify()函数
_leaf()函数计算了Merkle Tree的叶子地址的哈希
简介
白名单是推广NFT项目和奖励早期进入及热情参与者的好方法,有很多方法可以实现白名单发放的机制,每种方法都有自己的优势和劣势。今天就来介绍用Merkle Tree发放白名单。一份拥有800个地址的白名单,更新一次所需的gas fee在市场活跃期,很容易超过1Eth,那么用Merkel Tree的结构结合Hash函数的特性,就能很容易的在节省gas的前提下,进行白名单的发放。
流程逻辑
根据所有白名单地址计算出 Merkle Root,并设置到合约中
前端会根据用户地址请求后端获取对应 Merkle Proof
将 Proof 传入合约进行校验
具体逻辑细节
我们知道,链上空间的存储Storage和计算是很费gas的,不必要的链上存储和计算尽量避免,尽可能通过链下的方式来实现,并通过前后端的配合实现必要信息的校验和计算。
根据所有白名单地址计算出 Merkle Root,并设置到合约中
在实际的开发中,可以通过javascript库merkletree.js根据白名单来链下生成Merkle Tree,包括Merkle Root,然后合约管理员将生成的 Merkle Root 设置到合约中。
一般通过构造器函数或者设置外部函数进行部署时放置:
constructor(string memory name, string memory symbol, bytes32 merkleroot)
ERC721(name, symbol)
{
root = merkleroot;
}
function setRoot(bytes32 _root) external onlyOwner {
root = _root;
}
前端会根据用户地址请求后端获取对应 Merkle Proof
前端(或者后端)根据当前的用户,生成一个 Merkle Proof。将 Proof 作为参数传入合约中,与 msg.sender(实际开发中用msg.sender作为参数进行传入是正确的范式,因为这样保证了安全,不然不是白名单的地址就可以传递白名单地址来给自己 mint) 和之前设置的 Merkle Root 进行校验。
根据步骤一,我们可以通过npm包管理工具install merkletree.js,代码大概如下:
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));
其中,claimingAddress 地址通过hash之后,通过merkleTree.getHexProof就可以拿到对应地址的Merkle Proof
// 选择一个白名单地址进行校验
const claimingAddress = keccak256("0xc6c74C251aa41FCB0De4c55fb751eec04f66774A");
// 计算这个地址的 Merkle Proof,注意这就是我们要传给合约的参数 Proof
const hexProof = merkleTree.getHexProof(claimingAddress);
console.log(hexProof);
将 Proof 传入合约进行校验
合约校验的逻辑如下:

假设如果我们想校验当前地址和L1是否相等,那么通过提取Hash 0-1,Hash 1,以及被测试的地址。然后,按照hash规则,将hash的输出与Top Hash(默克尔Merkle root)进行比较。如果结果相同,那么可以确保L1等于输入地址,也即白名单地址。那么其他节点,其实是不需要知道的,从而可以将其他不相关的节点都可以从链上拿下来,减少链上存储空间的使用,从而达到节省gas的目的。
// 校验
console.log(merkleTree.verify(hexProof, claimingAddress, rootHash));
通过merkleTree.verify函数从而校验是否是白名单!
总结
通过Merkle Tree方法校验白名单: 优点是:经济效益高(对项目方来说),易于验证 缺点是:对用户来说,铸造 gas 成本的略大,以及每次希望改变白名单时,都需要重新设 置Merkle Tree 根Root.
例子
一个简单的利用MerkleTree合约来发放NFT白名单: MerkleTree合约继承了ERC721标准,并利用了MerkleProof库
contract MerkleTree is ERC721 {
bytes32 immutable public root; // Merkle树的根
mapping(address => bool) public mintedAddress; // 记录已经mint的地址
// 构造函数,初始化NFT合集的名称、代号、Merkle树的根
constructor(string memory name, string memory symbol, bytes32 merkleroot)
ERC721(name, symbol)
{
root = merkleroot;
}
// 利用Merkle树验证地址并完成mint
function mint(address account, uint256 tokenId, bytes32[] calldata proof)
external
{
require(_verify(_leaf(account), proof), "Invalid merkle proof"); // Merkle检验通过
require(!mintedAddress[account], "Already minted!"); // 地址没有mint过
_mint(account, tokenId); // mint
mintedAddress[account] = true; // 记录mint过的地址
}
// 计算Merkle树叶子的哈希值
function _leaf(address account)
internal pure returns (bytes32)
{
return keccak256(abi.encodePacked(account));
}
// Merkle树验证,调用MerkleProof库的verify()函数
function _verify(bytes32 leaf, bytes32[] memory proof)
internal view returns (bool)
{
return MerkleProof.verify(proof, root, leaf);
}
}
解释:
状态变量
root存储了Merkle Tree的根,部署合约的时候赋值
mintedAddress是一个mapping,记录了已经mint过的地址,某地址mint成功后进行赋值
函数
构造函数初始化NFT的名称和代号,还有Merkle Tree的root
mint()函数接受地址address,tokenId和proof三个参数。首先验证address是否在白名单中,验证通过则把序号为tokenId的NFT铸造给该地址,并将它记录到mintedAddress。此过程中调用了_leaf()和_verify()函数
_leaf()函数计算了Merkle Tree的叶子地址的哈希
_verify()函数调用了MerkleProof库的verify()函数,来进行Merkle Tree验证
_verify()函数调用了MerkleProof库的verify()函数,来进行Merkle Tree验证
No activity yet