11// Zealous Swap (Kasplex) — Pools adapter for DefiLlama yield-server
2- // Pulls per- pool TVL (USD) and APR directly from our public API .
2+ // Fetches pool data from our API and reserves on-chain, calculating TVL using DefiLlama's price feeds .
33//
44// Endpoint used: https://kasplex.zealousswap.com/v1/pools
55// Notes:
6- // - We use `apr` from our API as `apyBase` (already annualized %).
7- // - If `apr` is missing, we fall back to fee APR from volume * feeRate.
8- // - We include `apyReward` only if our API reports a positive `farmApr`.
9- // - Only emit pools with `hasUSDValues === true`.
6+ // - Reserves are fetched on-chain via getReserves(), with API fallback if call fails
7+ // - TVL is calculated from token reserves using DefiLlama's price API
8+ // - Falls back to API's TVL value if DefiLlama prices are unavailable
9+ // - We use `apr` from our API as `apyBase` (already annualized %)
10+ // - If `apr` is missing, we fall back to fee APR from volume * feeRate
11+ // - We include `apyReward` only if our API reports a positive `farmApr`
1012
1113const axios = require ( "axios" ) ;
14+ const sdk = require ( "@defillama/sdk" ) ;
15+ const BigNumber = require ( "bignumber.js" ) ;
1216
1317const CHAIN = "kasplex" ;
1418const API = "https://kasplex.zealousswap.com/v1/pools" ;
@@ -33,16 +37,117 @@ function calcFeeAPR(volumeUSD, tvlUsd, feeRate) {
3337 return ( vol * fee / tvl ) * 365 * 100 ;
3438}
3539
40+ const getPrices = async ( addresses , chain ) => {
41+ const prices = (
42+ await axios . get (
43+ `https://coins.llama.fi/prices/current/${ addresses
44+ . map ( ( address ) => `${ chain } :${ address } ` )
45+ . join ( ',' )
46+ . toLowerCase ( ) } `
47+ )
48+ ) . data . coins ;
49+
50+ const pricesObj = Object . entries ( prices ) . reduce (
51+ ( acc , [ address , price ] ) => ( {
52+ ...acc ,
53+ [ address . split ( ':' ) [ 1 ] . toLowerCase ( ) ] : price . price ,
54+ } ) ,
55+ { }
56+ ) ;
57+
58+ return pricesObj ;
59+ } ;
60+
61+ const calculateReservesUSD = (
62+ reserve0 ,
63+ reserve1 ,
64+ token0Address ,
65+ token1Address ,
66+ decimals0 ,
67+ decimals1 ,
68+ tokenPrices
69+ ) => {
70+ const token0Price = tokenPrices [ token0Address . toLowerCase ( ) ] ;
71+ const token1Price = tokenPrices [ token1Address . toLowerCase ( ) ] ;
72+
73+ const reserve0Adjusted = new BigNumber ( reserve0 ) . div ( 10 ** decimals0 ) ;
74+ const reserve1Adjusted = new BigNumber ( reserve1 ) . div ( 10 ** decimals1 ) ;
75+
76+ if ( token0Price ) return reserve0Adjusted . times ( token0Price ) . times ( 2 ) ;
77+ if ( token1Price ) return reserve1Adjusted . times ( token1Price ) . times ( 2 ) ;
78+
79+ return null ;
80+ } ;
81+
3682async function apy ( ) {
3783 const { data } = await axios . get ( API ) ;
3884 const poolsObj = data ?. pools || { } ;
3985
86+ const poolAddresses = Object . keys ( poolsObj ) ;
87+ if ( poolAddresses . length === 0 ) return [ ] ;
88+
89+ const reservesResults = await sdk . api . abi . multiCall ( {
90+ abi : {
91+ inputs : [ ] ,
92+ name : "getReserves" ,
93+ outputs : [
94+ { internalType : "uint112" , name : "_reserve0" , type : "uint112" } ,
95+ { internalType : "uint112" , name : "_reserve1" , type : "uint112" } ,
96+ { internalType : "uint32" , name : "_blockTimestampLast" , type : "uint32" }
97+ ] ,
98+ stateMutability : "view" ,
99+ type : "function"
100+ } ,
101+ calls : poolAddresses . map ( ( address ) => ( {
102+ target : address ,
103+ } ) ) ,
104+ chain : CHAIN ,
105+ permitFailure : true ,
106+ } ) ;
107+
108+ const tokenAddresses = new Set ( ) ;
109+ for ( const p of Object . values ( poolsObj ) ) {
110+ if ( p . token0 ?. address ) tokenAddresses . add ( p . token0 . address . toLowerCase ( ) ) ;
111+ if ( p . token1 ?. address ) tokenAddresses . add ( p . token1 . address . toLowerCase ( ) ) ;
112+ }
113+
114+ const tokenPrices = await getPrices ( Array . from ( tokenAddresses ) , CHAIN ) ;
115+
40116 const results = [ ] ;
41117
42- for ( const [ address , p ] of Object . entries ( poolsObj ) ) {
118+ for ( let i = 0 ; i < poolAddresses . length ; i ++ ) {
119+ const address = poolAddresses [ i ] ;
120+ const p = poolsObj [ address ] ;
121+
122+ if ( ! p . token0 ?. address || ! p . token1 ?. address ) continue ;
123+
124+ const reserveData = reservesResults . output [ i ] ;
125+ let reserve0 , reserve1 ;
126+
127+ if ( reserveData && reserveData . success && reserveData . output ) {
128+ reserve0 = reserveData . output . _reserve0 || reserveData . output [ 0 ] ;
129+ reserve1 = reserveData . output . _reserve1 || reserveData . output [ 1 ] ;
130+ } else {
131+ reserve0 = p . token0Reserves ;
132+ reserve1 = p . token1Reserves ;
133+ }
134+
135+ if ( ! reserve0 || ! reserve1 ) continue ;
136+
137+ const tvlFromReserves = calculateReservesUSD (
138+ reserve0 ,
139+ reserve1 ,
140+ p . token0 . address ,
141+ p . token1 . address ,
142+ p . token0 . decimals ,
143+ p . token1 . decimals ,
144+ tokenPrices
145+ ) ;
146+
147+ const tvlUsd = tvlFromReserves
148+ ? Number ( tvlFromReserves . toString ( ) )
149+ : toNumber ( p . tvl ) ;
43150
44- if ( ! p ?. hasUSDValues ) continue ;
45- const tvlUsd = toNumber ( p . tvl ) ;
46151 if ( ! ( tvlUsd > 0 ) ) continue ;
47152
48153 let apyBase = toNumber ( p . apr ) ;
@@ -65,16 +170,17 @@ async function apy() {
65170 rewardTokens : p . hasActiveFarm
66171 ? [ "0xb7a95035618354D9ADFC49Eca49F38586B624040" ]
67172 : [ ] ,
68- underlyingTokens : [ p . token0 ? .address , p . token1 ? .address ] . filter ( Boolean ) ,
173+ underlyingTokens : [ p . token0 . address , p . token1 . address ] ,
69174 url : "https://app.zealousswap.com/liquidity" ,
70175 volumeUsd1d : toNumber ( p . volumeUSD ) ,
71176 } ) ;
72177 }
73178
74- return results . filter ( x => Number . isFinite ( x . tvlUsd ) ) ;
179+ return results . filter ( x => Number . isFinite ( x . tvlUsd ) && x . tvlUsd > 0 ) ;
75180}
76181
77182module . exports = {
78183 timetravel : false ,
79184 apy,
185+ url : API ,
80186} ;
0 commit comments