我正在编写一个自动化测试套件,需要针对Uniswap v2风格的自动市场标记测试功能:做掉期和使用不同的订单路由。因此,需要部署路由器。
关于如何在Brownie中部署可测试的Uniswap v2样式交换,有任何现有的示例吗?因为Brownie是一小部分聪明的合同开发商,有什么例子吗?
我也在探索使用主干网分叉的选择,但我不确定这个操作是否太昂贵(慢),无法用于单元测试。
发布于 2022-01-28 05:19:48
使用本地测试网允许您在测试期间非常精确地控制块链的状态。但是,它将要求您手动部署所需的每个合同。
mainnet的分叉将使您不必部署每个已经部署在主网网上的合同。但是,您将牺牲对环境的控制,并需要连接到节点。
我在测试网上部署了几次Uniswap2V。要做到这一点,您将需要字节码和ABI用于以下契约: UniswapV2Factory、UniswapV2Pair、UniswapV2Router02 (我想您需要第二个版本的路由器)。Uniswap文档很好地解释了如何从NPM下载它们。要使路由器正常工作,还需要部署WETH契约。我建议从这个github 页面中部署一个。
在运行此代码之前,只需确保您的链正在运行。对于硬帽子,运行以下命令:
npx hardhat node首先,将签名者连接到开发链:
var provider = new ethers.providers.WebSocketProvider("ws://localhost:8545");
var signer = provider.getSigner();使用ethers.js库,首先部署工厂:
const compiledUniswapFactory = require("@uniswap/v2-core/build/UniswapV2Factory.json");
var uniswapFactory = await new ethers.ContractFactory(compiledUniswapFactory.interface,compiledUniswapFactory.bytecode,signer).deploy(await signer.getAddress());那么WETH合同:
const compiledWETH = require("canonical-weth/build/conrtacts/WETH.json";
var WETH = await new ethers.ContractFactory(WETH.interface,WETH.bytecode,signer).deploy();您现在可以部署路由器了。
const compiledUniswapRouter = require("@uniswap/v2-periphery/build/UniswapV2Router02");
var router = await new ethers.ContractFactory(compiledUniswapRouter.abi,compiledUniswapRouter.bytecode,signer).deploy(uniswapFactory.address,WETH.address);您还需要部署所需的ERC20令牌(下面是我编写的令牌示例):
const compiledERC20 = require("../../../Ethereum/Ethereum/sources/ERC20.sol/Token.json");
var erc20Factory = new ethers.ContractFactory(compiledERC20.abi,compiledERC20.bytecode,signer);
var erc20_0 = await erc20Factory.deploy("1000000", "Token 0", "5", "T0");
var erc20_1 = await erc20Factory.deploy("1000000", "Token 1", "5", "T1");deploy函数的参数将取决于您希望部署的令牌的构造函数。
您还需要使用Uniswap工厂的createPair方法创建对。
uniswapFactory.createPair(erc20_0.address,erc20_1.address);请记住,在这对代币将由合同任意订购。ERC20_0可能不是两者中的第一位。
在此之后,只需等待所有事务通过,您就可以开始测试了。
发布于 2022-02-20 01:59:57
为了快速、方便的设置,我会使用草帽的主干网叉。
就像@Xavier说的那样,使用Mainnet叉意味着您不必部署您想要与之交互的每个契约。如果您想在多个交易所测试您的合同,这将是最简单的方法。但是,它确实需要连接到节点,因此单元测试运行得更慢。
举个例子,假设我想测试下面的契约,它使用ERC20方法在Uniswap上交换一个swapExactETHForTokens令牌。
pragma solidity ^0.6.6;
interface IUniswap {
function swapExactETHForTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline)
external
payable
returns (uint[] memory amounts);
function WETH() external pure returns (address);
}
contract UniswapTradeExample {
IUniswap uniswap;
// Pass in address of UniswapV2Router02
constructor(address _uniswap) public {
uniswap = IUniswap(_uniswap);
}
function swapExactETHForTokens(uint amountOutMin, address token) external payable {
address[] memory path = new address[](2);
path[0] = uniswap.WETH();
path[1] = token;
uniswap.swapExactETHForTokens{value: msg.value}(
amountOutMin,
path,
msg.sender,
now
);
}
}从草帽文档中,要做的第一件事是使用炼金术建立到存档节点的连接,这是免费使用的。
拥有节点的url后,将其作为网络添加到hardhat.config.js文件中:
networks: {
hardhat: {
forking: {
url: "https://eth-mainnet.alchemyapi.io/v2/<key>",
blockNumber: 14189520
}
}
}如果您设置了blockNumber,每次都会用硬帽子分叉这个块。如果您正在使用测试套件,并且希望您的测试是确定性的,则建议这样做。
最后,这里是测试类,它在上面的契约中测试swapExactETHForTokens。举个例子,我用1ETH来代替戴。
const { assert } = require("chai");
const { ethers } = require("hardhat");
const ERC20ABI = require('./ERC20.json');
const UNISWAPV2ROUTER02_ADDRESS = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D";
const DAI_ADDRESS = "0x6b175474e89094c44da98b954eedeac495271d0f";
describe("UniswapTradeExample", function () {
it("Swap ETH for DAI", async function () {
const provider = ethers.provider;
const [owner, addr1] = await ethers.getSigners();
const DAI = new ethers.Contract(DAI_ADDRESS, ERC20ABI, provider);
// Assert addr1 has 1000 ETH to start
addr1Balance = await provider.getBalance(addr1.address);
expectedBalance = ethers.BigNumber.from("10000000000000000000000");
assert(addr1Balance.eq(expectedBalance));
// Assert addr1 DAI balance is 0
addr1Dai = await DAI.balanceOf(addr1.address);
assert(addr1Dai.isZero());
// Deploy UniswapTradeExample
const uniswapTradeExample =
await ethers.getContractFactory("UniswapTradeExample")
.then(contract => contract.deploy(UNISWAPV2ROUTER02_ADDRESS));
await uniswapTradeExample.deployed();
// Swap 1 ETH for DAI
await uniswapTradeExample.connect(addr1).swapExactETHForTokens(
0,
DAI_ADDRESS,
{ value: ethers.utils.parseEther("1") }
);
// Assert addr1Balance contains one less ETH
expectedBalance = addr1Balance.sub(ethers.utils.parseEther("1"));
addr1Balance = await provider.getBalance(addr1.address);
assert(addr1Balance.lt(expectedBalance));
// Assert DAI balance increased
addr1Dai = await DAI.balanceOf(addr1.address);
assert(addr1Dai.gt(ethers.BigNumber.from("0")));
});
});注意顶部的const ERC20ABI = require('./ERC20.json');,它导入获取DAI合同并使用它的balanceOf()方法所需的ERC20ABI。
就这样。运行npx hardhat test应该显示这个测试通过了。
发布于 2022-01-31 13:51:02
泽维尔·哈默尔的回答让我朝着正确的方向前进。
不幸的是,Uniswap v2及其克隆不能在不编辑源代码的情况下重新编译和重新部署。这是因为
UniswapV2Factory,从而导致不同的哈希。pairFor生成无效的对契约地址而失败。这是个令人讨厌的问题。最后,我构建了整个库和工具来解决它。:请见见智能触点测试。它基于Sushiswap v2回购,因为他们的合同得到了最好的维护。
https://stackoverflow.com/questions/70846489
复制相似问题