Skip to content

Commit

Permalink
Keller fees + volume adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
0xArata committed Mar 28, 2024
1 parent 1839f30 commit 834f99d
Show file tree
Hide file tree
Showing 4 changed files with 455 additions and 0 deletions.
185 changes: 185 additions & 0 deletions dexs/keller/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
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";
import { getTimestamp } from "@defillama/sdk/build/util";

interface ILog {
data: string;
transactionHash: string;
}
interface IAmount {
amount0In: string;
amount1In: string;
amount0Out: string;
amount1Out: string;
}

const topic0 = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
const FACTORY_ADDRESS = '0xbc83f7dF70aE8A3e4192e1916d9D0F5C2ee86367';

type TABI = {
[k: string]: object;
}
const ABIs: TABI = {
allPairsLength: {
"type": "function",
"stateMutability": "view",
"outputs": [
{
"type": "uint256",
"name": "",
"internalType": "uint256"
}
],
"name": "allPairsLength",
"inputs": []
},
allPairs: {
"type": "function",
"stateMutability": "view",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"inputs": [
{
"type": "uint256",
"name": "",
"internalType": "uint256"
}
],
"name": "allPairs",
}
};

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.SCROLL,
abi: ABIs.allPairsLength,
}).catch(e =>{
console.log('error', e);
throw e;
})).output;
console.log('poolLength', poolLength);
const poolsRes = await sdk.api.abi.multiCall({
abi: ABIs.allPairs,
calls: Array.from(Array(Number(poolLength)).keys()).map((i) => ({
target: FACTORY_ADDRESS,
params: i,
})),
chain: CHAIN.SCROLL
});

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: 'scroll'
})
)
);

const tokens0 = underlyingToken0.output.map((res: any) => res.output);
const tokens1 = underlyingToken1.output.map((res: any) => res.output);
const fromBlock = await getBlock(fromTimestamp, CHAIN.SCROLL, {});
const toBlock = await getBlock(toTimestamp, CHAIN.SCROLL, {});

const logs: ILog[][] = (await Promise.all(lpTokens.map((address: string) => sdk.api.util.getLogs({
target: address,
topic: '',
toBlock: toBlock,
fromBlock: fromBlock,
keys: [],
chain: CHAIN.SCROLL,
topics: [topic0]
}))))
.map((p: any) => p)
.map((a: any) => a.output);
const rawCoins = [...tokens0, ...tokens1].map((e: string) => `scroll:${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[`scroll:${tokens0[index]}`]?.price || 0);
const token1Price = (prices[`scroll:${tokens1[index]}`]?.price || 0);
const token0Decimals = (prices[`scroll:${tokens0[index]}`]?.decimals || 0)
const token1Decimals = (prices[`scroll:${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: {
["scroll"]: {
fetch,
start: async () => getTimestamp(4265908, "scroll") ,
},
}
};

export default adapter;
75 changes: 75 additions & 0 deletions fees/keller/bribes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import * as sdk from "@defillama/sdk";
import { getPrices } from "../../utils/prices";
import { ethers } from "ethers";
import { CHAIN } from "../../helpers/chains";

const event_notify_reward = 'event NotifyReward(address indexed from,address indexed reward,uint256 indexed epoch,uint256 amount)';
const event_geuge_created = 'event GaugeCreated(address indexed poolFactory,address indexed votingRewardsFactory,address indexed gaugeFactory,address pool,address bribeVotingReward,address feeVotingReward,address gauge,address creator)'

const topic0_geuge_created = '0xef9f7d1ffff3b249c6b9bf2528499e935f7d96bb6d6ec4e7da504d1d3c6279e1';
const contract_interface = new ethers.Interface([
event_notify_reward,
event_geuge_created
]);

interface ILog {
data: string;
transactionHash: string;
topics: string[];
}

interface IBribes {
token: string;
amount: number;
}


export const fees_bribes = async (fromBlock: number, toBlock: number, timestamp: number): Promise<number> => {
try {
const voter = '0x30f827DECe6F25c74F37d0dD45bC245d893266e6';
const logs_geuge_created: ethers.EventLog[] = (await sdk.api.util.getLogs({
target: voter,
fromBlock: 4265908, //Block number of the contract's creation
toBlock: toBlock,
topic: '',
topics: [topic0_geuge_created],
chain: CHAIN.SCROLL,
keys: []
})).output;
const bribes_contract: string[] = logs_geuge_created.map((e: ethers.EventLog) => {
const value = contract_interface.parseLog(e);
return value?.args.bribeVotingReward;
})

const logs: ILog[] = (await Promise.all(bribes_contract.map((address: string) => sdk.api.util.getLogs({
target: address,
topic: '',
toBlock: toBlock,
fromBlock: fromBlock,
keys: [],
chain: CHAIN.SCROLL,
topics: ['0x52977ea98a2220a03ee9ba5cb003ada08d394ea10155483c95dc2dc77a7eb24b']
}))))
.map((p: any) => p)
.map((a: any) => a.output).flat();

const logs_bribes = logs.map((e: ILog) => {
const value = contract_interface.parseLog(e)
return {
token: value?.args.reward,
amount: Number(value?.args.amount._hex)
} as IBribes
})
const coins = [...new Set(logs_bribes.map((e: IBribes) => `${"scroll"}:${e.token.toLowerCase()}`))]
const prices = await getPrices(coins, timestamp);
const fees_bribes_usd = logs_bribes.map((e: IBribes) => {
const price = prices[`${"scroll"}:${e.token.toLowerCase()}`]?.price || 0;
const decimals = prices[`${"scroll"}:${e.token.toLowerCase()}`]?.decimals || 0;
return (Number(e.amount) / 10 ** decimals) * price;
}).reduce((a: number, b: number) => a+b, 0);
return fees_bribes_usd;
} catch (error) {
console.error(error);
throw error;
}
}
36 changes: 36 additions & 0 deletions fees/keller/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Adapter, FetchResultFees } from '../../adapters/types';
import { CHAIN } from '../../helpers/chains';
import { fetchV2 } from './keller-v2';
import { fees_bribes } from './bribes';
import { getBlock } from '../../helpers/getBlock';
import { getTimestamp } from '@defillama/sdk/build/util';


const getFees = async (timestamp: number): Promise<FetchResultFees> => {
const fromTimestamp = timestamp - 60 * 60 * 24
const toTimestamp = timestamp
const fromBlock = (await getBlock(fromTimestamp, CHAIN.SCROLL, {}));
const toBlock = (await getBlock(toTimestamp, CHAIN.SCROLL, {}));

const [feeV2, bribes] = await Promise.all([fetchV2(fromBlock, toBlock, timestamp), fees_bribes(fromBlock, toBlock, timestamp)]);
const dailyFees = Number(feeV2.dailyFees);
const dailyRevenue = Number(feeV2.dailyRevenue) + bribes;
const dailyHoldersRevenue = Number(feeV2.dailyHoldersRevenue) + bribes;
return {
dailyFees: `${dailyFees}`,
dailyRevenue: `${dailyRevenue}`,
dailyHoldersRevenue: `${dailyHoldersRevenue}`,
dailyBribesRevenue: `${bribes}`,
timestamp
}
}

const adapter: Adapter = {
adapter: {
[CHAIN.SCROLL]: {
fetch: getFees,
start: async () => getTimestamp(4265908, "scroll"), // TODO: Add accurate timestamp
},
},
};
export default adapter;
Loading

0 comments on commit 834f99d

Please sign in to comment.