go实现Uniswap本地测试和节点部署

go部署uniswap

### go实现Uniswap本地测试和节点部署 > 前段时段用remix部署uniswap,步骤挺繁琐而且remix占用内存较大,很容易崩溃,并且不能自动化部署,网上js调用合约的很多,go的却很少,自己动手写了一个当练手了。 Go的优势 * 本地可构造链,不需要找水龙头获取测试币 * 无需找RPC节点 * debug测试方便,其实也很挺麻烦的 编译sol生成Go文件可能麻烦点 ### 部署流程 总共需要发起6笔合约交易。 * 部署WETH合约 * 代币合约 * Uniswap工厂合约 * Uniswap RouterV2合约 * 代币合约授予`Approve` 路由`RouterV2`转移代币权限 * RouterV2合约调用`AddLiquidityETH`添加`代币`流动性 ### 准备工作 #### ABI和合约字节码BINCODE >合约部署的时候需要bincode,有了bincode合约当发生合约调用的时候,evm就知道如何执行,根据input中的参数去abi中转换成函数和参数,执行相应的调用。abi的作用是可以把二进制转换成函数参数。[可参考input解析](https://zhuanlan.zhihu.com/p/198037392) 合约对应的`bincode`和`abi`,这个可用`solcjs`编译和`remix`上获取,我`从uniswap`官网下的代码编译后获取了`abi`和`bincode`。 > 这里有个坑,createPair的时候需要Pair合约的·`bincode`的hash做计算,如果自己编译的话一定要把sol源码的值改了 “` // 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’96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f’ // init code hash )))); } “` #### 每个合约相应的GO文件 将abi和bincode转为相应的Go文件,指定bin和abi分别生成合约部署和合约调用的方法,这个主要是省下自己组装`input`。 “` abigen –bin=token_sol_ERCToken.bin –abi=token_sol_ERCToken.abi –pkg=token –out=Token.go “` ### 构造交易 交易中重要的概念 * `gas`可以通过`SuggestGasPrice`获取 * `gasLimit`可以通过`EstimateGas`通过模拟执行多次获取平均值 * `chainId`可通过`eth_chainId`方法获取 * `nonce`通过如下获取`PendingNonceAt`这种在同一高度同一账户下多笔会有问题 * `input`可自己拼装或用生成的go文件方法 按上面流程组装的交易,成功率应该很高。 ### 本地测试 本地测试需要节点模拟器,就是构造一条区块链,transactOpts是封装私钥的结构体包含转账金额的一些设置。 “` contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{ addr: {Balance: new(big.Int).SetUint64(10000000000000000000)}, testAddr: {Balance: big.NewInt(100000000000000)}}, 100000000) transactOpts := bind.NewKeyedTransactor(key) “` 每当有交易的时候调用`commit`打包交易。 “` contractBackend.Commit() “` 通过测试用例测试,很容易就可拿到执行结果。可参考代码`TestDeployUniswapLocal`设置这个变量可以获取合约执行过程的日志 `backends.SimulateDebug = true` ### 节点部署 节点部署比上面麻烦一点,有如下几点 * 合约有执行失败的可能,同一高度多笔交易`nonce`需要统一管理 * 交易上链时间不确定,需要持续获取交易状态 * 需要最新高度的发交易节点 刚开始的`没注意`到`模拟器`和`rpc返回的连接`都实现了这个接口`backend bind.ContractBackend`,`backend bind.ContractBackend`这个测试用例里面是本地部署成功后,获取交易的`Input`自己在组装发交易。后面发现接口一样,重新实现了`TestDeployUniswapRPC`,这个屏蔽了一些技术细节,只需要调生成的接口即可。 ### 代码讲解 这个主要是获取连接,输出一些节点的基本信息。 “` url := “http://157.245.118.249:8545” client, url := dialConn(url) printBaseInfo(client, url) PrintBalance(client, addr) “` 部署`eth` `工厂` `代币合约`,这两个没有顺序要求,可在同一块打包。 “` _, wtx, _, err := weth.DeployTokene(transactOpts, client) _, ftx, _, err := factory.DeployTokenf(transactOpts, client, addr) _, mtx, _, err := cdc.DeployTokenc(transactOpts, client) “` `getResult`方法首先查询交易是否在`pengding`状态,当交易不是`pengding`后查询收据`Receipt`,当`receipt.Status == types.ReceiptStatusSuccessful`时交易成功。 “` _, isPending, err := conn.TransactionByHash(context.Background(), txHash) if err != nil { fatallog.Fatal(err) return false, common.Address{} } receipt, err := conn.TransactionReceipt(context.Background(), txHash) if err != nil { fatallog.Fatal(err) } “` 当上面各个交易成功后,部署`Router`交易需要`工厂`和`weth`的合约地址,成功后代币合约部署者授予`Router`代币的转账权限,对方可调用`transaferFrom`转移代币。 “` _, routerTx, _, err := DeployToken(transactOpts, client, facR, wethR) result, routerAddr := getResult(client, routerTx.Hash()) if !result { fatallog.Fatal(“sendBaseContract routerTx”, err) return } mapTran, err := cdc.NewTokenc(mapTR, client) atx, err := mapTran.Approve(transactOpts, routerAddr, new(big.Int).SetUint64(1000000000000000000)) result, _ = getResult(client, atx.Hash()) if !result { fatallog.Fatal(“sendBaseContract atx”, err) return } “` 有了合约授权,添加流动性转移以太坊获取代币。 “` tik := new(big.Int).SetUint64(10000000000000000) tik1 := new(big.Int).SetUint64(1000000000000) transactOpts.Value = new(big.Int).SetUint64(1000000000000000000) RTran, err := NewToken(routerAddr, client) aHash, _ := RTran.AddLiquidityETH(transactOpts, mapTR, tik, tik, tik1, addr, new(big.Int).SetUint64(1699658290)) result, _ = getResult(client, aHash.Hash()) “` 以太坊确定较慢,部署需要等待一两分钟可结束。 [部署结果已上链,可查阅](https://goerli.etherscan.io/address/0x62c1692e74c9ba729c3d06cdb0810ce29202d0bc) [github链接 欢迎star](https://github.com/D-CDC/go-uniswap)

