-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #924 from valorem-labs-inc/master
Add Valorem Fees + Options Adapters
- Loading branch information
Showing
5 changed files
with
471 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
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, | ||
}; |
This file contains 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
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; | ||
}; |
This file contains 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
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; |
This file contains 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
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[]; | ||
} |
Oops, something went wrong.