Skip to content

Commit

Permalink
Merge pull request #573 from voodoo-trade/voodoo-trade
Browse files Browse the repository at this point in the history
feat: Add fees and DEX dashboard for voodoo trade
  • Loading branch information
dtmkeng committed Jul 3, 2023
2 parents c22cb8d + 44dc90f commit 1e539e2
Show file tree
Hide file tree
Showing 2 changed files with 229 additions and 0 deletions.
108 changes: 108 additions & 0 deletions dexs/voodoo-trade/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/// Project URL: https://voodoo.trade
/// Contact: [email protected]
///
/// Voodoo Trade is the ultimate FTM-focused perpetual DEX on Fantom Network.
/// Voodoo caters solely to FTM/stable pairs, offering the deepest liquidity and most competitive
/// margin fees available, on par with CEX rates. LPs can earn real yield from both margin trades
/// and swaps on Fantom’s most highly traded pair, with no need to hold any tokens besides FTM
/// and stables. Voodoo is a fair launch platform with support from an array of Fantom Ecosystem
/// stakeholders, and implements a long-term oriented tokenomics system that is the first of its
/// kind for perpetual DEXs.

import request, { gql } from "graphql-request";
import { BreakdownAdapter, Fetch } from "../../adapters/types";
import { CHAIN } from "../../helpers/chains";
import { getUniqStartOfTodayTimestamp } from "../../helpers/getUniSubgraphVolume";

const endpoints: { [key: string]: string } = {
[CHAIN.FANTOM]: "https://api.thegraph.com/subgraphs/name/chicken-juju/voodoo-fantom-stats",
}

const historicalDataSwap = gql`
query get_volume($period: String!, $id: String!) {
volumeStats(where: {period: $period, id: $id}) {
swap
}
}
`;

const historicalDataDerivatives = gql`
query get_volume($period: String!, $id: String!) {
volumeStats(where: {period: $period, id: $id}) {
liquidation
margin
}
}
`;

interface IGraphResponse {
volumeStats: Array<{
burn: string,
liquidation: string,
margin: string,
mint: string,
swap: string,
}>
}

const getFetch = (query: string) => (chain: string): Fetch => async (timestamp: number) => {
const dayTimestamp = getUniqStartOfTodayTimestamp(new Date((timestamp * 1000)));
const dailyData: IGraphResponse = await request(endpoints[chain], query, {
id: String(dayTimestamp) + ":daily",
period: "daily",
});
const totalData: IGraphResponse = await request(endpoints[chain], query, {
id: "total",
period: "total",
});

return {
timestamp: dayTimestamp,
dailyVolume:
dailyData.volumeStats.length == 1
? String(Number(Object.values(dailyData.volumeStats[0]).reduce((sum, element) => String(Number(sum) + Number(element)))) * 10 ** -30)
: undefined,
totalVolume:
totalData.volumeStats.length == 1
? String(Number(Object.values(totalData.volumeStats[0]).reduce((sum, element) => String(Number(sum) + Number(element)))) * 10 ** -30)
: undefined,
}
}

const getStartTimestamp = async (chain: string) => {
const startTimestamps: { [chain: string]: number } = {
[CHAIN.FANTOM]: 1686971650,
}
return startTimestamps[chain]
}

const adapter: BreakdownAdapter = {
breakdown: {
"swap": Object.keys(endpoints).reduce((acc, chain) => {
return {
...acc,
[chain]: {
fetch: getFetch(historicalDataSwap)(chain),
start: async () => getStartTimestamp(chain),
meta: {
methodology: "Volume from FTM <-> stablecoin swaps. Includes both market swaps and limit orders."
}
}
}
}, {}),
"derivatives": Object.keys(endpoints).reduce((acc, chain) => {
return {
...acc,
[chain]: {
fetch: getFetch(historicalDataDerivatives)(chain),
start: async () => getStartTimestamp(chain),
meta: {
methodology: "Volume from leveraged long and short positions on FTM. Includes liquidations and margin fee from market and limit orders."
}
}
}
}, {})
}
}

export default adapter;
121 changes: 121 additions & 0 deletions fees/voodoo-trade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/// Project URL: https://voodoo.trade
/// Contact: [email protected]
///
/// Voodoo Trade is the ultimate FTM-focused perpetual DEX on Fantom Network.
/// Voodoo caters solely to FTM/stable pairs, offering the deepest liquidity and most competitive
/// margin fees available, on par with CEX rates. LPs can earn real yield from both margin trades
/// and swaps on Fantom’s most highly traded pair, with no need to hold any tokens besides FTM
/// and stables. Voodoo is a fair launch platform with support from an array of Fantom Ecosystem
/// stakeholders, and implements a long-term oriented tokenomics system that is the first of its
/// kind for perpetual DEXs.

