
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!

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的地址,便于调用。储备量...
Share Dialog
Share Dialog
cypto is the future!

Subscribe to cyptoJune

Subscribe to cyptoJune
<100 subscribers
<100 subscribers
过去的两年,关于NFT的话题一直很火热,随着NFT以各种形式出圈,在可见的未来,nft会以各种形式出圈并火爆。
这篇文章主要是从技术视角来给大家介绍一下nft主流的玩法-发行,盲盒,公开发售等流程步骤,这也是目前市场上大部分项目的玩法。
本次文章nft开发主要分为两部分: 1. 合约部分 2. 拼图部分。
本次Nft开发系列我也准备分为两篇文章进行讲解,今天主要讲解合约部分。
建立和eth测试网互动的智能合约
Nft要能够被mint
Nft能够设定总量
Nft设定每个地址最大持有量
Nft要能够限定单次的mint的量
Nft要能够设定开关去公开发售
拼图部分主要是以下功能:
如何快速制作多种拼图以及meta资料
如何上传ipfs星际网络系统(测试网)
以上就是本次文章需要讲解的技术点。
metamask钱包插件
remix在线编译环境
具体钱包的安装以及remix的使用,就不再这边讲解。
首先将本次合约部分的源码直接粘贴在remix中进行编译。源码如下:
以上就是合约源码,其实很简单,也不放在git上了,直接贴出来了。下面我就重点源码做一些解

对erc721的扩展部分和权限部分进行引用,同时进行继承,重写erc721的部分方法,下文会提到。

这些状态变量主要是用来对Nft的一些属性进行设置,包括总量,每个地址最多mint的数量等。

上面是铸造方法的实现,先进行铸造前的条件校验,然后调用erc721的_safemint安全方法进行铸造。
将合约代码进行部署,其中有几个注意的点

同时将测试网络切到Rinkeby测试网,同时需要提前通过faucet进行测试币的领取。领取地址:
https://faucets.chain.link/rinkeby
https://fauceth.komputing.org/
部署完成之后,会出现

在我们mint之前,需要进行开启铸造开关(红色框内),点击将状态变量变成true.
红色函数是铸造方法,填入数量1,同时因为我们在合约的状态变量设置了最小Mint的价格是0.3ether,因此我们需要在msg.value中附带ether数量从而完成mint。

因为红色框中是不允许输入小数的,因此,通过ether网站进行费用单位转换

将0.3ether输入,从而得到Gwei的数量,将该数量赋值到以上红框中进行mint。成功之后,就会在ipfs测试网看到一张编号为0的图片。ipfs测试网地址:
https://testnets.opensea.io/assets/0x6ff2ed061397b69f4c9af4d61436d1907ba097fb/0

至此,我们nft开发中的合约部分已经完成。
过去的两年,关于NFT的话题一直很火热,随着NFT以各种形式出圈,在可见的未来,nft会以各种形式出圈并火爆。
这篇文章主要是从技术视角来给大家介绍一下nft主流的玩法-发行,盲盒,公开发售等流程步骤,这也是目前市场上大部分项目的玩法。
本次文章nft开发主要分为两部分: 1. 合约部分 2. 拼图部分。
本次Nft开发系列我也准备分为两篇文章进行讲解,今天主要讲解合约部分。
建立和eth测试网互动的智能合约
Nft要能够被mint
Nft能够设定总量
Nft设定每个地址最大持有量
Nft要能够限定单次的mint的量
Nft要能够设定开关去公开发售
拼图部分主要是以下功能:
如何快速制作多种拼图以及meta资料
如何上传ipfs星际网络系统(测试网)
以上就是本次文章需要讲解的技术点。
metamask钱包插件
remix在线编译环境
具体钱包的安装以及remix的使用,就不再这边讲解。
首先将本次合约部分的源码直接粘贴在remix中进行编译。源码如下:
以上就是合约源码,其实很简单,也不放在git上了,直接贴出来了。下面我就重点源码做一些解

对erc721的扩展部分和权限部分进行引用,同时进行继承,重写erc721的部分方法,下文会提到。

这些状态变量主要是用来对Nft的一些属性进行设置,包括总量,每个地址最多mint的数量等。

上面是铸造方法的实现,先进行铸造前的条件校验,然后调用erc721的_safemint安全方法进行铸造。
将合约代码进行部署,其中有几个注意的点

同时将测试网络切到Rinkeby测试网,同时需要提前通过faucet进行测试币的领取。领取地址:
https://faucets.chain.link/rinkeby
https://fauceth.komputing.org/
部署完成之后,会出现

在我们mint之前,需要进行开启铸造开关(红色框内),点击将状态变量变成true.
红色函数是铸造方法,填入数量1,同时因为我们在合约的状态变量设置了最小Mint的价格是0.3ether,因此我们需要在msg.value中附带ether数量从而完成mint。

