Skip to content

Commit

Permalink
feat: Added V3 dex test helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
Doublo54 committed Feb 27, 2023
1 parent 9aa6f0a commit dea299e
Show file tree
Hide file tree
Showing 9 changed files with 3,699 additions and 0 deletions.
107 changes: 107 additions & 0 deletions src/artifacts-apeswap/dex/contracts/NFTDescriptor.json

Large diffs are not rendered by default.

1,235 changes: 1,235 additions & 0 deletions src/artifacts-apeswap/dex/contracts/NonfungiblePositionManager.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

574 changes: 574 additions & 0 deletions src/artifacts-apeswap/dex/contracts/SwapRouter.json

Large diffs are not rendered by default.

245 changes: 245 additions & 0 deletions src/artifacts-apeswap/dex/contracts/UniswapV3Factory.json

Large diffs are not rendered by default.

1,092 changes: 1,092 additions & 0 deletions src/artifacts-apeswap/dex/contracts/UniswapV3Pool.json

Large diffs are not rendered by default.

180 changes: 180 additions & 0 deletions src/dexV3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
import { HardhatEthersHelpers } from 'hardhat/types'
import { ether } from './utils'
// Setup DEX Contracts
import UniswapV3Factory from './artifacts-apeswap/dex/contracts/UniswapV3Factory.json'
import UniswapV3Pool from './artifacts-apeswap/dex/contracts/UniswapV3Pool.json'
import NonfungiblePositionManagerBuild from './artifacts-apeswap/dex/contracts/NonfungiblePositionManager.json'
import NFTDescriptorBuild from './artifacts-apeswap/dex/contracts/NFTDescriptor.json'
import SwapRouterBuild from './artifacts-apeswap/dex/contracts/SwapRouter.json'
import NonfungibleTokenPositionDescriptorBuild from './artifacts-apeswap/dex/contracts/NonfungibleTokenPositionDescriptor.json'

// Setup Token Contracts
import ERC20MockBuild from './artifacts-apeswap/token/contracts/ERC20Mock.json'
import WNativeBuild from './artifacts-apeswap/token/contracts/WNative.json'

// Import Contract Types
import {
ApePair,
ApePair__factory,
ERC20Mock,
ERC20Mock__factory,
WNative__factory,
UniswapV3Factory__factory,
NFTDescriptor__factory,
SwapRouter__factory,
NonfungiblePositionManager__factory,
NonfungibleTokenPositionDescriptor__factory,
} from '../typechain-types'

