-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add Stabull fee, revenue and volume #4940
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| import {Adapter, FetchOptions, FetchResultV2} from "../../adapters/types"; | ||
| import {CHAIN} from "../../helpers/chains"; | ||
| import { addTokensReceived } from '../../helpers/token'; | ||
| import {Balances} from "@defillama/sdk"; | ||
| import {addOneToken} from "../../helpers/prices"; | ||
|
|
||
|
|
||
| const events = { | ||
| trade: 'event Trade( address indexed trader, address indexed origin, address indexed target, uint256 originAmount, uint256 targetAmount, int128 rawProtocolFee)', | ||
| newCurve: 'event NewCurve (address indexed caller, bytes32 indexed id, address indexed curve)' | ||
| } | ||
|
|
||
| // Sources: treasury address is defined on the curveFactory contract on method getProtocolTreasury | ||
| // Other contract addresses can be found at https://docs.stabull.finance/amm/contracts | ||
| const configs: Record<string, any> = { | ||
| [CHAIN.ETHEREUM]: { | ||
| factory: { address: "0x2e9E34b5Af24b66F12721113C1C8FFcbB7Bc8051", fromBlock: 19773852 }, | ||
| treasury: '0xaBd582EFa8ff22D5258FAFf769082002B3a1d919', | ||
| startDate: '2024-04-29', | ||
| }, | ||
| [CHAIN.POLYGON]: { | ||
| factory: { address: "0x3c60234db40e6e5b57504e401b1cdc79d91faf89", fromBlock: 56377840 }, | ||
| treasury: '0x2ABbb079e97bd30605CcD65b8aDFB756AFE4f511', | ||
| startDate: '2024-04-29', | ||
| }, | ||
| [CHAIN.BASE]: { | ||
| factory: { address: "0x86Ba17ebf8819f7fd32Cf1A43AbCaAe541A5BEbf", fromBlock: 32584321 }, | ||
| treasury: '0x1ea84Ba9598AA1083D7F4096E06B532Da2c3dEf5', | ||
| startDate: '2025-07-08', | ||
| }, | ||
| }; | ||
|
|
||
| // This method dynamically fetches pool addresses, this way newly created pools will be counted automatically in the future | ||
| const getPools = async (options: FetchOptions): Promise<string[]> => { | ||
| const {address, fromBlock} = configs[options.chain].factory | ||
| const logs = await options.getLogs({ | ||
| target: address, | ||
| eventAbi: events.newCurve, | ||
| onlyArgs: true, | ||
| cacheInCloud: true, | ||
| fromBlock, | ||
| }); | ||
| return logs.map(log => log.curve); | ||
| } | ||
|
|
||
| const fetchVolume = async (options: FetchOptions, targets: string[], dailyVolume: Balances) => { | ||
| const tradeEvents: Array<any> = await options.getLogs({ | ||
| targets: targets, | ||
| eventAbi: events.trade, | ||
| }) | ||
|
|
||
| for (const event of tradeEvents) { | ||
| addOneToken({balances: dailyVolume, chain: options.chain, token0: event.origin, amount0: event.originAmount, token1: event.target, amount1: event.targetAmount}) | ||
| } | ||
| } | ||
|
|
||
| const fetchRevenue = async (options: FetchOptions, fromAdddesses: string[], dailyFees: Balances) => { | ||
| await addTokensReceived({ | ||
| options, | ||
| balances: dailyFees, | ||
| targets: [configs[options.chain].treasury], // Treasury address | ||
| fromAdddesses, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. even though you are restricting the source here, it still has room for bugs and a bit inefficient as we are pulling two sets of logs when trade log already has fee info, also what happens when they change the treasury address?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the feeData on the event is raw and needs some calculations based on Oracle data at that exact moment to figure out the exact fee amount , unfortunately there is no event to read the calculated amount. for the treasury address i can dynamically read it from curve contracts if you still think this way of calculation of fee/revenue is inefficient, i can discard all of these and calculate fees based on volume. since the rates are fixed i think it will achieve the same result |
||
| }) | ||
| } | ||
|
|
||
| const fetch: any = async (options: FetchOptions): Promise<FetchResultV2> => { | ||
| const dailyVolume = options.createBalances() | ||
| const dailyRevenue = options.createBalances() | ||
|
|
||
| const poolAddresses = await getPools(options); | ||
|
|
||
| await fetchVolume(options, poolAddresses, dailyVolume); | ||
| await fetchRevenue(options, poolAddresses, dailyRevenue); | ||
|
|
||
|
|
||
| return { | ||
| dailyVolume, | ||
| dailyRevenue, | ||
| // Protocol collects 30% of total fees as revenue, so to derive the full 100% fees: | ||
| // dailyFees = dailyRevenue / 0.3, equivalently dailyRevenue * (10 / 3) | ||
| dailyFees: dailyRevenue.clone(10/3), | ||
| } | ||
| }; | ||
|
|
||
| const adapters: Adapter = { | ||
| version: 2, | ||
| adapter: Object.keys(configs).reduce((acc, chain) => { | ||
| return { | ||
| ...acc, | ||
| [chain]: { | ||
| fetch, | ||
| start: configs[chain].startDate, | ||
| } | ||
| } | ||
| }, {}), | ||
| methodology: { | ||
| Fees: 'Stabull charges a flat rate of 0.15% per swap per pool', | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if it was flat fee of 0.15%, why don't you just count fees from volume and don't need to fetch token received revenue? |
||
| Revenue: '30% of all fees goes to the protocol treasury', | ||
| }, | ||
| }; | ||
| export default adapters; | ||
Uh oh!
There was an error while loading. Please reload this page.