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

Subscribe to cyptoJune

Subscribe to cyptoJune
<100 subscribers
<100 subscribers
在uniV2中,关于erc20的合约其实有两部分,一个是erc20合约的接口部分,一个是接口合约对应的实现。、
IUniswapV2 ERC20解析
接口合约规定了需要实现的所有erc20标准方法,以下主要是接口合约的代码

UniswapV2 ERC20解析
其实现代码如下:
pragma solidity =0.5.16;
import "./interfaces/IUniswapV2ERC20.sol";
import "./libraries/SafeMath.sol";
contract UniswapV2ERC20 is IUniswapV2ERC20 {
using SafeMath for uint256;
//token名称
string public constant name = "Uniswap V2";
//token缩写
string public constant symbol = "UNI-V2";
//token精度
uint8 public constant decimals = 18;
//累计流动性LP总量
uint256 public totalSupply;
//余额映射
mapping(address => uint256) public balanceOf;
//批准映射
mapping(address => mapping(address => uint256)) public allowance;
//域分割
bytes32 public DOMAIN_SEPARATOR;
// keccak256('Permit(address owner,address spender,uint value,uint nonce,uint deadline)');
bytes32
public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
//nonces映射
mapping(address => uint256) public nonces;
//批准事件
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
//发送事件
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev 构造函数
*/
constructor() public {
uint256 chainId;
// solium-disable-next-line
assembly {
chainId := chainid
}
//EIP712Domain
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
),
keccak256(bytes(name)),
keccak256(bytes("1")),
chainId,
address(this)
)
);
}
function _mint(address to, uint256 value) internal {
totalSupply = totalSupply.add(value);
balanceOf[to] = balanceOf[to].add(value);
emit Transfer(address(0), to, value);
}
function _burn(address from, uint256 value) internal {
balanceOf[from] = balanceOf[from].sub(value);
totalSupply = totalSupply.sub(value);
emit Transfer(from, address(0), value);
}
function _approve(
address owner,
address spender,
uint256 value
) private {
allowance[owner][spender] = value;
emit Approval(owner, spender, value);
}
function _transfer(
address from,
address to,
uint256 value
) private {
balanceOf[from] = balanceOf[from].sub(value);
balanceOf[to] = balanceOf[to].add(value);
emit Transfer(from, to, value);
}
function approve(address spender, uint256 value) external returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
function transfer(address to, uint256 value) external returns (bool) {
_transfer(msg.sender, to, value);
return true;
}
function transferFrom(
address from,
address to,
uint256 value
) external returns (bool) {
if (allowance[from][msg.sender] != uint256(-1)) {
allowance[from][msg.sender] = allowance[from][msg.sender].sub(
value
);
}
_transfer(from, to, value);
return true;
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external {
// solium-disable-next-line security/no-block-members
require(deadline >= block.timestamp, "UniswapV2: EXPIRED");
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(
abi.encode(
PERMIT_TYPEHASH,
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(
recoveredAddress != address(0) && recoveredAddress == owner,
"UniswapV2: INVALID_SIGNATURE"
);
_approve(owner, spender, value);
}
}
以上我只调选核心的函数进行说明

铸币方法:主要是向某个地址发送一定数量的token

销毁方法: 目的是销毁某个地址所持有的token
授权私有方法:修改allowance对应的映射并发出event

私有 转账方法:其逻辑就是将from对应的balanceO减去value,to对应的balanceOf加上value,最后触发Transfer事件。
以上就是erc20的主要常用的方法,实际上UniswapV2 中并没有直接使用上述erc20合约,在智能合约中部署的是配对合约,而配对合约是继承自上述合约。
常见问题解答:
为什么在一些合约中要用call方法,而不是把合约的接口引入进去直接用该方法?

主要原因是想获得某些函数的返回直接结果 来判断该方法执行是否执行成功!
而一些函数的实现并没有按照标准的erc20的要求返回一个是否成功的标识状态。
2. 有小伙伴问在工厂合约中uniswapV2Factory继承了IUniswapV2Factory接口,而IUniswapV2Factory中有八个未实现方法,而uniswapV2Factory中只实现四种方法,为什么没有全部实现接口方法呢?


是这样,在uniswapV2Factory中状态变量的类型是public类型,而public类型是自动会生成对应名称的getter函数。具体可参考这篇文章。
https://learnblockchain.cn/docs/solidity/contracts.html#getter-functions
在uniV2中,关于erc20的合约其实有两部分,一个是erc20合约的接口部分,一个是接口合约对应的实现。、
IUniswapV2 ERC20解析
接口合约规定了需要实现的所有erc20标准方法,以下主要是接口合约的代码

UniswapV2 ERC20解析
其实现代码如下:
pragma solidity =0.5.16;
import "./interfaces/IUniswapV2ERC20.sol";
import "./libraries/SafeMath.sol";
contract UniswapV2ERC20 is IUniswapV2ERC20 {
using SafeMath for uint256;
//token名称
string public constant name = "Uniswap V2";
//token缩写
string public constant symbol = "UNI-V2";
//token精度
uint8 public constant decimals = 18;
//累计流动性LP总量
uint256 public totalSupply;
//余额映射
mapping(address => uint256) public balanceOf;
//批准映射
mapping(address => mapping(address => uint256)) public allowance;
//域分割
bytes32 public DOMAIN_SEPARATOR;
// keccak256('Permit(address owner,address spender,uint value,uint nonce,uint deadline)');
bytes32
public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
//nonces映射
mapping(address => uint256) public nonces;
//批准事件
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
//发送事件
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev 构造函数
*/
constructor() public {
uint256 chainId;
// solium-disable-next-line
assembly {
chainId := chainid
}
//EIP712Domain
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
),
keccak256(bytes(name)),
keccak256(bytes("1")),
chainId,
address(this)
)
);
}
function _mint(address to, uint256 value) internal {
totalSupply = totalSupply.add(value);
balanceOf[to] = balanceOf[to].add(value);
emit Transfer(address(0), to, value);
}
function _burn(address from, uint256 value) internal {
balanceOf[from] = balanceOf[from].sub(value);
totalSupply = totalSupply.sub(value);
emit Transfer(from, address(0), value);
}
function _approve(
address owner,
address spender,
uint256 value
) private {
allowance[owner][spender] = value;
emit Approval(owner, spender, value);
}
function _transfer(
address from,
address to,
uint256 value
) private {
balanceOf[from] = balanceOf[from].sub(value);
balanceOf[to] = balanceOf[to].add(value);
emit Transfer(from, to, value);
}
function approve(address spender, uint256 value) external returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
function transfer(address to, uint256 value) external returns (bool) {
_transfer(msg.sender, to, value);
return true;
}
function transferFrom(
address from,
address to,
uint256 value
) external returns (bool) {
if (allowance[from][msg.sender] != uint256(-1)) {
allowance[from][msg.sender] = allowance[from][msg.sender].sub(
value
);
}
_transfer(from, to, value);
return true;
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external {
// solium-disable-next-line security/no-block-members
require(deadline >= block.timestamp, "UniswapV2: EXPIRED");
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(
abi.encode(
PERMIT_TYPEHASH,
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(
recoveredAddress != address(0) && recoveredAddress == owner,
"UniswapV2: INVALID_SIGNATURE"
);
_approve(owner, spender, value);
}
}
以上我只调选核心的函数进行说明

铸币方法:主要是向某个地址发送一定数量的token

销毁方法: 目的是销毁某个地址所持有的token
授权私有方法:修改allowance对应的映射并发出event

私有 转账方法:其逻辑就是将from对应的balanceO减去value,to对应的balanceOf加上value,最后触发Transfer事件。
以上就是erc20的主要常用的方法,实际上UniswapV2 中并没有直接使用上述erc20合约,在智能合约中部署的是配对合约,而配对合约是继承自上述合约。
常见问题解答:
为什么在一些合约中要用call方法,而不是把合约的接口引入进去直接用该方法?

主要原因是想获得某些函数的返回直接结果 来判断该方法执行是否执行成功!
而一些函数的实现并没有按照标准的erc20的要求返回一个是否成功的标识状态。
2. 有小伙伴问在工厂合约中uniswapV2Factory继承了IUniswapV2Factory接口,而IUniswapV2Factory中有八个未实现方法,而uniswapV2Factory中只实现四种方法,为什么没有全部实现接口方法呢?


是这样,在uniswapV2Factory中状态变量的类型是public类型,而public类型是自动会生成对应名称的getter函数。具体可参考这篇文章。
https://learnblockchain.cn/docs/solidity/contracts.html#getter-functions
No activity yet