乐趣区

关于区块链:在以太坊测试网络部署-uniswap-v2-去中心化交易所

1. 筹备工作

Uniswap 是以太坊最为风行的去中心化交易所,它的代码齐全开源,本文将以 uniswap v2 版本为例,解说如何将 uniswap v2 智能合约部署到以太坊 goerli 测试网络,并且搭建前端进行操作。uniswap-v2 版本智能合约局部的代码寄存在 uniswap-v2-core 和 uniswap-v2-periphery 两个仓库,编译智能合约须要 node@>=10 版本。

首先装置 nodejs,并指定版本 node@>=10

sudo apt update -y
sudo apt install curl git npm -y
sudo npm install -g n yarn -y
sudo n 10 && PATH="$PATH"

而后将 clone 两个智能合约的代码仓库到本地:

git clone https://github.com/Uniswap/uniswap-v2-core.git
git clone https://github.com/Uniswap/uniswap-v2-periphery.git

以及 clone uniswap-interface 前端仓库,把 tag 切换到 v2.6.5,因为后续的版本推出了 UNI 代币和治理性能,这里不进行部署。

git clone https://github.com/Uniswap/uniswap-interface.git
cd uniswap-interface && git checkout v2.6.5

咱们还须要筹备一个凋谢了 JSON RPC API 的以太坊节点,嫌麻烦能够去 https://infura.io 申请一个收费的 API Key。以及一个领有足够 ETH 余额的以太坊地址,goerli 测试网络能够关上 https://faucet.goerli.mudit.blog 水龙头为你的地址获取测试的 ETH 代币。

当初筹备工作实现了,上面开始编译并且部署智能合约。

2. 部署合约

因为智能合约代码寄存在两个仓库,不便对立部署,咱们先创立一个文件夹 uniswap-contracts 保留后续编译的智能合约代码。

mkdir uniswap-contracts

以后的目录构造如下:

root@54ab88b47042:~# ls
uniswap-contracts  uniswap-interface  uniswap-v2-core  uniswap-v2-periphery

接下来咱们别离编译两个我的项目的智能合约代码,而后拷贝到 uniswap-contracts 目录。

2.1 编译合约

首先是 uniswap-v2-core 我的项目,进入目录后拉取依赖而后编译:

cd uniswap-v2-core
yarn && yarn compile

编译后的代码寄存在 build 目录,咱们须要把它拷贝至之前创立的 uniswap-contracts 目录。

cp -r build ../uniswap-contracts
cd ..

接下来编译 uniswap-v2-periphery 我的项目,也是雷同的步骤,最初将编译后的代码拷贝到 uniswap-contracts 目录:

cd uniswap-v2-periphery
yarn && yarn compile
cp -r build ../uniswap-contracts
cd ..

2.2 部署合约

编译好的合约代码咱们曾经全副拷贝到 uniswap-contracts 目录。接下来就是部署合约了,这一步略微麻烦一些,须要咱们编写一个脚本。

首先在 uniswap-contracts 目录下创立一个文本文件 deploy.js,并将上面的代码拷贝进去。

留神:常量 endpointhexPrivateKey 请自行批改,并保障地址外面有足够的 ETH 用于领取 GAS 费用。

const Web3 = require('web3')
const WETH9 = require('./build/WETH9.json')
const UniswapV2Pair = require('./build/UniswapV2Pair.json')
const UniswapV2Factory = require('./build/UniswapV2Factory.json')
const UniswapV2Router01 = require('./build/UniswapV2Router01.json')
const UniswapV2Router02 = require('./build/UniswapV2Router02.json')

const endpoint = 'https://goerli.infura.io/v3/5c5a4a14c82f4d6e852b7cc29b2cbb6e';
const hexPrivateKey = '0xfad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19';

async function sendTransaction(web3, chainId, account, data, nonce, gasPrice) {
    const message = {
        from: account.address,
        gas: 5000000,
        gasPrice: gasPrice,
        data: data.startsWith('0x') ? data : '0x' + data,
        nonce: nonce,
        chainId: chainId
    }
    const transaction = await account.signTransaction(message)
    return web3.eth.sendSignedTransaction(transaction.rawTransaction)
}

