Skip to content

Commit

Permalink
Merge pull request #211 from alchemyplatform/fast-exponentiation
Browse files Browse the repository at this point in the history
feat: use fast exponentiation for better perf
  • Loading branch information
mzywang authored Apr 2, 2024
2 parents 4c2a3ea + 0394686 commit 0eeb07a
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 18 deletions.
48 changes: 32 additions & 16 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions src/utils/tick.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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)

Expand Down

0 comments on commit 0eeb07a

Please sign in to comment.