diff --git a/adapters/api3/index.ts b/adapters/api3/index.ts index 12a12d8..2939c5e 100644 --- a/adapters/api3/index.ts +++ b/adapters/api3/index.ts @@ -1,9 +1,11 @@ import fetch from "node-fetch"; import { call, multiCall } from "@defillama/sdk/build/abi/abi2"; -import { CliffAdapterResult, BlockTime } from "../../types/adapters"; +import { CliffAdapterResult, TimeSeriesChainData } from "../../types/adapters"; import { abi } from "./abi"; import { getTimestamp } from "@defillama/sdk/build/util/index"; import { periodToSeconds } from "../../utils/time"; +import { PromisePool } from "@supercharge/promise-pool"; +import { filterRawAmounts, findBlockHeightArray } from "../../utils/chainCalls"; type RewardsRes = { atBlock: string; @@ -20,9 +22,10 @@ export async function latest(): Promise { .then((r) => r.json()) .then((r) => JSON.parse(r.body)) .then((r) => - r.metadata.custom == null || r.metadata.custom.lastRecord == null + r.metadata.incompleteSections == null || + r.metadata.incompleteSections[0].lastRecord == null ? 2688 * periodToSeconds.week // origin epoch * length - : r.metadata.custom.lastRecord, + : r.metadata.incompleteSections[0].lastRecord, ); return res; } @@ -54,7 +57,9 @@ export async function stakingRewards(): Promise { allEpochs.push(i); } - const rewards: RewardsRes[] = ( + const chainData: TimeSeriesChainData = {}; + + const amounts = ( await multiCall({ calls: allEpochs.map((n: number) => ({ target, @@ -65,19 +70,19 @@ export async function stakingRewards(): Promise { }) ).filter((r: any) => r.amount != 0); - const blockHeights: number[] = rewards.map((r: any) => Number(r.atBlock)); - const blockTimes: BlockTime[] = await Promise.all( - blockHeights.map((h: number) => - getTimestamp(h, chain).then((r: number) => ({ block: h, timestamp: r })), + await Promise.all( + amounts.map((r: any) => + getTimestamp(Number(r.atBlock), chain).then((timestamp: number) => { + chainData[r.atBlock] = { timestamp, result: r.amount }; + }), ), ); const sections: CliffAdapterResult[] = []; - blockTimes.map((t: BlockTime, i: number) => { - if (Number(rewards[i].atBlock) != t.block) - throw new Error(`block mismatch in API3 staking rewards adapter`); - const amount: number = Number(rewards[i].amount) / 10 ** decimals; - sections.push({ type: "cliff", start: t.timestamp, amount }); + Object.values(chainData).map((v: any) => { + const amount: number = v.result / 10 ** decimals; + sections.push({ type: "cliff", start: v.timestamp, amount }); }); + return sections; } diff --git a/adapters/balance/index.ts b/adapters/balance/index.ts index d265c0e..69a095f 100644 --- a/adapters/balance/index.ts +++ b/adapters/balance/index.ts @@ -1,15 +1,10 @@ import { multiCall } from "@defillama/sdk/build/abi/abi2"; import fetch from "node-fetch"; import { call } from "@defillama/sdk/build/abi/abi2"; -import { CliffAdapterResult, BlockTime } from "../../types/adapters"; -import { - isFuture, - periodToSeconds, - sleep, - unixTimestampNow, -} from "../../utils/time"; -import { getBlock2 } from "../../utils/block"; -import { INCOMPLETE_SECTION_STEP } from "../../utils/constants"; +import { CliffAdapterResult } from "../../types/adapters"; +import { PromisePool } from "@supercharge/promise-pool"; +import { filterRawAmounts, findBlockHeightArray } from "../../utils/chainCalls"; + let res: number; export async function latest( @@ -30,8 +25,6 @@ export async function latest( return res; } -let blockHeightsSto: { [timestamp: number]: BlockTime } = {}; - export async function balance( owners: string[], target: string, @@ -51,91 +44,27 @@ export async function balance( }), ]); - const allTimestamps: number[] = []; - let currentTimestamp = Math.max(trackedTimestamp, timestampDeployed); - - while (!isFuture(currentTimestamp)) { - allTimestamps.push(currentTimestamp); - currentTimestamp += INCOMPLETE_SECTION_STEP; - } + const chainData = await findBlockHeightArray(trackedTimestamp, chain); - await Promise.all( - allTimestamps.map(async (t: number) => { - if (blockHeightsSto[t]) { - console.log("saved"); - return; - } - blockHeightsSto[t] = await getBlock2(chain, t).then( - (h: number | undefined) => ({ - timestamp: t, - block: h == null ? -1 : h, - }), - ); - }), - ); - - const blockHeights: BlockTime[] = allTimestamps.map( - (t: number) => blockHeightsSto[t], - ); - - let balances: any[] = []; - try { - balances = await Promise.all( - blockHeights.map((b: BlockTime) => - multiCall({ + await PromisePool.withConcurrency(10) + .for(Object.keys(chainData)) + .process( + async (block) => + await multiCall({ calls: owners.map((o: string) => ({ target, params: [o] })), abi: "erc20:balanceOf", chain, - block: b.block, + block, requery: true, }).then((r: (number | null)[]) => { if (r.includes(null)) throw new Error(`balance call failed for ${adapter}`); - return r.reduce((p: number, c: any) => Number(p) + Number(c), 0); + chainData[block].result = r.reduce( + (p: number, c: any) => Number(p) + Number(c), + 0, + ); }), - ), ); - } catch { - for (let block of blockHeights) { - await sleep(2000); - balances.push( - await multiCall({ - calls: owners.map((o: string) => ({ target, params: [o] })), - abi: "erc20:balanceOf", - chain, - block: block.block, - requery: true, - }), - ); - } - } - - if (balances.length != blockHeights.length) - throw new Error(`block mismatch in ${adapter} balance adapter`); - - const sections: CliffAdapterResult[] = []; - let depositIndex: number = 0; - for (let i = 0; i < balances.length; i++) { - const thisBalance: number = balances[i]; - if (depositIndex == 0 && thisBalance == 0) continue; - - depositIndex += 1; - if (depositIndex == 1) continue; - - const amount = (balances[i - 1] - thisBalance) / 10 ** decimals; - if (amount <= 0) continue; - - const start = blockHeights[i].timestamp; - sections.push({ type: "cliff", start, amount }); - } - if (sections.length == 0) - return [ - { - type: "cliff", - start: unixTimestampNow() - periodToSeconds.week, - amount: 0, - }, - ]; - return sections; + return filterRawAmounts(chainData, decimals); } diff --git a/adapters/conic-finance/index.ts b/adapters/conic-finance/index.ts index 0001729..d381e73 100644 --- a/adapters/conic-finance/index.ts +++ b/adapters/conic-finance/index.ts @@ -1,8 +1,8 @@ import fetch from "node-fetch"; import { call } from "@defillama/sdk/build/abi/abi2"; -import { CliffAdapterResult, BlockTime } from "../../types/adapters"; -import { isFuture, periodToSeconds } from "../../utils/time"; -import { getBlock2 } from "../../utils/block"; +import { CliffAdapterResult } from "../../types/adapters"; +import { filterRawAmounts, findBlockHeightArray } from "../../utils/chainCalls"; +import { PromisePool } from "@supercharge/promise-pool"; let res: number; @@ -24,59 +24,44 @@ export async function rebalancing(): Promise { const target: string = "0x017f5f86df6aa8d5b3c01e47e410d66f356a94a6"; const chain: string = "ethereum"; const decimals: number = 18; - const trackedTimestamp: number = await latest(); - const allTimestamps: number[] = []; - let currentTimestamp = trackedTimestamp; + const chainData = await findBlockHeightArray(await latest(), chain); - while (!isFuture(currentTimestamp)) { - allTimestamps.push(currentTimestamp); - currentTimestamp += periodToSeconds.week; - } + await PromisePool.withConcurrency(10) + .for(Object.keys(chainData)) + .process( + async (block) => + await call({ + target, + abi: { + inputs: [], + name: "totalCncMinted", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + chain, + block, + }).then((r: number | null) => { + chainData[block].result = r; + }), + ); - const blockHeights: BlockTime[] = await Promise.all( - allTimestamps.map((t: number) => - getBlock2(chain, t).then((h: number | undefined) => ({ - timestamp: t, - block: h == null ? -1 : h, - })), - ), - ); + // const sections: CliffAdapterResult[] = []; + // let atStart: boolean = true; + // for (let i = 0; i < emitted.length; i++) { + // const thisBalance: number | null = emitted[i]; - const emitted = await Promise.all( - blockHeights.map((b: BlockTime) => - call({ - target, - abi: { - inputs: [], - name: "totalCncMinted", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function", - }, - chain, - block: b.block, - }), - ), - ); + // if ((atStart && thisBalance == 0) || thisBalance == null) continue; + // atStart = false; - if (emitted.length != blockHeights.length) - throw new Error(`block mismatch in conic adapter`); + // const amount = (thisBalance - emitted[i - 1]) / 10 ** decimals; + // if (amount == 0) continue; - const sections: CliffAdapterResult[] = []; - let atStart: boolean = true; - for (let i = 0; i < emitted.length; i++) { - const thisBalance: number | null = emitted[i]; + // const start = blockHeights[i].timestamp; + // sections.push({ type: "cliff", start, amount }); + // } - if ((atStart && thisBalance == 0) || thisBalance == null) continue; - atStart = false; - - const amount = (thisBalance - emitted[i - 1]) / 10 ** decimals; - if (amount == 0) continue; - - const start = blockHeights[i].timestamp; - sections.push({ type: "cliff", start, amount }); - } - - return sections; + // return sections; + return filterRawAmounts(chainData, decimals); } diff --git a/adapters/supply/index.ts b/adapters/supply/index.ts index 36f09bd..5f07886 100644 --- a/adapters/supply/index.ts +++ b/adapters/supply/index.ts @@ -1,9 +1,8 @@ import fetch from "node-fetch"; import { call } from "@defillama/sdk/build/abi/abi2"; -import { isFuture, sleep } from "../../utils/time"; -import { getBlock2 } from "../../utils/block"; -import { INCOMPLETE_SECTION_STEP } from "../../utils/constants"; -import { CliffAdapterResult, BlockTime } from "../../types/adapters"; +import { CliffAdapterResult } from "../../types/adapters"; +import { findBlockHeightArray } from "../../utils/chainCalls"; +import { PromisePool } from "@supercharge/promise-pool"; let res: number; @@ -41,62 +40,37 @@ export async function supply( }), ]); - const allTimestamps: number[] = []; - let currentTimestamp = trackedTimestamp; + const chainData = await findBlockHeightArray(trackedTimestamp, chain); - while (!isFuture(currentTimestamp)) { - allTimestamps.push(currentTimestamp); - currentTimestamp += INCOMPLETE_SECTION_STEP; - } - - const blockHeights: BlockTime[] = await Promise.all( - allTimestamps.map((t: number) => - getBlock2(chain, t).then((h: number | undefined) => ({ - timestamp: t, - block: h == null ? -1 : h, - })), - ), - ); - - let supplies: number[] = []; - try { - supplies = await Promise.all( - blockHeights.map((b: BlockTime) => - call({ + await PromisePool.withConcurrency(10) + .for(Object.keys(chainData)) + .process( + async (block) => + await call({ target, chain, abi: "erc20:totalSupply", - block: b.block, - }).then((res: number) => res / 10 ** decimals - excluded), - ), + block, + }).then((res: number) => { + chainData[block].result = res / 10 ** decimals - excluded; + }), ); - } catch { - for (let block of blockHeights.map((b: BlockTime) => b.block)) { - await sleep(2000); - const supply = await call({ - block, - target, - chain, - abi: "erc20:totalSupply", - }); - supplies.push(supply / 10 ** decimals - excluded); - } - } - - if (supplies.length != blockHeights.length) throw new Error(`block mismatch`); const sections: CliffAdapterResult[] = []; let supplyIndex: number = 0; + const supplies = Object.values(chainData); + for (let i = 0; i < supplies.length; i++) { - const thisBalance: number = supplies[i]; - if (supplyIndex == 0 && thisBalance == 0) continue; + const thisSupply: number = supplies[i].result; + if (supplyIndex == 0 && thisSupply == 0) continue; supplyIndex += 1; - const amount = thisBalance - supplies[i - 1]; + const amount = thisSupply - supplies[i - 1].result; if (amount <= 0) continue; - const start = blockHeights[i].timestamp; + const start = supplies[i].timestamp; sections.push({ type: "cliff", start, amount }); } + return sections; } diff --git a/types/adapters.ts b/types/adapters.ts index 31a08f9..2f6ecfc 100644 --- a/types/adapters.ts +++ b/types/adapters.ts @@ -151,8 +151,10 @@ export type Event = { timestamp: number; noOfTokens: number[]; }; -export type BlockTime = { - block: number; - timestamp: number; +export type TimeSeriesChainData = { + [block: string]: { + timestamp: number; + result?: any; + }; }; export type Allocations = { [category: string]: number }; diff --git a/utils/chainCalls.ts b/utils/chainCalls.ts new file mode 100644 index 0000000..e128b91 --- /dev/null +++ b/utils/chainCalls.ts @@ -0,0 +1,62 @@ +import { PromisePool } from "@supercharge/promise-pool"; +import { lookupBlock } from "@defillama/sdk/build/util"; +import { INCOMPLETE_SECTION_STEP } from "./constants"; +import { isFuture, unixTimestampNow } from "./time"; +import { TimeSeriesChainData, CliffAdapterResult } from "../types/adapters"; + +export async function findBlockHeightArray( + trackedTimestamp: number, + chain: any, +): Promise { + const allTimestamps: number[] = []; + let currentTimestamp = trackedTimestamp; + + while (!isFuture(currentTimestamp)) { + allTimestamps.push(currentTimestamp); + currentTimestamp += INCOMPLETE_SECTION_STEP; + } + + const chainData: TimeSeriesChainData = {}; + await PromisePool.withConcurrency(10) + .for(allTimestamps) + .process(async (t) => { + const { block, timestamp } = await lookupBlock(t, { chain }); + chainData[block] = { timestamp }; + }); + + return chainData; +} +export function filterRawAmounts( + chainData: TimeSeriesChainData, + decimals: number, +): CliffAdapterResult[] { + const sections: CliffAdapterResult[] = []; + let depositIndex: number = 0; + + const data = Object.values(chainData); + for (let i = 0; i < data.length; i++) { + const thisBalance = data[i].result; + if (depositIndex == 0 && thisBalance == 0) continue; + + depositIndex += 1; + if (depositIndex == 1) continue; + if (thisBalance == 0) continue; + + const amount = (data[i - 1].result - thisBalance) / 10 ** decimals; + if (amount == 0) continue; + const start = data[i].timestamp; + + sections.push({ type: "cliff", start, amount }); + } + + if (sections.length == 0) + return [ + { + type: "cliff", + start: unixTimestampNow() - INCOMPLETE_SECTION_STEP, + amount: 0, + }, + ]; + + return sections; +} diff --git a/utils/convertToChartData.ts b/utils/convertToChartData.ts index 22cd259..e6258a9 100644 --- a/utils/convertToChartData.ts +++ b/utils/convertToChartData.ts @@ -30,6 +30,7 @@ export async function createChartData( data.rawSections.map((r: any) => { r.results.map((s: any[]) => { s.map((d: any) => { + // if (r.section != "Rewards") return; chartData.push({ data: rawToChartData( protocol, @@ -64,8 +65,9 @@ async function appendMissingDataSections( let res = await fetch(`https://api.llama.fi/emission/${protocol}`).then((r) => r.json(), ); - res = res.body ? JSON.parse(res.body).data : []; + // res = res.body ? JSON.parse(res.body).data : []; + res = []; incompleteSections.map((i: IncompleteSection) => { const sectionRes: any = res.find((s: ApiChartData) => s.label == i.key); @@ -134,6 +136,8 @@ function appendForecast( const gradient: number = recentlyEmitted / (timestamp - gradientLength * RESOLUTION_SECONDS); const change: number = incompleteSection.allocation - totalEmitted; + if (change < 0) return; + const continuousEnd: number = Math.round( Math.min( timestamp + change / gradient, @@ -214,6 +218,7 @@ function consolidateDuplicateKeys(data: ChartSection[], isTest: boolean) { const maxSectionLength: number = Math.max(...sectionLengths); data.map((d: any) => { + // if (d.section != "Rewards") return; const sortedKeys = sortedData.map((s: any) => s.section); // normalize to extrapolations