mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-01-06 19:38:05 +00:00
Test for entities after TransferEvent (#196)
* Add resolver for Position entity in uni-info-watcher. * Test for entities after TransferEvent. Co-authored-by: prathamesh0 <prathamesh.musale0@gmail.com>
This commit is contained in:
parent
abd3175c09
commit
0c2efde028
@ -1,4 +1,4 @@
|
||||
timeout: '50000'
|
||||
timeout: '60000'
|
||||
bail: true
|
||||
exit: true # TODO: Find out why the program doesn't exit on its own.
|
||||
require: 'ts-node/register'
|
||||
|
@ -1139,7 +1139,7 @@ export class Indexer {
|
||||
|
||||
const [token0, token1] = await Promise.all([
|
||||
this._db.getTokenNoTx({ id: token0Address, blockHash }),
|
||||
this._db.getTokenNoTx({ id: token0Address, blockHash })
|
||||
this._db.getTokenNoTx({ id: token1Address, blockHash })
|
||||
]);
|
||||
assert(token0 && token1);
|
||||
position.token0 = token0;
|
||||
|
@ -16,6 +16,7 @@ import { TokenDayData } from './entity/TokenDayData';
|
||||
import { TokenHourData } from './entity/TokenHourData';
|
||||
import { Transaction } from './entity/Transaction';
|
||||
import { UniswapDayData } from './entity/UniswapDayData';
|
||||
import { Position } from './entity/Position';
|
||||
|
||||
const log = debug('vulcanize:resolver');
|
||||
|
||||
@ -122,6 +123,12 @@ export const createResolvers = async (indexer: Indexer): Promise<any> => {
|
||||
log('uniswapDayDatas', first, skip, orderBy, orderDirection, where);
|
||||
|
||||
return indexer.getEntities(UniswapDayData, {}, where, { limit: first, skip, orderBy, orderDirection });
|
||||
},
|
||||
|
||||
positions: async (_: any, { first, where }: { first: number, where: { [key: string]: any } }) => {
|
||||
log('uniswapDayDatas', first, where);
|
||||
|
||||
return indexer.getEntities(Position, {}, where, { limit: first }, ['pool', 'token0', 'token1', 'tickLower', 'tickUpper', 'transaction']);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -138,6 +138,24 @@ type TokenHourData {
|
||||
periodStartUnix: Int!
|
||||
}
|
||||
|
||||
type Position {
|
||||
id: ID!
|
||||
pool: Pool!
|
||||
token0: Token!
|
||||
token1: Token!
|
||||
tickLower: Tick!
|
||||
tickUpper: Tick!
|
||||
transaction: Transaction!
|
||||
liquidity: BigInt!
|
||||
depositedToken0: BigDecimal!
|
||||
depositedToken1: BigDecimal!
|
||||
collectedFeesToken0: BigDecimal!
|
||||
collectedFeesToken1: BigDecimal!
|
||||
owner: Bytes!
|
||||
feeGrowthInside0LastX128: BigInt!
|
||||
feeGrowthInside1LastX128: BigInt!
|
||||
}
|
||||
|
||||
enum OrderDirection {
|
||||
asc
|
||||
desc
|
||||
@ -242,6 +260,10 @@ enum TokenHourData_orderBy {
|
||||
periodStartUnix
|
||||
}
|
||||
|
||||
input Position_filter {
|
||||
id: ID
|
||||
}
|
||||
|
||||
type Query {
|
||||
bundle(
|
||||
id: ID!
|
||||
@ -382,5 +404,10 @@ type Query {
|
||||
orderDirection: OrderDirection
|
||||
where: UniswapDayData_filter
|
||||
): [UniswapDayData!]!
|
||||
|
||||
positions(
|
||||
first: Int = 100
|
||||
where: Position_filter
|
||||
): [Position!]!
|
||||
}
|
||||
`;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { expect } from 'chai';
|
||||
import { ethers, Contract, Signer } from 'ethers';
|
||||
import { ethers, Contract, Signer, constants } from 'ethers';
|
||||
import { request } from 'graphql-request';
|
||||
import 'mocha';
|
||||
import _ from 'lodash';
|
||||
@ -18,7 +18,8 @@ import {
|
||||
initializePool,
|
||||
getMinTick,
|
||||
getMaxTick,
|
||||
approveToken
|
||||
approveToken,
|
||||
NFPM_ABI
|
||||
} from '@vulcanize/util/test';
|
||||
import { Client as UniClient, watchEvent } from '@vulcanize/uni-watcher';
|
||||
import {
|
||||
@ -37,7 +38,8 @@ import {
|
||||
queryMints,
|
||||
queryTicks,
|
||||
queryBurns,
|
||||
querySwaps
|
||||
querySwaps,
|
||||
queryPositions
|
||||
} from '../test/queries';
|
||||
import {
|
||||
checkUniswapDayData,
|
||||
@ -57,6 +59,7 @@ describe('uni-info-watcher', () => {
|
||||
let token1: Contract;
|
||||
let token0Address: string;
|
||||
let token1Address: string;
|
||||
let nfpm: Contract;
|
||||
|
||||
let tickLower: number;
|
||||
let tickUpper: number;
|
||||
@ -105,7 +108,7 @@ describe('uni-info-watcher', () => {
|
||||
});
|
||||
|
||||
describe('PoolCreatedEvent', () => {
|
||||
// NOTE Skipping checking entity updates that cannot be gotten/derived using queries.
|
||||
// NOTE: Skipping checking entity updates that cannot be gotten/derived using queries.
|
||||
// Checked entities: Token, Pool.
|
||||
|
||||
const fee = 500;
|
||||
@ -135,7 +138,7 @@ describe('uni-info-watcher', () => {
|
||||
const eventType = 'PoolCreatedEvent';
|
||||
await watchEvent(uniClient, eventType);
|
||||
|
||||
// Sleeping for 10 sec for the entities to be processed.
|
||||
// Sleeping for 10 sec for the event to be processed.
|
||||
await wait(10000);
|
||||
});
|
||||
|
||||
@ -187,13 +190,14 @@ describe('uni-info-watcher', () => {
|
||||
});
|
||||
|
||||
it('should trigger InitializeEvent', async () => {
|
||||
// Initialize Pool.
|
||||
initializePool(pool, sqrtPrice);
|
||||
|
||||
// Wait for InitializeEvent.
|
||||
const eventType = 'InitializeEvent';
|
||||
await watchEvent(uniClient, eventType);
|
||||
|
||||
// Sleeping for 5 sec for the entities to be processed.
|
||||
// Sleeping for 5 sec for the event to be processed.
|
||||
await wait(5000);
|
||||
});
|
||||
|
||||
@ -260,7 +264,7 @@ describe('uni-info-watcher', () => {
|
||||
const eventType = 'MintEvent';
|
||||
await watchEvent(uniClient, eventType);
|
||||
|
||||
// Sleeping for 20 sec for the entities to be processed.
|
||||
// Sleeping for 20 sec for the event to be processed.
|
||||
await wait(20000);
|
||||
});
|
||||
|
||||
@ -432,7 +436,7 @@ describe('uni-info-watcher', () => {
|
||||
const eventType = 'BurnEvent';
|
||||
await watchEvent(uniClient, eventType);
|
||||
|
||||
// Sleeping for 15 sec for the entities to be processed.
|
||||
// Sleeping for 15 sec for the event to be processed.
|
||||
await wait(15000);
|
||||
});
|
||||
|
||||
@ -602,7 +606,7 @@ describe('uni-info-watcher', () => {
|
||||
const eventType = 'SwapEvent';
|
||||
eventValue = await watchEvent(uniClient, eventType);
|
||||
|
||||
// Sleeping for 5 sec for the entities to be processed.
|
||||
// Sleeping for 5 sec for the event to be processed.
|
||||
await wait(5000);
|
||||
});
|
||||
|
||||
@ -703,4 +707,122 @@ describe('uni-info-watcher', () => {
|
||||
checkTokenHourData(endpoint, token1.address);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TransferEvent', () => {
|
||||
// NOTE: The test cases for TransferEvent are written such that IncreaseLiquidityEvent has also been processed right after.
|
||||
// Checked entities: Transaction, Position.
|
||||
|
||||
const fee = 3000;
|
||||
const sqrtPrice = '79228162514264337593543950336';
|
||||
let eventType: string;
|
||||
let eventValue: any;
|
||||
let expectedTxID: string;
|
||||
|
||||
const amount0Desired = 15;
|
||||
const amount1Desired = 15;
|
||||
const amount0Min = 0;
|
||||
const amount1Min = 0;
|
||||
const deadline = 1634367993;
|
||||
|
||||
before(async () => {
|
||||
// Get the NFPM contract.
|
||||
const latestContract = await uniClient.getLatestContract('nfpm');
|
||||
expect(latestContract.address).to.not.be.empty;
|
||||
|
||||
nfpm = new Contract(latestContract.address, NFPM_ABI, signer);
|
||||
|
||||
// Create Pool.
|
||||
createPool(factory, token0Address, token1Address, fee);
|
||||
|
||||
// Wait for PoolCreatedEvent.
|
||||
eventType = 'PoolCreatedEvent';
|
||||
eventValue = await watchEvent(uniClient, eventType);
|
||||
|
||||
// Sleeping for 10 sec for the event to be processed.
|
||||
await wait(10000); // not needed
|
||||
|
||||
// Reinitializing the pool variable.
|
||||
const poolAddress = eventValue.event.pool;
|
||||
pool = new Contract(poolAddress, POOL_ABI, signer);
|
||||
expect(pool.address).to.not.be.empty;
|
||||
|
||||
// Reinitializing the ticks
|
||||
const tickSpacing = await pool.tickSpacing();
|
||||
tickLower = getMinTick(tickSpacing);
|
||||
tickUpper = getMaxTick(tickSpacing);
|
||||
|
||||
// Initialize Pool.
|
||||
initializePool(pool, sqrtPrice);
|
||||
|
||||
// Wait for InitializeEvent.
|
||||
eventType = 'InitializeEvent';
|
||||
await watchEvent(uniClient, eventType);
|
||||
|
||||
// Sleeping for 5 sec for the event to be processed.
|
||||
await wait(5000);
|
||||
|
||||
// Approving tokens for NonfungiblePositionManager contract.
|
||||
await approveToken(token0, nfpm.address, BigInt(constants.MaxUint256.toString()));
|
||||
await approveToken(token1, nfpm.address, BigInt(constants.MaxUint256.toString()));
|
||||
});
|
||||
|
||||
it('should trigger TransferEvent', async () => {
|
||||
nfpm.mint({
|
||||
token0: token0Address,
|
||||
token1: token1Address,
|
||||
tickLower,
|
||||
tickUpper,
|
||||
amount0Desired,
|
||||
amount1Desired,
|
||||
amount0Min,
|
||||
amount1Min,
|
||||
recipient,
|
||||
deadline,
|
||||
fee
|
||||
});
|
||||
|
||||
// Wait for MintEvent.
|
||||
eventType = 'MintEvent';
|
||||
await watchEvent(uniClient, eventType);
|
||||
|
||||
// Wait for TransferEvent.
|
||||
eventType = 'TransferEvent';
|
||||
eventValue = await watchEvent(uniClient, eventType);
|
||||
|
||||
// Wait for IncreaseLiquidityEvent.
|
||||
eventType = 'IncreaseLiquidityEvent';
|
||||
await watchEvent(uniClient, eventType);
|
||||
|
||||
// Sleeping for 15 sec for the events to be processed.
|
||||
await wait(15000);
|
||||
});
|
||||
|
||||
it('should create a Transaction entity', async () => {
|
||||
const eventType = 'MintEvent';
|
||||
({ expectedTxID } = await checkTransaction(endpoint, eventType));
|
||||
});
|
||||
|
||||
it('should createa a Position entity', async () => {
|
||||
// Checked values: pool, token0, token1, tickLower, tickUpper, transaction, owner.
|
||||
// Unchecked values: feeGrowthInside0LastX128, feeGrowthInside0LastX128.
|
||||
|
||||
// Get the Position using tokenId.
|
||||
const data = await request(endpoint, queryPositions, { id: Number(eventValue.event.tokenId) });
|
||||
expect(data.positions).to.not.be.empty;
|
||||
|
||||
const position = data.positions[0];
|
||||
const positionTickLower = position.tickLower.id.split('#')[1];
|
||||
const positionTickUpper = position.tickUpper.id.split('#')[1];
|
||||
|
||||
const expectedOwner = eventValue.event.to;
|
||||
|
||||
expect(position.pool.id).to.be.equal(pool.address);
|
||||
expect(position.token0.id).to.be.equal(token0.address);
|
||||
expect(position.token1.id).to.be.equal(token1.address);
|
||||
expect(positionTickLower).to.be.equal(tickLower.toString());
|
||||
expect(positionTickUpper).to.be.equal(tickUpper.toString());
|
||||
expect(position.transaction.id).to.be.equal(expectedTxID);
|
||||
expect(position.owner).to.be.equal(expectedOwner);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -252,3 +252,37 @@ query queryTransactions(
|
||||
timestamp
|
||||
}
|
||||
}`;
|
||||
|
||||
// Getting position filtered by id.
|
||||
export const queryPositions = gql`
|
||||
query queryPositions($first: Int, $id: ID) {
|
||||
positions(first: $first, where: { id: $id }) {
|
||||
id,
|
||||
pool {
|
||||
id
|
||||
},
|
||||
token0 {
|
||||
id
|
||||
},
|
||||
token1 {
|
||||
id
|
||||
},
|
||||
tickLower {
|
||||
id
|
||||
},
|
||||
tickUpper {
|
||||
id
|
||||
},
|
||||
transaction {
|
||||
id
|
||||
},
|
||||
liquidity,
|
||||
depositedToken0,
|
||||
depositedToken1,
|
||||
collectedFeesToken0,
|
||||
collectedFeesToken1,
|
||||
owner,
|
||||
feeGrowthInside0LastX128,
|
||||
feeGrowthInside1LastX128
|
||||
}
|
||||
}`;
|
||||
|
@ -28,8 +28,7 @@
|
||||
{
|
||||
"files": ["*.test.ts", "test/*.ts"],
|
||||
"rules": {
|
||||
"no-unused-expressions": "off",
|
||||
"no-prototype-builtins": "off"
|
||||
"no-unused-expressions": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -7,9 +7,6 @@ const config: HardhatUserConfig = {
|
||||
compilers: [
|
||||
{
|
||||
version: '0.7.6'
|
||||
},
|
||||
{
|
||||
version: '0.5.0'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { gql } from '@apollo/client/core';
|
||||
import { GraphQLClient, GraphQLConfig } from '@vulcanize/ipld-eth-client';
|
||||
|
||||
import { queryGetPool, queryPoolIdToPoolKey, queryPosition, queryEvents, subscribeEvents } from './queries';
|
||||
import { queryGetPool, queryPoolIdToPoolKey, queryPosition, queryEvents, subscribeEvents, queryLatestContract } from './queries';
|
||||
|
||||
export class Client {
|
||||
_config: GraphQLConfig;
|
||||
@ -71,4 +71,15 @@ export class Client {
|
||||
|
||||
return getPool;
|
||||
}
|
||||
|
||||
async getLatestContract (type: string): Promise<any> {
|
||||
const { latestContract } = await this._client.query(
|
||||
gql(queryLatestContract),
|
||||
{
|
||||
type
|
||||
}
|
||||
);
|
||||
|
||||
return latestContract;
|
||||
}
|
||||
}
|
||||
|
@ -604,4 +604,11 @@ export class Indexer {
|
||||
...mappingKeys
|
||||
);
|
||||
}
|
||||
|
||||
async getLatestContract (type: string): Promise<any> {
|
||||
const contract = await this._db.getLatestContract(type);
|
||||
assert(contract, `No ${type} contract watched.`);
|
||||
|
||||
return contract;
|
||||
}
|
||||
}
|
||||
|
@ -155,3 +155,11 @@ query getPool($blockHash: String!, $token0: String!, $token1: String!, $fee: Str
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const queryLatestContract = gql`
|
||||
query queryLatestContract($type: String!) {
|
||||
latestContract(type: $type) {
|
||||
address
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -89,6 +89,11 @@ export const createResolvers = async (indexer: Indexer, eventWatcher: EventWatch
|
||||
getPool: (_: any, { blockHash, token0, token1, fee }: { blockHash: string, token0: string, token1: string, fee: string }) => {
|
||||
log('getPool', blockHash, token0, token1, fee);
|
||||
return indexer.getPool(blockHash, token0, token1, fee);
|
||||
},
|
||||
|
||||
latestContract: (_: any, { type }: { type: string }) => {
|
||||
log('latestContract', type);
|
||||
return indexer.getLatestContract(type);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -193,6 +193,10 @@ type BlockProgressEvent {
|
||||
isComplete: Boolean!
|
||||
}
|
||||
|
||||
type Contract {
|
||||
address: String!
|
||||
}
|
||||
|
||||
#
|
||||
# Queries
|
||||
#
|
||||
@ -201,6 +205,10 @@ type Query {
|
||||
|
||||
# NonfungiblePositionManager
|
||||
|
||||
latestContract(
|
||||
type: String!
|
||||
): Contract
|
||||
|
||||
position(
|
||||
blockHash: String!
|
||||
tokenId: String!
|
||||
|
@ -13,7 +13,9 @@ import {
|
||||
TICK_MIN,
|
||||
getMinTick,
|
||||
getMaxTick,
|
||||
approveToken
|
||||
approveToken,
|
||||
deployWETH9Token,
|
||||
deployNFPM
|
||||
} from '@vulcanize/util/test';
|
||||
import { Client as UniClient } from '@vulcanize/uni-watcher';
|
||||
import { getCache } from '@vulcanize/cache';
|
||||
@ -25,25 +27,11 @@ import {
|
||||
import {
|
||||
abi as POOL_ABI
|
||||
} from '@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json';
|
||||
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';
|
||||
|
||||
import { Indexer } from './indexer';
|
||||
import { Database } from './database';
|
||||
import { watchContract } from './utils/index';
|
||||
import {
|
||||
linkLibraries,
|
||||
testCreatePool,
|
||||
testInitialize,
|
||||
checkMintEvent,
|
||||
@ -54,10 +42,6 @@ import {
|
||||
checkDecreaseLiquidityEvent,
|
||||
checksCollectEvent
|
||||
} from '../test/utils';
|
||||
import {
|
||||
abi as WETH9_ABI,
|
||||
bytecode as WETH9_BYTECODE
|
||||
} from '../artifacts/test/contracts/WETH9.sol/WETH9.json';
|
||||
|
||||
const NETWORK_RPC_URL = 'http://localhost:8545';
|
||||
|
||||
@ -270,47 +254,18 @@ describe('uni-watcher', () => {
|
||||
|
||||
it('should deploy a WETH9 token', async () => {
|
||||
// Deploy weth9 token.
|
||||
const WETH9 = new ethers.ContractFactory(WETH9_ABI, WETH9_BYTECODE, signer);
|
||||
const weth9 = await WETH9.deploy();
|
||||
|
||||
weth9Address = weth9.address;
|
||||
expect(weth9.address).to.not.be.empty;
|
||||
weth9Address = await deployWETH9Token(signer);
|
||||
expect(weth9Address).to.not.be.empty;
|
||||
});
|
||||
|
||||
it('should deploy NonfungiblePositionManager', async () => {
|
||||
// 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();
|
||||
expect(nftDescriptorLibrary.address).to.not.be.empty;
|
||||
|
||||
// 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);
|
||||
expect(nftDescriptor.address).to.not.be.empty;
|
||||
|
||||
const positionManagerFactory = new ethers.ContractFactory(
|
||||
NFPM_ABI,
|
||||
NFPM_BYTECODE,
|
||||
signer);
|
||||
nfpm = await positionManagerFactory.deploy(factory.address, weth9Address, nftDescriptor.address);
|
||||
|
||||
nfpm = await deployNFPM(signer, factory, weth9Address);
|
||||
expect(nfpm.address).to.not.be.empty;
|
||||
});
|
||||
|
||||
it('should watch NonfungiblePositionManager contract', async () => {
|
||||
// Watch factory contract.
|
||||
// Watch NFPM contract.
|
||||
await watchContract(db, nfpm.address, 'nfpm', 100);
|
||||
|
||||
// Verifying with the db.
|
||||
|
@ -1,39 +1,10 @@
|
||||
import { ethers, utils, Contract, Signer } from 'ethers';
|
||||
import { ethers, Contract, Signer } from 'ethers';
|
||||
import { expect } from 'chai';
|
||||
import 'mocha';
|
||||
|
||||
import { Client as UniClient } from '@vulcanize/uni-watcher';
|
||||
import { createPool, initializePool } from '@vulcanize/util/test';
|
||||
|
||||
// https://github.com/ethers-io/ethers.js/issues/195
|
||||
export 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 (!libraries.hasOwnProperty(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 testCreatePool = async (
|
||||
uniClient: UniClient,
|
||||
factory: Contract,
|
||||
@ -201,13 +172,13 @@ export const checkTransferEvent = (
|
||||
value: any,
|
||||
expectedContract: string,
|
||||
from: string,
|
||||
recipient: string
|
||||
to: string
|
||||
): void => {
|
||||
checkEventCommonValues(value, expectedContract);
|
||||
|
||||
expect(value.event.__typename).to.equal('TransferEvent');
|
||||
expect(value.event.from).to.equal(from);
|
||||
expect(value.event.to).to.equal(recipient);
|
||||
expect(value.event.to).to.equal(to);
|
||||
expect(value.event.tokenId).to.equal('1');
|
||||
};
|
||||
|
||||
|
@ -7,6 +7,9 @@ const config: HardhatUserConfig = {
|
||||
compilers: [
|
||||
{
|
||||
version: '0.7.6'
|
||||
},
|
||||
{
|
||||
version: '0.5.0'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1,4 +1,19 @@
|
||||
import { ethers, Contract, ContractTransaction, Signer, BigNumber } from 'ethers';
|
||||
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';
|
||||
|
||||
import {
|
||||
abi as TESTERC20_ABI,
|
||||
@ -8,7 +23,12 @@ import {
|
||||
abi as TESTUNISWAPV3CALLEE_ABI,
|
||||
bytecode as TESTUNISWAPV3CALLEE_BYTECODE
|
||||
} from '../artifacts/test/contracts/TestUniswapV3Callee.sol/TestUniswapV3Callee.json';
|
||||
import {
|
||||
abi as WETH9_ABI,
|
||||
bytecode as WETH9_BYTECODE
|
||||
} from '../artifacts/test/contracts/WETH9.sol/WETH9.json';
|
||||
|
||||
export { abi as NFPM_ABI } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json';
|
||||
export { abi as TESTERC20_ABI } from '../artifacts/test/contracts/TestERC20.sol/TestERC20.json';
|
||||
|
||||
export const TICK_MIN = -887272;
|
||||
@ -57,3 +77,69 @@ export const initializePool = async (pool: Contract, sqrtPrice: string): Promise
|
||||
const transaction: ContractTransaction = await pool.initialize(BigNumber.from(sqrtPrice));
|
||||
await transaction.wait();
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user