因为红色框中是不允许输入小数的,因此,通过ether网站进行费用单位转换

将0.3ether输入,从而得到Gwei的数量,将该数量赋值到以上红框中进行mint。成功之后,就会在ipfs测试网看到一张编号为0的图片。ipfs测试网地址:
https://testnets.opensea.io/assets/0x6ff2ed061397b69f4c9af4d61436d1907ba097fb/0

至此,我们nft开发中的合约部分已经完成。
// SPDX-License-Identifier: MIT
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
contract NftMeta is ERC721Enumerable, Ownable {
using Strings for uint256;
// 是否准许nft开卖-开关
bool public _isSaleActive = false;
// 初始化盲盒,等到一定时机可以随机开箱,变成true
bool public _revealed = false;
// nft的总数量
uint256 public constant MAX_SUPPLY = 10;
// 铸造Nft的价格
uint256 public mintPrice = 0.3 ether;
// 铸造的钱包最多只能有一个nft数量
uint256 public maxBalance = 1;
// 一次mint的nft的数量
uint256 public maxMint = 1;
// 盲盒开关打开后,需要显示开箱的图片的base地址
string baseURI;
// 盲盒图片的meta,json地址,后文会提到
string public notRevealedUri;
// 默认地址的扩展类型
string public baseExtension = ".json";
mapping(uint256 => string) private _tokenURIs;
// 构造器
constructor(string memory initBaseURI, string memory initNotRevealedUri)
ERC721("Nft Meta", "NM") // 实现了ERC721的父类构造器,是子类继承的一种实现方式
{
setBaseURI(initBaseURI);
setNotRevealedURI(initNotRevealedUri);
}
// 外部地址进行铸造nft的函数调用
function mintNftMeta(uint256 tokenQuantity) public payable {
// 校验总供应量+每次铸造的数量<= nft的总数量
require(
totalSupply() + tokenQuantity <= MAX_SUPPLY,
"Sale would exceed max supply"
);
// 校验是否开启开卖状态
require(_isSaleActive, "Sale must be active to mint NicMetas");
// 校验铸造的钱包地址中的nft的数量 + 本次铸造的数量 <= 该钱包最大拥有的nft的数量
require(
balanceOf(msg.sender) + tokenQuantity <= maxBalance,
"Sale would exceed max balance"
);
// 校验本次铸造的数量*铸造的价格 <= 本次消息附带的eth的数量
require(
tokenQuantity * mintPrice <= msg.value,
"Not enough ether sent"
);
// 校验本次铸造的数量 <= 本次铸造的最大数量
require(tokenQuantity <= maxMint, "Can only mint 1 tokens at a time");
// 以上校验条件满足,进行nft的铸造
_mintNftMeta(tokenQuantity);
}
// 进行铸造
function _mintNftMeta(uint256 tokenQuantity) internal {
for (uint256 i = 0; i < tokenQuantity; i++) {
// mintIndex是铸造nft的序号,按照总供应量从0开始累加
uint256 mintIndex = totalSupply();
if (totalSupply() < MAX_SUPPLY) {
// 调用erc721的安全铸造方法进行调用
_safeMint(msg.sender, mintIndex);
}
}
}
// 返回每个nft地址的Uri,这里包含了nft的整个信息,包括名字,描述,属性等
function tokenURI(uint256 tokenId)
public
view
virtual
override
returns (string memory)
{
require(
_exists(tokenId),
"ERC721Metadata: URI query for nonexistent token"
);
// 盲盒还没开启,那么默认是一张黑色背景图片或者其他图片
if (_revealed == false) {
return notRevealedUri;
}
string memory _tokenURI = _tokenURIs[tokenId];
string memory base = _baseURI();
// If there is no base URI, return the token URI.
if (bytes(base).length == 0) {
return _tokenURI;
}
// If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
if (bytes(_tokenURI).length > 0) {
return string(abi.encodePacked(base, _tokenURI));
}
// If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
return
string(abi.encodePacked(base, tokenId.toString(), baseExtension));
}
// internal
function _baseURI() internal view virtual override returns (string memory) {
return baseURI;
}
//only owner
function flipSaleActive() public onlyOwner {
_isSaleActive = !_isSaleActive;
}
function flipReveal() public onlyOwner {
_revealed = !_revealed;
}
function setMintPrice(uint256 _mintPrice) public onlyOwner {
mintPrice = _mintPrice;
}
function setNotRevealedURI(string memory _notRevealedURI) public onlyOwner {
notRevealedUri = _notRevealedURI;
}
function setBaseURI(string memory _newBaseURI) public onlyOwner {
baseURI = _newBaseURI;
}
function setBaseExtension(string memory _newBaseExtension)
public
onlyOwner
{
baseExtension = _newBaseExtension;
}
function setMaxBalance(uint256 _maxBalance) public onlyOwner {
maxBalance = _maxBalance;
}
function setMaxMint(uint256 _maxMint) public onlyOwner {
maxMint = _maxMint;
}
function withdraw(address to) public onlyOwner {
uint256 balance = address(this).balance;
payable(to).transfer(balance);
}
}
// SPDX-License-Identifier: MIT
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
contract NftMeta is ERC721Enumerable, Ownable {
using Strings for uint256;
// 是否准许nft开卖-开关
bool public _isSaleActive = false;
// 初始化盲盒,等到一定时机可以随机开箱,变成true
bool public _revealed = false;
// nft的总数量
uint256 public constant MAX_SUPPLY = 10;
// 铸造Nft的价格
uint256 public mintPrice = 0.3 ether;
// 铸造的钱包最多只能有一个nft数量
uint256 public maxBalance = 1;
// 一次mint的nft的数量
uint256 public maxMint = 1;
// 盲盒开关打开后,需要显示开箱的图片的base地址
string baseURI;
// 盲盒图片的meta,json地址,后文会提到
string public notRevealedUri;
// 默认地址的扩展类型
string public baseExtension = ".json";
mapping(uint256 => string) private _tokenURIs;
// 构造器
constructor(string memory initBaseURI, string memory initNotRevealedUri)
ERC721("Nft Meta", "NM") // 实现了ERC721的父类构造器,是子类继承的一种实现方式
{
setBaseURI(initBaseURI);
setNotRevealedURI(initNotRevealedUri);
}
// 外部地址进行铸造nft的函数调用
function mintNftMeta(uint256 tokenQuantity) public payable {
// 校验总供应量+每次铸造的数量<= nft的总数量
require(
totalSupply() + tokenQuantity <= MAX_SUPPLY,
"Sale would exceed max supply"
);
// 校验是否开启开卖状态
require(_isSaleActive, "Sale must be active to mint NicMetas");
// 校验铸造的钱包地址中的nft的数量 + 本次铸造的数量 <= 该钱包最大拥有的nft的数量
require(
balanceOf(msg.sender) + tokenQuantity <= maxBalance,
"Sale would exceed max balance"
);
// 校验本次铸造的数量*铸造的价格 <= 本次消息附带的eth的数量
require(
tokenQuantity * mintPrice <= msg.value,
"Not enough ether sent"
);
// 校验本次铸造的数量 <= 本次铸造的最大数量
require(tokenQuantity <= maxMint, "Can only mint 1 tokens at a time");
// 以上校验条件满足,进行nft的铸造
_mintNftMeta(tokenQuantity);
}
// 进行铸造
function _mintNftMeta(uint256 tokenQuantity) internal {
for (uint256 i = 0; i < tokenQuantity; i++) {
// mintIndex是铸造nft的序号,按照总供应量从0开始累加
uint256 mintIndex = totalSupply();
if (totalSupply() < MAX_SUPPLY) {
// 调用erc721的安全铸造方法进行调用
_safeMint(msg.sender, mintIndex);
}
}
}
// 返回每个nft地址的Uri,这里包含了nft的整个信息,包括名字,描述,属性等
function tokenURI(uint256 tokenId)
public
view
virtual
override
returns (string memory)
{
require(
_exists(tokenId),
"ERC721Metadata: URI query for nonexistent token"
);
// 盲盒还没开启,那么默认是一张黑色背景图片或者其他图片
if (_revealed == false) {
return notRevealedUri;
}
string memory _tokenURI = _tokenURIs[tokenId];
string memory base = _baseURI();
// If there is no base URI, return the token URI.
if (bytes(base).length == 0) {
return _tokenURI;
}
// If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
if (bytes(_tokenURI).length > 0) {
return string(abi.encodePacked(base, _tokenURI));
}
// If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
return
string(abi.encodePacked(base, tokenId.toString(), baseExtension));
}
// internal
function _baseURI() internal view virtual override returns (string memory) {
return baseURI;
}
//only owner
function flipSaleActive() public onlyOwner {
_isSaleActive = !_isSaleActive;
}
function flipReveal() public onlyOwner {
_revealed = !_revealed;
}
function setMintPrice(uint256 _mintPrice) public onlyOwner {
mintPrice = _mintPrice;
}
function setNotRevealedURI(string memory _notRevealedURI) public onlyOwner {
notRevealedUri = _notRevealedURI;
}
function setBaseURI(string memory _newBaseURI) public onlyOwner {
baseURI = _newBaseURI;
}
function setBaseExtension(string memory _newBaseExtension)
public
onlyOwner
{
baseExtension = _newBaseExtension;
}
function setMaxBalance(uint256 _maxBalance) public onlyOwner {
maxBalance = _maxBalance;
}
function setMaxMint(uint256 _maxMint) public onlyOwner {
maxMint = _maxMint;
}
function withdraw(address to) public onlyOwner {
uint256 balance = address(this).balance;
payable(to).transfer(balance);
}
}
No activity yet