文章目錄
- 前提條件
- 部署合約
- 部署工廠和WETH合約
- 部署路由合約(重要環節!!)
- 步驟1 獲取位元組碼
- 步驟2 獲得initCode
- 步驟3 替換路由中的initCode
- 當前部署結果
- 部署前端
- 其他補充(懂solidity的可以看看)
- 添加流動性
- 交換方法
- 工具
- in/out計算公式推導
參考鏈接 崔棉大師的教程
手把手教你部署自己的uniswap交易所
之前部署是跟著崔棉大師的教程走的,但是部署完了,沒法實際使用,添加流動性還是交易會報錯
這里主要是做補充;
前提條件
- 自己有賬號,且申請測驗以太坊 (ropsten直接小狐貍 buy 打開鏈接領,rinkeby需要推特發鏈接再去領取)
- 會使用 remix 部署合約
- 部署前端需會使用 npm / yarn
部署合約
合約源代碼
此處只部署routerV2
- 工廠合約
- WETH
- 路由02
注意事項

部署工廠合約和路由合約時,EVM VERSION 選擇 istanbul, COMPILER CONFIGURATION 中勾選 Enable optimization ;
WETH部署時 EVM VERSION 選擇 default;
工廠合約和WETH合約可以直接部署,路由合約需要修改一個Code
部署工廠和WETH合約
該步驟略, 這兩個直接部署即可
weth代碼中添加以下代碼,便于直接獲取任意數量WETH,方便測驗大額交易
//直接獲取WETH
function mint(uint _value)public payable{
balanceOf[msg.sender] += _value;
Deposit(msg.sender, _value);
}
部署路由合約(重要環節!!)
initCode
重要環節,我剛開始部署的時候就是這一步不清楚導致的部署的合約,無法使用, 至于為啥會不一樣,不太清楚!
步驟1 獲取位元組碼
編譯工廠合約,獲取pair的位元組碼, 看下圖

獲得類似這樣的
得到以下結構的內容, 只需要object欄位的內容; 復制
{
"linkReferences": {},
"object": "取這里的內容",
"opcodes": "-",
"sourceMap": "-"
}
步驟2 獲得initCode
打開網址 http://emn178.github.io/online-tools/keccak_256.html
將剛才得到的object欄位內容粘貼,選擇input type HEX
如下圖

