Skip to content

Commit

Permalink
Add hourly, monthly and quarterly snapshots
Browse files Browse the repository at this point in the history
  • Loading branch information
prevostc committed Apr 10, 2024
1 parent 964c187 commit f4d8970
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 6 deletions.
4 changes: 2 additions & 2 deletions src/utils/apr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class AprState {
entry.collectTimestamp.toString(),
lastEntry.collectTimestamp.toString(),
])
throw new Error("AprCalc: collectTimestamp is not in order")
throw Error("AprCalc: collectTimestamp is not in order")
} else {
// latest entry is the last one
this.collects.push(entry)
Expand All @@ -67,7 +67,7 @@ export class AprCalc {
public static calculateLastApr(period: BigInt, state: AprState, now: BigInt): BigDecimal {
if (period.lt(ZERO_BI) || period.equals(ZERO_BI)) {
log.error("AprCalc: period cannot be negative or zero, got {}", [period.toString()])
throw new Error("AprCalc: period cannot be negative or zero")
throw Error("AprCalc: period cannot be negative or zero")
}
// we need at least 1 entry to compute the apr
if (state.collects.length === 0) {
Expand Down
2 changes: 1 addition & 1 deletion src/utils/daily-avg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class DailyAvgCalc {
public static evictOldEntries(entriesToUse: BigInt, state: DailyAvgState): DailyAvgState {
if (entriesToUse.lt(ZERO_BI) || entriesToUse.equals(ZERO_BI)) {
log.error("DailyAvgCalc: entriesToUse cannot be negative or zero, got {}", [entriesToUse.toString()])
throw new Error("DailyAvgCalc: entriesToUse cannot be negative or zero")
throw Error("DailyAvgCalc: entriesToUse cannot be negative or zero")
}

let lastEntryIdx = state.closedValues.length - 1
Expand Down
9 changes: 7 additions & 2 deletions src/utils/snapshot.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BigInt, Bytes } from "@graphprotocol/graph-ts"
import { YEAR, getPreviousIntervalFromTimestamp } from "./time"


@inline
Expand All @@ -8,8 +9,12 @@ export function getSnapshotIdSuffix(period: BigInt, interval: BigInt): Bytes {


@inline
export function getPreviousSnapshotIdSuffix(period: BigInt, interval: BigInt): Bytes {
export function getPreviousSnapshotIdSuffix(period: BigInt, timestamp: BigInt): Bytes {
// just a test to prevent developer mistakes
if (timestamp.lt(YEAR)) {
throw new Error("This function, unlike getSnapshotIdSuffix, expects the timestamp instead of the interval")
}
return Bytes.fromByteArray(Bytes.fromBigInt(period)).concat(
Bytes.fromByteArray(Bytes.fromBigInt(interval.minus(period))),
Bytes.fromByteArray(Bytes.fromBigInt(getPreviousIntervalFromTimestamp(timestamp, period))),
)
}
39 changes: 38 additions & 1 deletion src/utils/time.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,51 @@
import { BigInt } from "@graphprotocol/graph-ts"
import { log } from "matchstick-as"

export const MINUTES_15 = BigInt.fromI32(60 * 15)
export const HOUR = BigInt.fromI32(60 * 60)
export const DAY = BigInt.fromI32(60 * 60 * 24)
export const WEEK = BigInt.fromI32(60 * 60 * 24 * 7)
export const MONTH = BigInt.fromI32(60 * 60 * 24 * 30)
export const QUARTER = BigInt.fromI32(60 * 60 * 24 * 30 * 3)
export const YEAR = BigInt.fromI32(60 * 60 * 24 * 365)
export const SNAPSHOT_PERIODS = [DAY, WEEK, YEAR]
export const SNAPSHOT_PERIODS = [HOUR, DAY, WEEK, MONTH, QUARTER, YEAR]


@inline
export function getIntervalFromTimestamp(timestamp: BigInt, period: BigInt): BigInt {
// if the period is not stable, use date math to calculate the interval
if (period.ge(WEEK)) {
const date = new Date(timestamp.toI64() * 1000)
date.setUTCMilliseconds(0)
date.setUTCSeconds(0)
date.setUTCMinutes(0)
date.setUTCHours(0)
date.setUTCDate(date.getUTCDate() - date.getUTCDay())
if (period.equals(WEEK)) {
return BigInt.fromI64(date.getTime() / 1000)
}
date.setUTCDate(1)
if (period.equals(MONTH)) {
return BigInt.fromI64(date.getTime() / 1000)
}
date.setUTCMonth(date.getUTCMonth() - (date.getUTCMonth() % 3))
if (period.equals(QUARTER)) {
return BigInt.fromI64(date.getTime() / 1000)
}
date.setUTCMonth(0)
if (period.equals(YEAR)) {
return BigInt.fromI64(date.getTime() / 1000)
}

log.error("Unsupported period: {}", [period.toString()])
throw Error("Unsupported period: " + period.toString())
}
return timestamp.div(period).times(period)
}


@inline
export function getPreviousIntervalFromTimestamp(timestamp: BigInt, period: BigInt): BigInt {
const truncated = getIntervalFromTimestamp(timestamp, period)
return getIntervalFromTimestamp(truncated.minus(BigInt.fromI32(10)), period)
}
60 changes: 60 additions & 0 deletions tests/utils/time.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { assert, test, describe } from "matchstick-as/assembly/index"
import { BigInt } from "@graphprotocol/graph-ts"
import {
DAY,
HOUR,
MONTH,
QUARTER,
WEEK,
YEAR,
getIntervalFromTimestamp,
getPreviousIntervalFromTimestamp,
} from "../../src/utils/time"

describe("time.getIntervalFromTimestamp", () => {
test("Support all the different periods", () => {
const timestamp = BigInt.fromString("1712744972")

// simple periods
let res = getIntervalFromTimestamp(timestamp, HOUR)
assert.assertTrue(res.equals(BigInt.fromString("1712743200")))

res = getIntervalFromTimestamp(timestamp, DAY)
assert.assertTrue(res.equals(BigInt.fromString("1712707200")))

res = getIntervalFromTimestamp(timestamp, WEEK)
assert.assertTrue(res.equals(BigInt.fromString("1712448000")))

res = getIntervalFromTimestamp(timestamp, MONTH)
assert.assertTrue(res.equals(BigInt.fromString("1711929600")))

res = getIntervalFromTimestamp(timestamp, QUARTER)
assert.assertTrue(res.equals(BigInt.fromString("1711929600")))

res = getIntervalFromTimestamp(timestamp, YEAR)
assert.assertTrue(res.equals(BigInt.fromString("1704067200")))
})

test("can query the previous interval as well", () => {
const timestamp = BigInt.fromString("1712744972")

// simple periods
let res = getPreviousIntervalFromTimestamp(timestamp, HOUR)
assert.assertTrue(res.equals(BigInt.fromString("1712739600")))

res = getPreviousIntervalFromTimestamp(timestamp, DAY)
assert.assertTrue(res.equals(BigInt.fromString("1712620800")))

res = getPreviousIntervalFromTimestamp(timestamp, WEEK)
assert.assertTrue(res.equals(BigInt.fromString("1711843200")))

res = getPreviousIntervalFromTimestamp(timestamp, MONTH)
assert.assertTrue(res.equals(BigInt.fromString("1709251200")))

res = getPreviousIntervalFromTimestamp(timestamp, QUARTER)
assert.assertTrue(res.equals(BigInt.fromString("1704067200")))

res = getPreviousIntervalFromTimestamp(timestamp, YEAR)
assert.assertTrue(res.equals(BigInt.fromString("1672531200")))
})
})

0 comments on commit f4d8970

Please sign in to comment.