import { GraphQLClient, gql } from "graphql-request";
import { Adapter } from "../adapters/types";
import { CHAIN } from "../helpers/chains";
import { getUniqStartOfTodayTimestamp } from "../helpers/getUniSubgraphVolume";

// Smart contract pads values with 10^30. I.e. 10 USD is stored as 10 * 10^30
const DECIMAL_PLACES = BigInt(10)**BigInt(30);

const graphQLClient = new GraphQLClient("https://api.thegraph.com/subgraphs/name/chicken-juju/voodoo-fantom-stats");

const GET_FEE_BY_ID = gql`query getFeeById($id: ID!) {
feeStat(id: $id) {
id
marginAndLiquidation
swap
mint
burn
}
}`;

interface FeeStat {
marginAndLiquidation: string;
swap: string;
mint: string;
burn: string;
}

interface GetFeeByIdResponse {
feeStat: FeeStat | null;
}

function sumOfFees(feeStat: FeeStat | null): bigint {
if (feeStat == null) {
return BigInt(0);
}
const { marginAndLiquidation, swap, mint, burn } = feeStat;
return BigInt(marginAndLiquidation) + BigInt(swap) + BigInt(mint) + BigInt(burn);
}

const getFetch = () => async (timestamp: number) => {
const dayTimestamp = getUniqStartOfTodayTimestamp(new Date(timestamp * 1000));

const {
feeStat,
} = await graphQLClient.request<GetFeeByIdResponse>(GET_FEE_BY_ID, {
id: `${dayTimestamp}:daily`
});

// Hack to retain 2 decimal places. BigInt division doesn't preserve decimal places.
const dailyFees = Number(sumOfFees(feeStat) / (DECIMAL_PLACES / BigInt(100))) / 100;

const FEE_DISTRIBUTION_PERCENTAGES = {
vmxFtmLp: 30,
vlp: 30,
esVmx: 10,
team: 20,
buyAndBurn: 5,
buyAndAddLiquidity: 5
}

const dailySupplySideRevenue = dailyFees * (
FEE_DISTRIBUTION_PERCENTAGES.vlp
) / (100);

const dailyHoldersRevenue = dailyFees * (
FEE_DISTRIBUTION_PERCENTAGES.vmxFtmLp +
FEE_DISTRIBUTION_PERCENTAGES.esVmx +
FEE_DISTRIBUTION_PERCENTAGES.buyAndBurn +
FEE_DISTRIBUTION_PERCENTAGES.buyAndAddLiquidity
) / (100);

const dailyProtocolRevenue = dailyFees * (
FEE_DISTRIBUTION_PERCENTAGES.team
) / (100);

const dailyRevenue = dailyHoldersRevenue + dailyProtocolRevenue;

return {
timestamp: dayTimestamp,
dailyFees: dailyFees.toFixed(2),
dailyUserFees: dailyFees.toFixed(2),
dailySupplySideRevenue: dailySupplySideRevenue.toFixed(2),
dailyHoldersRevenue: dailyHoldersRevenue.toFixed(2),
dailyProtocolRevenue: dailyProtocolRevenue.toFixed(2),
dailyRevenue: dailyRevenue.toFixed(2)
};
}

const methodology = {
Fees: "Fees from open/close position (0.1%), swap (0.18% to 0.8%), mint and burn (based on tokens balance in the pool) and hourly borrow fee ((assets borrowed)/(total assets in pool)*0.0045%)",
UserFees: "Fees from open/close position (0.1%), swap (0.18% to 0.8%) and borrow fee ((assets borrowed)/(total assets in pool)*0.0045%)",
HoldersRevenue: "50% of all collected fees goes to the VMX token- 30% to VMX-FTM LP token stakers, 10% to esVMX stakers, 5% to buy and burns and 5% to buyback and liquidity provisioning",
SupplySideRevenue: "30% of all collected fees goes to VLP holders",
Revenue: "Revenue is 70% of all collected fees, which goes to VMX stakers",
ProtocolRevenue: "20% of all collected fees goes to the treasury"
}

const adapter: Adapter = {
adapter: {
[CHAIN.FANTOM]: {
fetch: getFetch(),
start: async () => 1686971650,
meta: {
methodology
}
},
},
}

export default adapter;

0 comments on commit 1e539e2

Please sign in to comment.