Sub-Adapters 6
Preview and test each sub adapter.
Curve - Ethereum (curve-ethereum)
Curve - Arbitrum (curve-arbitrum)
Curve - Optimism (curve-optimism)
Curve - Polygon (curve-polygon)
Curve - Avalanche (curve-avalanche)
Curve - Fantom (curve-fantom)
Adapter Code
Check the entire code written for the Adapter.
Source code
Showing TS source.
1export const name = 'Curve Fees';
2export const version = '0.1.3';
3export const license = 'MIT';
4export const changelog = 'Bugfix avalanche aave pool has volume denominated in usd';
5
6interface NetInfo {
7 subgraph: string;
8 blockchain: string;
9 protocolLaunch: string;
10}
11
12// Pools reporting wrong volumes
13const exclude_pools = {
14 'fantom': ['fusdt'],
15 'arbitrum': ['mim'],
16}
17// Pools with no asset types
18const assetTypesFromPoolName = {
19 'aave': 'USD',
20 '3pool': 'USD',
21 '2pool': 'USD',
22 'ren': 'BTC',
23 'mim': 'USD',
24 }
25const FEES_TYPES = {
26 'protocol': 'PROTOCOL',
27 'total': 'TOTAL'
28}
29
30// https://api.thegraph.com/subgraphs/name/curvefi/curve and before that 'sistemico/curve',
31const networks: { [network: string]: NetInfo } = {
32 ethereum: {
33 subgraph: 'curvefi/curve',
34 blockchain: 'Ethereum',
35 protocolLaunch: '2020-01-01',
36 },
37 arbitrum: {
38 subgraph: 'dmihal/curve-arbitrum',
39 blockchain: 'Arbitrum',
40 protocolLaunch: '2021-09-20',
41 },
42 optimism: {
43 subgraph: 'dmihal/curve-optimism',
44 blockchain: 'Optimism',
45 protocolLaunch: '2022-01-18',
46 },
47 polygon: {
48 subgraph: 'dmihal/curve-polygon',
49 blockchain: 'Polygon',
50 protocolLaunch: '2021-05-03',
51 },
52 avalanche: {
53 subgraph: 'dmihal/curve-avalanche',
54 blockchain: 'Avalanche',
55 protocolLaunch: '2021-10-06',
56 },
57 fantom: {
58 subgraph: 'dmihal/curve-fantom',
59 blockchain: 'Fantom',
60 protocolLaunch: '2021-05-09',
61 },
62}
63
64export function setup(sdk: Context) {
65 const createFeeDataQuery = (subgraph: string, network: string, feesType: string) => async (date: string): Promise<number> => {
66 const startDate = date;
67 const endDate = sdk.date.offsetDaysFormatted(date, +1);
68 const oneDay = await createFeeRangeQuery(subgraph, network, feesType)(startDate, endDate);
69 return oneDay;
70 }
71
72const graphQuery = `query fees($timestamp_gte: Int!, $timestamp_lte: Int!)
73 {
74 dailyVolumes (
75 orderBy: timestamp
76 orderDirection: desc
77 first: 1000
78 where: {
79 timestamp_gte: $timestamp_gte
80 timestamp_lt: $timestamp_lte
81 }
82 ) {
83 pool {
84 fee
85 adminFee
86 assetType
87 name
88 }
89 volume
90 timestamp
91 }
92 }
93 `;
94 const createFeeRangeQuery =
95 (subgraph: string, network: string, feesType: string) => async (startDate: string, endDate: string): Promise<number> => {
96 const data = await sdk.graph.query(
97 subgraph,
98 graphQuery,
99 {
100 variables: {
101 timestamp_gte: sdk.date.dateToTimestamp(startDate),
102 timestamp_lte: sdk.date.dateToTimestamp(endDate),
103 network
104 },
105 }
106 );
107 const volumesLength = data.dailyVolumes.length;
108 if (volumesLength === 0) {
109 throw new Error(`No curve data found between ${startDate} and ${endDate} from ${subgraph}`);
110 }
111 // return volumesLength
112 // Convert symbol (returned by curve graph as type) to coingecko id
113 //const types = data.dailyVolumes.map(vol => vol.pool.assetType)
114 // .filter((value, index, self) => self.indexOf(value) === index);
115 // USD ETH BTC LINK EUR
116 const get_coingecko_id = {
117 "USD": 'usd-coin',
118 "ETH": 'ethereum',
119 "BTC": 'bitcoin',
120 "LINK": 'chainlink',
121 "EUR": "stasis-eurs"
122 };
123 const asset_type_price:any = {'null': 0};
124 await Promise.all(
125 Object.keys(get_coingecko_id).map(async (item) => {
126 asset_type_price[item] = parseFloat(
127 await sdk.coinGecko.getHistoricalPrice(get_coingecko_id[item], endDate)
128 );
129 }));
130 // sdk.log(asset_type_price)
131 const feesPerPool = data.dailyVolumes.map((vol: any):number => {
132 const feeMult = (feesType == FEES_TYPES.protocol)? parseFloat(vol.pool.adminFee) : 1;
133
134 // Handle particular cases
135 // Pools with wrong volumes
136 if (exclude_pools[network] && exclude_pools[network].indexOf(vol.pool.name) > -1) {
137 return 0; // returns wrong volume: fusdt on fantom or mim on arbi
138 }
139 // And pools with no asset type
140 if (!vol.pool.assetType) {
141 vol.pool.assetType = assetTypesFromPoolName[vol.pool.name];
142 }
143
144 // Compute fees as volume * fee * feeMult * asset_price_usd
145 // sdk.log('vol', vol)
146 return parseFloat(vol.volume) * parseFloat(vol.pool.fee)
147 * asset_type_price[vol.pool.assetType] * feeMult;
148 })
149 sdk.log('feesPerPool', feesPerPool)
150
151 const feesDuringRange = feesPerPool.reduce((acc: number, curr: number) => acc + curr, 0.);
152 return feesDuringRange;
153 }
154
155 const metadata = {
156 category: 'dex',
157 name: 'Curve',
158 description: 'Curve is a community-owned permissionless, decentralized exchange',
159 feeDescription: 'Trading fees are paid by traders to liquidity providers and pool admins',
160 source: 'The Graph Protocol',
161 tokenTicker: 'CRV',
162 tokenCoingecko: 'curve-dao-token',
163 website: 'https://curve.fi/',
164 icon: sdk.ipfs.getDataURILoader('QmeGCLQAfUANUB79AJ6hMnY7DeBdX3WssantuZDBXNbAF8', 'image/png'),
165 protocolLaunch: '2020-01-01',
166 };
167
168 sdk.registerBundle('curve', metadata);
169
170 Object.entries(networks).map(([network, { subgraph, blockchain, protocolLaunch }]: [string, NetInfo]) => {
171 sdk.register({
172 id: `curve-${network}`,
173 bundle: 'curve',
174 queries: {
175 oneDayTotalFees: createFeeDataQuery(subgraph, network, FEES_TYPES.total),
176 oneDayProtocolFees: createFeeDataQuery(subgraph, network, FEES_TYPES.protocol),
177 },
178 metadata: {
179 ...metadata,
180 subtitle: blockchain,
181 blockchain,
182 protocolLaunch,
183 },
184 })
185 })
186}
It's something off?
Report it to the discussion board on Discord, we will take care of it.