Skip to content
Open
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
3 changes: 3 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ type Token @entity {
pairDayDataQuote: [PairDayData!]! @derivedFrom(field: "token1")
pairBase: [Pair!]! @derivedFrom(field: "token0")
pairQuote: [Pair!]! @derivedFrom(field: "token1")

# pairs used for USD pricing
priceTrackingPairs: [Pair!]!
}

type Pair @entity {
Expand Down
55 changes: 40 additions & 15 deletions src/mappings/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { updatePairDayData, updatePairHourData, updateTokenDayData, updateUniswa
import { ADDRESS_ZERO, BI_18, convertTokenToDecimal, createUser, FACTORY_ADDRESS, ONE_BI, ZERO_BD } from './helpers'
import { findEthPerToken, getEthPriceInUSD, getTrackedLiquidityUSD, getTrackedVolumeUSD } from './pricing'

const ALMOST_ZERO_BD = BigDecimal.fromString('0.000001')

function isCompleteMint(mintId: string): boolean {
return MintEvent.load(mintId)!.sender !== null // sufficient checks
}
Expand Down Expand Up @@ -66,7 +68,10 @@ export function handleTransfer(event: Transfer): void {
// this is to make sure all the mints are under the same transaction
if (mints.length === 0 || isCompleteMint(mints[mints.length - 1])) {
let mint = new MintEvent(
event.transaction.hash.toHexString().concat('-').concat(BigInt.fromI32(mints.length).toString()),
event.transaction.hash
.toHexString()
.concat('-')
.concat(BigInt.fromI32(mints.length).toString())
)
mint.transaction = transaction.id
mint.pair = pair.id
Expand All @@ -92,7 +97,10 @@ export function handleTransfer(event: Transfer): void {
if (event.params.to.toHexString() == pair.id) {
let burns = transaction.burns
let burn = new BurnEvent(
event.transaction.hash.toHexString().concat('-').concat(BigInt.fromI32(burns.length).toString()),
event.transaction.hash
.toHexString()
.concat('-')
.concat(BigInt.fromI32(burns.length).toString())
)
burn.transaction = transaction.id
burn.pair = pair.id
Expand Down Expand Up @@ -128,7 +136,10 @@ export function handleTransfer(event: Transfer): void {
burn = currentBurn as BurnEvent
} else {
burn = new BurnEvent(
event.transaction.hash.toHexString().concat('-').concat(BigInt.fromI32(burns.length).toString()),
event.transaction.hash
.toHexString()
.concat('-')
.concat(BigInt.fromI32(burns.length).toString())
)
burn.transaction = transaction.id
burn.needsComplete = false
Expand All @@ -139,7 +150,10 @@ export function handleTransfer(event: Transfer): void {
}
} else {
burn = new BurnEvent(
event.transaction.hash.toHexString().concat('-').concat(BigInt.fromI32(burns.length).toString()),
event.transaction.hash
.toHexString()
.concat('-')
.concat(BigInt.fromI32(burns.length).toString())
)
burn.transaction = transaction.id
burn.needsComplete = false
Expand Down Expand Up @@ -226,11 +240,11 @@ export function handleSync(event: Sync): void {
token0.save()
token1.save()

// get tracked liquidity - will be 0 if neither is in whitelist
// get tracked liquidity - will be 0 if neither is a price tracking token
let trackedLiquidityETH: BigDecimal
if (bundle.ethPrice.notEqual(ZERO_BD)) {
trackedLiquidityETH = getTrackedLiquidityUSD(pair.reserve0, token0 as Token, pair.reserve1, token1 as Token).div(
bundle.ethPrice,
bundle.ethPrice
)
} else {
trackedLiquidityETH = ZERO_BD
Expand Down Expand Up @@ -407,14 +421,22 @@ export function handleSwap(event: Swap): void {
// ETH/USD prices
let bundle = Bundle.load('1')!

// get total amounts of derived USD and ETH for tracking
let derivedAmountETH = token1.derivedETH
.times(amount1Total)
.plus(token0.derivedETH.times(amount0Total))
.div(BigDecimal.fromString('2'))
const derivedEthToken1 = token1.derivedETH.times(amount1Total)
const derivedEthToken0 = token0.derivedETH.times(amount0Total)

// default to legacy calculation
let derivedAmountETH = derivedEthToken1.plus(derivedEthToken0).div(BigDecimal.fromString('2'))

// Only divide by 2 if both derivedETH values are non-zero
if (derivedEthToken0.le(ALMOST_ZERO_BD) || derivedEthToken1.le(ALMOST_ZERO_BD)) {
derivedAmountETH = derivedEthToken0.plus(derivedEthToken1)
} else {
derivedAmountETH = derivedEthToken0.plus(derivedEthToken1).div(BigDecimal.fromString('2'))
}

let derivedAmountUSD = derivedAmountETH.times(bundle.ethPrice)

// only accounts for volume through white listed tokens
// only accounts for volume through price tracking tokens
let trackedAmountUSD = getTrackedVolumeUSD(amount0Total, token0 as Token, amount1Total, token1 as Token, pair as Pair)

let trackedAmountETH: BigDecimal
Expand Down Expand Up @@ -470,7 +492,10 @@ export function handleSwap(event: Swap): void {
}
let swaps = transaction.swaps
let swap = new SwapEvent(
event.transaction.hash.toHexString().concat('-').concat(BigInt.fromI32(swaps.length).toString()),
event.transaction.hash
.toHexString()
.concat('-')
.concat(BigInt.fromI32(swaps.length).toString())
)

// update swap event
Expand Down Expand Up @@ -527,15 +552,15 @@ export function handleSwap(event: Swap): void {
token0DayData.dailyVolumeToken = token0DayData.dailyVolumeToken.plus(amount0Total)
token0DayData.dailyVolumeETH = token0DayData.dailyVolumeETH.plus(amount0Total.times(token0.derivedETH as BigDecimal))
token0DayData.dailyVolumeUSD = token0DayData.dailyVolumeUSD.plus(
amount0Total.times(token0.derivedETH as BigDecimal).times(bundle.ethPrice),
amount0Total.times(token0.derivedETH as BigDecimal).times(bundle.ethPrice)
)
token0DayData.save()

// swap specific updating
token1DayData.dailyVolumeToken = token1DayData.dailyVolumeToken.plus(amount1Total)
token1DayData.dailyVolumeETH = token1DayData.dailyVolumeETH.plus(amount1Total.times(token1.derivedETH as BigDecimal))
token1DayData.dailyVolumeUSD = token1DayData.dailyVolumeUSD.plus(
amount1Total.times(token1.derivedETH as BigDecimal).times(bundle.ethPrice),
amount1Total.times(token1.derivedETH as BigDecimal).times(bundle.ethPrice)
)
token1DayData.save()
}
19 changes: 15 additions & 4 deletions src/mappings/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
ZERO_BD,
ZERO_BI,
} from './helpers'
import { PRICE_TRACKING_TOKENS } from './pricing'

export function handleNewPair(event: PairCreated): void {
// load factory (create if first exchange)
Expand All @@ -35,7 +36,7 @@ export function handleNewPair(event: PairCreated): void {
factory.pairCount = factory.pairCount + 1
factory.save()

// create the tokens
let pair = new Pair(event.params.pair.toHexString()) as Pair
let token0 = Token.load(event.params.token0.toHexString())
let token1 = Token.load(event.params.token1.toHexString())

Expand All @@ -59,8 +60,8 @@ export function handleNewPair(event: PairCreated): void {
token0.tradeVolumeUSD = ZERO_BD
token0.untrackedVolumeUSD = ZERO_BD
token0.totalLiquidity = ZERO_BD
// token0.allPairs = []
token0.txCount = ZERO_BI
token0.priceTrackingPairs = []
}

// fetch info if null
Expand All @@ -81,11 +82,21 @@ export function handleNewPair(event: PairCreated): void {
token1.tradeVolumeUSD = ZERO_BD
token1.untrackedVolumeUSD = ZERO_BD
token1.totalLiquidity = ZERO_BD
// token1.allPairs = []
token1.txCount = ZERO_BI
token1.priceTrackingPairs = []
}

if (PRICE_TRACKING_TOKENS.includes(token0.id)) {
const priceTrackingPairs = token1.priceTrackingPairs
priceTrackingPairs.push(pair.id)
token1.priceTrackingPairs = priceTrackingPairs
}
if (PRICE_TRACKING_TOKENS.includes(token1.id)) {
const priceTrackingPairs = token0.priceTrackingPairs
priceTrackingPairs.push(pair.id)
token0.priceTrackingPairs = priceTrackingPairs
}

let pair = new Pair(event.params.pair.toHexString()) as Pair
pair.token0 = token0.id
pair.token1 = token1.id
pair.liquidityProviderCount = ZERO_BI
Expand Down
86 changes: 43 additions & 43 deletions src/mappings/pricing.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* eslint-disable prefer-const */
import { Address, BigDecimal, BigInt } from '@graphprotocol/graph-ts/index'
import { BigDecimal, BigInt } from '@graphprotocol/graph-ts/index'

import { Bundle, Pair, Token } from '../types/schema'
import { ADDRESS_ZERO, factoryContract, ONE_BD, UNTRACKED_PAIRS, ZERO_BD } from './helpers'
import { ONE_BD, UNTRACKED_PAIRS, ZERO_BD } from './helpers'

const WETH_ADDRESS = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
const USDC_WETH_PAIR = '0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc' // created 10008355
Expand Down Expand Up @@ -40,7 +40,7 @@ export function getEthPriceInUSD(): BigDecimal {
}

// token where amounts should contribute to tracked volume and liquidity
let WHITELIST: string[] = [
export const PRICE_TRACKING_TOKENS: string[] = [
'0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH
'0x6b175474e89094c44da98b954eedeac495271d0f', // DAI
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC
Expand Down Expand Up @@ -78,36 +78,36 @@ export function findEthPerToken(token: Token): BigDecimal {
if (token.id == WETH_ADDRESS) {
return ONE_BD
}
// loop through whitelist and check if paired with any
for (let i = 0; i < WHITELIST.length; ++i) {
let pairAddress = factoryContract.getPair(Address.fromString(token.id), Address.fromString(WHITELIST[i]))
if (pairAddress.toHexString() != ADDRESS_ZERO) {
let pair = Pair.load(pairAddress.toHexString())
if (pair === null) {
const priceTrackingPairs = token.priceTrackingPairs
for (let i = 0; i < priceTrackingPairs.length; i++) {
const pairAddress = priceTrackingPairs[i]
const pair = Pair.load(pairAddress)

if (pair === null) {
continue
}
if (pair.token0 == token.id && pair.reserveETH.gt(MINIMUM_LIQUIDITY_THRESHOLD_ETH)) {
let token1 = Token.load(pair.token1)
if (token1 === null) {
continue
}
if (pair.token0 == token.id && pair.reserveETH.gt(MINIMUM_LIQUIDITY_THRESHOLD_ETH)) {
let token1 = Token.load(pair.token1)
if (token1 === null) {
continue
}
return pair.token1Price.times(token1.derivedETH as BigDecimal) // return token1 per our token * Eth per token 1
}
if (pair.token1 == token.id && pair.reserveETH.gt(MINIMUM_LIQUIDITY_THRESHOLD_ETH)) {
let token0 = Token.load(pair.token0)
if (token0 === null) {
continue
}
return pair.token0Price.times(token0.derivedETH as BigDecimal) // return token0 per our token * ETH per token 0
return pair.token1Price.times(token1.derivedETH as BigDecimal) // return token1 per our token * Eth per token 1
}
if (pair.token1 == token.id && pair.reserveETH.gt(MINIMUM_LIQUIDITY_THRESHOLD_ETH)) {
let token0 = Token.load(pair.token0)
if (token0 === null) {
continue
}
return pair.token0Price.times(token0.derivedETH as BigDecimal) // return token0 per our token * ETH per token 0
}
}

return ZERO_BD // nothing was found return 0
}

/**
* Accepts tokens and amounts, return tracked amount based on token whitelist
* If one token on whitelist, return amount in that token converted to USD.
* Accepts tokens and amounts, return tracked amount based on price tracking tokens
* If one token is a price tracking token, return amount in that token converted to USD.
* If both are, return average of two amounts
* If neither is, return 0
*/
Expand All @@ -131,45 +131,45 @@ export function getTrackedVolumeUSD(
if (pair.liquidityProviderCount.lt(BigInt.fromI32(5))) {
let reserve0USD = pair.reserve0.times(price0)
let reserve1USD = pair.reserve1.times(price1)
if (WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) {
if (PRICE_TRACKING_TOKENS.includes(token0.id) && PRICE_TRACKING_TOKENS.includes(token1.id)) {
if (reserve0USD.plus(reserve1USD).lt(MINIMUM_USD_THRESHOLD_NEW_PAIRS)) {
return ZERO_BD
}
}
if (WHITELIST.includes(token0.id) && !WHITELIST.includes(token1.id)) {
if (PRICE_TRACKING_TOKENS.includes(token0.id) && !PRICE_TRACKING_TOKENS.includes(token1.id)) {
if (reserve0USD.times(BigDecimal.fromString('2')).lt(MINIMUM_USD_THRESHOLD_NEW_PAIRS)) {
return ZERO_BD
}
}
if (!WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) {
if (!PRICE_TRACKING_TOKENS.includes(token0.id) && PRICE_TRACKING_TOKENS.includes(token1.id)) {
if (reserve1USD.times(BigDecimal.fromString('2')).lt(MINIMUM_USD_THRESHOLD_NEW_PAIRS)) {
return ZERO_BD
}
}
}

// both are whitelist tokens, take average of both amounts
if (WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) {
// both are price tracking tokens, take average of both amounts
if (PRICE_TRACKING_TOKENS.includes(token0.id) && PRICE_TRACKING_TOKENS.includes(token1.id)) {
return tokenAmount0.times(price0).plus(tokenAmount1.times(price1)).div(BigDecimal.fromString('2'))
}

// take full value of the whitelisted token amount
if (WHITELIST.includes(token0.id) && !WHITELIST.includes(token1.id)) {
// take full value of the price tracking token amount
if (PRICE_TRACKING_TOKENS.includes(token0.id) && !PRICE_TRACKING_TOKENS.includes(token1.id)) {
return tokenAmount0.times(price0)
}

// take full value of the whitelisted token amount
if (!WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) {
// take full value of the price tracking token amount
if (!PRICE_TRACKING_TOKENS.includes(token0.id) && PRICE_TRACKING_TOKENS.includes(token1.id)) {
return tokenAmount1.times(price1)
}

// neither token is on white list, tracked volume is 0
// neither token is a price tracking token, tracked volume is 0
return ZERO_BD
}

/**
* Accepts tokens and amounts, return tracked amount based on token whitelist
* If one token on whitelist, return amount in that token converted to USD * 2.
* Accepts tokens and amounts, return tracked amount based on price tracking token list
* If one token is a price tracking token, return amount in that token converted to USD * 2.
* If both are, return sum of two amounts
* If neither is, return 0
*/
Expand All @@ -183,21 +183,21 @@ export function getTrackedLiquidityUSD(
let price0 = token0.derivedETH.times(bundle.ethPrice)
let price1 = token1.derivedETH.times(bundle.ethPrice)

// both are whitelist tokens, take average of both amounts
if (WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) {
// both are price tracking tokens, take average of both amounts
if (PRICE_TRACKING_TOKENS.includes(token0.id) && PRICE_TRACKING_TOKENS.includes(token1.id)) {
return tokenAmount0.times(price0).plus(tokenAmount1.times(price1))
}

// take double value of the whitelisted token amount
if (WHITELIST.includes(token0.id) && !WHITELIST.includes(token1.id)) {
// take double value of the price tracking token amount
if (PRICE_TRACKING_TOKENS.includes(token0.id) && !PRICE_TRACKING_TOKENS.includes(token1.id)) {
return tokenAmount0.times(price0).times(BigDecimal.fromString('2'))
}

// take double value of the whitelisted token amount
if (!WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) {
// take double value of the price tracking token amount
if (!PRICE_TRACKING_TOKENS.includes(token0.id) && PRICE_TRACKING_TOKENS.includes(token1.id)) {
return tokenAmount1.times(price1).times(BigDecimal.fromString('2'))
}

// neither token is on white list, tracked volume is 0
// neither token is a price tracking token, tracked volume is 0
return ZERO_BD
}
Loading