From b22849c58bbd41b47468e40b583f3b5400de9f9a Mon Sep 17 00:00:00 2001 From: Michael Wang <44713145+mzywang@users.noreply.github.com> Date: Wed, 29 May 2024 15:02:39 -0400 Subject: [PATCH] add unit tests for intervalUpdates (#229) --- .prettierrc | 6 + package.json | 5 - src/mappings/factory.ts | 2 +- src/mappings/pool/collect.ts | 6 +- src/mappings/pool/swap.ts | 2 +- src/utils/intervalUpdates.ts | 5 +- src/utils/pricing.ts | 11 +- tests/constants.ts | 4 +- tests/intervalUpdates.test.ts | 487 ++++++++++++++++++++++++++++++++++ 9 files changed, 507 insertions(+), 21 deletions(-) create mode 100644 .prettierrc create mode 100644 tests/intervalUpdates.test.ts diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..82e09f45 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": false, + "singleQuote": true, + "printWidth": 120, + "trailingComma": "all" +} diff --git a/package.json b/package.json index cb4bc74e..5930e091 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,5 @@ "npm-run-all": "^4.1.5", "prettier": "^1.18.2", "typescript": "^3.5.2" - }, - "prettier": { - "printWidth": 120, - "semi": false, - "singleQuote": true } } diff --git a/src/mappings/factory.ts b/src/mappings/factory.ts index 9af8dd28..ab2cc02c 100644 --- a/src/mappings/factory.ts +++ b/src/mappings/factory.ts @@ -20,7 +20,7 @@ export function handlePoolCreatedHelper( event: PoolCreated, factoryAddress: string = FACTORY_ADDRESS, whitelistTokens: string[] = WHITELIST_TOKENS, - staticTokenDefinitions: StaticTokenDefinition[] = STATIC_TOKEN_DEFINITIONS + staticTokenDefinitions: StaticTokenDefinition[] = STATIC_TOKEN_DEFINITIONS, ): void { // temp fix if (event.params.pool == Address.fromHexString('0x8fe8d9bb8eeba3ed688069c3d6b556c9ca258248')) { diff --git a/src/mappings/pool/collect.ts b/src/mappings/pool/collect.ts index 8bc0be23..6cbf87a4 100644 --- a/src/mappings/pool/collect.ts +++ b/src/mappings/pool/collect.ts @@ -9,7 +9,7 @@ import { updatePoolHourData, updateTokenDayData, updateTokenHourData, - updateUniswapDayData + updateUniswapDayData, } from '../../utils/intervalUpdates' import { getTrackedAmountUSD, WHITELIST_TOKENS } from '../../utils/pricing' @@ -20,7 +20,7 @@ export function handleCollect(event: CollectEvent): void { export function handleCollectHelper( event: CollectEvent, factoryAddress: string = FACTORY_ADDRESS, - whitelistTokens: string[] = WHITELIST_TOKENS + whitelistTokens: string[] = WHITELIST_TOKENS, ): void { const bundle = Bundle.load('1')! const pool = Pool.load(event.address.toHexString()) @@ -44,7 +44,7 @@ export function handleCollectHelper( token0 as Token, collectedAmountToken1, token1 as Token, - whitelistTokens + whitelistTokens, ) // Reset tvl aggregates until new amounts calculated diff --git a/src/mappings/pool/swap.ts b/src/mappings/pool/swap.ts index 1743b261..f20d680a 100644 --- a/src/mappings/pool/swap.ts +++ b/src/mappings/pool/swap.ts @@ -48,7 +48,7 @@ export function handleSwap(event: SwapEvent): void { // get amount that should be tracked only - div 2 because cant count both input and output as volume const amountTotalUSDTracked = getTrackedAmountUSD(amount0Abs, token0 as Token, amount1Abs, token1 as Token).div( - BigDecimal.fromString('2') + BigDecimal.fromString('2'), ) const amountTotalETHTracked = safeDiv(amountTotalUSDTracked, bundle.ethPriceUSD) const amountTotalUSDUntracked = amount0USD.plus(amount1USD).div(BigDecimal.fromString('2')) diff --git a/src/utils/intervalUpdates.ts b/src/utils/intervalUpdates.ts index fcdfadeb..2d2232b0 100644 --- a/src/utils/intervalUpdates.ts +++ b/src/utils/intervalUpdates.ts @@ -18,8 +18,8 @@ import { FACTORY_ADDRESS } from './constants' * Tracks global aggregate data over daily windows * @param event */ -export function updateUniswapDayData(event: ethereum.Event): UniswapDayData { - const uniswap = Factory.load(FACTORY_ADDRESS)! +export function updateUniswapDayData(event: ethereum.Event, factoryAddress: string = FACTORY_ADDRESS): UniswapDayData { + const uniswap = Factory.load(factoryAddress)! const timestamp = event.block.timestamp.toI32() const dayID = timestamp / 86400 // rounded const dayStartTimestamp = dayID * 86400 @@ -72,6 +72,7 @@ export function updatePoolDayData(event: ethereum.Event): PoolDayData { poolDayData.sqrtPrice = pool.sqrtPrice poolDayData.token0Price = pool.token0Price poolDayData.token1Price = pool.token1Price + poolDayData.close = pool.token0Price poolDayData.tick = pool.tick poolDayData.tvlUSD = pool.totalValueLockedUSD poolDayData.txCount = poolDayData.txCount.plus(ONE_BI) diff --git a/src/utils/pricing.ts b/src/utils/pricing.ts index 7d63f642..1ec27745 100644 --- a/src/utils/pricing.ts +++ b/src/utils/pricing.ts @@ -30,7 +30,7 @@ export const WHITELIST_TOKENS: string[] = [ '0x956f47f50a910163d8bf957cf5846d573e7f87ca', // FEI '0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0', // MATIC '0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9', // AAVE - '0xfe2e637202056d30016725477c5da089ab0a043a' // sETH2 + '0xfe2e637202056d30016725477c5da089ab0a043a', // sETH2 ] const STABLE_COINS: string[] = [ @@ -39,7 +39,7 @@ const STABLE_COINS: string[] = [ '0xdac17f958d2ee523a2206206994597c13d831ec7', '0x0000000000085d4780b73119b644ae5ecd22b376', '0x956f47f50a910163d8bf957cf5846d573e7f87ca', - '0x4dd28568d05f09b02220b09c2cb307bfd837cb95' + '0x4dd28568d05f09b02220b09c2cb307bfd837cb95', ] const MINIMUM_ETH_LOCKED = BigDecimal.fromString('60') @@ -48,10 +48,7 @@ const Q192 = BigInt.fromI32(2).pow(192 as u8) export function sqrtPriceX96ToTokenPrices(sqrtPriceX96: BigInt, token0: Token, token1: Token): BigDecimal[] { const num = sqrtPriceX96.times(sqrtPriceX96).toBigDecimal() const denom = BigDecimal.fromString(Q192.toString()) - const price1 = num - .div(denom) - .times(exponentToBigDecimal(token0.decimals)) - .div(exponentToBigDecimal(token1.decimals)) + const price1 = num.div(denom).times(exponentToBigDecimal(token0.decimals)).div(exponentToBigDecimal(token1.decimals)) const price0 = safeDiv(BigDecimal.fromString('1'), price1) return [price0, price1] @@ -136,7 +133,7 @@ export function getTrackedAmountUSD( token0: Token, tokenAmount1: BigDecimal, token1: Token, - whitelistTokens: string[] = WHITELIST_TOKENS + whitelistTokens: string[] = WHITELIST_TOKENS, ): BigDecimal { const bundle = Bundle.load('1')! const price0USD = token0.derivedETH.times(bundle.ethPriceUSD) diff --git a/tests/constants.ts b/tests/constants.ts index f4ce6e0b..39e45c6b 100644 --- a/tests/constants.ts +++ b/tests/constants.ts @@ -47,7 +47,7 @@ export const createTestPool = ( token1: TokenFixture, poolAddressHexString: string, feeTier: number, - tickSpacing: number + tickSpacing: number, ): void => { const mockEvent = newMockEvent() const token0Address = Address.fromString(token0.address) @@ -68,7 +68,7 @@ export const createTestPool = ( mockEvent.block, mockEvent.transaction, parameters, - mockEvent.receipt + mockEvent.receipt, ) // create mock contract calls for token0 createMockedFunction(token0Address, 'symbol', 'symbol():(string)').returns([ethereum.Value.fromString(token0.symbol)]) diff --git a/tests/intervalUpdates.test.ts b/tests/intervalUpdates.test.ts new file mode 100644 index 00000000..39d00932 --- /dev/null +++ b/tests/intervalUpdates.test.ts @@ -0,0 +1,487 @@ +import { Address, BigDecimal, BigInt } from '@graphprotocol/graph-ts' +import { beforeEach, clearStore, describe, test } from 'matchstick-as' + +import { Bundle, Factory, Pool, Token } from '../src/types/schema' +import { ADDRESS_ZERO, FACTORY_ADDRESS, ZERO_BD, ZERO_BI } from '../src/utils/constants' +import { + updatePoolDayData, + updatePoolHourData, + updateTokenDayData, + updateTokenHourData, + updateUniswapDayData, +} from '../src/utils/intervalUpdates' +import { + assertObjectMatches, + MOCK_EVENT, + MOCK_EVENT as poolEvent, + POOL_FEE_TIER_03, + TEST_ETH_PRICE_USD, + USDC_MAINNET_FIXTURE, + USDC_WETH_03_MAINNET_POOL, + WETH_MAINNET_FIXTURE, +} from './constants' + +describe('uniswap interval data', () => { + beforeEach(() => { + clearStore() + + const factory = new Factory(FACTORY_ADDRESS) + factory.poolCount = ZERO_BI + factory.totalVolumeUSD = ZERO_BD + factory.totalVolumeETH = ZERO_BD + factory.totalFeesUSD = ZERO_BD + factory.totalFeesETH = ZERO_BD + factory.untrackedVolumeUSD = ZERO_BD + factory.totalValueLockedUSDUntracked = ZERO_BD + factory.totalValueLockedETHUntracked = ZERO_BD + factory.totalValueLockedETH = ZERO_BD + factory.txCount = ZERO_BI + factory.totalValueLockedUSD = ZERO_BD + factory.owner = ADDRESS_ZERO + + factory.save() + }) + + test('success - create and update uniswapDayData', () => { + // these are the only two fields that get persisted to uniswapDayData, set them to non-zero values + const factory = Factory.load(FACTORY_ADDRESS)! + const uniswapTxCount = BigInt.fromString('10') + const uniswapTotalValueLockedUSD = BigDecimal.fromString('100') + factory.txCount = uniswapTxCount + factory.totalValueLockedUSD = uniswapTotalValueLockedUSD + factory.save() + + updateUniswapDayData(poolEvent) + const dayId = poolEvent.block.timestamp.toI32() / 86400 + const dayStartTimestamp = dayId * 86400 + + assertObjectMatches('UniswapDayData', dayId.toString(), [ + ['date', dayStartTimestamp.toString()], + ['volumeETH', '0'], + ['volumeUSD', '0'], + ['volumeUSDUntracked', '0'], + ['feesUSD', '0'], + ['tvlUSD', uniswapTotalValueLockedUSD.toString()], + ['txCount', uniswapTxCount.toString()], + ]) + + const updatedTxCount = BigInt.fromString('20') + factory.txCount = updatedTxCount + factory.save() + + updateUniswapDayData(poolEvent) + + assertObjectMatches('UniswapDayData', dayId.toString(), [['txCount', updatedTxCount.toString()]]) + }) +}) + +describe('pool interval data', () => { + beforeEach(() => { + clearStore() + + const pool = new Pool(USDC_WETH_03_MAINNET_POOL) + pool.createdAtTimestamp = ZERO_BI + pool.createdAtBlockNumber = ZERO_BI + pool.token0 = USDC_MAINNET_FIXTURE.address + pool.token1 = WETH_MAINNET_FIXTURE.address + pool.feeTier = BigInt.fromI32(POOL_FEE_TIER_03) + 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() + }) + + test('success - create and update poolDayData', () => { + const pool = Pool.load(USDC_WETH_03_MAINNET_POOL)! + pool.token0Price = BigDecimal.fromString('1') + pool.token1Price = BigDecimal.fromString('2') + pool.liquidity = BigInt.fromString('100') + pool.sqrtPrice = BigInt.fromString('200') + pool.tick = BigInt.fromString('300') + pool.totalValueLockedUSD = BigDecimal.fromString('1000') + pool.save() + + const poolEvent = MOCK_EVENT + poolEvent.address = Address.fromString(USDC_WETH_03_MAINNET_POOL) + + updatePoolDayData(poolEvent) + + const dayId = poolEvent.block.timestamp.toI32() / 86400 + const dayStartTimestamp = dayId * 86400 + const dayPoolID = poolEvent.address.toHexString().concat('-').concat(dayId.toString()) + + assertObjectMatches('PoolDayData', dayPoolID, [ + ['date', dayStartTimestamp.toString()], + ['pool', USDC_WETH_03_MAINNET_POOL], + ['volumeToken0', '0'], + ['volumeToken1', '0'], + ['volumeUSD', '0'], + ['feesUSD', '0'], + ['txCount', '1'], + ['open', '1'], + ['high', '1'], + ['low', '1'], + ['close', '1'], + ['token0Price', '1'], + ['token1Price', '2'], + ['liquidity', '100'], + ['sqrtPrice', '200'], + ['tick', '300'], + ['tvlUSD', '1000'], + ]) + + // update the high price + pool.token0Price = BigDecimal.fromString('2') + pool.save() + + updatePoolDayData(poolEvent) + + assertObjectMatches('PoolDayData', dayPoolID, [ + ['date', dayStartTimestamp.toString()], + ['pool', USDC_WETH_03_MAINNET_POOL], + ['volumeToken0', '0'], + ['volumeToken1', '0'], + ['volumeUSD', '0'], + ['feesUSD', '0'], + ['txCount', '2'], + ['open', '1'], + ['high', '2'], + ['low', '1'], + ['close', '2'], + ['token0Price', '2'], + ['token1Price', '2'], + ['liquidity', '100'], + ['sqrtPrice', '200'], + ['tick', '300'], + ['tvlUSD', '1000'], + ]) + + // update the low price + pool.token0Price = BigDecimal.fromString('0') + pool.save() + + updatePoolDayData(poolEvent) + + assertObjectMatches('PoolDayData', dayPoolID, [ + ['date', dayStartTimestamp.toString()], + ['pool', USDC_WETH_03_MAINNET_POOL], + ['volumeToken0', '0'], + ['volumeToken1', '0'], + ['volumeUSD', '0'], + ['feesUSD', '0'], + ['txCount', '3'], + ['open', '1'], + ['high', '2'], + ['low', '0'], + ['close', '0'], + ['token0Price', '0'], + ['token1Price', '2'], + ['liquidity', '100'], + ['sqrtPrice', '200'], + ['tick', '300'], + ['tvlUSD', '1000'], + ]) + }) + + test('success - create and update poolHourData', () => { + const pool = Pool.load(USDC_WETH_03_MAINNET_POOL)! + pool.token0Price = BigDecimal.fromString('1') + pool.token1Price = BigDecimal.fromString('2') + pool.liquidity = BigInt.fromString('100') + pool.sqrtPrice = BigInt.fromString('200') + pool.tick = BigInt.fromString('300') + pool.totalValueLockedUSD = BigDecimal.fromString('1000') + pool.save() + + const poolEvent = MOCK_EVENT + poolEvent.address = Address.fromString(USDC_WETH_03_MAINNET_POOL) + + updatePoolHourData(poolEvent) + + const hourIndex = poolEvent.block.timestamp.toI32() / 3600 + const hourStartUnix = hourIndex * 3600 + const hourPoolID = poolEvent.address.toHexString().concat('-').concat(hourIndex.toString()) + + assertObjectMatches('PoolHourData', hourPoolID, [ + ['periodStartUnix', hourStartUnix.toString()], + ['pool', USDC_WETH_03_MAINNET_POOL], + ['volumeToken0', '0'], + ['volumeToken1', '0'], + ['volumeUSD', '0'], + ['feesUSD', '0'], + ['txCount', '1'], + ['open', '1'], + ['high', '1'], + ['low', '1'], + ['close', '1'], + ['token0Price', '1'], + ['token1Price', '2'], + ['liquidity', '100'], + ['sqrtPrice', '200'], + ['tick', '300'], + ['tvlUSD', '1000'], + ]) + + // update the high price + pool.token0Price = BigDecimal.fromString('2') + pool.save() + + updatePoolHourData(poolEvent) + + assertObjectMatches('PoolHourData', hourPoolID, [ + ['periodStartUnix', hourStartUnix.toString()], + ['pool', USDC_WETH_03_MAINNET_POOL], + ['volumeToken0', '0'], + ['volumeToken1', '0'], + ['volumeUSD', '0'], + ['feesUSD', '0'], + ['txCount', '2'], + ['open', '1'], + ['high', '2'], + ['low', '1'], + ['close', '2'], + ['token0Price', '2'], + ['token1Price', '2'], + ['liquidity', '100'], + ['sqrtPrice', '200'], + ['tick', '300'], + ['tvlUSD', '1000'], + ]) + + // update the low price + pool.token0Price = BigDecimal.fromString('0') + pool.save() + + updatePoolHourData(poolEvent) + + assertObjectMatches('PoolHourData', hourPoolID, [ + ['periodStartUnix', hourStartUnix.toString()], + ['pool', USDC_WETH_03_MAINNET_POOL], + ['volumeToken0', '0'], + ['volumeToken1', '0'], + ['volumeUSD', '0'], + ['feesUSD', '0'], + ['txCount', '3'], + ['open', '1'], + ['high', '2'], + ['low', '0'], + ['close', '0'], + ['token0Price', '0'], + ['token1Price', '2'], + ['liquidity', '100'], + ['sqrtPrice', '200'], + ['tick', '300'], + ['tvlUSD', '1000'], + ]) + }) +}) + +describe('token interval data', () => { + beforeEach(() => { + clearStore() + + const token = new Token(WETH_MAINNET_FIXTURE.address) + token.symbol = WETH_MAINNET_FIXTURE.symbol + token.name = WETH_MAINNET_FIXTURE.name + token.decimals = BigInt.fromString(WETH_MAINNET_FIXTURE.decimals) + token.totalSupply = BigInt.fromString(WETH_MAINNET_FIXTURE.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() + + const bundle = new Bundle('1') + bundle.ethPriceUSD = ZERO_BD + bundle.save() + }) + + test('success - create and update tokenDayData', () => { + const token = Token.load(WETH_MAINNET_FIXTURE.address)! + token.derivedETH = BigDecimal.fromString('1') + token.totalValueLocked = BigDecimal.fromString('100') + token.totalValueLockedUSD = BigDecimal.fromString('1000') + token.save() + + const bundle = Bundle.load('1')! + bundle.ethPriceUSD = TEST_ETH_PRICE_USD + bundle.save() + + updateTokenDayData(token, MOCK_EVENT) + + const dayId = MOCK_EVENT.block.timestamp.toI32() / 86400 + const dayStartTimestamp = dayId * 86400 + const tokenDayID = token.id.toString().concat('-').concat(dayId.toString()) + + assertObjectMatches('TokenDayData', tokenDayID, [ + ['date', dayStartTimestamp.toString()], + ['token', WETH_MAINNET_FIXTURE.address], + ['volume', '0'], + ['volumeUSD', '0'], + ['feesUSD', '0'], + ['untrackedVolumeUSD', '0'], + ['open', TEST_ETH_PRICE_USD.toString()], + ['high', TEST_ETH_PRICE_USD.toString()], + ['low', TEST_ETH_PRICE_USD.toString()], + ['close', TEST_ETH_PRICE_USD.toString()], + ['priceUSD', TEST_ETH_PRICE_USD.toString()], + ['totalValueLocked', '100'], + ['totalValueLockedUSD', '1000'], + ]) + + // update the high price + token.derivedETH = BigDecimal.fromString('2') + token.save() + + const highPriceStr = TEST_ETH_PRICE_USD.times(BigDecimal.fromString('2')).toString() + + updateTokenDayData(token, MOCK_EVENT) + + assertObjectMatches('TokenDayData', tokenDayID, [ + ['date', dayStartTimestamp.toString()], + ['token', WETH_MAINNET_FIXTURE.address], + ['volume', '0'], + ['volumeUSD', '0'], + ['feesUSD', '0'], + ['untrackedVolumeUSD', '0'], + ['open', TEST_ETH_PRICE_USD.toString()], + ['high', highPriceStr], + ['low', TEST_ETH_PRICE_USD.toString()], + ['close', highPriceStr], + ['priceUSD', highPriceStr], + ['totalValueLocked', '100'], + ['totalValueLockedUSD', '1000'], + ]) + + // update the low price + token.derivedETH = ZERO_BD + token.save() + const lowPriceStr = ZERO_BD.toString() + + updateTokenDayData(token, MOCK_EVENT) + + assertObjectMatches('TokenDayData', tokenDayID, [ + ['date', dayStartTimestamp.toString()], + ['token', WETH_MAINNET_FIXTURE.address], + ['volume', '0'], + ['volumeUSD', '0'], + ['feesUSD', '0'], + ['untrackedVolumeUSD', '0'], + ['open', TEST_ETH_PRICE_USD.toString()], + ['high', highPriceStr], + ['low', lowPriceStr], + ['close', lowPriceStr], + ['priceUSD', lowPriceStr], + ['totalValueLocked', '100'], + ['totalValueLockedUSD', '1000'], + ]) + }) + + test('success - create and update tokenHourData', () => { + const token = Token.load(WETH_MAINNET_FIXTURE.address)! + token.derivedETH = BigDecimal.fromString('1') + token.totalValueLocked = BigDecimal.fromString('100') + token.totalValueLockedUSD = BigDecimal.fromString('1000') + token.save() + + const bundle = Bundle.load('1')! + bundle.ethPriceUSD = TEST_ETH_PRICE_USD + bundle.save() + + updateTokenHourData(token, MOCK_EVENT) + + const hourIndex = MOCK_EVENT.block.timestamp.toI32() / 3600 + const hourStartUnix = hourIndex * 3600 + const tokenHourID = token.id.toString().concat('-').concat(hourIndex.toString()) + + assertObjectMatches('TokenHourData', tokenHourID, [ + ['periodStartUnix', hourStartUnix.toString()], + ['token', WETH_MAINNET_FIXTURE.address], + ['volume', '0'], + ['volumeUSD', '0'], + ['feesUSD', '0'], + ['untrackedVolumeUSD', '0'], + ['open', TEST_ETH_PRICE_USD.toString()], + ['high', TEST_ETH_PRICE_USD.toString()], + ['low', TEST_ETH_PRICE_USD.toString()], + ['close', TEST_ETH_PRICE_USD.toString()], + ['priceUSD', TEST_ETH_PRICE_USD.toString()], + ['totalValueLocked', '100'], + ['totalValueLockedUSD', '1000'], + ]) + + // update the high price + token.derivedETH = BigDecimal.fromString('2') + token.save() + + const highPriceStr = TEST_ETH_PRICE_USD.times(BigDecimal.fromString('2')).toString() + + updateTokenHourData(token, MOCK_EVENT) + + assertObjectMatches('TokenHourData', tokenHourID, [ + ['periodStartUnix', hourStartUnix.toString()], + ['token', WETH_MAINNET_FIXTURE.address], + ['volume', '0'], + ['volumeUSD', '0'], + ['feesUSD', '0'], + ['untrackedVolumeUSD', '0'], + ['open', TEST_ETH_PRICE_USD.toString()], + ['high', highPriceStr], + ['low', TEST_ETH_PRICE_USD.toString()], + ['close', highPriceStr], + ['priceUSD', highPriceStr], + ['totalValueLocked', '100'], + ['totalValueLockedUSD', '1000'], + ]) + + // update the low price + token.derivedETH = ZERO_BD + token.save() + const lowPriceStr = ZERO_BD.toString() + + updateTokenHourData(token, MOCK_EVENT) + + assertObjectMatches('TokenHourData', tokenHourID, [ + ['periodStartUnix', hourStartUnix.toString()], + ['token', WETH_MAINNET_FIXTURE.address], + ['volume', '0'], + ['volumeUSD', '0'], + ['feesUSD', '0'], + ['untrackedVolumeUSD', '0'], + ['open', TEST_ETH_PRICE_USD.toString()], + ['high', highPriceStr], + ['low', lowPriceStr], + ['close', lowPriceStr], + ['priceUSD', lowPriceStr], + ['totalValueLocked', '100'], + ['totalValueLockedUSD', '1000'], + ]) + }) +})