From 4e056bba3d24e08ca7ba058024d9c579031ae6b5 Mon Sep 17 00:00:00 2001 From: 0xgnek <0xgnek@gmail.com> Date: Fri, 23 Jun 2023 15:24:50 +0000 Subject: [PATCH 1/3] velodrome-v2 --- dexs/velodrome-v2/index.ts | 181 +++++++++++++++++++++++++++++++++++++ fees/velodrome-v2.ts | 173 +++++++++++++++++++++++++++++++++++ 2 files changed, 354 insertions(+) create mode 100644 dexs/velodrome-v2/index.ts create mode 100644 fees/velodrome-v2.ts diff --git a/dexs/velodrome-v2/index.ts b/dexs/velodrome-v2/index.ts new file mode 100644 index 0000000000..790527e7fc --- /dev/null +++ b/dexs/velodrome-v2/index.ts @@ -0,0 +1,181 @@ +import { SimpleAdapter } from "../../adapters/types"; +import { CHAIN } from "../../helpers/chains"; +import * as sdk from "@defillama/sdk"; +import { getBlock } from "../../helpers/getBlock"; +import { getPrices } from "../../utils/prices"; +import BigNumber from "bignumber.js"; + +interface ILog { + data: string; + transactionHash: string; +} +interface IAmount { + amount0In: string; + amount1In: string; + amount0Out: string; + amount1Out: string; +} +const topic_name = 'Swap(index_topic_1 address sender, index_topic_2 address to, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out)'; +const topic0 = '0xb3e2773606abfd36b5bd91394b3a54d1398336c65005baf7bf7a05efeffaf75b'; +const FACTORY_ADDRESS = '0xF1046053aa5682b4F9a81b5481394DA16BE5FF5a'; + +type TABI = { + [k: string]: object; +} +const ABIs: TABI = { + allPoolsLength: { + "inputs": [], + "name": "allPoolsLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + allPools: { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allPools", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +}; + +const PAIR_TOKEN_ABI = (token: string): object => { + return { + "constant": true, + "inputs": [], + "name": token, + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +}; + + +const fetch = async (timestamp: number) => { + const fromTimestamp = timestamp - 60 * 60 * 24 + const toTimestamp = timestamp + try { + const poolLength = (await sdk.api.abi.call({ + target: FACTORY_ADDRESS, + chain: CHAIN.OPTIMISM, + abi: ABIs.allPoolsLength, + })).output; + + const poolsRes = await sdk.api.abi.multiCall({ + abi: ABIs.allPools, + calls: Array.from(Array(Number(poolLength)).keys()).map((i) => ({ + target: FACTORY_ADDRESS, + params: i, + })), + chain: CHAIN.OPTIMISM + }); + + const lpTokens = poolsRes.output + .map(({ output }: any) => output); + + const [underlyingToken0, underlyingToken1] = await Promise.all( + ['token0', 'token1'].map((method) => + sdk.api.abi.multiCall({ + abi: PAIR_TOKEN_ABI(method), + calls: lpTokens.map((address: string) => ({ + target: address, + })), + chain: CHAIN.OPTIMISM, + permitFailure: true, + }) + ) + ); + + const tokens0 = underlyingToken0.output.map((res: any) => res.output); + const tokens1 = underlyingToken1.output.map((res: any) => res.output); + const fromBlock = (await getBlock(fromTimestamp, CHAIN.OPTIMISM, {})); + const toBlock = (await getBlock(toTimestamp, CHAIN.OPTIMISM, {})); + const logs: ILog[][] = (await Promise.all(lpTokens.map((address: string) => sdk.api.util.getLogs({ + target: address, + topic: topic_name, + toBlock: toBlock, + fromBlock: fromBlock, + keys: [], + chain: CHAIN.OPTIMISM, + topics: [topic0] + })))) + .map((p: any) => p) + .map((a: any) => a.output); + const rawCoins = [...tokens0, ...tokens1].map((e: string) => `${CHAIN.OPTIMISM}:${e}`); + const coins = [...new Set(rawCoins)] + const prices = await getPrices(coins, timestamp); + const untrackVolumes: number[] = lpTokens.map((_: string, index: number) => { + const log: IAmount[] = logs[index] + .map((e: ILog) => { return { ...e, data: e.data.replace('0x', '') } }) + .map((p: ILog) => { + BigNumber.config({ POW_PRECISION: 100 }); + const amount0In = new BigNumber('0x' + p.data.slice(0, 64)).toString(); + const amount1In = new BigNumber('0x' + p.data.slice(64, 128)).toString(); + const amount0Out = new BigNumber('0x' + p.data.slice(128, 192)).toString(); + const amount1Out = new BigNumber('0x' + p.data.slice(192, 256)).toString(); + return { + amount0In, + amount1In, + amount0Out, + amount1Out, + } as IAmount + }) as IAmount[]; + const token0Price = (prices[`${CHAIN.OPTIMISM}:${tokens0[index]}`]?.price || 0); + const token1Price = (prices[`${CHAIN.OPTIMISM}:${tokens1[index]}`]?.price || 0); + const token0Decimals = (prices[`${CHAIN.OPTIMISM}:${tokens0[index]}`]?.decimals || 0) + const token1Decimals = (prices[`${CHAIN.OPTIMISM}:${tokens1[index]}`]?.decimals || 0) + const totalAmount0 = log + .reduce((a: number, b: IAmount) => Number(b.amount0In) + Number(b.amount0Out) + a, 0) / 10 ** token0Decimals * token0Price; + const totalAmount1 = log + .reduce((a: number, b: IAmount) => Number(b.amount1In) + Number(b.amount1Out) + a, 0) / 10 ** token1Decimals * token1Price; + + const untrackAmountUSD = token0Price !== 0 ? totalAmount0 : token1Price !== 0 ? totalAmount1 : 0; // counted only we have price data + return untrackAmountUSD; + }); + + const dailyVolume = untrackVolumes.reduce((a: number, b: number) => a + b, 0); + return { + dailyVolume: `${dailyVolume}`, + timestamp, + }; + } catch(error) { + console.error(error); + throw error; + } +} + +const adapter: SimpleAdapter = { + adapter: { + [CHAIN.OPTIMISM]: { + fetch, + start: async () => 1687305600, + }, + } +}; + +export default adapter; diff --git a/fees/velodrome-v2.ts b/fees/velodrome-v2.ts new file mode 100644 index 0000000000..dec242137e --- /dev/null +++ b/fees/velodrome-v2.ts @@ -0,0 +1,173 @@ +import { FetchResultFees, SimpleAdapter } from "../adapters/types"; +import { CHAIN } from "../helpers/chains"; +import * as sdk from "@defillama/sdk"; +import { getBlock } from "../helpers/getBlock"; +import { getPrices } from "../utils/prices"; +import BigNumber from "bignumber.js"; + +interface ILog { + data: string; + transactionHash: string; +} +interface IAmount { + amount0: string; + amount1: string; +} +const topic_name = 'Swap(index_topic_1 address sender, index_topic_2 address to, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out)'; +const topic0 = '0x112c256902bf554b6ed882d2936687aaeb4225e8cd5b51303c90ca6cf43a8602'; +const FACTORY_ADDRESS = '0xF1046053aa5682b4F9a81b5481394DA16BE5FF5a'; + +type TABI = { + [k: string]: object; +} +const ABIs: TABI = { + allPoolsLength: { + "inputs": [], + "name": "allPoolsLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + allPools: { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allPools", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +}; + +const PAIR_TOKEN_ABI = (token: string): object => { + return { + "constant": true, + "inputs": [], + "name": token, + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +}; + + +const fetch = async (timestamp: number): Promise => { + const fromTimestamp = timestamp - 60 * 60 * 24 + const toTimestamp = timestamp + try { + const poolLength = (await sdk.api.abi.call({ + target: FACTORY_ADDRESS, + chain: CHAIN.OPTIMISM, + abi: ABIs.allPoolsLength, + })).output; + + const poolsRes = await sdk.api.abi.multiCall({ + abi: ABIs.allPools, + calls: Array.from(Array(Number(poolLength)).keys()).map((i) => ({ + target: FACTORY_ADDRESS, + params: i, + })), + chain: CHAIN.OPTIMISM + }); + + const lpTokens = poolsRes.output + .map(({ output }: any) => output); + + const [underlyingToken0, underlyingToken1] = await Promise.all( + ['token0', 'token1'].map((method) => + sdk.api.abi.multiCall({ + abi: PAIR_TOKEN_ABI(method), + calls: lpTokens.map((address: string) => ({ + target: address, + })), + chain: CHAIN.OPTIMISM, + permitFailure: true, + }) + ) + ); + + const tokens0 = underlyingToken0.output.map((res: any) => res.output); + const tokens1 = underlyingToken1.output.map((res: any) => res.output); + const fromBlock = (await getBlock(fromTimestamp, CHAIN.OPTIMISM, {})); + const toBlock = (await getBlock(toTimestamp, CHAIN.OPTIMISM, {})); + const logs: ILog[][] = (await Promise.all(lpTokens.map((address: string) => sdk.api.util.getLogs({ + target: address, + topic: topic_name, + toBlock: toBlock, + fromBlock: fromBlock, + keys: [], + chain: CHAIN.OPTIMISM, + topics: [topic0] + })))) + .map((p: any) => p) + .map((a: any) => a.output); + const rawCoins = [...tokens0, ...tokens1].map((e: string) => `${CHAIN.OPTIMISM}:${e}`); + const coins = [...new Set(rawCoins)] + const prices = await getPrices(coins, timestamp); + const untrackVolumes: number[] = lpTokens.map((_: string, index: number) => { + const log: IAmount[] = logs[index] + .map((e: ILog) => { return { ...e, data: e.data.replace('0x', '') } }) + .map((p: ILog) => { + BigNumber.config({ POW_PRECISION: 100 }); + const amount0 = new BigNumber('0x' + p.data.slice(0, 64)).toString(); + const amount1 = new BigNumber('0x' + p.data.slice(64, 128)).toString(); + return { + amount0, + amount1, + } as IAmount + }) as IAmount[]; + const token0Price = (prices[`${CHAIN.OPTIMISM}:${tokens0[index]}`]?.price || 0); + const token1Price = (prices[`${CHAIN.OPTIMISM}:${tokens1[index]}`]?.price || 0); + const token0Decimals = (prices[`${CHAIN.OPTIMISM}:${tokens0[index]}`]?.decimals || 0) + const token1Decimals = (prices[`${CHAIN.OPTIMISM}:${tokens1[index]}`]?.decimals || 0) + const totalAmount0 = log + .reduce((a: number, b: IAmount) => Number(b.amount0) + a, 0) / 10 ** token0Decimals * token0Price; + const totalAmount1 = log + .reduce((a: number, b: IAmount) => Number(b.amount1) + Number(b.amount1) + a, 0) / 10 ** token1Decimals * token1Price; + return (totalAmount0 + totalAmount1); + }); + + const dailyFees = untrackVolumes.reduce((a: number, b: number) => a + b, 0); + return { + dailyFees: `${dailyFees}`, + timestamp, + }; + } catch(error) { + console.error(error); + throw error; + } +} + +const adapter: SimpleAdapter = { + adapter: { + [CHAIN.OPTIMISM]: { + fetch, + start: async () => 1687305600, + }, + } +}; + +export default adapter; From 899af18366f46a4190100f16b1487caa8aa2105f Mon Sep 17 00:00:00 2001 From: 0xgnek <0xgnek@gmail.com> Date: Wed, 5 Jul 2023 15:44:43 +0000 Subject: [PATCH 2/3] megere fees --- fees/velodrome/index.ts | 28 ++++++++++++++++++++++++++++ fees/{ => velodrome}/velodrome-v2.ts | 23 +++++++---------------- fees/{ => velodrome}/velodrome.ts | 27 +++++++++++++-------------- 3 files changed, 48 insertions(+), 30 deletions(-) create mode 100644 fees/velodrome/index.ts rename fees/{ => velodrome}/velodrome-v2.ts (91%) rename fees/{ => velodrome}/velodrome.ts (83%) diff --git a/fees/velodrome/index.ts b/fees/velodrome/index.ts new file mode 100644 index 0000000000..5983162a0b --- /dev/null +++ b/fees/velodrome/index.ts @@ -0,0 +1,28 @@ +import { Adapter } from '../../adapters/types'; +import { OPTIMISM } from '../../helpers/chains'; +import { fetchV1 } from './velodrome'; +import { fetchV2 } from './velodrome-v2'; + + +const getFees = async (timestamp: number) => { + const [feeV1, feeV2] = await Promise.all([fetchV1()(timestamp), fetchV2(timestamp)]); + const dailyFees = Number(feeV1.dailyFees) + Number(feeV2.dailyFees); + const dailyRevenue = Number(feeV1.dailyRevenue) + Number(feeV2.dailyRevenue); + const dailyHoldersRevenue = Number(feeV1.dailyHoldersRevenue) + Number(feeV2.dailyHoldersRevenue); + return { + dailyFees: `${dailyFees}`, + dailyRevenue: `${dailyRevenue}`, + dailyHoldersRevenue: `${dailyHoldersRevenue}`, + timestamp + } +} + +const adapter: Adapter = { + adapter: { + [OPTIMISM]: { + fetch: getFees, + start: async () => 1677110400, // TODO: Add accurate timestamp + }, + }, +}; +export default adapter; diff --git a/fees/velodrome-v2.ts b/fees/velodrome/velodrome-v2.ts similarity index 91% rename from fees/velodrome-v2.ts rename to fees/velodrome/velodrome-v2.ts index dec242137e..dad824ce28 100644 --- a/fees/velodrome-v2.ts +++ b/fees/velodrome/velodrome-v2.ts @@ -1,8 +1,8 @@ -import { FetchResultFees, SimpleAdapter } from "../adapters/types"; -import { CHAIN } from "../helpers/chains"; +import { FetchResultFees } from "../../adapters/types"; +import { CHAIN } from "../../helpers/chains"; import * as sdk from "@defillama/sdk"; -import { getBlock } from "../helpers/getBlock"; -import { getPrices } from "../utils/prices"; +import { getBlock } from "../../helpers/getBlock"; +import { getPrices } from "../../utils/prices"; import BigNumber from "bignumber.js"; interface ILog { @@ -74,7 +74,7 @@ const PAIR_TOKEN_ABI = (token: string): object => { }; -const fetch = async (timestamp: number): Promise => { +export const fetchV2 = async (timestamp: number): Promise => { const fromTimestamp = timestamp - 60 * 60 * 24 const toTimestamp = timestamp try { @@ -153,6 +153,8 @@ const fetch = async (timestamp: number): Promise => { const dailyFees = untrackVolumes.reduce((a: number, b: number) => a + b, 0); return { dailyFees: `${dailyFees}`, + dailyRevenue: `${dailyFees}`, + dailyHoldersRevenue: `${dailyFees}`, timestamp, }; } catch(error) { @@ -160,14 +162,3 @@ const fetch = async (timestamp: number): Promise => { throw error; } } - -const adapter: SimpleAdapter = { - adapter: { - [CHAIN.OPTIMISM]: { - fetch, - start: async () => 1687305600, - }, - } -}; - -export default adapter; diff --git a/fees/velodrome.ts b/fees/velodrome/velodrome.ts similarity index 83% rename from fees/velodrome.ts rename to fees/velodrome/velodrome.ts index 1e2d4d3d79..e4c6854421 100644 --- a/fees/velodrome.ts +++ b/fees/velodrome/velodrome.ts @@ -1,19 +1,18 @@ import request, { gql } from "graphql-request"; -import { Adapter } from "../adapters/types"; -import { getBlock } from "../helpers/getBlock"; +import { Adapter } from "../../adapters/types"; +import { getBlock } from "../../helpers/getBlock"; import { getTimestampAtStartOfDayUTC, getTimestampAtStartOfPreviousDayUTC -} from "../utils/date"; +} from "../../utils/date"; import BigNumber from "bignumber.js"; -import { OPTIMISM } from "../helpers/chains"; const STABLE_FEES = 0.0002; const VOLATILE_FEES = 0.0005; const endpoint = "https://api.thegraph.com/subgraphs/name/dmihal/velodrome"; -const getFees = () => { +export const fetchV1 = () => { return async (timestamp: number) => { const todaysTimestamp = getTimestampAtStartOfDayUTC(timestamp); const yesterdaysTimestamp = getTimestampAtStartOfPreviousDayUTC(timestamp); @@ -66,13 +65,13 @@ const getFees = () => { }; }; -const adapter: Adapter = { - adapter: { - [OPTIMISM]: { - fetch: getFees(), - start: async () => 1677110400, // TODO: Add accurate timestamp - }, - }, -}; +// const adapter: Adapter = { +// adapter: { +// [OPTIMISM]: { +// fetch: getFees(), +// start: async () => 1677110400, // TODO: Add accurate timestamp +// }, +// }, +// }; -export default adapter; +// export default adapter; From 1722ed691efb603d00aa727ae7cb9bde8bfbd930 Mon Sep 17 00:00:00 2001 From: 0xgnek <0xgnek@gmail.com> Date: Wed, 5 Jul 2023 15:55:45 +0000 Subject: [PATCH 3/3] merge dex --- dexs/velodrome/index.ts | 22 ++++++++++++------ .../index.ts => velodrome/v2.ts} | 23 ++++--------------- 2 files changed, 20 insertions(+), 25 deletions(-) rename dexs/{velodrome-v2/index.ts => velodrome/v2.ts} (88%) diff --git a/dexs/velodrome/index.ts b/dexs/velodrome/index.ts index f6fe885f55..eff4b2aea5 100644 --- a/dexs/velodrome/index.ts +++ b/dexs/velodrome/index.ts @@ -2,6 +2,8 @@ import { SimpleAdapter } from "../../adapters/types"; import { getStartTimestamp } from "../../helpers/getStartTimestamp"; import { DEFAULT_DAILY_VOLUME_FIELD, DEFAULT_TOTAL_VOLUME_FIELD, getChainVolume } from "../../helpers/getUniSubgraphVolume"; import { CHAIN } from "../../helpers/chains"; +import { Chain } from "@defillama/sdk/build/general"; +import { fetchV2 } from "./v2"; const endpoints = { [CHAIN.OPTIMISM]: "https://api.thegraph.com/subgraphs/name/dmihal/velodrome", @@ -19,16 +21,22 @@ const graphs = getChainVolume({ }, }); +const fetch = (chain: Chain) => { + return async (timestamp: number) => { + const [v1, v2] = await Promise.all([graphs(chain)(timestamp, {}), fetchV2(timestamp)]) + const dailyVolume = Number(v1.dailyVolume) + Number(v2.dailyVolume); + return { + dailyVolume: `${dailyVolume}`, + timestamp + } + } +} + const adapter: SimpleAdapter = { adapter: { [CHAIN.OPTIMISM]: { - fetch: graphs(CHAIN.OPTIMISM), - start: getStartTimestamp({ - endpoints: endpoints, - chain: CHAIN.OPTIMISM, - volumeField: DEFAULT_DAILY_VOLUME_FIELD, - dailyDataField: "dayDatas" - }) + fetch: fetch(CHAIN.OPTIMISM), + start: async () => 1677110400 }, }, }; diff --git a/dexs/velodrome-v2/index.ts b/dexs/velodrome/v2.ts similarity index 88% rename from dexs/velodrome-v2/index.ts rename to dexs/velodrome/v2.ts index 790527e7fc..50145732e5 100644 --- a/dexs/velodrome-v2/index.ts +++ b/dexs/velodrome/v2.ts @@ -3,7 +3,6 @@ import { CHAIN } from "../../helpers/chains"; import * as sdk from "@defillama/sdk"; import { getBlock } from "../../helpers/getBlock"; import { getPrices } from "../../utils/prices"; -import BigNumber from "bignumber.js"; interface ILog { data: string; @@ -76,7 +75,7 @@ const PAIR_TOKEN_ABI = (token: string): object => { }; -const fetch = async (timestamp: number) => { +export const fetchV2 = async (timestamp: number) => { const fromTimestamp = timestamp - 60 * 60 * 24 const toTimestamp = timestamp try { @@ -133,11 +132,10 @@ const fetch = async (timestamp: number) => { const log: IAmount[] = logs[index] .map((e: ILog) => { return { ...e, data: e.data.replace('0x', '') } }) .map((p: ILog) => { - BigNumber.config({ POW_PRECISION: 100 }); - const amount0In = new BigNumber('0x' + p.data.slice(0, 64)).toString(); - const amount1In = new BigNumber('0x' + p.data.slice(64, 128)).toString(); - const amount0Out = new BigNumber('0x' + p.data.slice(128, 192)).toString(); - const amount1Out = new BigNumber('0x' + p.data.slice(192, 256)).toString(); + const amount0In = Number('0x' + p.data.slice(0, 64)).toString(); + const amount1In = Number('0x' + p.data.slice(64, 128)).toString(); + const amount0Out = Number('0x' + p.data.slice(128, 192)).toString(); + const amount1Out = Number('0x' + p.data.slice(192, 256)).toString(); return { amount0In, amount1In, @@ -168,14 +166,3 @@ const fetch = async (timestamp: number) => { throw error; } } - -const adapter: SimpleAdapter = { - adapter: { - [CHAIN.OPTIMISM]: { - fetch, - start: async () => 1687305600, - }, - } -}; - -export default adapter;