Skip to content

Instantly share code, notes, and snippets.

@mgild
Created November 10, 2022 19:03
Show Gist options
  • Select an option

  • Save mgild/b5cd4bc2b18783f0d8606079d2761c0b to your computer and use it in GitHub Desktop.

Select an option

Save mgild/b5cd4bc2b18783f0d8606079d2761c0b to your computer and use it in GitHub Desktop.
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