Skip to content

Commit

Permalink
Merge pull request #924 from valorem-labs-inc/master
Browse files Browse the repository at this point in the history
Add Valorem Fees + Options Adapters
  • Loading branch information
dtmkeng authored Oct 27, 2023
2 parents bde7454 + 64d284a commit 614c0f5
Show file tree
Hide file tree
Showing 5 changed files with 471 additions and 0 deletions.
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

0 comments on commit 614c0f5

Please sign in to comment.