Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add unit tests for initialize #230

Merged
merged 1 commit into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ require('@uniswap/eslint-config/load')
module.exports = {
extends: ['@uniswap/eslint-config/node'],
rules: {
'@typescript-eslint/no-inferrable-types': 'off',
'import/no-unused-modules': 'off',
'@typescript-eslint/no-restricted-imports': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' }
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' },
],
'@typescript-eslint/no-this-alias': [
'error',
{
allowDestructuring: true, // Allow `const { props, state } = this`; false by default
allowedNames: [
'self' // Allow `const self= this`; `[]` by default
]
}
'self', // Allow `const self= this`; `[]` by default
],
},
],
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-explicit-any': 'off',
Expand All @@ -25,22 +26,22 @@ module.exports = {
{
types: {
// Allow BigInt (uppercase)
BigInt: false
}
}
]
BigInt: false,
},
},
],
},
overrides: [
{
files: ['tests/**/*.ts'],
settings: {
jest: {
version: 26
version: 26,
},
// jest is added as a plugin in our org's eslint config, but we use
// matchstick, and this would crash when linting matchstick files.
'disable/plugins': ['jest']
}
}
]
'disable/plugins': ['jest'],
},
},
],
}
28 changes: 23 additions & 5 deletions src/mappings/pool/initialize.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
import { BigInt } from '@graphprotocol/graph-ts'
import { BigDecimal, BigInt } from '@graphprotocol/graph-ts'

import { Bundle, Pool, Token } from '../../types/schema'
import { Initialize } from '../../types/templates/Pool/Pool'
import { updatePoolDayData, updatePoolHourData } from '../../utils/intervalUpdates'
import { findEthPerToken, getEthPriceInUSD } from '../../utils/pricing'
import {
findEthPerToken,
getEthPriceInUSD,
MINIMUM_ETH_LOCKED,
STABLE_COINS,
USDC_WETH_03_POOL,
WETH_ADDRESS,
} from '../../utils/pricing'

export function handleInitialize(event: Initialize): void {
handleInitializeHelper(event)
}