步驟3 替換路由中的initCode
將 96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f
替換成
de683b3097cb455dd2d3ea50f1f95386fdeca75180cc01bb6b12207c44272e17 (步驟2中獲取的)
// 路由中該代碼
// calculates the CREATE2 address for a pair without making any external calls
function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
(address token0, address token1) = sortTokens(tokenA, tokenB);
pair = address(uint(keccak256(abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encodePacked(token0, token1)),
hex'de683b3097cb455dd2d3ea50f1f95386fdeca75180cc01bb6b12207c44272e17' //init code hash
//hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' //init code hash
))));
}
編譯部署即可
當前部署結果
添加流動性
ropsten
https://ropsten.etherscan.io/tx/0x90a860e95f2796b08985b02a1163eccb58efefd053e0a80ebf75cca8f7f5b8fa
rinkeby
https://rinkeby.etherscan.io/tx/0x4c82a23ec995bf404a98e39b490c5e7893945c4341495c7fe6de43b90e646aeb
新賬號,所以rinkeby和ropsten兩個測驗網都是部署的以下地址
工廠
0x2CD020750216583CCF657a0949F0843ec1f73EFE
WETH
0x57E25a96A6dBA2cA02e9C96d08f672574c6E6B13
路由
0x9A36D38C6De905f969C172a85dD362E3Bc36B936
initCode
de683b3097cb455dd2d3ea50f1f95386fdeca75180cc01bb6b12207c44272e17
測驗Token
0xa5BA457F1DfdCC3E65515E69E545292D203b0E76
另外吐槽下…
ropsten 獲取測驗幣最簡便,但是打包忒慢了
rinkeby 打包很快,不過要發推后再領, 有梯子的建議使用這個
部署前端
前端代碼
可以clone最好, 太慢的話就直接下載zip解壓
1、自行下載好原始碼
2、安裝好yarn
3、修改代碼
修改檔案: 專案目錄/uniswap-interface/src/constants/index.ts 第 6 行
export const ROUTER_ADDRESS = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D' //修改成你的路由合約地址
注:
前端代碼可以打開IDE, 然后全域搜索替換成自己部署的資訊,之后編譯代碼就行了!!!
//uniswap官方部署的資訊
工廠
0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f
WETH
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
路由
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
initCode
96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f
將以上4個資訊都替換成自己部署的合約地址
工廠
0x2CD020750216583CCF657a0949F0843ec1f73EFE
WETH
0x57E25a96A6dBA2cA02e9C96d08f672574c6E6B13
路由
0x9A36D38C6De905f969C172a85dD362E3Bc36B936
initCode
de683b3097cb455dd2d3ea50f1f95386fdeca75180cc01bb6b12207c44272e17
如要替換weth,需要注意環境替換
{
mainnet:'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
ropsten:'0xc778417E063141139Fce010982780140Aa0cD5Ab', ( "chainId": 3,)
rinkeby:'0xc778417E063141139Fce010982780140Aa0cD5Ab', ("chainId": 4)
goerli:'0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6',
kovan:'0xd0A1E359811322d97991E03f863a0C30C2cF029C'
}
替換完后,編譯
$ cd uniswap-interface
$ yarn
$ yarn start
//執行 yarn start 后跳出個網頁 http://localhost:3000/#/swap
其他補充(懂solidity的可以看看)
如果會solidity,且看uniswap原始碼的,可以往下看看
崔棉大師有個Uniswap原始碼中文注解的檔案,有需要的可以去購買
添加流動性
添加流動性需要輸入兩個token, 帶ETH的方法,router會幫你轉成WETH,最終實際就是該方法
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
)
交換方法
這里主要說明交易的方法
這里主要歸納為3個方法(帶ETH的方法,router會幫你轉成WETH,最終都是兩個ERC20交換)
說明:
- 交換方法中,不存在買賣的說法,只有in/out; 我下面的方法注釋寫買/賣是為了便于理解
- 交換方法中所有引數in/out 都是相對于路由自己
//方法1 需要獲取精確的輸出,輸入不確定金額(也可以理解買,如購買100個UNI TOKEN,需要未知weth)
function swapTokensForExactTokens(
uint amountOut,//期望輸出金額
uint amountInMax,//最大輸入 (如果到你打包的交易時,如果實際需要輸入的金額大于該金額,交易失敗!)
address[] calldata path,
address to,
uint deadline
)
//方法2 通過輸入金額,輸出不確定金額 (也可以理解為賣, 如賣掉100個UNI TOKEN,可以獲得未知WETH)
function swapExactTokensForTokens(
uint amountIn,//實際輸入金額
uint amountOutMin,//最小輸出 (如果到打包你的交易時,實際輸出小于該金額,交易失敗!)
address[] calldata path,
address to,
uint deadline
)
//方法3 通過輸入金額,輸出不確定金額
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
)
此處說明上面方法2/3 的區別
方法3是以交易對實際獲取到了多少代幣,去呼叫交易對的交換
方法2是以用戶呼叫轉賬輸入的金額(該金額可能是錯的,比如沒有實際輸入,或者扣了手續費),去呼叫交易對交換
比如黑幣,在你轉賬的時候,扣除你20% 30%等
如果使用方法2 是無法成功 會提示UniswapV2: K
如果使用方法3,把amountOutMin填0,那么這個交易一定可以成功,哪怕交易對只給回傳0.00000000001個以太坊
工具
// returns sorted token addresses, used to handle return values from pairs sorted in this order
//兩個地址排序
function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES');
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
}
// calculates the CREATE2 address for a pair without making any external calls
// 計算交易對地址, 注意這個init code hash... 這是個坑
function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
(address token0, address token1) = sortTokens(tokenA, tokenB);
pair = address(uint(keccak256(abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encodePacked(token0, token1)),
hex'de683b3097cb455dd2d3ea50f1f95386fdeca75180cc01bb6b12207c44272e17' // init code hash
))));
}
// fetches and sorts the reserves for a pair
//獲取當前儲備量,回傳值會根據你輸入的token排序
function getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) {
(address token0,) = sortTokens(tokenA, tokenB);
(uint reserve0, uint reserve1,) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
(reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
// given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
//添加流動性時,通過tokenA輸入額,計算tokenB需要輸入多少
function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {
require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
amountB = amountA.mul(reserveB) / reserveA;
}
// given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
//通過in計算out (后面詳細說明)
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
uint amountInWithFee = amountIn.mul(997);
uint numerator = amountInWithFee.mul(reserveOut);
uint denominator = reserveIn.mul(1000).add(amountInWithFee);
amountOut = numerator / denominator;
}
// given an output amount of an asset and pair reserves, returns a required input amount of the other asset
//通過out 計算in (后面詳細說明)
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) {
require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
uint numerator = reserveIn.mul(amountOut).mul(1000);
uint denominator = reserveOut.sub(amountOut).mul(997);
amountIn = (numerator / denominator).add(1);
}
in/out計算公式推導
注:
此處主要說getAmountOut 和 getAmountIn 兩個方法
上面工具中的兩個方法內部演算法是簡化過的,
/**
*
*
* 推導公式
* in 輸入金額, out 輸出金額
* rIn tokenIn的流動性, rOut,tokenOut的流動性
* fee 手續費,注:當前帶入0.997 也就是997/1000
*
* 兩個計算公式實際是一樣的, 只是一個求in,一個求out
* (rIn + in * f) * (rOut - out) = rIn * rOut
*
*
* 由out計算in
* (rIn + in * f) * (rOut - out) = rIn * rOut
* rIn * rOut + in * f * rOut - rIn * out - in * f * out = rIn * rOut
* rIn * out = in * f * rOut - in * f * out
* in = rIn * out / (f * (rOut - out)) + 1 (尾部的 +1應該是避免精度計算,最后一位小了,會成交不了)
*
*
* 由in計算out
* (rIn + in * f) * (rOut - out) = rIn * rOut
* rIn * rOut + in * f * rOut - rIn * out - in * f * out = rIn * rOut
* in * f * rOut = rIn * out + in * f * out
* out = in * f * rOut / rIn + in *f
*
*/
待補充
另外,交易對中,還有個錯誤 UniswapV2: K 提示
該提示主要是校驗交換的金額是否正確,如果黑幣進出賬少了,或者外部修改手續費也無法交易
暫時沒弄清楚簡化公式怎么來的
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/179073.html
標籤:AI