(async () => {const options = { timeout: 1000 * 30}
    const web3 = new Web3(new Web3.providers.HttpProvider(endpoint, options))
    const account = web3.eth.accounts.privateKeyToAccount(hexPrivateKey)

    const chainId = await web3.eth.getChainId()
    const gasPrice = await web3.eth.getGasPrice()
    let nonce = await web3.eth.getTransactionCount(account.address)

    // deploy WETH contract
    let weth = null
    {const contract = new web3.eth.Contract(WETH9.abi)
        const data = contract.deploy({data: WETH9.bytecode}).encodeABI()
        const receipt = await sendTransaction(web3, chainId, account, data, nonce, gasPrice)
        console.info('WETH:', weth = receipt.contractAddress)
        nonce = nonce + 1
    }

    // deploy UniswapV2Factory contract
    let factory = null
    {const contract = new web3.eth.Contract(UniswapV2Factory.abi)
        const options = {data: UniswapV2Factory.bytecode, arguments: [account.address] }
        const data = contract.deploy(options).encodeABI()
        const receipt = await sendTransaction(web3, chainId, account, data, nonce, gasPrice)
        console.info('UniswapV2Factory:', factory = receipt.contractAddress)
        nonce = nonce + 1
    }

    // deploy UniswapV2Router01 contract
    {const contract = new web3.eth.Contract(UniswapV2Router01.abi)
        const options = {data: UniswapV2Router01.bytecode, arguments: [factory, weth] }
        const data = contract.deploy(options).encodeABI()
        const receipt = await sendTransaction(web3, chainId, account, data, nonce, gasPrice)
        console.info('UniswapV2Router01:', receipt.contractAddress)
        nonce = nonce + 1
    }

    // deploy UniswapV2Router02 contract
    {const contract = new web3.eth.Contract(UniswapV2Router02.abi)
        const options = {data: UniswapV2Router02.bytecode, arguments: [factory, weth] }
        const data = contract.deploy(options).encodeABI()
        const receipt = await sendTransaction(web3, chainId, account, data, nonce, gasPrice)
        console.info('UniswapV2Router02:', receipt.contractAddress)
        nonce = nonce + 1
    }

    let data = UniswapV2Pair.bytecode
    if (!data.startsWith('0x')) data = '0x' + data
    console.info('INIT_CODE_HASH:', web3.utils.keccak256(data))
})()

而后再拉取依赖:

npm init -f && npm install web3

最初执行部署合约脚本:

node deploy.js 

稍等片刻终端就会输出部署后的合约地址了,如下所示:

root@7f704ee78d9d:~/uniswap-contracts# node deploy.js 
WETH: 0x3bBb875C856b5607DF7740fBd3a40B2B443D6597
UniswapV2Factory: 0xf94B5efD90972e5a5e77b5E3dE5236333CedCe6F
UniswapV2Router01: 0x41Db8E670e03d864A449ef1106537E6ca0C18dEC
UniswapV2Router02: 0x81A14364BF285aeA0BAFf5925670a4bDBD575E99
INIT_CODE_HASH: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f

到这里合约就部署实现了,把终端输入的合约地址记录下来,部署前端的时候须要进行配置。

3. 部署前端

智能合约曾经部署到测试网络,最初咱们运行前端看看是否失常工作了吧。首先进入 uniswap-interface 目录而后拉取依赖:

cd uniswap-interface
yarn

因为咱们部署了新的合约,而前端配置外面还是 uniswap 官网的合约地址,所以须要进行如下批改:

  1. 批改 uniswap-interface/src/constants/index.ts 文件中 ROUTER_ADDRESS 的值为 ${UniswapV2Router02}
  2. 批改 uniswap-interface/src/state/swap/hooks.ts 文件中 BAD_RECIPIENT_ADDRESSES 数组的值为 [${UniswapV2Factory}, ${UniswapV2Router01}, ${UniswapV2Router02}]。
  3. 批改 uniswap-interface/node_modules/@uniswap/sdk/dist/sdk.cjs.development.jsuniswap-interface/node_modules/@uniswap/sdk/dist/sdk.esm.js 文件中 FACTORY_ADDRESS${UniswapV2Factory}
  4. 批改 uniswap-interface/node_modules/@uniswap/sdk/dist/sdk.cjs.development.jsuniswap-interface/node_modules/@uniswap/sdk/dist/sdk.esm.js 文件中 INIT_CODE_HASH${INIT_CODE_HASH}
  5. 批改 uniswap-interface/node_modules/@uniswap/sdk/dist/sdk.cjs.development.jsuniswap-interface/node_modules/@uniswap/sdk/dist/sdk.esm.js 文件中全局变量 WETH,将其中 keyGÖRLIToken 类型的地址批改为 ${WETH}