/**
* Deploy a mock dex.
*
* - LP fees are sent to `feeTo`
* - Initial LP tokens are minted to `alice`
*/
// NOTE: Currently does not create a BANANA/WBNB pair
export async function deployMockDex(
ethers: HardhatEthersHelpers,
[owner, feeTo, alice]: [
SignerWithAddress,
SignerWithAddress,
SignerWithAddress
],
numPairs = 2
) {
const SwapRouter = (await ethers.getContractFactory(
SwapRouterBuild.abi,
SwapRouterBuild.bytecode
)) as SwapRouter__factory
const uniV3Factory = (await ethers.getContractFactory(
UniswapV3Factory.abi,
UniswapV3Factory.bytecode
)) as UniswapV3Factory__factory
const nonfungiblePositionManager = (await ethers.getContractFactory(
NonfungiblePositionManagerBuild.abi,
NonfungiblePositionManagerBuild.bytecode
)) as NonfungiblePositionManager__factory
const NFTDescriptor = (await ethers.getContractFactory(
NFTDescriptorBuild.abi,
NFTDescriptorBuild.bytecode
)) as NFTDescriptor__factory

const nftDescriptor = await NFTDescriptor.deploy()
const re = new RegExp('__(.*?)__')
const NonfungibleTokenPositionDescriptorBuildByteCode: any =
NonfungibleTokenPositionDescriptorBuild.bytecode.replace(
re,
nftDescriptor.address.split('0x')[1]
)

const nonfungibleTokenPositionDescriptor = (await ethers.getContractFactory(
NonfungibleTokenPositionDescriptorBuild.abi,
NonfungibleTokenPositionDescriptorBuildByteCode
)) as NonfungibleTokenPositionDescriptor__factory
// Setup Token Contracts
const ERC20Mock = (await ethers.getContractFactory(
ERC20MockBuild.abi,
ERC20MockBuild.bytecode
)) as ERC20Mock__factory
const WNative = (await ethers.getContractFactory(
WNativeBuild.abi,
WNativeBuild.bytecode
)) as WNative__factory

const TOKEN_BASE_BALANCE = ether('1000')
const WBNB_BASE_BALANCE = ether('1')
// Setup DEX factory
const dexFactory = await uniV3Factory.connect(owner).deploy()

// Setup pairs
const mockWBNB = await WNative.connect(owner).deploy()
const positionDescriptor = await nonfungibleTokenPositionDescriptor
.connect(owner)
.deploy(
mockWBNB.address,
'0x4c4f43414c205445535400000000000000000000000000000000000000000000'
)
const positionManager = await nonfungiblePositionManager
.connect(owner)
.deploy(dexFactory.address, mockWBNB.address, positionDescriptor.address)

const dexRouter = await SwapRouter.deploy(
dexFactory.address,
mockWBNB.address
)

const mockTokens: ERC20Mock[] = []
for (let index = 0; index < numPairs; index++) {
// Mint pair token
const mockToken = await ERC20Mock.connect(owner).deploy(
`Mock Token ${index}`,
`MOCK${index}`
)

await mockToken.connect(owner).mint(TOKEN_BASE_BALANCE)
await mockToken
.connect(owner)
.approve(positionManager.address, TOKEN_BASE_BALANCE)

await mockWBNB.connect(owner).deposit({
value: WBNB_BASE_BALANCE, // Adding ETH liquidity which gets exchanged for WETH
})
await mockWBNB
.connect(owner)
.approve(positionManager.address, WBNB_BASE_BALANCE)

let token0 = mockWBNB.address
let token1 = mockToken.address
if (mockWBNB.address > mockToken.address) {
token0 = mockToken.address
token1 = mockWBNB.address
}

await positionManager.createAndInitializePoolIfNecessary(
token0,
token1,
500,
'79229023000000000000000000000',
{ gasLimit: '30000000' }
)

await positionManager.mint(
{
token0: token0,
token1: token1,
fee: 500,
tickLower: -887270,
tickUpper: 887270,
amount0Desired: TOKEN_BASE_BALANCE,
amount1Desired: WBNB_BASE_BALANCE,
amount0Min: 0,
amount1Min: 0,
recipient: owner.address,
deadline: '999999999999999',
},
{ gasLimit: '30000000' }
)

// NOTE: Alternative way to create pairs directly through ApeFactory
// Create an initial pair
// await dexFactory.createPair(mockWBNB.address, mockToken.address);
// const pairCreated = await ApePair.at(await dexFactory.allPairs(index));

// // Obtain LP Tokens
// await mockWBNB.transfer(pairCreated.address, WBNB_BASE_BALANCE);
// await mockToken.transfer(pairCreated.address, TOKEN_BASE_BALANCE);
// await pairCreated.mint(alice);

mockTokens.push(mockToken)
}

return {
dexFactory,
dexRouter,
positionManager,
positionDescriptor,
mockWBNB,
mockTokens,
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* the smart contracts developed in this repo.
*/
export * as dex from './dex';
export * as dexV3 from './dexV3';
export * as farm from './farm';
export * as farmV2 from './farmV2';
export * as token from './token';
Expand Down
104 changes: 104 additions & 0 deletions test/dexV3.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { time, loadFixture } from '@nomicfoundation/hardhat-network-helpers'
import { ether } from '../src/utils'
import { dexV3 } from '../src'

/**
* hardhat-chai-matchers reference
* https://hardhat.org/hardhat-chai-matchers/docs/reference
*
* The @nomicfoundation/hardhat-chai-matchers plugin is meant to be a drop-in replacement
* for the @nomiclabs/hardhat-waffle plugin
*
* https://hardhat.org/hardhat-chai-matchers/docs/migrate-from-waffle
*
* VSCode + Hardhat:
* https://marketplace.visualstudio.com/items?itemName=NomicFoundation.hardhat-solidity
*/
import { anyValue } from '@nomicfoundation/hardhat-chai-matchers/withArgs'
import { expect } from 'chai'
// @ts-ignore
import { ethers } from 'hardhat'

// Setup Token Contracts
import ERC20MockBuild from '../src/artifacts-apeswap/token/contracts/ERC20Mock.json'

// Import Contract Types
import { ERC20Mock__factory } from '../typechain-types'

describe('V3 DEX', function () {
// We define a fixture to reuse the same setup in every test.
// We use loadFixture to run this setup once, snapshot that state,
// and reset Hardhat Network to that snapshot in every test.
async function deployMockDexFixture() {
// Contracts are deployed using the first signer/account by default
const [owner, feeTo, alice] = await ethers.getSigners()

const ERC20Mock = (await ethers.getContractFactory(
ERC20MockBuild.abi,
ERC20MockBuild.bytecode
)) as ERC20Mock__factory

const {
dexFactory,
dexRouter,
positionDescriptor,
positionManager,
mockWBNB,
mockTokens,
} = await dexV3.deployMockDex(ethers, [owner, feeTo, alice], 2) // accounts passed will be used in the deployment

return {
dexFactory,
dexRouter,
positionDescriptor,
positionManager,
mockWBNB,
mockTokens,
accounts: {
owner,
feeTo,
alice,
},
}
}

it('should have pair', async () => {
const mockDex = await loadFixture(deployMockDexFixture)
expect(
(
await mockDex.dexFactory.getPool(
mockDex.mockTokens[0].address,
mockDex.mockWBNB.address,
500
)
).toString()
).to.not.equal('0x0000000000000000000000000000000000000000')
})

it('should swap', async () => {
const mockDex = await loadFixture(deployMockDexFixture)
const token0 = mockDex.mockTokens[0]
const mockWBNB = mockDex.mockWBNB
const account = mockDex.accounts.owner.address

const bnbBalanceBefore = await mockWBNB.balanceOf(account)

await token0.approve(mockDex.dexRouter.address, ether('0.01'))
await mockDex.dexRouter.exactInputSingle({
tokenIn: token0.address,
tokenOut: mockWBNB.address,
fee: 500,
recipient: account,
deadline: '99999999999999',
amountIn: ether('0.01'),
amountOutMinimum: 0,
sqrtPriceLimitX96: 0,
})

const bnbBalanceAfter = await mockWBNB.balanceOf(account)
expect(Number(bnbBalanceAfter)).to.be.gt(
Number(bnbBalanceBefore),
'Did not receive any bnb on swap'
)
})
})

0 comments on commit dea299e

Please sign in to comment.