2021-08-12 09:58:13 +00:00
|
|
|
//
|
|
|
|
// Copyright 2021 Vulcanize, Inc.
|
|
|
|
//
|
|
|
|
|
2021-08-10 06:39:00 +00:00
|
|
|
import { ethers, Contract, ContractTransaction, Signer, BigNumber, utils } from 'ethers';
|
|
|
|
import assert from 'assert';
|
|
|
|
|
|
|
|
import {
|
|
|
|
abi as NFTD_ABI,
|
|
|
|
bytecode as NFTD_BYTECODE
|
|
|
|
} from '@uniswap/v3-periphery/artifacts/contracts/libraries/NFTDescriptor.sol/NFTDescriptor.json';
|
|
|
|
import {
|
|
|
|
abi as NFTPD_ABI,
|
|
|
|
bytecode as NFTPD_BYTECODE,
|
|
|
|
linkReferences as NFTPD_LINKREFS
|
|
|
|
} from '@uniswap/v3-periphery/artifacts/contracts/NonfungibleTokenPositionDescriptor.sol/NonfungibleTokenPositionDescriptor.json';
|
|
|
|
import {
|
|
|
|
abi as NFPM_ABI,
|
|
|
|
bytecode as NFPM_BYTECODE
|
|
|
|
} from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json';
|
2021-08-04 13:27:44 +00:00
|
|
|
|
|
|
|
import {
|
|
|
|
abi as TESTERC20_ABI,
|
|
|
|
bytecode as TESTERC20_BYTECODE
|
|
|
|
} from '../artifacts/test/contracts/TestERC20.sol/TestERC20.json';
|
2021-08-05 11:42:11 +00:00
|
|
|
import {
|
|
|
|
abi as TESTUNISWAPV3CALLEE_ABI,
|
|
|
|
bytecode as TESTUNISWAPV3CALLEE_BYTECODE
|
|
|
|
} from '../artifacts/test/contracts/TestUniswapV3Callee.sol/TestUniswapV3Callee.json';
|
2021-08-10 06:39:00 +00:00
|
|
|
import {
|
|
|
|
abi as WETH9_ABI,
|
|
|
|
bytecode as WETH9_BYTECODE
|
|
|
|
} from '../artifacts/test/contracts/WETH9.sol/WETH9.json';
|
2021-08-04 13:27:44 +00:00
|
|
|
|
2021-08-20 06:56:37 +00:00
|
|
|
import { DatabaseInterface } from '../src/types';
|
|
|
|
|
2021-08-10 06:39:00 +00:00
|
|
|
export { abi as NFPM_ABI } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json';
|
2021-08-04 13:27:44 +00:00
|
|
|
export { abi as TESTERC20_ABI } from '../artifacts/test/contracts/TestERC20.sol/TestERC20.json';
|
|
|
|
|
2021-08-05 13:03:47 +00:00
|
|
|
export const TICK_MIN = -887272;
|
|
|
|
export const TICK_MAX = 887272;
|
|
|
|
|
|
|
|
export const getMinTick = (tickSpacing: number): number => {
|
|
|
|
return Math.ceil(TICK_MIN / tickSpacing) * tickSpacing;
|
|
|
|
};
|
|
|
|
|
|
|
|
export const getMaxTick = (tickSpacing: number): number => {
|
|
|
|
return Math.floor(TICK_MAX / tickSpacing) * tickSpacing;
|
|
|
|
};
|
2021-08-05 11:42:11 +00:00
|
|
|
|
2021-08-04 13:27:44 +00:00
|
|
|
export const deployTokens = async (signer: Signer): Promise<{token0Address: string, token1Address: string}> => {
|
|
|
|
const Token = new ethers.ContractFactory(TESTERC20_ABI, TESTERC20_BYTECODE, signer);
|
|
|
|
|
|
|
|
const token0 = await Token.deploy(ethers.BigNumber.from(2).pow(255));
|
|
|
|
const token0Address = token0.address;
|
|
|
|
|
|
|
|
const token1 = await Token.deploy(ethers.BigNumber.from(2).pow(255));
|
|
|
|
const token1Address = token1.address;
|
|
|
|
|
|
|
|
return { token0Address, token1Address };
|
|
|
|
};
|
|
|
|
|
2021-08-05 11:42:11 +00:00
|
|
|
export const deployUniswapV3Callee = async (signer: Signer): Promise<Contract> => {
|
|
|
|
const TestUniswapV3Callee = new ethers.ContractFactory(TESTUNISWAPV3CALLEE_ABI, TESTUNISWAPV3CALLEE_BYTECODE, signer);
|
|
|
|
return await TestUniswapV3Callee.deploy();
|
|
|
|
};
|
|
|
|
|
|
|
|
export const approveToken = async (token: Contract, address: string, approveAmount: bigint): Promise<void> => {
|
|
|
|
const transaction: ContractTransaction = await token.approve(address, approveAmount);
|
|
|
|
await transaction.wait();
|
|
|
|
};
|
|
|
|
|
2021-08-04 13:27:44 +00:00
|
|
|
export const createPool = async (
|
|
|
|
factory: Contract,
|
|
|
|
token0Address: string,
|
|
|
|
token1Address: string,
|
|
|
|
fee: number): Promise<void> => {
|
|
|
|
const transaction: ContractTransaction = await factory.createPool(token0Address, token1Address, fee);
|
|
|
|
await transaction.wait();
|
|
|
|
};
|
|
|
|
|
|
|
|
export const initializePool = async (pool: Contract, sqrtPrice: string): Promise<void> => {
|
|
|
|
const transaction: ContractTransaction = await pool.initialize(BigNumber.from(sqrtPrice));
|
|
|
|
await transaction.wait();
|
|
|
|
};
|
2021-08-10 06:39:00 +00:00
|
|
|
|
|
|
|
export const deployWETH9Token = async (signer: Signer): Promise<string> => {
|
|
|
|
const WETH9 = new ethers.ContractFactory(WETH9_ABI, WETH9_BYTECODE, signer);
|
|
|
|
const weth9 = await WETH9.deploy();
|
|
|
|
|
|
|
|
return weth9.address;
|
|
|
|
};
|
|
|
|
|
|
|
|
// https://github.com/ethers-io/ethers.js/issues/195
|
|
|
|
const linkLibraries = (
|
|
|
|
{
|
|
|
|
bytecode,
|
|
|
|
linkReferences
|
|
|
|
}: {
|
|
|
|
bytecode: string
|
|
|
|
linkReferences: { [fileName: string]: { [contractName: string]: { length: number; start: number }[] } }
|
|
|
|
},
|
|
|
|
libraries: { [libraryName: string]: string }): string => {
|
|
|
|
Object.keys(linkReferences).forEach((fileName) => {
|
|
|
|
Object.keys(linkReferences[fileName]).forEach((contractName) => {
|
|
|
|
if (!Object.prototype.hasOwnProperty.call(libraries, contractName)) {
|
|
|
|
throw new Error(`Missing link library name ${contractName}`);
|
|
|
|
}
|
|
|
|
const address = utils.getAddress(libraries[contractName]).toLowerCase().slice(2);
|
|
|
|
linkReferences[fileName][contractName].forEach(({ start: byteStart, length: byteLength }) => {
|
|
|
|
const start = 2 + byteStart * 2;
|
|
|
|
const length = byteLength * 2;
|
|
|
|
bytecode = bytecode
|
|
|
|
.slice(0, start)
|
|
|
|
.concat(address)
|
|
|
|
.concat(bytecode.slice(start + length, bytecode.length));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
return bytecode;
|
|
|
|
};
|
|
|
|
|
|
|
|
export const deployNFPM = async (signer: Signer, factory: Contract, weth9Address: string): Promise<Contract> => {
|
|
|
|
// Deploy NonfungiblePositionManager.
|
|
|
|
// https://github.com/Uniswap/uniswap-v3-periphery/blob/main/test/shared/completeFixture.ts#L31
|
|
|
|
const nftDescriptorLibraryFactory = new ethers.ContractFactory(NFTD_ABI, NFTD_BYTECODE, signer);
|
|
|
|
const nftDescriptorLibrary = await nftDescriptorLibraryFactory.deploy();
|
|
|
|
assert(nftDescriptorLibrary.address, 'NFTDescriptorLibrary not deployed.');
|
|
|
|
|
|
|
|
// Linking NFTDescriptor library to NFTPD before deploying.
|
|
|
|
const linkedNFTPDBytecode = linkLibraries({
|
|
|
|
bytecode: NFTPD_BYTECODE,
|
|
|
|
linkReferences: NFTPD_LINKREFS
|
|
|
|
}, {
|
|
|
|
NFTDescriptor: nftDescriptorLibrary.address
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
const positionDescriptorFactory = new ethers.ContractFactory(
|
|
|
|
NFTPD_ABI,
|
|
|
|
linkedNFTPDBytecode,
|
|
|
|
signer);
|
|
|
|
const nftDescriptor = await positionDescriptorFactory.deploy(weth9Address);
|
|
|
|
assert(nftDescriptor.address, 'NFTDescriptor not deployed.');
|
|
|
|
|
|
|
|
const positionManagerFactory = new ethers.ContractFactory(
|
|
|
|
NFPM_ABI,
|
|
|
|
NFPM_BYTECODE,
|
|
|
|
signer);
|
|
|
|
return await positionManagerFactory.deploy(factory.address, weth9Address, nftDescriptor.address);
|
|
|
|
};
|
2021-08-20 06:56:37 +00:00
|
|
|
|
|
|
|
export const insertDummyBlock = async (db: DatabaseInterface, parentBlock: any): Promise<any> => {
|
|
|
|
// Insert a dummy BlockProgress entity after parentBlock.
|
|
|
|
|
|
|
|
const dbTx = await db.createTransactionRunner();
|
|
|
|
|
|
|
|
try {
|
|
|
|
const randomByte = ethers.utils.randomBytes(10);
|
|
|
|
const blockHash = ethers.utils.sha256(randomByte);
|
|
|
|
const blockTimestamp = Math.floor(Date.now() / 1000);
|
|
|
|
const parentHash = parentBlock.hash;
|
|
|
|
const blockNumber = parentBlock.number + 1;
|
|
|
|
|
|
|
|
const block = {
|
|
|
|
blockNumber,
|
|
|
|
blockHash,
|
|
|
|
blockTimestamp,
|
|
|
|
parentHash
|
|
|
|
};
|
|
|
|
|
|
|
|
await db.updateSyncStatusChainHead(dbTx, blockHash, blockNumber);
|
|
|
|
await db.saveEvents(dbTx, block, []);
|
|
|
|
await db.updateSyncStatusIndexedBlock(dbTx, blockHash, blockNumber);
|
|
|
|
|
|
|
|
await dbTx.commitTransaction();
|
|
|
|
|
|
|
|
return {
|
|
|
|
number: blockNumber,
|
|
|
|
hash: blockHash,
|
|
|
|
timestamp: blockTimestamp,
|
|
|
|
parent: {
|
|
|
|
hash: parentHash
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} catch (error) {
|
|
|
|
await dbTx.rollbackTransaction();
|
|
|
|
throw error;
|
|
|
|
} finally {
|
|
|
|
await dbTx.release();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
export const insertNDummyBlocks = async (db: DatabaseInterface, numberOfBlocks:number, parentBlock?: any): Promise<any[]> => {
|
|
|
|
// Insert n dummy BlockProgress serially after parentBlock.
|
|
|
|
|
|
|
|
const blocksArray: any[] = [];
|
|
|
|
if (!parentBlock) {
|
|
|
|
const randomByte = ethers.utils.randomBytes(10);
|
|
|
|
const hash = ethers.utils.sha256(randomByte);
|
|
|
|
parentBlock = {
|
|
|
|
number: 0,
|
|
|
|
hash,
|
|
|
|
timestamp: -1,
|
|
|
|
parent: {
|
|
|
|
hash: ''
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
let block = parentBlock;
|
|
|
|
for (let i = 0; i < numberOfBlocks; i++) {
|
|
|
|
block = await insertDummyBlock(db, block);
|
|
|
|
blocksArray.push(block);
|
|
|
|
}
|
|
|
|
|
|
|
|
return blocksArray;
|
|
|
|
};
|
|
|
|
|
|
|
|
export async function removeEntities<Entity> (db: DatabaseInterface, entity: new () => Entity): Promise<void> {
|
|
|
|
// Remove all entries of the specified entity from database.
|
|
|
|
|
|
|
|
const dbTx = await db.createTransactionRunner();
|
|
|
|
|
|
|
|
try {
|
|
|
|
await db.removeEntities(dbTx, entity);
|
|
|
|
dbTx.commitTransaction();
|
|
|
|
} catch (error) {
|
|
|
|
await dbTx.rollbackTransaction();
|
|
|
|
throw error;
|
|
|
|
} finally {
|
|
|
|
await dbTx.release();
|
|
|
|
}
|
|
|
|
}
|