go实现Uniswap本地测试和节点部署

前段时段用remix部署uniswap,步骤挺繁琐而且remix占用内存较大,很容易崩溃,并且不能自动化部署,网上js调用合约的很多,go的却很少,自己动手写了一个当练手了。

Go的优势

  • 本地可构造链,不需要找水龙头获取测试币
  • 无需找RPC节点
  • debug测试方便,其实也很挺麻烦的

编译sol生成Go文件可能麻烦点

部署流程

总共需要发起6笔合约交易。

  • 部署WETH合约
  • 代币合约
  • Uniswap工厂合约
  • Uniswap RouterV2合约
  • 代币合约授予Approve 路由RouterV2转移代币权限
  • RouterV2合约调用AddLiquidityETH添加代币流动性

准备工作

ABI和合约字节码BINCODE

合约部署的时候需要bincode,有了bincode合约当发生合约调用的时候,evm就知道如何执行,根据input中的参数去abi中转换成函数和参数,执行相应的调用。abi的作用是可以把二进制转换成函数参数。可参考input解析

合约对应的bincodeabi,这个可用solcjs编译和remix上获取,我从uniswap官网下的代码编译后获取了abibincode

这里有个坑,createPair的时候需要Pair合约的·bincode的hash做计算,如果自己编译的话一定要把sol源码的值改了

// 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'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash
))));
}

每个合约相应的GO文件

将abi和bincode转为相应的Go文件,指定bin和abi分别生成合约部署和合约调用的方法,这个主要是省下自己组装input

abigen --bin=token_sol_ERCToken.bin --abi=token_sol_ERCToken.abi --pkg=token --out=Token.go

构造交易

交易中重要的概念

  • gas可以通过SuggestGasPrice获取
  • gasLimit可以通过EstimateGas通过模拟执行多次获取平均值
  • chainId可通过eth_chainId方法获取
  • nonce通过如下获取PendingNonceAt这种在同一高度同一账户下多笔会有问题
  • input可自己拼装或用生成的go文件方法