export function handleInitializeHelper(
event: Initialize,
stablecoinWrappedNativePoolAddress: string = USDC_WETH_03_POOL,
stablecoinIsToken0: boolean = true,
wrappedNativeAddress: string = WETH_ADDRESS,
stablecoinAddresses: string[] = STABLE_COINS,
minimumEthLocked: BigDecimal = MINIMUM_ETH_LOCKED,
): void {
// update pool sqrt price and tick
const pool = Pool.load(event.address.toHexString())!
pool.sqrtPrice = event.params.sqrtPriceX96
Expand All @@ -18,16 +36,16 @@ export function handleInitialize(event: Initialize): void {

// update ETH price now that prices could have changed
const bundle = Bundle.load('1')!
bundle.ethPriceUSD = getEthPriceInUSD()
bundle.ethPriceUSD = getEthPriceInUSD(stablecoinWrappedNativePoolAddress, stablecoinIsToken0)
bundle.save()

updatePoolDayData(event)
updatePoolHourData(event)

// update token prices
if (token0 && token1) {
token0.derivedETH = findEthPerToken(token0 as Token)
token1.derivedETH = findEthPerToken(token1 as Token)
token0.derivedETH = findEthPerToken(token0 as Token, wrappedNativeAddress, stablecoinAddresses, minimumEthLocked)
token1.derivedETH = findEthPerToken(token1 as Token, wrappedNativeAddress, stablecoinAddresses, minimumEthLocked)
token0.save()
token1.save()
}
Expand Down
35 changes: 21 additions & 14 deletions src/utils/pricing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { exponentToBigDecimal, safeDiv } from '../utils/index'
import { Bundle, Pool, Token } from './../types/schema'
import { ONE_BD, ZERO_BD, ZERO_BI } from './constants'

const WETH_ADDRESS = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
const USDC_WETH_03_POOL = '0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8'
export const WETH_ADDRESS = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
export const USDC_WETH_03_POOL = '0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8'

// token where amounts should contribute to tracked volume and liquidity
// usually tokens that many tokens are paired with s
Expand Down Expand Up @@ -33,7 +33,7 @@ export const WHITELIST_TOKENS: string[] = [
'0xfe2e637202056d30016725477c5da089ab0a043a', // sETH2
]

const STABLE_COINS: string[] = [
export const STABLE_COINS: string[] = [
'0x6b175474e89094c44da98b954eedeac495271d0f',
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
'0xdac17f958d2ee523a2206206994597c13d831ec7',
Expand All @@ -42,7 +42,7 @@ const STABLE_COINS: string[] = [
'0x4dd28568d05f09b02220b09c2cb307bfd837cb95',
]

const MINIMUM_ETH_LOCKED = BigDecimal.fromString('60')
export const MINIMUM_ETH_LOCKED = BigDecimal.fromString('60')

const Q192 = BigInt.fromI32(2).pow(192 as u8)
export function sqrtPriceX96ToTokenPrices(sqrtPriceX96: BigInt, token0: Token, token1: Token): BigDecimal[] {
Expand All @@ -54,11 +54,13 @@ export function sqrtPriceX96ToTokenPrices(sqrtPriceX96: BigInt, token0: Token, t
return [price0, price1]
}

export function getEthPriceInUSD(): BigDecimal {
// fetch eth prices for each stablecoin
const usdcPool = Pool.load(USDC_WETH_03_POOL) // dai is token0
if (usdcPool !== null) {
return usdcPool.token0Price
export function getEthPriceInUSD(
stablecoinWrappedNativePoolAddress: string = USDC_WETH_03_POOL,
stablecoinIsToken0: boolean = true, // true is stablecoin is token0, false if stablecoin is token1
): BigDecimal {
const stablecoinWrappedNativePool = Pool.load(stablecoinWrappedNativePoolAddress)
if (stablecoinWrappedNativePool !== null) {
return stablecoinIsToken0 ? stablecoinWrappedNativePool.token0Price : stablecoinWrappedNativePool.token1Price
} else {
return ZERO_BD
}
Expand All @@ -68,8 +70,13 @@ export function getEthPriceInUSD(): BigDecimal {
* Search through graph to find derived Eth per token.
* @todo update to be derived ETH (add stablecoin estimates)
**/
export function findEthPerToken(token: Token): BigDecimal {
if (token.id == WETH_ADDRESS) {
export function findEthPerToken(
token: Token,
wrappedNativeAddress: string = WETH_ADDRESS,
stablecoinAddresses: string[] = STABLE_COINS,
minimumEthLocked: BigDecimal = MINIMUM_ETH_LOCKED,
): BigDecimal {
if (token.id == wrappedNativeAddress) {
return ONE_BD
}
const whiteList = token.whitelistPools
Expand All @@ -81,7 +88,7 @@ export function findEthPerToken(token: Token): BigDecimal {

// hardcoded fix for incorrect rates
// if whitelist includes token - get the safe price
if (STABLE_COINS.includes(token.id)) {
if (stablecoinAddresses.includes(token.id)) {
priceSoFar = safeDiv(ONE_BD, bundle.ethPriceUSD)
} else {
for (let i = 0; i < whiteList.length; ++i) {
Expand All @@ -96,7 +103,7 @@ export function findEthPerToken(token: Token): BigDecimal {
// get the derived ETH in pool
if (token1) {
const ethLocked = pool.totalValueLockedToken1.times(token1.derivedETH)
if (ethLocked.gt(largestLiquidityETH) && ethLocked.gt(MINIMUM_ETH_LOCKED)) {
if (ethLocked.gt(largestLiquidityETH) && ethLocked.gt(minimumEthLocked)) {
largestLiquidityETH = ethLocked
// token1 per our token * Eth per token1
priceSoFar = pool.token1Price.times(token1.derivedETH as BigDecimal)
Expand All @@ -108,7 +115,7 @@ export function findEthPerToken(token: Token): BigDecimal {
// get the derived ETH in pool
if (token0) {
const ethLocked = pool.totalValueLockedToken0.times(token0.derivedETH)
if (ethLocked.gt(largestLiquidityETH) && ethLocked.gt(MINIMUM_ETH_LOCKED)) {
if (ethLocked.gt(largestLiquidityETH) && ethLocked.gt(minimumEthLocked)) {
largestLiquidityETH = ethLocked
// token0 per our token * ETH per token0
priceSoFar = pool.token0Price.times(token0.derivedETH as BigDecimal)
Expand Down
77 changes: 76 additions & 1 deletion tests/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import { assert, createMockedFunction, newMockEvent } from 'matchstick-as'

import { handlePoolCreatedHelper } from '../src/mappings/factory'
import { PoolCreated } from '../src/types/Factory/Factory'
import { Pool, Token } from '../src/types/schema'
import { ZERO_BD, ZERO_BI } from '../src/utils/constants'

const USDC_MAINNET_ADDRESS = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
const WETH_MAINNET_ADDRESS = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
const WBTC_MAINNET_ADDRESS = '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599'
export const USDC_WETH_03_MAINNET_POOL = '0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8'
export const WBTC_WETH_03_MAINNET_POOL = '0xcbcdf9626bc03e24f779434178a73a0b4bad62ed'
export const POOL_FEE_TIER_03 = 3000
export const POOL_TICK_SPACING_03 = 60

Expand Down Expand Up @@ -34,13 +38,21 @@ export const WETH_MAINNET_FIXTURE: TokenFixture = {
decimals: '18',
}

export const WBTC_MAINNET_FIXTURE: TokenFixture = {
address: WBTC_MAINNET_ADDRESS,
symbol: 'WBTC',
name: 'Wrapped Bitcoin',
totalSupply: '200',
decimals: '8',
}

export const TEST_ETH_PRICE_USD = BigDecimal.fromString('2000')
export const TEST_USDC_DERIVED_ETH = BigDecimal.fromString('1').div(BigDecimal.fromString('2000'))
export const TEST_WETH_DERIVED_ETH = BigDecimal.fromString('1')

export const MOCK_EVENT = newMockEvent()

export const createTestPool = (
export const invokePoolCreatedWithMockedEthCalls = (
mockEvent: ethereum.Event,
factoryAddress: string,
token0: TokenFixture,
Expand Down Expand Up @@ -91,6 +103,69 @@ export const createTestPool = (
handlePoolCreatedHelper(poolCreatedEvent, factoryAddress, [token0.address, token1.address])
}

// More lightweight than the method above which invokes handlePoolCreated. This
// method only creates the pool entity while the above method also creates the
// relevant token and factory entities.
export const createAndStoreTestPool = (
poolAddress: string,
token0Address: string,
token1Address: string,
feeTier: i32,
): Pool => {
const pool = new Pool(poolAddress)
pool.createdAtTimestamp = ZERO_BI
pool.createdAtBlockNumber = ZERO_BI
pool.token0 = token0Address
pool.token1 = token1Address
pool.feeTier = BigInt.fromI32(feeTier)
pool.liquidity = ZERO_BI
pool.sqrtPrice = ZERO_BI
pool.token0Price = ZERO_BD
pool.token1Price = ZERO_BD
pool.tick = ZERO_BI
pool.observationIndex = ZERO_BI
pool.volumeToken0 = ZERO_BD
pool.volumeToken1 = ZERO_BD
pool.volumeUSD = ZERO_BD
pool.untrackedVolumeUSD = ZERO_BD
pool.feesUSD = ZERO_BD
pool.txCount = ZERO_BI
pool.collectedFeesToken0 = ZERO_BD
pool.collectedFeesToken1 = ZERO_BD
pool.collectedFeesUSD = ZERO_BD
pool.totalValueLockedToken0 = ZERO_BD
pool.totalValueLockedToken1 = ZERO_BD
pool.totalValueLockedUSD = ZERO_BD
pool.totalValueLockedETH = ZERO_BD
pool.totalValueLockedUSDUntracked = ZERO_BD
pool.liquidityProviderCount = ZERO_BI

pool.save()
return pool
}

export const createAndStoreTestToken = (tokenFixture: TokenFixture): Token => {
const token = new Token(tokenFixture.address)
token.symbol = tokenFixture.symbol
token.name = tokenFixture.name
token.decimals = BigInt.fromString(tokenFixture.decimals)
token.totalSupply = BigInt.fromString(tokenFixture.totalSupply)
token.volume = ZERO_BD
token.volumeUSD = ZERO_BD
token.untrackedVolumeUSD = ZERO_BD
token.feesUSD = ZERO_BD
token.txCount = ZERO_BI
token.poolCount = ZERO_BI
token.totalValueLocked = ZERO_BD
token.totalValueLockedUSD = ZERO_BD
token.totalValueLockedUSDUntracked = ZERO_BD
token.derivedETH = ZERO_BD
token.whitelistPools = []

token.save()
return token
}

// Typescript for Subgraphs do not support Record types so we use a 2D string array to represent the object instead.
export const assertObjectMatches = (entityType: string, id: string, obj: string[][]): void => {
for (let i = 0; i < obj.length; i++) {
Expand Down
4 changes: 2 additions & 2 deletions tests/handleBurn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { convertTokenToDecimal, fastExponentiation, safeDiv } from '../src/utils
import { FACTORY_ADDRESS, ONE_BD, ZERO_BI } from '../src/utils/constants'
import {
assertObjectMatches,
createTestPool,
invokePoolCreatedWithMockedEthCalls,
MOCK_EVENT,
POOL_FEE_TIER_03,
POOL_TICK_SPACING_03,
Expand Down Expand Up @@ -59,7 +59,7 @@ const BURN_EVENT = new Burn(

describe('handleBurn', () => {
beforeAll(() => {
createTestPool(
invokePoolCreatedWithMockedEthCalls(
MOCK_EVENT,
FACTORY_ADDRESS,
USDC_MAINNET_FIXTURE,
Expand Down
4 changes: 2 additions & 2 deletions tests/handleCollect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { convertTokenToDecimal } from '../src/utils'
import { FACTORY_ADDRESS, ZERO_BD } from '../src/utils/constants'
import {
assertObjectMatches,
createTestPool,
invokePoolCreatedWithMockedEthCalls,
MOCK_EVENT,
POOL_FEE_TIER_03,
POOL_TICK_SPACING_03,
Expand Down Expand Up @@ -59,7 +59,7 @@ const COLLECT_EVENT = new Collect(

describe('handleMint', () => {
beforeAll(() => {
createTestPool(
invokePoolCreatedWithMockedEthCalls(
MOCK_EVENT,
FACTORY_ADDRESS,
USDC_MAINNET_FIXTURE,
Expand Down
Loading
Loading