-
-
Save mgild/b5cd4bc2b18783f0d8606079d2761c0b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Big from "big.js"; | |
| import BN from "bn.js"; | |
| import * as anchor from "@project-serum/anchor"; | |
| import { Connection, Keypair, PublicKey } from "@solana/web3.js"; | |
| import { OracleJob, SwitchboardDecimal } from "@switchboard-xyz/common"; | |
| import { AggregatorHistoryRow } from "@switchboard-xyz/switchboard-v2"; | |
| async function weightedTwap( | |
| history: Array<AggregatorHistoryRow>, | |
| minSamples: number, | |
| historyInterval: [number, number], | |
| prioritizeMinSamples: boolean = true | |
| ): Promise<string> { | |
| const startTimestamp = new BN(historyInterval[0]); | |
| const endTimestamp = new BN(historyInterval[1]); | |
| const lastIndex = history.length - 1; | |
| let idx = lastIndex; | |
| let weightedSum = new Big(0); | |
| let nextTimestamp = endTimestamp; | |
| let interval = new BN(0); | |
| let numSamples = 0; | |
| let currentRow = history[idx]; | |
| while ( | |
| idx >= 0 && | |
| (currentRow.timestamp.gte(startTimestamp) || | |
| (prioritizeMinSamples && numSamples < minSamples)) | |
| ) { | |
| if (currentRow.timestamp.gt(endTimestamp)) { | |
| --idx; | |
| currentRow = history[idx]; | |
| continue; | |
| } | |
| const propagationTime = nextTimestamp.sub(currentRow.timestamp); | |
| nextTimestamp = currentRow.timestamp; | |
| weightedSum = weightedSum.add( | |
| currentRow.value.mul(fromBN(propagationTime)) | |
| ); | |
| ++numSamples; | |
| --idx; | |
| interval = interval.add(propagationTime); | |
| currentRow = history[idx]; | |
| } | |
| if (nextTimestamp.sub(startTimestamp).gt(new anchor.BN(0))) { | |
| const propagationTime = nextTimestamp.sub(startTimestamp); | |
| interval = interval.add(propagationTime); | |
| weightedSum = weightedSum.add( | |
| currentRow.value.mul(fromBN(propagationTime)) | |
| ); | |
| ++numSamples; | |
| } | |
| if (numSamples < minSamples) { | |
| throw new Error( | |
| `InsufficientHistoryForTwapError, need ${minSamples}, have ${ | |
| numSamples - 1 | |
| }` | |
| ); | |
| } | |
| // If nextTimestamp < startTimestamp, then we exceeded the actual interval | |
| // and the actual number of weighted propogation samples needs to be | |
| // from the first included timestamp. | |
| if (nextTimestamp.sub(startTimestamp).lt(new anchor.BN(0))) { | |
| const actualInterval = endTimestamp.sub(nextTimestamp); | |
| const result = safeDiv(weightedSum, fromBN(actualInterval)); | |
| return result.toString(); // Big.js cant be encoded by Node.js worker | |
| } | |
| const result = safeDiv(weightedSum, fromBN(interval)); | |
| return result.toString(); // Big.js cant be encoded by Node.js worker | |
| } | |
| export function safeDiv(number_: Big, denominator: Big, decimals = 20): Big { | |
| if (denominator.eq(new Big(0))) { | |
| throw new Error(`Cannot divide big.js by 0`); | |
| } | |
| const oldDp = Big.DP; | |
| Big.DP = decimals; | |
| const result = number_.div(denominator); | |
| Big.DP = oldDp; | |
| return result; | |
| } | |
| export function fromBN(n: anchor.BN, decimals = 0): Big { | |
| const big = new SwitchboardDecimal(n, decimals).toBig(); | |
| return big; | |
| } | |
| (async function main() { | |
| let history = [ | |
| { timestamp: new BN(5), value: new Big(5) }, | |
| { timestamp: new BN(7), value: new Big(6) }, | |
| { timestamp: new BN(10), value: new Big(7) }, | |
| { timestamp: new BN(14), value: new Big(8) }, | |
| ]; | |
| let minSamples = 3; | |
| let historyInterval: [number, number] = [8, 11]; | |
| console.log(await weightedTwap(history, minSamples, historyInterval)); | |
| console.log(`Expected: 6`); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment