Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Valorem Fees + Options Adapters #924

Merged
merged 12 commits into from
Oct 27, 2023
25 changes: 25 additions & 0 deletions fees/valorem/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ChainEndpoints } from "../../adapters/types";
import { CHAIN } from "../../helpers/chains";

export const endpoints: ChainEndpoints = {
[CHAIN.ARBITRUM]:
"https://api.thegraph.com/subgraphs/name/valorem-labs-inc/valorem-v1-arbitrum",
};

export const methodology = {
Fees: "All fees come from users of Valorem Protocol.",
UserFees: "Valorem collects fees when users write and exercise options.",
Revenue: "All revenue generated comes from user fees.",
ProtocolRevenue:
"Valorem collects fees when users write and exercise options.",
HoldersRevenue: "Valorem has no governance token.",
SupplySideRevenue: "Valorem has no LPs.",
NotionalVolume:
"Notional Volume is calculated with the market value of the Underlying + Exercise assets of a position at the time of Write/Exercise/Redeem/Transfer.",
PremiumVolume:
"Premium Volume is calculated with the market price an Option/Claim position is trading for on the Exchange.",
};

export const OSE_DEPLOY_TIMESTAMP_BY_CHAIN = {
[CHAIN.ARBITRUM]: 1693526399,
};
168 changes: 168 additions & 0 deletions fees/valorem/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import request, { gql } from "graphql-request";
import { Chain } from "@defillama/sdk/build/general";
import type { ChainEndpoints } from "../../adapters/types";
import {
IValoremDailyRecordsResponse,
IValoremDailyTokenRecordsResponse,
IValoremDayData,
IValoremTokenDayData,
} from "./interfaces";
import BigNumber from "bignumber.js";

export const dayDataQuery = gql`
query ($skipNum: Int, $timestamp: Int) {
dayDatas(
first: 1000
skip: $skipNum
orderBy: date
orderDirection: asc
where: { date_lte: $timestamp }
) {
date
notionalVolWrittenUSD
notionalVolExercisedUSD
notionalVolRedeemedUSD
notionalVolTransferredUSD
notionalVolSettledUSD
notionalVolCoreSumUSD
volFeesAccruedUSD
volFeesSweptUSD
}
}
`;

export const getAllDailyRecords = async (
graphUrls: ChainEndpoints,
chain: Chain,
timestamp: number
): Promise<IValoremDayData[]> => {
const allDailyRecords: IValoremDayData[] = [];

let moreRemaining = true;
let i = 0;
// should really never have to loop more than once, at least for a few more years
while (moreRemaining) {
const { dayDatas }: IValoremDailyRecordsResponse = await request(
graphUrls[chain],
dayDataQuery,
{
skipNum: i * 1000,
timestamp: timestamp,
}
);

allDailyRecords.push(...dayDatas);

if (dayDatas.length < 1000) {
moreRemaining = false;
}

i++;
}

return allDailyRecords;
};

export const tokensQuery = gql`
query {
tokens(first: 1000) {
id
decimals
}
}
`;

export const tokenDayDataQuery = gql`
query ($tokenId: String, $skipNum: Int, $timestamp: Int) {
tokenDayDatas(
first: 1000
skip: $skipNum
orderBy: date
orderDirection: asc
where: { date_lte: $timestamp, token_: { id: $tokenId } }
) {
date
token {
symbol
}
notionalVolWritten
notionalVolTransferred
notionalVolSettled
notionalVolRedeemed
notionalVolExercised
notionalVolCoreSum
volFeesAccrued
volFeesSwept
}
}
`;

export type DailyTokenRecords = { [key: string]: IValoremTokenDayData[] };

export const getAllDailyTokenRecords = async (
graphUrls: ChainEndpoints,
chain: Chain,
timestamp: number
): Promise<DailyTokenRecords> => {
const { tokens }: { tokens: { id: string; decimals: number }[] } =
await request(graphUrls[chain], tokensQuery);

let allDailyTokenRecords: DailyTokenRecords = {};

const promises = tokens.map(async (token) => {
const key = `${chain}:${token.id}`;

allDailyTokenRecords[key] = [];

let moreRemaining = true;
let i = 0;

// should really never have to loop more than once, at least for a few more years
while (moreRemaining) {
const { tokenDayDatas }: IValoremDailyTokenRecordsResponse =
await request(graphUrls[chain], tokenDayDataQuery, {
tokenId: token.id,
skipNum: i * 1000,
timestamp: timestamp,
});

const parsed = tokenDayDatas.map((tokenDayData) => {
let parsedValue = Object.keys(tokenDayData).reduce(
(acc, key) => {
if (key === "token" || key === "date") {
return acc;
}
try {
const asBigInt = new BigNumber(
tokenDayData[
key as keyof Omit<IValoremTokenDayData, "date" | "token">
]
).times(new BigNumber(10 ** -token.decimals));

acc[key] = asBigInt.toString();
} catch (error) {}
return acc;
},
{
date: tokenDayData.date,
token: { symbol: tokenDayData.token.symbol },
} as Record<any, any>
);

return parsedValue as unknown as IValoremTokenDayData;
});

allDailyTokenRecords[key] = parsed;

if (tokenDayDatas.length < 1000) {
moreRemaining = false;
}

i++;
}
});

await Promise.all(promises);

return allDailyTokenRecords;
};
119 changes: 119 additions & 0 deletions fees/valorem/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { Adapter } from "../../adapters/types";
import { ARBITRUM } from "../../helpers/chains";
import { Chain } from "@defillama/sdk/build/general";
import { getUniqStartOfTodayTimestamp } from "../../helpers/getUniSubgraphVolume";
import type { ChainEndpoints } from "../../adapters/types";
import {
endpoints,
OSE_DEPLOY_TIMESTAMP_BY_CHAIN,
methodology,
} from "./constants";
import { IValoremDayData } from "./interfaces";
import { getAllDailyRecords } from "./helpers";

