mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-01-23 11:39: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
|
bail: true
|
||||||
exit: true # TODO: Find out why the program doesn't exit on its own.
|
exit: true # TODO: Find out why the program doesn't exit on its own.
|
||||||
require: 'ts-node/register'
|
require: 'ts-node/register'
|
||||||
|
@ -1139,7 +1139,7 @@ export class Indexer {
|
|||||||
|
|
||||||
const [token0, token1] = await Promise.all([
|
const [token0, token1] = await Promise.all([
|
||||||
this._db.getTokenNoTx({ id: token0Address, blockHash }),
|
this._db.getTokenNoTx({ id: token0Address, blockHash }),
|
||||||
this._db.getTokenNoTx({ id: token0Address, blockHash })
|
this._db.getTokenNoTx({ id: token1Address, blockHash })
|
||||||
]);
|
]);
|
||||||
assert(token0 && token1);
|
assert(token0 && token1);
|
||||||
position.token0 = token0;
|
position.token0 = token0;
|
||||||
|
@ -16,6 +16,7 @@ import { TokenDayData } from './entity/TokenDayData';
|
|||||||
import { TokenHourData } from './entity/TokenHourData';
|
import { TokenHourData } from './entity/TokenHourData';
|
||||||
import { Transaction } from './entity/Transaction';
|
import { Transaction } from './entity/Transaction';
|
||||||
import { UniswapDayData } from './entity/UniswapDayData';
|
import { UniswapDayData } from './entity/UniswapDayData';
|
||||||
|
import { Position } from './entity/Position';
|
||||||
|
|
||||||
const log = debug('vulcanize:resolver');
|
const log = debug('vulcanize:resolver');
|
||||||
|
|
||||||
@ -122,6 +123,12 @@ export const createResolvers = async (indexer: Indexer): Promise<any> => {
|
|||||||
log('uniswapDayDatas', first, skip, orderBy, orderDirection, where);
|
log('uniswapDayDatas', first, skip, orderBy, orderDirection, where);
|
||||||
|
|
||||||
return indexer.getEntities(UniswapDayData, {}, where, { limit: first, skip, orderBy, orderDirection });
|
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!
|
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 {
|
enum OrderDirection {
|
||||||
asc
|
asc
|
||||||
desc
|
desc
|
||||||
@ -242,6 +260,10 @@ enum TokenHourData_orderBy {
|
|||||||
periodStartUnix
|
periodStartUnix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input Position_filter {
|
||||||
|
id: ID
|
||||||
|
}
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
bundle(
|
bundle(
|
||||||
id: ID!
|
id: ID!
|
||||||
@ -382,5 +404,10 @@ type Query {
|
|||||||
orderDirection: OrderDirection
|
orderDirection: OrderDirection
|
||||||
where: UniswapDayData_filter
|
where: UniswapDayData_filter
|
||||||
): [UniswapDayData!]!
|
): [UniswapDayData!]!
|
||||||
|
|
||||||
|
positions(
|
||||||
|
first: Int = 100
|
||||||
|
where: Position_filter
|
||||||
|
): [Position!]!
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { ethers, Contract, Signer } from 'ethers';
|
import { ethers, Contract, Signer, constants } from 'ethers';
|
||||||
import { request } from 'graphql-request';
|
import { request } from 'graphql-request';
|
||||||
import 'mocha';
|
import 'mocha';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
@ -18,7 +18,8 @@ import {
|
|||||||
initializePool,
|
initializePool,
|
||||||
getMinTick,
|
getMinTick,
|
||||||
getMaxTick,
|
getMaxTick,
|
||||||
approveToken
|
approveToken,
|
||||||
|
NFPM_ABI
|
||||||
} from '@vulcanize/util/test';
|
} from '@vulcanize/util/test';
|
||||||
import { Client as UniClient, watchEvent } from '@vulcanize/uni-watcher';
|
import { Client as UniClient, watchEvent } from '@vulcanize/uni-watcher';
|
||||||
import {
|
import {
|
||||||
@ -37,7 +38,8 @@ import {
|
|||||||
queryMints,
|
queryMints,
|
||||||
queryTicks,
|
queryTicks,
|
||||||
queryBurns,
|
queryBurns,
|
||||||
querySwaps
|
querySwaps,
|
||||||
|
queryPositions
|
||||||
} from '../test/queries';
|
} from '../test/queries';
|
||||||
import {
|
import {
|
||||||
checkUniswapDayData,
|
checkUniswapDayData,
|
||||||
@ -57,6 +59,7 @@ describe('uni-info-watcher', () => {
|
|||||||
let token1: Contract;
|
let token1: Contract;
|
||||||
let token0Address: string;
|
let token0Address: string;
|
||||||
let token1Address: string;
|
let token1Address: string;
|
||||||
|
let nfpm: Contract;
|
||||||
|
|
||||||
let tickLower: number;
|
let tickLower: number;
|
||||||
let tickUpper: number;
|
let tickUpper: number;
|
||||||
@ -105,7 +108,7 @@ describe('uni-info-watcher', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PoolCreatedEvent', () => {
|
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.
|
// Checked entities: Token, Pool.
|
||||||
|
|
||||||
const fee = 500;
|
const fee = 500;
|
||||||
@ -135,7 +138,7 @@ describe('uni-info-watcher', () => {
|
|||||||
const eventType = 'PoolCreatedEvent';
|
const eventType = 'PoolCreatedEvent';
|
||||||
await watchEvent(uniClient, eventType);
|
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);
|
await wait(10000);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -187,13 +190,14 @@ describe('uni-info-watcher', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should trigger InitializeEvent', async () => {
|
it('should trigger InitializeEvent', async () => {
|
||||||
|
// Initialize Pool.
|
||||||
initializePool(pool, sqrtPrice);
|
initializePool(pool, sqrtPrice);
|
||||||
|
|
||||||
// Wait for InitializeEvent.
|
// Wait for InitializeEvent.
|
||||||
const eventType = 'InitializeEvent';
|
const eventType = 'InitializeEvent';
|
||||||
await watchEvent(uniClient, eventType);
|
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);
|
await wait(5000);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -260,7 +264,7 @@ describe('uni-info-watcher', () => {
|
|||||||
const eventType = 'MintEvent';
|
const eventType = 'MintEvent';
|
||||||
await watchEvent(uniClient, eventType);
|
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);
|
await wait(20000);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -432,7 +436,7 @@ describe('uni-info-watcher', () => {
|
|||||||
const eventType = 'BurnEvent';
|
const eventType = 'BurnEvent';
|
||||||
await watchEvent(uniClient, eventType);
|
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);
|
await wait(15000);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -602,7 +606,7 @@ describe('uni-info-watcher', () => {
|
|||||||
const eventType = 'SwapEvent';
|
const eventType = 'SwapEvent';
|
||||||
eventValue = await watchEvent(uniClient, eventType);
|
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);
|
await wait(5000);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -703,4 +707,122 @@ describe('uni-info-watcher', () => {
|
|||||||
checkTokenHourData(endpoint, token1.address);
|
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
|
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"],
|
"files": ["*.test.ts", "test/*.ts"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-unused-expressions": "off",
|
"no-unused-expressions": "off"
|
||||||
"no-prototype-builtins": "off"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -7,9 +7,6 @@ const config: HardhatUserConfig = {
|
|||||||
compilers: [
|
compilers: [
|
||||||
{
|
{
|
||||||
version: '0.7.6'
|
version: '0.7.6'
|
||||||
},
|
|
||||||
{
|
|
||||||
version: '0.5.0'
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { gql } from '@apollo/client/core';
|
import { gql } from '@apollo/client/core';
|
||||||
import { GraphQLClient, GraphQLConfig } from '@vulcanize/ipld-eth-client';
|
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 {
|
export class Client {
|
||||||
_config: GraphQLConfig;
|
_config: GraphQLConfig;
|
||||||
@ -71,4 +71,15 @@ export class Client {
|
|||||||
|
|
||||||
return getPool;
|
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
|
...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 }) => {
|
getPool: (_: any, { blockHash, token0, token1, fee }: { blockHash: string, token0: string, token1: string, fee: string }) => {
|
||||||
log('getPool', blockHash, token0, token1, fee);
|
log('getPool', blockHash, token0, token1, fee);
|
||||||
return indexer.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!
|
isComplete: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Contract {
|
||||||
|
address: String!
|
||||||
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Queries
|
# Queries
|
||||||
#
|
#
|
||||||
@ -201,6 +205,10 @@ type Query {
|
|||||||
|
|
||||||
# NonfungiblePositionManager
|
# NonfungiblePositionManager
|
||||||
|
|
||||||
|
latestContract(
|
||||||
|
type: String!
|
||||||
|
): Contract
|
||||||
|
|
||||||
position(
|
position(
|
||||||
blockHash: String!
|
blockHash: String!
|
||||||
tokenId: String!
|
tokenId: String!
|
||||||
|
@ -13,7 +13,9 @@ import {
|
|||||||
TICK_MIN,
|
TICK_MIN,
|
||||||
getMinTick,
|
getMinTick,
|
||||||
getMaxTick,
|
getMaxTick,
|
||||||
approveToken
|
approveToken,
|
||||||
|
deployWETH9Token,
|
||||||
|
deployNFPM
|
||||||
} from '@vulcanize/util/test';
|
} from '@vulcanize/util/test';
|
||||||
import { Client as UniClient } from '@vulcanize/uni-watcher';
|
import { Client as UniClient } from '@vulcanize/uni-watcher';
|
||||||
import { getCache } from '@vulcanize/cache';
|
import { getCache } from '@vulcanize/cache';
|
||||||
@ -25,25 +27,11 @@ import {
|
|||||||
import {
|
import {
|
||||||
abi as POOL_ABI
|
abi as POOL_ABI
|
||||||
} from '@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json';
|
} 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 { Indexer } from './indexer';
|
||||||
import { Database } from './database';
|
import { Database } from './database';
|
||||||
import { watchContract } from './utils/index';
|
import { watchContract } from './utils/index';
|
||||||
import {
|
import {
|
||||||
linkLibraries,
|
|
||||||
testCreatePool,
|
testCreatePool,
|
||||||
testInitialize,
|
testInitialize,
|
||||||
checkMintEvent,
|
checkMintEvent,
|
||||||
@ -54,10 +42,6 @@ import {
|
|||||||
checkDecreaseLiquidityEvent,
|
checkDecreaseLiquidityEvent,
|
||||||
checksCollectEvent
|
checksCollectEvent
|
||||||
} from '../test/utils';
|
} 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';
|
const NETWORK_RPC_URL = 'http://localhost:8545';
|
||||||
|
|
||||||
@ -270,47 +254,18 @@ describe('uni-watcher', () => {
|
|||||||
|
|
||||||
it('should deploy a WETH9 token', async () => {
|
it('should deploy a WETH9 token', async () => {
|
||||||
// Deploy weth9 token.
|
// Deploy weth9 token.
|
||||||
const WETH9 = new ethers.ContractFactory(WETH9_ABI, WETH9_BYTECODE, signer);
|
weth9Address = await deployWETH9Token(signer);
|
||||||
const weth9 = await WETH9.deploy();
|
expect(weth9Address).to.not.be.empty;
|
||||||
|
|
||||||
weth9Address = weth9.address;
|
|
||||||
expect(weth9.address).to.not.be.empty;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should deploy NonfungiblePositionManager', async () => {
|
it('should deploy NonfungiblePositionManager', async () => {
|
||||||
// Deploy NonfungiblePositionManager.
|
// Deploy NonfungiblePositionManager.
|
||||||
// https://github.com/Uniswap/uniswap-v3-periphery/blob/main/test/shared/completeFixture.ts#L31
|
nfpm = await deployNFPM(signer, factory, weth9Address);
|
||||||
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);
|
|
||||||
|
|
||||||
expect(nfpm.address).to.not.be.empty;
|
expect(nfpm.address).to.not.be.empty;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should watch NonfungiblePositionManager contract', async () => {
|
it('should watch NonfungiblePositionManager contract', async () => {
|
||||||
// Watch factory contract.
|
// Watch NFPM contract.
|
||||||
await watchContract(db, nfpm.address, 'nfpm', 100);
|
await watchContract(db, nfpm.address, 'nfpm', 100);
|
||||||
|
|
||||||
// Verifying with the db.
|
// 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 { expect } from 'chai';
|
||||||
import 'mocha';
|
import 'mocha';
|
||||||
|
|
||||||
import { Client as UniClient } from '@vulcanize/uni-watcher';
|
import { Client as UniClient } from '@vulcanize/uni-watcher';
|
||||||
import { createPool, initializePool } from '@vulcanize/util/test';
|
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 (
|
export const testCreatePool = async (
|
||||||
uniClient: UniClient,
|
uniClient: UniClient,
|
||||||
factory: Contract,
|
factory: Contract,
|
||||||
@ -201,13 +172,13 @@ export const checkTransferEvent = (
|
|||||||
value: any,
|
value: any,
|
||||||
expectedContract: string,
|
expectedContract: string,
|
||||||
from: string,
|
from: string,
|
||||||
recipient: string
|
to: string
|
||||||
): void => {
|
): void => {
|
||||||
checkEventCommonValues(value, expectedContract);
|
checkEventCommonValues(value, expectedContract);
|
||||||
|
|
||||||
expect(value.event.__typename).to.equal('TransferEvent');
|
expect(value.event.__typename).to.equal('TransferEvent');
|
||||||
expect(value.event.from).to.equal(from);
|
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');
|
expect(value.event.tokenId).to.equal('1');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,6 +7,9 @@ const config: HardhatUserConfig = {
|
|||||||
compilers: [
|
compilers: [
|
||||||
{
|
{
|
||||||
version: '0.7.6'
|
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 {
|
import {
|
||||||
abi as TESTERC20_ABI,
|
abi as TESTERC20_ABI,
|
||||||
@ -8,7 +23,12 @@ import {
|
|||||||
abi as TESTUNISWAPV3CALLEE_ABI,
|
abi as TESTUNISWAPV3CALLEE_ABI,
|
||||||
bytecode as TESTUNISWAPV3CALLEE_BYTECODE
|
bytecode as TESTUNISWAPV3CALLEE_BYTECODE
|
||||||
} from '../artifacts/test/contracts/TestUniswapV3Callee.sol/TestUniswapV3Callee.json';
|
} 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 { abi as TESTERC20_ABI } from '../artifacts/test/contracts/TestERC20.sol/TestERC20.json';
|
||||||
|
|
||||||
export const TICK_MIN = -887272;
|
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));
|
const transaction: ContractTransaction = await pool.initialize(BigNumber.from(sqrtPrice));
|
||||||
await transaction.wait();
|
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