按上面流程组装的交易,成功率应该很高。

本地测试

本地测试需要节点模拟器,就是构造一条区块链,transactOpts是封装私钥的结构体包含转账金额的一些设置。

    contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{
        addr:     {Balance: new(big.Int).SetUint64(10000000000000000000)},
        testAddr: {Balance: big.NewInt(100000000000000)}},
        100000000)
    transactOpts := bind.NewKeyedTransactor(key)

每当有交易的时候调用commit打包交易。

contractBackend.Commit()

通过测试用例测试,很容易就可拿到执行结果。可参考代码TestDeployUniswapLocal设置这个变量可以获取合约执行过程的日志 backends.SimulateDebug = true

节点部署

节点部署比上面麻烦一点,有如下几点

  • 合约有执行失败的可能,同一高度多笔交易nonce需要统一管理
  • 交易上链时间不确定,需要持续获取交易状态
  • 需要最新高度的发交易节点

刚开始的没注意模拟器rpc返回的连接都实现了这个接口backend bind.ContractBackendbackend bind.ContractBackend这个测试用例里面是本地部署成功后,获取交易的Input自己在组装发交易。后面发现接口一样,重新实现了TestDeployUniswapRPC,这个屏蔽了一些技术细节,只需要调生成的接口即可。

代码讲解

这个主要是获取连接,输出一些节点的基本信息。

    url := "http://157.245.118.249:8545"
    client, url := dialConn(url)
    printBaseInfo(client, url)
    PrintBalance(client, addr)

部署eth 工厂 代币合约,这两个没有顺序要求,可在同一块打包。

    _, wtx, _, err := weth.DeployTokene(transactOpts, client)
    _, ftx, _, err := factory.DeployTokenf(transactOpts, client, addr)
    _, mtx, _, err := cdc.DeployTokenc(transactOpts, client)

getResult方法首先查询交易是否在pengding状态,当交易不是pengding后查询收据Receipt,当receipt.Status == types.ReceiptStatusSuccessful时交易成功。

_, isPending, err := conn.TransactionByHash(context.Background(), txHash)
        if err != nil {
            fatallog.Fatal(err)
            return false, common.Address{}
        }
            receipt, err := conn.TransactionReceipt(context.Background(), txHash)
    if err != nil {
        fatallog.Fatal(err)
    }

当上面各个交易成功后,部署Router交易需要工厂weth的合约地址,成功后代币合约部署者授予Router代币的转账权限,对方可调用transaferFrom转移代币。

    _, routerTx, _, err := DeployToken(transactOpts, client, facR, wethR)
    result, routerAddr := getResult(client, routerTx.Hash())
    if !result {
        fatallog.Fatal("sendBaseContract routerTx", err)
        return
    }

    mapTran, err := cdc.NewTokenc(mapTR, client)
    atx, err := mapTran.Approve(transactOpts, routerAddr, new(big.Int).SetUint64(1000000000000000000))
    result, _ = getResult(client, atx.Hash())
    if !result {
        fatallog.Fatal("sendBaseContract atx", err)
        return
    }

有了合约授权,添加流动性转移以太坊获取代币。

    tik := new(big.Int).SetUint64(10000000000000000)
    tik1 := new(big.Int).SetUint64(1000000000000)
    transactOpts.Value = new(big.Int).SetUint64(1000000000000000000)
    RTran, err := NewToken(routerAddr, client)
    aHash, _ := RTran.AddLiquidityETH(transactOpts, mapTR, tik, tik, tik1, addr, new(big.Int).SetUint64(1699658290))
    result, _ = getResult(client, aHash.Hash())

以太坊确定较慢,部署需要等待一两分钟可结束。 部署结果已上链,可查阅 github链接 欢迎star

本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

  • 发表于 2020-08-30 21:31
  • 阅读 ( 886 )
  • 学分 ( 89 )
  • 分类:Geth

该内容来自于互联网公开内容,非区块链原创内容,如若转载,请注明出处:https://htzkw.com/archives/27764

联系我们

aliyinhang@gmail.com