const graphOptions = (graphUrls: ChainEndpoints) => {
return (chain: Chain) => {
return async (timestamp: number) => {
const formattedTimestamp = getUniqStartOfTodayTimestamp(
new Date(timestamp * 1000)
);

// get all daily records and filter out any that are after the timestamp
const allDailyRecords = await getAllDailyRecords(
graphUrls,
chain,
timestamp
);
const filteredRecords = allDailyRecords
.map((dayData) => {
if (dayData.date <= formattedTimestamp) {
return dayData;
}
})
.filter((x) => x !== undefined) as IValoremDayData[];

const getTodaysStats = () => {
let todayStats = filteredRecords.find(
(dayData) => dayData.date === formattedTimestamp
);

// return with values set to 0 if not found
if (!todayStats) {
return {
dailyFees: undefined,
dailyUserFees: undefined,
dailyRevenue: undefined,
dailyProtocolRevenue: undefined,
};
}

return {
dailyFees: todayStats.volFeesAccruedUSD,
dailyUserFees: todayStats.volFeesAccruedUSD,
dailyRevenue: todayStats.volFeesAccruedUSD,
dailyProtocolRevenue: todayStats.volFeesAccruedUSD,
};
};

const todaysStats = getTodaysStats();

// add up totals from each individual preceding day
const totalStatsUpToToday = filteredRecords.reduce(
(acc, dayData) => {
return {
totalFees: acc.totalFees + Number(dayData.volFeesAccruedUSD),
totalUserFees:
acc.totalUserFees + Number(dayData.volFeesAccruedUSD),
totalRevenue: acc.totalRevenue + Number(dayData.volFeesAccruedUSD),
totalProtocolRevenue:
acc.totalProtocolRevenue + Number(dayData.volFeesAccruedUSD),
};
},
{
totalFees: 0,
totalUserFees: 0,
totalRevenue: 0,
totalProtocolRevenue: 0,
}
);

return {
timestamp,
dailyFees: todaysStats.dailyFees,
dailyUserFees: todaysStats.dailyUserFees,
dailyRevenue: todaysStats.dailyRevenue,
dailyProtocolRevenue: todaysStats.dailyProtocolRevenue,
totalFees:
totalStatsUpToToday.totalFees > 0
? totalStatsUpToToday.totalFees.toString()
: undefined,
totalUserFees:
totalStatsUpToToday.totalUserFees > 0
? totalStatsUpToToday.totalUserFees.toString()
: undefined,
totalRevenue:
totalStatsUpToToday.totalRevenue > 0
? totalStatsUpToToday.totalRevenue.toString()
: undefined,
totalProtocolRevenue:
totalStatsUpToToday.totalProtocolRevenue > 0
? totalStatsUpToToday.totalProtocolRevenue.toString()
: undefined,
};
};
};
};

const adapter: Adapter = {
adapter: {
[ARBITRUM]: {
fetch: graphOptions(endpoints)(ARBITRUM),
start: async () => OSE_DEPLOY_TIMESTAMP_BY_CHAIN[ARBITRUM],
meta: {
methodology,
},
},
},
};

export default adapter;
34 changes: 34 additions & 0 deletions fees/valorem/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export interface IValoremDayData {
date: number;
notionalVolWrittenUSD: string;
notionalVolExercisedUSD: string;
notionalVolRedeemedUSD: string;
notionalVolTransferredUSD: string;
notionalVolSettledUSD: string;
notionalVolCoreSumUSD: string;
volFeesAccruedUSD: string;
volFeesSweptUSD: string;
}

export interface IValoremDailyRecordsResponse {
dayDatas: IValoremDayData[];
}

export interface IValoremTokenDayData {
date: number;
token: {
symbol: string;
};
notionalVolWritten: string;
notionalVolTransferred: string;
notionalVolSettled: string;
notionalVolRedeemed: string;
notionalVolExercised: string;
notionalVolCoreSum: string;
volFeesAccrued: string;
volFeesSwept: string;
}

export interface IValoremDailyTokenRecordsResponse {
tokenDayDatas: IValoremTokenDayData[];
}
Loading
Loading