diff --git a/src/utils/index.ts b/src/utils/index.ts index 9c3d9737..38a3172c 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,14 +1,16 @@ /* eslint-disable prefer-const */ import { BigInt, BigDecimal, ethereum } from '@graphprotocol/graph-ts' import { Transaction } from '../types/schema' -import { ONE_BI, ZERO_BI, ZERO_BD, ONE_BD } from '../utils/constants' +import { ZERO_BI, ZERO_BD, ONE_BD } from '../utils/constants' export function exponentToBigDecimal(decimals: BigInt): BigDecimal { - let bd = BigDecimal.fromString('1') - for (let i = ZERO_BI; i.lt(decimals as BigInt); i = i.plus(ONE_BI)) { - bd = bd.times(BigDecimal.fromString('10')) + let resultString = '1'; + + for (let i = 0; i < decimals.toI32(); i++) { + resultString += '0'; } - return bd + + return BigDecimal.fromString(resultString); } // return 0 if denominator is 0 in division @@ -20,22 +22,36 @@ export function safeDiv(amount0: BigDecimal, amount1: BigDecimal): BigDecimal { } } -export function bigDecimalExponated(value: BigDecimal, power: BigInt): BigDecimal { - if (power.equals(ZERO_BI)) { - return ONE_BD +/** + * Implements exponentiation by squaring + * (see https://en.wikipedia.org/wiki/Exponentiation_by_squaring ) + * to minimize the number of BigDecimal operations and their impact on performance. + */ +export function fastExponentiation(value: BigDecimal, power: i32): BigDecimal { + if (power < 0) { + let 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; } - return result + let halfPower = power / 2; + let 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; } export function tokenAmountToDecimal(tokenAmount: BigInt, exchangeDecimals: BigInt): BigDecimal { diff --git a/src/utils/tick.ts b/src/utils/tick.ts index 4f36729a..4f8f2317 100644 --- a/src/utils/tick.ts +++ b/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' @@ -20,7 +20,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)