Skip to content

Commit 573c372

Browse files
authored
Update Thorswap adapter for fees/revenues calulcation (#4694)
1 parent 346f362 commit 573c372

File tree

1 file changed

+30
-147
lines changed

1 file changed

+30
-147
lines changed

fees/thorswap/index.ts

Lines changed: 30 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1,153 +1,36 @@
1-
import BigNumber from "bignumber.js";
2-
import { FetchOptions, SimpleAdapter } from "../../adapters/types"
3-
import { CHAIN } from "../../helpers/chains"
4-
import { httpGet } from "../../utils/fetchURL";
5-
import { getTimestampAtStartOfDayUTC } from "../../utils/date";
6-
7-
const chainMapping: any = {
8-
ETH: CHAIN.ETHEREUM,
9-
BTC: CHAIN.BITCOIN,
10-
AVAX: CHAIN.AVAX,
11-
BSC: CHAIN.BSC,
12-
LTC: CHAIN.LITECOIN,
13-
BCH: CHAIN.BITCOIN_CASH,
14-
DOGE: CHAIN.DOGECHAIN,
15-
GAIA: CHAIN.COSMOS,
16-
BASE: CHAIN.BASE,
17-
THOR: CHAIN.THORCHAIN,
18-
XRP: CHAIN.RIPPLE,
19-
}
20-
21-
const THORCHAIN_SUPPORTED_CHAINS = ['BTC', 'ETH', 'LTC', 'DOGE', 'GAIA', 'AVAX', 'BSC', 'BCH', 'BASE', 'THOR', 'XRP']
22-
23-
interface Pool {
24-
assetLiquidityFees: string
25-
earnings: string
26-
pool: string
27-
rewards: string
28-
runeLiquidityFees: string
29-
saverEarning: string
30-
totalLiquidityFeesRune: string
1+
import { Chain } from "../../adapters/types";
2+
import { CHAIN } from "../../helpers/chains";
3+
import { Adapter, FetchOptions, FetchResultFees } from "../../adapters/types";
4+
import { addTokensReceived } from "../../helpers/token";
5+
6+
const graph = (_chain: Chain): any => {
7+
return async (timestamp: number, _: any, options: FetchOptions): Promise<FetchResultFees> => {
8+
const dailyFees = await addTokensReceived({
9+
targets: ['0x546e7b1f4b4Df6CDb19fbDdFF325133EBFE04BA7', '0x6Ee1f539DDf1515eE49B58A5E9ae84C2E7643490'], // v3 and v4 fee collectors
10+
tokens: ['0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'],
11+
options
12+
})
13+
const dailyRevenue = dailyFees.clone();
14+
const dailyProtocolRevenue = dailyRevenue.clone(0.25); // 25% of revenue goes to protocol
15+
const dailyHoldersRevenue = dailyRevenue.clone(0.75); // 75% of revenue goes to holders
16+
return { dailyFees, dailyRevenue: dailyRevenue, dailyProtocolRevenue: dailyProtocolRevenue, dailyHoldersRevenue: dailyHoldersRevenue, timestamp }
17+
}
3118
}
3219

33-
const assetFromString = (s: string) => {
34-
35-
const NATIVE_ASSET_DELIMITER = '.'
36-
const SYNTH_ASSET_DELIMITER = '/'
37-
const TRADE_ASSET_DELIMITER = '~'
38-
39-
const isSynth = s.includes(SYNTH_ASSET_DELIMITER)
40-
const isTrade = s.includes(TRADE_ASSET_DELIMITER)
41-
const delimiter = isSynth ? SYNTH_ASSET_DELIMITER : isTrade ? TRADE_ASSET_DELIMITER : NATIVE_ASSET_DELIMITER
42-
43-
const data = s.split(delimiter)
44-
if (data.length <= 1 || !data[1]) return null
4520

46-
const chain = data[0].trim()
47-
const symbol = data[1].trim()
48-
const ticker = symbol.split('-')[0]
49-
50-
if (!symbol || !chain) return null
51-
52-
return { chain, symbol, ticker }
53-
}
54-
55-
const findInterval = (timestamp: number, intervals: any) => {
56-
for (const interval of intervals) {
57-
if (interval.startTime <= timestamp && timestamp < interval.endTime) {
58-
return interval;
59-
}
21+
const adapter: Adapter = {
22+
adapter: {
23+
[CHAIN.ETHEREUM]: {
24+
fetch: graph(CHAIN.ETHEREUM),
25+
start: '2025-02-19',
26+
},
27+
},
28+
methodology: {
29+
Fees: 'Swap fees paid by users.',
30+
Revenue: 'Swap fees paid by users.',
31+
ProtocolRevenue: '25% of revenue goes to protocol treasury.',
32+
HoldersRevenue: '75% of revenue goes to THOR stakers. Stakers can choose in which token they want to receive their rewards either THOR or USDC.',
6033
}
61-
return null;
62-
};
63-
64-
type IRequest = {
65-
[key: string]: Promise<any>;
66-
}
67-
const requests: IRequest = {}
68-
69-
export async function fetchCacheURL(url: string) {
70-
const key = url;
71-
if (!requests[key])
72-
requests[key] = httpGet(url, { headers: {"x-client-id": "defillama"}});
73-
return requests[key]
7434
}
7535

76-
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
77-
78-
79-
// New function to generate fetch logic for a single chain
80-
const getFetchForChain = (chainShortName: string) => {
81-
return async (_a:any, _b:any, options: FetchOptions) => {
82-
const startOfDay = getTimestampAtStartOfDayUTC(options.startOfDay);
83-
const earningsUrl = `https://midgard.ninerealms.com/v2/history/earnings?interval=day&from=${options.startTimestamp}&to=${options.endTimestamp}`;
84-
const reserveUrl = `https://midgard.ninerealms.com/v2/history/reserve?interval=day&from=${options.startTimestamp}&to=${options.endTimestamp}`;
85-
const poolsUrl = `https://midgard.ninerealms.com/v2/pools?period=24h`;
86-
87-
const earnings = await fetchCacheURL(earningsUrl);
88-
await sleep(3000);
89-
const revenue = await fetchCacheURL(reserveUrl);
90-
await sleep(2000);
91-
const pools = await fetchCacheURL(poolsUrl);
92-
await sleep(2000);
93-
94-
const selectedEarningInterval = findInterval(startOfDay, earnings.intervals);
95-
const selectedRevenueInterval = findInterval(startOfDay, revenue.intervals);
96-
97-
98-
const poolsByChainEarnings: Pool[] = selectedEarningInterval.pools.filter((pool: any) => assetFromString(pool.pool)?.chain === chainShortName);
99-
100-
const totalRuneDepth = pools.reduce((acum: BigNumber, pool: any) => acum.plus(pool.runeDepth), BigNumber(0));
101-
const poolsByChainData = pools.filter((pool: any) => assetFromString(pool.asset)?.chain === chainShortName);
102-
const runeDepthPerChain = poolsByChainData.reduce((acum: BigNumber, pool: any) => acum.plus(pool.runeDepth), BigNumber(0));
103-
104-
const protocolRevenue = BigNumber(selectedRevenueInterval.gasFeeOutbound || 0).minus(BigNumber(selectedRevenueInterval.gasReimbursement || 0));
105-
106-
const runePercentagePerChain = totalRuneDepth.isZero() ? BigNumber(0) : runeDepthPerChain.div(totalRuneDepth);
107-
const bondingEarnings = selectedEarningInterval.bondingEarnings ? BigNumber(selectedEarningInterval.bondingEarnings) : BigNumber(0);
108-
const bondingRewardPerChainBasedOnRuneDepth = bondingEarnings.times(runePercentagePerChain); // TODO: Artificial distribution according to the liquidity of the pools. But it is a protocol level data
109-
const protocolRevenuePerChainBasedOnRuneDepth = protocolRevenue.times(runePercentagePerChain);
110-
111-
const dailyFees = poolsByChainEarnings.reduce((acum, pool) => {
112-
const liquidityFeesPerPoolInDollars = BigNumber(pool.totalLiquidityFeesRune).div(1e8).times(BigNumber(selectedEarningInterval.runePriceUSD));
113-
const saverLiquidityFeesPerPoolInDollars = BigNumber(pool.saverEarning).div(1e8).times(BigNumber(selectedEarningInterval.runePriceUSD));
114-
const totalLiquidityFees = liquidityFeesPerPoolInDollars.plus(saverLiquidityFeesPerPoolInDollars);
115-
return acum.plus(totalLiquidityFees);
116-
}, BigNumber(0));
117-
118-
const dailySupplysideRevenue = poolsByChainEarnings.reduce((acum, pool) => {
119-
const liquidityFeesPerPoolInDollars = BigNumber(pool.totalLiquidityFeesRune).div(1e8).times(BigNumber(selectedEarningInterval.runePriceUSD));
120-
const saverLiquidityFeesPerPoolInDollars = BigNumber(pool.saverEarning).div(1e8).times(BigNumber(selectedEarningInterval.runePriceUSD));
121-
const rewardsInDollars = BigNumber(pool.rewards).div(1e8).times(BigNumber(selectedEarningInterval.runePriceUSD));
122-
const totalLiquidityFees = liquidityFeesPerPoolInDollars.plus(saverLiquidityFeesPerPoolInDollars).plus(rewardsInDollars);
123-
return acum.plus(totalLiquidityFees);
124-
}, BigNumber(0));
125-
126-
const runePriceUSDNum = selectedEarningInterval.runePriceUSD ? Number(selectedEarningInterval.runePriceUSD) : 0;
127-
const protocolRevenueByChainInDollars = protocolRevenuePerChainBasedOnRuneDepth.div(1e8).times(runePriceUSDNum);
128-
const dailyHoldersRevenue = bondingRewardPerChainBasedOnRuneDepth.div(1e8).times(runePriceUSDNum);
129-
// if (dailyFees.isZero()) throw new Error("No fees found for this day");
130-
131-
return {
132-
dailyFees,
133-
dailyUserFees: dailyFees,
134-
dailyRevenue: `${dailyHoldersRevenue.plus(protocolRevenueByChainInDollars)}`,
135-
dailyProtocolRevenue: protocolRevenueByChainInDollars.gt(0) ? protocolRevenueByChainInDollars : 0,
136-
dailyHoldersRevenue: dailyHoldersRevenue,
137-
dailySupplySideRevenue: dailySupplysideRevenue,
138-
timestamp: startOfDay
139-
};
140-
};
141-
};
142-
143-
const adapters: SimpleAdapter = {
144-
adapter: THORCHAIN_SUPPORTED_CHAINS.reduce((acc, chainKey) => {
145-
(acc as any)[chainMapping[chainKey]] = {
146-
fetch: getFetchForChain(chainKey) as any,
147-
// runAtCurrTime: true,
148-
};
149-
return acc;
150-
}, {}),
151-
};
152-
153-
export default adapters
36+
export default adapter;

0 commit comments

Comments
 (0)