Skip to content

Commit 4366846

Browse files
alex-wombatkoi-narwhaloz-agentbheluga
authored
wombat-exchange: add missing pools/chains, fees adapter, user routers (DefiLlama#6148)
* wombat-exchange: add missing pools/chains, fees adapter, user router addresses Dex adapter: - Add ~22 missing pool entries across BSC, ARB, ETH, AVAX, OPT, Scroll - Add 3 new chains: Polygon, Monad, HyperEVM Fees adapter (new): - Per-pool haircutRate and lpDividendRatio for accurate fee/revenue split - Token incentives via Voter DistributeReward events (BSC, ARB, ETH) - Bribes via BribeV2 OnReward events (historical) Users adapter: - Add Wombat router addresses for all 10 chains Co-Authored-By: Oz <oz-agent@warp.dev> * remove monad and hyperliquid due to minimal activity * Update convertChain.ts * combine fees and dexs * Delete fees/wombat-exchange/index.ts --------- Co-authored-by: koi-narwhal <koi-narwhal@users.noreply.github.com> Co-authored-by: Oz <oz-agent@warp.dev> Co-authored-by: bheluga <bheluga@defillama.com>
1 parent 946349b commit 4366846

File tree

3 files changed

+183
-21
lines changed

3 files changed

+183
-21
lines changed

dexs/wombat-exchange/index.ts

Lines changed: 168 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { BaseAdapter, FetchOptions, SimpleAdapter } from "../../adapters/types";
1+
import { FetchOptions, SimpleAdapter } from "../../adapters/types";
22
import { CHAIN } from "../../helpers/chains";
33
import { addOneToken } from "../../helpers/prices";
44

5-
const config: any = {
5+
export const config: any = {
66
[CHAIN.BSC]: {
77
wom: "0xAD6742A35fB341A9Cc6ad674738Dd8da98b94Fb1",
88
veWom: "0x3DA62816dD31c56D9CdF22C6771ddb892cB5b0Cc",
@@ -35,6 +35,12 @@ const config: any = {
3535
zBNB: "0x9a39f4AB3f52026432835dEe6D3DB721D95f3D28",
3636
zUSD: "0xC26b7Cbe7e695a0d11a8cB96140D1Cd502945A2C",
3737
USDV: "0xC897a2Ae2E45f0D7ba8cbE397208C3e9f8914A9f",
38+
CUSD: "0x4dFa92842d05a790252A7f374323b9C86D7b7E12",
39+
LINA: "0x84a14A10E7258C68413168c98E905483f9183D7a",
40+
StandalonePool: "0x6569DDC1Cc2648c89BC8025046A7dd65EB8940F3",
41+
StandalonePool2: "0xfcd11c01c14e4c12C3F9835CD5192fE774038d46",
42+
StandalonePool3: "0xaded24B510a137b05a8eD958a029DACD6a59efDc",
43+
volatilePool: "0x5b573F2E034e37Cc883F2a614BDdC77b07081B6f",
3844
},
3945
},
4046
[CHAIN.ARBITRUM]: {
@@ -58,6 +64,10 @@ const config: any = {
5864
crossChainPool: "0xe78876C360716f2225F55A6726B32324FE1B1145",
5965
USDV: "0xa6eF6C45EbFDBc13f6D032fbDFeC9b389C1603E5",
6066
sFRAX: "0xaBF19eAdb08693278FdbAD35Cb4E3c1D6484c8Bb",
67+
fraxUSDV: "0x3cc8c886575968642Cab9F430261c81C5b044d4b",
68+
StandalonePool: "0xD64816Fbdf50a1C4AEa456A4006ad21A928305f3",
69+
ePendle: "0x3257EaA9C919fe01EF628fe9031BA2Cd8927A3b1",
70+
volatilePool: "0x39a2f59875bC636b7eFEcAc30b6E97066a850B1e",
6171
},
6272
},
6373
[CHAIN.ETHEREUM]: {
@@ -71,6 +81,9 @@ const config: any = {
7181
ETHx: "0x647CC8816C2d60A5fF4d1ffeF27a5b3637d5ac81",
7282
crossChainPool: "0xA45C0ABeef67C363364E0e73832df9986aBa3800",
7383
USDV: "0x05A33c0eaf81367Ce953d2dCd4ea1BE8758f4D32",
84+
volatilePool: "0x89B88A45E23978b38A14695b63F475d4e4CcaF95",
85+
mWOM: "0xcf2e56E086fcD21eaB3614A5A78c8Ae27c2F0536",
86+
wmxWOM: "0xe43c1695df76CcA4D6079061924D7150Fd553c21",
7487
},
7588
},
7689
// does not have wom yet
@@ -79,6 +92,7 @@ const config: any = {
7992
veWom: "",
8093
pools: {
8194
crossChainPool: "0x80f088ae72DB6d1AC337340cd6Aa0EB1F67337CE",
95+
volatilePool: "0x15dcC2da1a73194C9c5BB83ecdA86251F0b1a17F",
8296
},
8397
},
8498
[CHAIN.AVAX]: {
@@ -89,6 +103,10 @@ const config: any = {
89103
sAVAX: "0xE3Abc29B035874a9f6dCDB06f8F20d9975069D87",
90104
USDV: "0x108c990c93Fa8E3cD88DDb13594D39f09D9B3C02",
91105
ggAVAX: "0xBbA43749efC1bC29eA434d88ebaf8A97DC7aEB77",
106+
triPool: "0xc12c0Ced34b115655234E8a4dB87EBc8F6F362d0",
107+
AUSD: "0x911a98f54da5355EAba1c8D57933ae5493c4223b",
108+
Axon: "0x74163B79733AEA2d9C4cED777dc49D591Db739E9",
109+
volatilePool: "0x89B88A45E23978b38A14695b63F475d4e4CcaF95",
92110
},
93111
},
94112
[CHAIN.BASE]: {
@@ -108,31 +126,161 @@ const config: any = {
108126
frax: "0x6BB82A9b0b9b9716B885baeEfDBE47b685a0F919",
109127
dola: "0x489818F2eeAef737105887710F7C5b9323Ad3d01",
110128
frxETH: "0xB86BA65b75D34402bf377cF83b184554a18Fcafa",
129+
StandalonePool: "0x7B1f9C537efCa25501d15a77Bdc1d23287839623",
111130
},
112131
},
132+
[CHAIN.POLYGON]: {
133+
wom: "",
134+
veWom: "",
135+
pools: {
136+
crossChainPool: "0x4705b477d35112f7B7cA2Bc5059eD9b78bb46134",
137+
},
138+
},
139+
[CHAIN.MONAD]: {
140+
wom: "",
141+
veWom: "",
142+
pools: {
143+
mainPool: "0x25FAa3176efa09658E65853F077810bb2CCa82a4",
144+
},
145+
},
146+
[CHAIN.HYPERLIQUID]: {
147+
wom: "",
148+
veWom: "",
149+
pools: {
150+
mainPool: "0xeF420C965d80fb24A211155a6B489C0D62b7e07a",
151+
},
152+
},
153+
};
154+
155+
// Voter contract addresses for chains with governance (for bribes + token incentives)
156+
const voterConfig: Record<string, { voter: string; }> = {
157+
[CHAIN.BSC]: {
158+
voter: "0x04D4e1C1F3D6539071b6D3849fDaED04d48D563d",
159+
},
160+
[CHAIN.ARBITRUM]: {
161+
voter: "0x3f90a5a47364c0467031fB00246192d40E3D2D9D",
162+
},
163+
[CHAIN.ETHEREUM]: {
164+
voter: "0x32A936CbA2629619b46684cDf923CB556f09442c",
165+
},
113166
};
114167

168+
async function fetch(options: FetchOptions) {
169+
const { chain, getLogs, createBalances, api } = options;
170+
const pools = Object.values(config[chain].pools) as string[];
171+
172+
const swapAbi = "event Swap (address indexed sender, address fromToken, address toToken, uint256 fromAmount, uint256 toAmount, address indexed to)";
173+
174+
// 1. Read haircutRate and lpDividendRatio from each pool
175+
const [haircutRates, lpDividendRatios] = await Promise.all([
176+
api.multiCall({ abi: "function haircutRate() view returns (uint256)", calls: pools }).catch(() => pools.map(() => "400000000000000")), // default 4bps
177+
api.multiCall({ abi: "function lpDividendRatio() view returns (uint256)", calls: pools }).catch(() => pools.map(() => "1000000000000000000")), // default 100%
178+
]);
179+
180+
// 2. Compute per-pool volume, fees, and revenue split
181+
const dailyVolume = createBalances();
182+
const dailyFees = createBalances();
183+
const dailySupplySideRevenue = createBalances();
184+
const dailyProtocolRevenue = createBalances();
185+
186+
const perPoolLogs = await Promise.all(
187+
pools.map((pool) => getLogs({ target: pool, eventAbi: swapAbi }).catch(() => []))
188+
);
189+
190+
for (let i = 0; i < pools.length; i++) {
191+
const poolLogs = perPoolLogs[i];
192+
const hr = Number(haircutRates[i]) / 1e18;
193+
const lpRatio = Number(lpDividendRatios[i]) / 1e18;
194+
195+
const poolVolume = createBalances();
196+
poolLogs.forEach((log: any) => {
197+
addOneToken({ chain, balances: poolVolume, token0: log.fromToken, amount0: log.fromAmount, token1: log.toToken, amount1: log.toAmount });
198+
});
199+
200+
dailyVolume.addBalances(poolVolume);
201+
const poolFees = poolVolume.clone(hr);
202+
dailyFees.addBalances(poolFees);
203+
dailySupplySideRevenue.addBalances(poolFees.clone(lpRatio));
204+
dailyProtocolRevenue.addBalances(poolFees.clone(1 - lpRatio));
205+
}
206+
207+
// 5. Bribes: OnReward events from BribeV2 contracts (via Voter)
208+
const dailyBribesRevenue = createBalances();
209+
if (voterConfig[chain]) {
210+
const { voter } = voterConfig[chain];
211+
212+
// Get all asset (LP token) addresses from pools, then look up bribes from Voter
213+
const allUnderlyingTokens = await api.multiCall({
214+
abi: "address[]:getTokens",
215+
calls: pools,
216+
}).catch(() => []);
217+
218+
const assetCalls: { target: string; params: string }[] = [];
219+
allUnderlyingTokens.forEach((tokens: string[], poolIdx: number) => {
220+
(tokens || []).forEach((token: string) => {
221+
assetCalls.push({ target: pools[poolIdx], params: token });
222+
});
223+
});
224+
225+
if (assetCalls.length > 0) {
226+
const assetAddresses = await api.multiCall({
227+
abi: "function addressOfAsset(address) view returns (address)",
228+
calls: assetCalls,
229+
}).catch(() => []);
230+
231+
// Look up bribe address for each asset from Voter
232+
const infoCalls = assetAddresses
233+
.filter((a: string) => a && a !== "0x0000000000000000000000000000000000000000");
234+
235+
if (infoCalls.length > 0) {
236+
const infos = await api.multiCall({
237+
abi: "function infos(address) view returns (uint104 supplyBaseIndex, uint104 supplyVoteIndex, uint40 nextEpochStartTime, uint128 claimable, bool whitelist, address gaugeManager, address bribe)",
238+
calls: infoCalls.map((a: string) => ({ target: voter, params: a })),
239+
}).catch(() => []);
240+
241+
const bribeAddresses = infos
242+
.map((info: any) => info?.bribe)
243+
.filter((b: string) => b && b !== "0x0000000000000000000000000000000000000000");
244+
245+
if (bribeAddresses.length > 0) {
246+
// Get OnReward events from all bribe contracts
247+
const bribeLogs = await getLogs({
248+
targets: bribeAddresses,
249+
eventAbi: "event OnReward(address indexed rewardToken, address indexed user, uint256 amount)",
250+
}).catch(() => []);
251+
252+
bribeLogs.forEach((log: any) => {
253+
dailyBribesRevenue.add(log.rewardToken, log.amount);
254+
});
255+
}
256+
}
257+
}
258+
}
259+
260+
return {
261+
dailyVolume,
262+
dailyFees,
263+
dailyUserFees: dailyFees,
264+
dailySupplySideRevenue,
265+
dailyProtocolRevenue,
266+
dailyRevenue: dailyProtocolRevenue,
267+
dailyBribesRevenue,
268+
};
269+
}
270+
115271
const adapter: SimpleAdapter = {
116272
version: 2,
117273
pullHourly: true,
118-
adapter: {},
274+
fetch,
275+
chains: Object.keys(config),
276+
methodology: {
277+
Fees: "Swap fees paid by users (haircutRate applied to each swap).",
278+
UserFees: "Same as Fees — all swap fees are paid by the user.",
279+
SupplySideRevenue: "Share of fees distributed to LPs, determined by the pool's lpDividendRatio.",
280+
ProtocolRevenue: "Share of fees retained by the protocol (tip bucket + feeTo), i.e. 1 - lpDividendRatio.",
281+
HoldersRevenue: "Not applicable.",
282+
Revenue: "Same as ProtocolRevenue.",
283+
},
119284
};
120285

121-
Object.keys(config).forEach((chain) => {
122-
(adapter.adapter as BaseAdapter)[chain] = { fetch }
123-
})
124-
125286
export default adapter;
126-
127-
128-
async function fetch({ chain, getLogs, createBalances, }: FetchOptions) {
129-
const pools = Object.values(config[chain].pools)
130-
const dailyVolume = createBalances()
131-
const eventAbi = "event Swap (address indexed sender, address fromToken, address toToken, uint256 fromAmount, uint256 toAmount, address indexed to)"
132-
const logs = await getLogs({ targets: pools as any, eventAbi })
133-
logs.forEach((log: any) => {
134-
const { fromToken, toToken, fromAmount, toAmount } = log
135-
addOneToken({ chain, balances: dailyVolume, token0: fromToken, amount0: fromAmount, token1: toToken, amount1: toAmount })
136-
})
137-
return { dailyVolume, dailyFees: dailyVolume.clone(0.0004) }
138-
}

users/routers/routerAddresses.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2443,6 +2443,20 @@ export default (
24432443
],
24442444
},
24452445
},
2446+
{
2447+
id: "parent#wombat-exchange",
2448+
name: "Wombat Exchange",
2449+
addresses: {
2450+
bsc: ["0x19609B03C976CCA288fbDae5c21d4290e9a4aDD7"],
2451+
arbitrum: ["0xc4B2F992496376C6127e73F1211450322E580668"],
2452+
ethereum: ["0x6BB82A9b0b9b9716B885baeEfDBE47b685a0F919"],
2453+
avax: ["0x4A88C44B8D9B9f3F2BA4D97236F737CF03DF76CD"],
2454+
optimism: ["0x35d531Fd45D5E3A5d407A4898360c757C98Bdf79"],
2455+
base: ["0x4A88C44B8D9B9f3F2BA4D97236F737CF03DF76CD"],
2456+
scroll: ["0x010931D4d82ff3F5Ae8bD94e0752570711f78959"],
2457+
polygon: ["0x34E2F923bBa206358EcE221af73E8d121837F873"],
2458+
},
2459+
},
24462460

24472461
] as ProtocolAddresses[]
24482462
).filter(isAddressesUsable);

users/utils/convertChain.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ export const convertChainToFlipside = (chain: string) => ({
88
avax: "avalanche"
99
}[chain] ?? chain)
1010

11-
export const isAcceptedChain = (chain:string) => ["arbitrum", "avax", "ethereum", "optimism", "polygon", "base", "bsc", "scroll", "polygon_zkevm", "starknet"].includes(chain)
11+
export const isAcceptedChain = (chain:string) => ["arbitrum", "avax", "ethereum", "optimism", "polygon", "base", "bsc", "scroll", "polygon_zkevm", "starknet"].includes(chain)

0 commit comments

Comments
 (0)