批改实现之后运行前端程序:

yarn start
Starting the development server...

Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

Files successfully emitted, waiting for typecheck results...

Compiled successfully!

You can now view @uniswap/interface in the browser.

  Local:            http://localhost:3000
  On Your Network:  http://172.23.227.86:3000

Note that the development build is not optimized.
To create a production build, use yarn build.

最初关上浏览器拜访地址 http://localhost:3000 查看成果。

  1. 点击 Connect to a wallet,抉择 MetaMask 钱包 (须要事后装置浏览器插件)。
  2. 切换钱包网络至 Goerli 测试网络,因为咱们的智能合约部署在下面。
  3. 当初能够开始增加流动性或者交易了。

4. 增加代币列表

当初 uniswap v2 曾经胜利部署了,如果咱们想要在交易所增加本人的代币该怎么办呢?上面我就来一步一步解说如何增加自定义代币。

uniswap v2 前端显示的代币列表配置在 uniswap-interface/src/constants/lists.ts 文件中的 DEFAULT_LIST_OF_LISTS 常量数组,数组元素的值能够是一个 http 地址 ifps 地址 ENS name。地址返回后果必须是指定构造的 json 文件,咱们能够通过向 DEFAULT_LIST_OF_LISTS 常量数组增加新的地址达到增加自定义代币的目标。

上面就来通过增加一个代币详细描述这个过程。

1. 创立 tokens.json 文件

文件格式如下:

{
    "name": "Test Tokens List",
    "version": {
        "major": 1,
        "minor": 0,
        "patch": 0
    },
    "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0/logo.png",
    "timestamp": "2021-07-25 00:00:00.000+00:00",
    "tokens": [
        {
            "chainId": 5,
            "address": "0x499d11E0b6eAC7c0593d8Fb292DCBbF815Fb29Ae",
            "name": "Matic Token",
            "symbol": "MATIC",
            "decimals": 18,
            "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0/logo.png"
        }
    ]
}

tokens 字段是一个数组类型,它负责形容代币列表蕴含的所有代币。咱们在外面增加了一个名为 Matic Token 的代币 (当然也能够增加多个代币),符号是 MATIC,合约地址是 0x499d11E0b6eAC7c0593d8Fb292DCBbF815Fb29Ae。请留神外面有一个 chainId 字段,值为 5,这是因为 goerli 测试网络的 chainId 是 5,前端只有在钱包连贯网络的 chainId 为 5 时才会显示这个代币。其它以太坊网络 chainId 的值请参考:https://besu.hyperledger.org/en/stable/Concepts/NetworkID-And-ChainID。

2. 上传 tokens.json 文件

tokens.json 文件实现编辑后就能够上传至服务器了。轻易上传到哪里都能够,比方你本人的 HTTP 文件服务器,只有可能公网拜访就行。我将它上传到了 gist.github.com (须要翻墙),拜访地址是:https://gist.githubusercontent.com/pygdev/ec497ed8bba8008c2512b3f241bfb5ef/raw/ac6d0286e3e5a39499a71575a065f16787782a70/tokens.json。

3. 在前端增加 Tokens List

首先关上 uniswap v2 前端页面,连贯钱包,并切换至 goerli 网络,而后点击 抉择通证 按钮。

而后在输入框输出 tokens.json 的地址,点击 Add 按钮。

增加 tokens.json 胜利后 Tokens List 就会呈现在列表外面了,点击 Select 按钮增加代币列表。

增加胜利,当初能够在交易所外面为 MATIC 代币增加流动性或者进行兑换了。

留神 :增加流动性须要获取帐户里有一些 MATIC 代币,能够到 MATIC Faucet 进行支付。

退出移动版