go部署uniswap
前段时段用remix部署uniswap,步骤挺繁琐而且remix占用内存较大,很容易崩溃,并且不能自动化部署,网上js调用合约的很多,go的却很少,自己动手写了一个当练手了。
Go的优势
编译sol生成Go文件可能麻烦点
总共需要发起6笔合约交易。
Approve
路由RouterV2
转移代币权限AddLiquidityETH
添加代币
流动性合约部署的时候需要bincode,有了bincode合约当发生合约调用的时候,evm就知道如何执行,根据input中的参数去abi中转换成函数和参数,执行相应的调用。abi的作用是可以把二进制转换成函数参数。可参考input解析
合约对应的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())
以太坊确定较慢,部署需要等待一两分钟可结束。 部署结果已上链,可查阅 github链接 欢迎star
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!