Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions dexs/stabull/index.ts
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,
Copy link
Member

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.
here is the respective code:
https://basescan.org/address/0x1246B19c59FfF6A92E875b57402743cf576C86bB#code#L2184

for the treasury address i can dynamically read it from curve contracts
https://basescan.org/address/0x86Ba17ebf8819f7fd32Cf1A43AbCaAe541A5BEbf#readContract#F10

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',
Copy link
Contributor

Choose a reason for hiding this comment

The 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;
Loading