diff --git a/Algebra/.gitignore b/Algebra/.gitignore index 0da354ab..0cdc4f34 100644 --- a/Algebra/.gitignore +++ b/Algebra/.gitignore @@ -2,4 +2,5 @@ build/ node_modules/ src/types/ .DS_STORE -yarn-error.log \ No newline at end of file +yarn-error.log +generated/ \ No newline at end of file diff --git a/Algebra/schema.graphql b/Algebra/schema.graphql index c2fdf651..7e5ef634 100644 --- a/Algebra/schema.graphql +++ b/Algebra/schema.graphql @@ -1,4 +1,4 @@ -type Factory @entity { +type Factory @entity(immutable: false) { # factory address id: ID! # amount of pools created @@ -28,13 +28,13 @@ type Factory @entity { } # stores for USD calculations -type Bundle @entity { +type Bundle @entity(immutable: false) { id: ID! # price of Matic in usd maticPriceUSD: BigDecimal! } -type Token @entity { +type Token @entity(immutable: false) { # token address id: ID! # token symbol @@ -71,7 +71,7 @@ type Token @entity { tokenDayData: [TokenDayData!]! @derivedFrom(field: "token") } -type Pool @entity { +type Pool @entity(immutable: false) { # pool address id: ID! # creation @@ -148,7 +148,7 @@ type Pool @entity { ticks: [Tick!]! @derivedFrom(field: "pool") } -type Tick @entity { +type Tick @entity(immutable: false) { # format: # id: ID! # pool address @@ -194,7 +194,16 @@ type Tick @entity { feeGrowthOutside1X128: BigInt! } -type Position @entity { +type PoolPosition @entity(immutable: false) { + id: ID! + pool: Pool! + lowerTick: Tick! + upperTick: Tick! + owner: Bytes! + liquidity: BigInt! +} + +type Position @entity(immutable: false) { # Positions created through NonfungiblePositionManager # NFT token id id: ID! @@ -238,7 +247,7 @@ type Position @entity { token1Tvl : BigDecimal } -type PositionSnapshot @entity { +type PositionSnapshot @entity(immutable: false) { # # id: ID! # owner of the NFT @@ -272,7 +281,7 @@ type PositionSnapshot @entity { feeGrowthInside1LastX128: BigInt! } -type Transaction @entity { +type Transaction @entity(immutable: false) { # txn hash id: ID! # block txn was included in @@ -290,7 +299,7 @@ type Transaction @entity { collects: [Collect!]! @derivedFrom(field: "transaction") } -type Mint @entity { +type Mint @entity(immutable: false) { # transaction hash + "#" + index in mints Transaction array id: ID! # which txn the mint was included in @@ -325,7 +334,7 @@ type Mint @entity { logIndex: BigInt } -type Burn @entity { +type Burn @entity(immutable: false) { # transaction hash + "#" + index in mints Transaction array id: ID! # txn burn was included in @@ -358,7 +367,7 @@ type Burn @entity { logIndex: BigInt } -type Swap @entity { +type Swap @entity(immutable: false) { # transaction hash + "#" + index in swaps Transaction array id: ID! # pointer to transaction @@ -393,7 +402,7 @@ type Swap @entity { logIndex: BigInt } -type Collect @entity { +type Collect @entity(immutable: false) { # transaction hash + "#" + index in collect Transaction array id: ID! # pointer to txn @@ -418,7 +427,7 @@ type Collect @entity { logIndex: BigInt } -type Flash @entity { +type Flash @entity(immutable: false) { # transaction hash + "-" + index in collect Transaction array id: ID! # pointer to txn @@ -446,7 +455,7 @@ type Flash @entity { } # Data accumulated and condensed into day stats for all of Algebra -type AlgebraDayData @entity { +type AlgebraDayData @entity(immutable: false) { # timestamp rounded to current day by dividing by 86400 id: ID! # timestamp rounded to current day by dividing by 86400 @@ -466,7 +475,7 @@ type AlgebraDayData @entity { } # Data accumulated and condensed into day stats for each pool -type PoolDayData @entity { +type PoolDayData @entity(immutable: false) { # timestamp rounded to current day by dividing by 86400 id: ID! # timestamp rounded to current day by dividing by 86400 @@ -513,7 +522,7 @@ type PoolDayData @entity { close: BigDecimal! } -type PoolFeeData @entity{ +type PoolFeeData @entity(immutable: false) { id: ID! pool: String @@ -525,7 +534,7 @@ type PoolFeeData @entity{ } # hourly stats tracker for pool -type PoolHourData @entity { +type PoolHourData @entity(immutable: false) { # format: - id: ID! # unix timestamp for start of hour @@ -569,7 +578,7 @@ type PoolHourData @entity { close: BigDecimal! } -type TickHourData @entity { +type TickHourData @entity(immutable: false) { # format: -- id: ID! # unix timestamp for start of hour @@ -594,7 +603,7 @@ type TickHourData @entity { # Data accumulated and condensed into day stats for each exchange # Note: this entity gets saved only if there is a change during the day -type TickDayData @entity { +type TickDayData @entity(immutable: false) { # format: -- id: ID! # timestamp rounded to current day by dividing by 86400 @@ -620,7 +629,7 @@ type TickDayData @entity { feeGrowthOutside1X128: BigInt! } -type TokenDayData @entity { +type TokenDayData @entity(immutable: false) { # token address concatendated with date id: ID! # timestamp rounded to current day by dividing by 86400 @@ -651,7 +660,7 @@ type TokenDayData @entity { close: BigDecimal! } -type TokenHourData @entity { +type TokenHourData @entity(immutable: false) { # token address concatendated with date id: ID! # unix timestamp for start of hour @@ -682,7 +691,7 @@ type TokenHourData @entity { close: BigDecimal! } -type FeeHourData @entity { +type FeeHourData @entity(immutable: false) { # id: ID! # diff --git a/Algebra/src/mappings/core.ts b/Algebra/src/mappings/core.ts index deda8afc..b941cac6 100644 --- a/Algebra/src/mappings/core.ts +++ b/Algebra/src/mappings/core.ts @@ -1,5 +1,5 @@ /* eslint-disable prefer-const */ -import { Bundle, Burn, Factory, Mint, Pool, Swap, Tick, Token,PoolFeeData } from '../types/schema' +import { Bundle, Burn, Factory, Mint, Pool, Swap, Tick, PoolPosition, Token,PoolFeeData } from '../types/schema' import { Pool as PoolABI } from '../types/Factory/Pool' import { BigDecimal, BigInt, ethereum, log} from '@graphprotocol/graph-ts' @@ -13,7 +13,7 @@ import { CommunityFee } from '../types/templates/Pool/Pool' import { convertTokenToDecimal, loadTransaction, safeDiv } from '../utils' -import { FACTORY_ADDRESS, ONE_BI, ZERO_BD, ZERO_BI, pools_list, TICK_SPACING } from '../utils/constants' +import { FACTORY_ADDRESS, ONE_BI, ZERO_BD, ZERO_BI, pools_list, TICK_SPACING, MAX_TVL } from '../utils/constants' import { findEthPerToken, getEthPriceInUSD, getTrackedAmountUSD, priceToTokenPrices } from '../utils/pricing' import { updatePoolDayData, @@ -107,6 +107,7 @@ export function handleMint(event: MintEvent): void { pool.totalValueLockedMatic = pool.totalValueLockedToken0 .times(token0.derivedMatic) .plus(pool.totalValueLockedToken1.times(token1.derivedMatic)) + if (pool.totalValueLockedMatic > MAX_TVL) pool.totalValueLockedMatic = ZERO_BD pool.totalValueLockedUSD = pool.totalValueLockedMatic.times(bundle.maticPriceUSD) // reset aggregates with new amounts @@ -154,6 +155,20 @@ export function handleMint(event: MintEvent): void { upperTick.liquidityGross = upperTick.liquidityGross.plus(amount) upperTick.liquidityNet = upperTick.liquidityNet.minus(amount) + let poolPositionid = pool.id + "#" + event.params.owner.toHexString() + '#' + BigInt.fromI32(event.params.bottomTick).toString() + "#" + BigInt.fromI32(event.params.topTick).toString() + let poolPosition = PoolPosition.load(poolPositionid) + if (poolPosition){ + poolPosition.liquidity += event.params.liquidityAmount + } + else{ + poolPosition = new PoolPosition(poolPositionid) + poolPosition.pool = pool.id + poolPosition.lowerTick = lowerTick.id + poolPosition.upperTick = upperTick.id + poolPosition.liquidity = event.params.liquidityAmount + poolPosition.owner = event.params.owner + } + // TODO: Update Tick's volume, fees, and liquidity provider count updateAlgebraDayData(event) @@ -167,6 +182,7 @@ export function handleMint(event: MintEvent): void { token0.save() token1.save() pool.save() + poolPosition.save() factory.save() mint.save() @@ -177,7 +193,6 @@ export function handleMint(event: MintEvent): void { } export function handleBurn(event: BurnEvent): void { - let bundle = Bundle.load('1')! let poolAddress = event.address.toHexString() let pool = Pool.load(poolAddress)! @@ -233,6 +248,7 @@ export function handleBurn(event: BurnEvent): void { pool.totalValueLockedMatic = pool.totalValueLockedToken0 .times(token0.derivedMatic) .plus(pool.totalValueLockedToken1.times(token1.derivedMatic)) + if (pool.totalValueLockedMatic > MAX_TVL) pool.totalValueLockedMatic = ZERO_BD pool.totalValueLockedUSD = pool.totalValueLockedMatic.times(bundle.maticPriceUSD) // reset aggregates with new amounts @@ -268,6 +284,13 @@ export function handleBurn(event: BurnEvent): void { upperTick.liquidityGross = upperTick.liquidityGross.minus(amount) upperTick.liquidityNet = upperTick.liquidityNet.plus(amount) + let poolPositionid = pool.id + "#" + event.params.owner.toHexString() + '#' + BigInt.fromI32(event.params.bottomTick).toString() + "#" + BigInt.fromI32(event.params.topTick).toString() + let poolPosition = PoolPosition.load(poolPositionid) + if (poolPosition){ + poolPosition.liquidity -= event.params.liquidityAmount + poolPosition.save() + } + updateAlgebraDayData(event) updatePoolDayData(event) updatePoolHourData(event) @@ -286,6 +309,8 @@ export function handleBurn(event: BurnEvent): void { } export function handleSwap(event: SwapEvent): void { + if (event.block.number == BigInt.fromString("73551725")) + return let bundle = Bundle.load('1')! let factory = Factory.load(FACTORY_ADDRESS)! let pool = Pool.load(event.address.toHexString())! @@ -427,6 +452,7 @@ export function handleSwap(event: SwapEvent): void { pool.totalValueLockedMatic = pool.totalValueLockedToken0 .times(token0.derivedMatic) .plus(pool.totalValueLockedToken1.times(token1.derivedMatic)) + if (pool.totalValueLockedMatic > MAX_TVL) pool.totalValueLockedMatic = ZERO_BD pool.totalValueLockedUSD = pool.totalValueLockedMatic.times(bundle.maticPriceUSD) factory.totalValueLockedMatic = factory.totalValueLockedMatic.plus(pool.totalValueLockedMatic) @@ -536,29 +562,6 @@ export function handleSwap(event: SwapEvent): void { // Current tick is initialized and needs to be updated loadTickUpdateFeeVarsAndSave(newTick.toI32(), event) } - - let numIters = oldTick - .minus(newTick) - .abs() - .div(TICK_SPACING) - - if (numIters.gt(BigInt.fromI32(100))) { - // In case more than 100 ticks need to be updated ignore the update in - // order to avoid timeouts. From testing this behavior occurs only upon - // pool initialization. This should not be a big issue as the ticks get - // updated later. For early users this error also disappears when calling - // collect - } else if (newTick.gt(oldTick)) { - let firstInitialized = oldTick.plus(TICK_SPACING.minus(modulo)) - for (let i = firstInitialized; i.le(newTick); i = i.plus(TICK_SPACING)) { - loadTickUpdateFeeVarsAndSave(i.toI32(), event) - } - } else if (newTick.lt(oldTick)) { - let firstInitialized = oldTick.minus(modulo) - for (let i = firstInitialized; i.ge(newTick); i = i.minus(TICK_SPACING)) { - loadTickUpdateFeeVarsAndSave(i.toI32(), event) - } - } } export function handleSetCommunityFee(event: CommunityFee): void { @@ -629,6 +632,7 @@ export function handleCollect(event: Collect): void { pool.totalValueLockedMatic = pool.totalValueLockedToken0 .times(token0.derivedMatic) .plus(pool.totalValueLockedToken1.times(token1.derivedMatic)) + if (pool.totalValueLockedMatic > MAX_TVL) pool.totalValueLockedMatic = ZERO_BD pool.totalValueLockedUSD = pool.totalValueLockedMatic.times(bundle.maticPriceUSD) // reset aggregates with new amounts @@ -644,13 +648,6 @@ export function handleCollect(event: Collect): void { function updateTickFeeVarsAndSave(tick: Tick, event: ethereum.Event): void { - let poolAddress = event.address - // not all ticks are initialized so obtaining null is expected behavior - let poolContract = PoolABI.bind(poolAddress) - - let tickResult = poolContract.ticks(tick.tickIdx.toI32()) - tick.feeGrowthOutside0X128 = tickResult.value2 - tick.feeGrowthOutside1X128 = tickResult.value3 tick.save() updateTickDayData(tick, event) } diff --git a/Algebra/src/mappings/factory.ts b/Algebra/src/mappings/factory.ts index fceb9a40..f5dcf956 100644 --- a/Algebra/src/mappings/factory.ts +++ b/Algebra/src/mappings/factory.ts @@ -145,7 +145,8 @@ export function handlePoolCreated(event: PoolEvent): void { pool.feesToken0 = ZERO_BD pool.feesToken1 = ZERO_BD pool.untrackedVolumeUSD = ZERO_BD - + pool.untrackedFeesUSD = ZERO_BD + pool.tick = ZERO_BI pool.collectedFeesToken0 = ZERO_BD pool.collectedFeesToken1 = ZERO_BD pool.collectedFeesUSD = ZERO_BD diff --git a/Algebra/src/utils/constants.ts b/Algebra/src/utils/constants.ts index 019a8fc1..5a3bbcea 100644 --- a/Algebra/src/utils/constants.ts +++ b/Algebra/src/utils/constants.ts @@ -4,7 +4,7 @@ import { Factory as FactoryContract } from '../types/templates/Pool/Factory' export const ADDRESS_ZERO = '0x0000000000000000000000000000000000000000' -export const FACTORY_ADDRESS = '0x9742E5C4452ccA62ce115d302756e9150CbA36Aa' +export const FACTORY_ADDRESS = '0x411b0fAcC3489691f28ad58c47006AF5E3Ab3A28' export let ZERO_BI = BigInt.fromI32(0) export let ONE_BI = BigInt.fromI32(1) @@ -12,6 +12,7 @@ export let ZERO_BD = BigDecimal.fromString('0') export let ONE_BD = BigDecimal.fromString('1') export let BI_18 = BigInt.fromI32(18) export let TICK_SPACING = BigInt.fromI32(60) +export let MAX_TVL = BigDecimal.fromString('500000000') export let factoryContract = FactoryContract.bind(Address.fromString(FACTORY_ADDRESS)) diff --git a/Algebra/src/utils/index.ts b/Algebra/src/utils/index.ts index c1d8054c..da00ed98 100644 --- a/Algebra/src/utils/index.ts +++ b/Algebra/src/utils/index.ts @@ -20,21 +20,30 @@ export function safeDiv(amount0: BigDecimal, amount1: BigDecimal): BigDecimal { } } -export function bigDecimalExponated(value: BigDecimal, power: BigInt): BigDecimal { - if (power.equals(ZERO_BI)) { - return ONE_BD +export function fastExponentiation(value: BigDecimal, power: i32): BigDecimal { + if (power < 0) { + const result = fastExponentiation(value, -power) + return safeDiv(ONE_BD, result) } - let negativePower = power.lt(ZERO_BI) - let result = ZERO_BD.plus(value) - let powerAbs = power.abs() - for (let i = ONE_BI; i.lt(powerAbs); i = i.plus(ONE_BI)) { - result = result.times(value) + + if (power == 0) { + return ONE_BD } - if (negativePower) { - result = safeDiv(ONE_BD, result) + if (power == 1) { + return value } + const halfPower = power / 2 + const halfResult = fastExponentiation(value, halfPower) + + // Use the fact that x ^ (2n) = (x ^ n) * (x ^ n) and we can compute (x ^ n) only once. + let result = halfResult.times(halfResult) + + // For odd powers, x ^ (2n + 1) = (x ^ 2n) * x + if (power % 2 == 1) { + result = result.times(value) + } return result } diff --git a/Algebra/src/utils/intervalUpdates.ts b/Algebra/src/utils/intervalUpdates.ts index 6834cae3..76319623 100644 --- a/Algebra/src/utils/intervalUpdates.ts +++ b/Algebra/src/utils/intervalUpdates.ts @@ -66,6 +66,8 @@ export function updatePoolDayData(event: ethereum.Event): PoolDayData { poolDayData.txCount = ZERO_BI poolDayData.feeGrowthGlobal0X128 = ZERO_BI poolDayData.feeGrowthGlobal1X128 = ZERO_BI + poolDayData.feesToken0 = ZERO_BD + poolDayData.feesToken1 = ZERO_BD poolDayData.open = pool.token0Price poolDayData.high = pool.token0Price poolDayData.low = pool.token0Price diff --git a/Algebra/src/utils/pricing.ts b/Algebra/src/utils/pricing.ts index f7f606af..478c780d 100644 --- a/Algebra/src/utils/pricing.ts +++ b/Algebra/src/utils/pricing.ts @@ -1,135 +1,139 @@ -/* eslint-disable prefer-const */ -import { ONE_BD, ZERO_BD, ZERO_BI } from './constants' -import { Bundle, Pool, Token } from './../types/schema' -import { BigDecimal, BigInt } from '@graphprotocol/graph-ts' -import { exponentToBigDecimal, safeDiv } from '../utils/index' - -const WMatic_ADDRESS = '0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270' -const USDC_WMatic_03_POOL = '0xc3c4074fbc2d504fb8ccd28e3ae46914a1ecc5ed' - -// token where amounts should contribute to tracked volume and liquidity -// usually tokens that many tokens are paired with s -export let WHITELIST_TOKENS: string[] = [ - '0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270', // WMATIC - '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC - '0xc2132d05d31c914a87c6611c10748aeb04b58e8f' // USDT -] - -let MINIMUM_Matic_LOCKED = BigDecimal.fromString('0') - -let Q192 = Math.pow(2, 192) - -let STABLE_COINS: string[] = [ - '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC - '0xc2132d05d31c914a87c6611c10748aeb04b58e8f' // SUDT -] - - -export function priceToTokenPrices(price: BigInt, token0: Token, token1: Token): BigDecimal[] { - let num = price.times(price).toBigDecimal() - let denom = BigDecimal.fromString(Q192.toString()) - let price1 = num - .div(denom) - .times(exponentToBigDecimal(token0.decimals)) - .div(exponentToBigDecimal(token1.decimals)) - - let price0 = safeDiv(BigDecimal.fromString('1'), price1) - return [price0, price1] -} - -export function getEthPriceInUSD(): BigDecimal { - let usdcPool = Pool.load(USDC_WMatic_03_POOL) // dai is token0 - if (usdcPool !== null) { - return usdcPool.token0Price - } else { - return ZERO_BD - } -} - - -/** - * Search through graph to find derived Eth per token. - * @todo update to be derived Matic (add stablecoin estimates) - **/ -export function findEthPerToken(token: Token): BigDecimal { - if (token.id == WMatic_ADDRESS) { - return ONE_BD - } - let whiteList = token.whitelistPools - // for now just take USD from pool with greatest TVL - // need to update this to actually detect best rate based on liquidity distribution - let largestLiquidityMatic = ZERO_BD - let priceSoFar = ZERO_BD - let bundle = Bundle.load('1') - - // hardcoded fix for incorrect rates - // if whitelist includes token - get the safe price - if (STABLE_COINS.includes(token.id)) { - priceSoFar = safeDiv(ONE_BD, bundle!.maticPriceUSD) - } else { - for (let i = 0; i < whiteList.length; ++i) { - let poolAddress = whiteList[i] - let pool = Pool.load(poolAddress)! - if (pool.liquidity.gt(ZERO_BI)) { - - if (pool.token0 == token.id) { - // whitelist token is token1 - let token1 = Token.load(pool.token1)! - // get the derived Matic in pool - let maticLocked = pool.totalValueLockedToken1.times(token1.derivedMatic) - if (maticLocked.gt(largestLiquidityMatic) && maticLocked.gt(MINIMUM_Matic_LOCKED)) { - largestLiquidityMatic = maticLocked - // token1 per our token * Eth per token1 - priceSoFar = pool.token1Price.times(token1.derivedMatic as BigDecimal) - } - } - if (pool.token1 == token.id) { - let token0 = Token.load(pool.token0)! - // get the derived Matic in pool - let maticLocked = pool.totalValueLockedToken0.times(token0.derivedMatic) - if (maticLocked.gt(largestLiquidityMatic) && maticLocked.gt(MINIMUM_Matic_LOCKED)) { - largestLiquidityMatic = maticLocked - // token0 per our token * Matic per token0 - priceSoFar = pool.token0Price.times(token0.derivedMatic as BigDecimal) - } - } - } - } -} - return priceSoFar // 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 * 2. - * If both are, return sum of two amounts - * If neither is, return 0 - */ -export function getTrackedAmountUSD( - tokenAmount0: BigDecimal, - token0: Token, - tokenAmount1: BigDecimal, - token1: Token -): BigDecimal { - let bundle = Bundle.load('1')! - let price0USD = token0.derivedMatic.times(bundle.maticPriceUSD) - let price1USD = token1.derivedMatic.times(bundle.maticPriceUSD) - - // both are whitelist tokens, return sum of both amounts - if (WHITELIST_TOKENS.includes(token0.id) && WHITELIST_TOKENS.includes(token1.id)) { - return tokenAmount0.times(price0USD).plus(tokenAmount1.times(price1USD)) - } - - // take double value of the whitelisted token amount - if (WHITELIST_TOKENS.includes(token0.id) && !WHITELIST_TOKENS.includes(token1.id)) { - return tokenAmount0.times(price0USD).times(BigDecimal.fromString('2')) - } - - // take double value of the whitelisted token amount - if (!WHITELIST_TOKENS.includes(token0.id) && WHITELIST_TOKENS.includes(token1.id)) { - return tokenAmount1.times(price1USD).times(BigDecimal.fromString('2')) - } - - // neither token is on white list, tracked amount is 0 - return ZERO_BD -} +/* eslint-disable prefer-const */ +import { ONE_BD, ZERO_BD, ZERO_BI } from './constants' +import { Bundle, Pool, Token } from './../types/schema' +import { BigDecimal, BigInt } from '@graphprotocol/graph-ts' +import { exponentToBigDecimal, safeDiv } from '../utils/index' + +const WMatic_ADDRESS = '0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270' +const USDC_WMatic_03_POOL = '0xae81fac689a1b4b1e06e7ef4a2ab4cd8ac0a087d' + +// token where amounts should contribute to tracked volume and liquidity +// usually tokens that many tokens are paired with s +export let WHITELIST_TOKENS: string[] = [ + '0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270', // WMATIC + '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC + '0xc2132d05d31c914a87c6611c10748aeb04b58e8f', // USDT + '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619', // WETH + '0xb5c064f955d8e7f38fe0460c556a72987494ee17', // quick + '0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6' // BTC +] + +let MINIMUM_Matic_LOCKED = BigDecimal.fromString('150000') + +let Q192 = Math.pow(2, 192) + +let STABLE_COINS: string[] = [ + '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359', + '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC + '0xc2132d05d31c914a87c6611c10748aeb04b58e8f' // SUDT +] + + +export function priceToTokenPrices(price: BigInt, token0: Token, token1: Token): BigDecimal[] { + let num = price.times(price).toBigDecimal() + let denom = BigDecimal.fromString(Q192.toString()) + let price1 = num + .div(denom) + .times(exponentToBigDecimal(token0.decimals)) + .div(exponentToBigDecimal(token1.decimals)) + + let price0 = safeDiv(BigDecimal.fromString('1'), price1) + return [price0, price1] +} + +export function getEthPriceInUSD(): BigDecimal { + let usdcPool = Pool.load(USDC_WMatic_03_POOL) // dai is token0 + if (usdcPool !== null) { + return usdcPool.token1Price + } else { + return ZERO_BD + } +} + + +/** + * Search through graph to find derived Eth per token. + * @todo update to be derived Matic (add stablecoin estimates) + **/ +export function findEthPerToken(token: Token): BigDecimal { + if (token.id == WMatic_ADDRESS) { + return ONE_BD + } + let whiteList = token.whitelistPools + // for now just take USD from pool with greatest TVL + // need to update this to actually detect best rate based on liquidity distribution + let largestLiquidityMatic = ZERO_BD + let priceSoFar = ZERO_BD + let bundle = Bundle.load('1') + + // hardcoded fix for incorrect rates + // if whitelist includes token - get the safe price + if (STABLE_COINS.includes(token.id)) { + priceSoFar = safeDiv(ONE_BD, bundle!.maticPriceUSD) + } else { + for (let i = 0; i < whiteList.length; ++i) { + let poolAddress = whiteList[i] + let pool = Pool.load(poolAddress)! + if (pool.liquidity.gt(ZERO_BI)) { + + if (pool.token0 == token.id) { + // whitelist token is token1 + let token1 = Token.load(pool.token1)! + // get the derived Matic in pool + let maticLocked = pool.totalValueLockedToken1.times(token1.derivedMatic) + if (maticLocked.gt(largestLiquidityMatic) && maticLocked.gt(MINIMUM_Matic_LOCKED)) { + largestLiquidityMatic = maticLocked + // token1 per our token * Eth per token1 + priceSoFar = pool.token1Price.times(token1.derivedMatic as BigDecimal) + } + } + if (pool.token1 == token.id) { + let token0 = Token.load(pool.token0)! + // get the derived Matic in pool + let maticLocked = pool.totalValueLockedToken0.times(token0.derivedMatic) + if (maticLocked.gt(largestLiquidityMatic) && maticLocked.gt(MINIMUM_Matic_LOCKED)) { + largestLiquidityMatic = maticLocked + // token0 per our token * Matic per token0 + priceSoFar = pool.token0Price.times(token0.derivedMatic as BigDecimal) + } + } + } + } +} + return priceSoFar // 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 * 2. + * If both are, return sum of two amounts + * If neither is, return 0 + */ +export function getTrackedAmountUSD( + tokenAmount0: BigDecimal, + token0: Token, + tokenAmount1: BigDecimal, + token1: Token +): BigDecimal { + let bundle = Bundle.load('1')! + let price0USD = token0.derivedMatic.times(bundle.maticPriceUSD) + let price1USD = token1.derivedMatic.times(bundle.maticPriceUSD) + + // both are whitelist tokens, return sum of both amounts + if (WHITELIST_TOKENS.includes(token0.id) && WHITELIST_TOKENS.includes(token1.id)) { + return tokenAmount0.times(price0USD).plus(tokenAmount1.times(price1USD)) + } + + // take double value of the whitelisted token amount + if (WHITELIST_TOKENS.includes(token0.id) && !WHITELIST_TOKENS.includes(token1.id)) { + return tokenAmount0.times(price0USD).times(BigDecimal.fromString('2')) + } + + // take double value of the whitelisted token amount + if (!WHITELIST_TOKENS.includes(token0.id) && WHITELIST_TOKENS.includes(token1.id)) { + return tokenAmount1.times(price1USD).times(BigDecimal.fromString('2')) + } + + // neither token is on white list, tracked amount is 0 + return ZERO_BD +} diff --git a/Algebra/src/utils/tick.ts b/Algebra/src/utils/tick.ts index 4d1abe4d..4c715b62 100644 --- a/Algebra/src/utils/tick.ts +++ b/Algebra/src/utils/tick.ts @@ -1,6 +1,6 @@ /* eslint-disable prefer-const */ import { BigDecimal, BigInt } from '@graphprotocol/graph-ts' -import { bigDecimalExponated, safeDiv } from '.' +import { fastExponentiation, safeDiv } from '.' import { Tick } from '../types/schema' import { Mint as MintEvent } from '../types/templates/Pool/Pool' import { ONE_BD, ZERO_BD, ZERO_BI } from './constants' @@ -23,7 +23,7 @@ export function createTick(tickId: string, tickIdx: i32, poolId: string, event: tick.price1 = ONE_BD // 1.0001^tick is token1/token0. - let price0 = bigDecimalExponated(BigDecimal.fromString('1.0001'), BigInt.fromI32(tickIdx)) + let price0 = fastExponentiation(BigDecimal.fromString('1.0001'), tickIdx) tick.price0 = price0 tick.price1 = safeDiv(ONE_BD, price0) diff --git a/Algebra/subgraph.yaml b/Algebra/subgraph.yaml index 710e5341..6d2e136c 100644 --- a/Algebra/subgraph.yaml +++ b/Algebra/subgraph.yaml @@ -1,104 +1,75 @@ -specVersion: 0.0.2 -description: Algebra is a decentralized protocol for automated token exchange on Polygon. -schema: - file: ./schema.graphql -dataSources: - - kind: ethereum/contract - name: Factory - network: matic - source: - address: '0x9742E5C4452ccA62ce115d302756e9150CbA36Aa' - abi: Factory - startBlock: 31656556 - mapping: - kind: ethereum/events - apiVersion: 0.0.5 - language: wasm/assemblyscript - file: ./src/mappings/factory.ts - entities: - - Pool - - Token - abis: - - name: Factory - file: ./abis/factory.json - - name: ERC20 - file: ./abis/ERC20.json - - name: ERC20SymbolBytes - file: ./abis/ERC20SymbolBytes.json - - name: ERC20NameBytes - file: ./abis/ERC20NameBytes.json - - name: Pool - file: ./abis/pool.json - eventHandlers: - - event: Pool(indexed address,indexed address,address) - handler: handlePoolCreated - - kind: ethereum/contract - name: NonfungiblePositionManager - network: matic - source: - address: '0x5c43b3C6d5b7dF8A672db8DD66d0b5D74BAf099F' - abi: NonfungiblePositionManager - startBlock: 31656556 - mapping: - kind: ethereum/events - apiVersion: 0.0.5 - language: wasm/assemblyscript - file: ./src/mappings/position-manager.ts - entities: - - Pool - - Token - - Deposit - abis: - - name: NonfungiblePositionManager - file: ./abis/NonfungiblePositionManager.json - - name: Pool - file: ./abis/pool.json - - name: Factory - file: ./abis/factory.json - - name: ERC20 - file: ./abis/ERC20.json - eventHandlers: - - event: Collect(indexed uint256,address,uint256,uint256) - handler: handleCollect - - event: DecreaseLiquidity(indexed uint256,uint128,uint256,uint256) - handler: handleDecreaseLiquidity - - event: IncreaseLiquidity(indexed uint256,uint128,uint128,uint256,uint256,address) - handler: handleIncreaseLiquidity - - event: Transfer(indexed address,indexed address,indexed uint256) - handler: handleTransfer -templates: - - kind: ethereum/contract - name: Pool - network: matic - source: - abi: Pool - mapping: - kind: ethereum/events - apiVersion: 0.0.5 - language: wasm/assemblyscript - file: ./src/mappings/core.ts - entities: - - Pool - - Token - abis: - - name: Pool - file: ./abis/pool.json - - name: Factory - file: ./abis/factory.json - - name: ERC20 - file: ./abis/ERC20.json - eventHandlers: - - event: Initialize(uint160,int24) - handler: handleInitialize - - event: Swap(indexed address,indexed address,int256,int256,uint160,uint128,int24) - handler: handleSwap - - event: Mint(address,indexed address,indexed int24,indexed int24,uint128,uint256,uint256) - handler: handleMint - - event: Burn(indexed address,indexed int24,indexed int24,uint128,uint256,uint256) - handler: handleBurn - - event: Fee(uint16) - handler: handleChangeFee - - event: Collect(indexed address,address,indexed int24,indexed int24,uint128,uint128) - handler: handleCollect - - event: CommunityFee(uint8,uint8) +specVersion: 0.0.4 +description: Algebra is a decentralized protocol for automated token exchange on Polygon. +schema: + file: ./schema.graphql +features: + - grafting # feature name +graft: + base: QmawGMzoJKV8Zh6TvVSd68TtYbzhJYhfMPgwX56j4YDtFA # Subgraph ID of base Subgraph + block: 73550000 # block number +dataSources: + - kind: ethereum/contract + name: Factory + network: matic + source: + address: '0x411b0fAcC3489691f28ad58c47006AF5E3Ab3A28' + abi: Factory + startBlock: 31656555 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + file: ./src/mappings/factory.ts + entities: + - Pool + - Token + abis: + - name: Factory + file: ./abis/factory.json + - name: ERC20 + file: ./abis/ERC20.json + - name: ERC20SymbolBytes + file: ./abis/ERC20SymbolBytes.json + - name: ERC20NameBytes + file: ./abis/ERC20NameBytes.json + - name: Pool + file: ./abis/pool.json + eventHandlers: + - event: Pool(indexed address,indexed address,address) + handler: handlePoolCreated +templates: + - kind: ethereum/contract + name: Pool + network: matic + source: + abi: Pool + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + file: ./src/mappings/core.ts + entities: + - Pool + - Token + abis: + - name: Pool + file: ./abis/pool.json + - name: Factory + file: ./abis/factory.json + - name: ERC20 + file: ./abis/ERC20.json + eventHandlers: + - event: Initialize(uint160,int24) + handler: handleInitialize + - event: Swap(indexed address,indexed address,int256,int256,uint160,uint128,int24) + handler: handleSwap + - event: Mint(address,indexed address,indexed int24,indexed int24,uint128,uint256,uint256) + handler: handleMint + - event: Burn(indexed address,indexed int24,indexed int24,uint128,uint256,uint256) + handler: handleBurn + - event: Fee(uint16) + handler: handleChangeFee + - event: Collect(indexed address,address,indexed int24,indexed int24,uint128,uint128) + handler: handleCollect + - event: CommunityFee(uint8,uint8) handler: handleSetCommunityFee \ No newline at end of file