-
Notifications
You must be signed in to change notification settings - Fork 6.6k
Add PumpSpace adapter (Avalanche) with V2 + V3 TVL and token mapping support #16805
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
Changes from 11 commits
94a262d
9985e74
c712bb7
bb8370c
bdfd36d
0a77c11
0f6144d
49a49bb
4f6353b
f8ad4d7
c9bed2d
7e6ce16
261fcd3
9ec2c94
f7cb315
2e9629b
cbc3dac
cbabb11
7854cdc
8e1e265
4486442
6e55f10
5d2e175
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 @@ | ||
| // projects/aquabank/index.js | ||
| const ADDRESSES = require('../helper/coreAssets.json') | ||
| const { sumTokens2 } = require('../helper/unwrapLPs') | ||
|
|
||
| const USDt = ADDRESSES.avax.USDt // 0x9702230A8Ea53601f5Cd2dc00fDBc13d4dF4A8c7 | ||
|
|
||
| // Vaults that hold receipt tokens (not the underlying) | ||
| const BENQI_VAULT = '0x7D336B49879a173626E51BFF780686D88b8081ec' | ||
| const EULER_VAULT = '0x61E8f77eD693d3edeCBCc2dd9c55c1d987c47775' | ||
|
|
||
| // Protocol receipt tokens (convertible to USDt underlying) | ||
| const BENQI_RECEIPT = '0xd8fcDa6ec4Bdc547C0827B8804e89aCd817d56EF' | ||
| const EULER_RECEIPT = '0xa446938b0204Aa4055cdFEd68Ddf0E0d1BAB3E9E' | ||
|
|
||
| // Bank staking contracts where bUSDT is deposited | ||
| const bUSDT = '0x3C594084dC7AB1864AC69DFd01AB77E8f65B83B7' | ||
|
||
| const STAKERS = [ | ||
| '0x00F8a3B9395B4B02d12ee26536046c3C52459674', // Benqi BankStaking | ||
| '0x743BcD612866fC7485BfC487B14Ebf9a67D753Cb', // Euler BankStaking | ||
| ] | ||
|
|
||
| // ABIs | ||
| const erc20Bal = 'function balanceOf(address) view returns (uint256)' | ||
|
|
||
| const benqiAbi = { | ||
| // Compound-style exchange rate (scaled by 1e18) | ||
| exchangeRateStored: 'function exchangeRateStored() view returns (uint256)', | ||
| underlying: 'function underlying() view returns (address)', | ||
| } | ||
|
|
||
| const eulerAbi = { | ||
| // ERC4626-style conversion from shares to assets | ||
| convertToAssets: 'function convertToAssets(uint256 shares) view returns (uint256)', | ||
| asset: 'function asset() view returns (address)', | ||
| } | ||
|
|
||
| const toStr = (x) => (x?.toString?.() ?? String(x)) | ||
| const toBN = (x) => BigInt(toStr(x)) | ||
|
|
||
| // TVL: sum USDt underlying represented by Benqi/Euler receipt tokens held in the vaults | ||
| async function tvl(api) { | ||
|
|
||
| // 1) Read receipt balances held by the two vaults | ||
| const [benqiShares, eulerShares] = await Promise.all([ | ||
| api.call({ target: BENQI_RECEIPT, abi: erc20Bal, params: BENQI_VAULT, permitFailure: true }), | ||
| api.call({ target: EULER_RECEIPT, abi: erc20Bal, params: EULER_VAULT, permitFailure: true }), | ||
| ]) | ||
|
|
||
| // 2) Convert Benqi shares -> USDt underlying using Compound-style exchange rate | ||
| let benqiUnderlying = 0n | ||
| if (benqiShares) { | ||
| const rate = await api.call({ target: BENQI_RECEIPT, abi: benqiAbi.exchangeRateStored, permitFailure: true }) | ||
| if (rate) { | ||
| // underlying = shares * rate / 1e18 | ||
| benqiUnderlying = (toBN(benqiShares) * toBN(rate)) / 10n**18n | ||
| } | ||
| } | ||
|
|
||
| // 3) Convert Euler shares -> USDt underlying using ERC4626 conversion | ||
| let eulerUnderlying = 0n | ||
| if (eulerShares) { | ||
| const out = await api.call({ | ||
| target: EULER_RECEIPT, abi: eulerAbi.convertToAssets, params: eulerShares, permitFailure: true | ||
| }) | ||
| if (out) eulerUnderlying = toBN(out) | ||
| } | ||
|
|
||
| // 4) Add both underlyings as USDt | ||
| if (benqiUnderlying > 0n) api.add(USDt, benqiUnderlying) | ||
|
||
| if (eulerUnderlying > 0n) api.add(USDt, eulerUnderlying) | ||
|
|
||
| return api.getBalances() | ||
| } | ||
|
|
||
| // Staking: sum bUSDT deposited in BankStaking contracts, mapped 1:1 to USDt | ||
| async function staking(api) { | ||
| const tokensAndOwners = STAKERS.map(owner => [bUSDT, owner]) | ||
|
|
||
| // - bUSDT → USDt (Tether on Avalanche) | ||
| return await sumTokens2({ | ||
| api, | ||
| tokensAndOwners, | ||
| transformAddress: (addr) => { | ||
| const a = addr.toLowerCase() | ||
| if (a === bUSDT.toLowerCase()) return `avax:${USDt.toLowerCase()}` | ||
| return `avax:${a}` | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
|
|
||
| module.exports = { | ||
| methodology: | ||
| ' TVL = USDt underlying represented by Benqi/Euler receipt tokens held in vaults (Benqi: shares*exchangeRate/1e18, Euler: convertToAssets). Staking = bUSDT deposited in BankStaking contracts, reported as USDt via 1:1 mapping.', | ||
| avax: { | ||
| tvl, | ||
| staking, | ||
|
||
| }, | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| // projects/pumpspace/index.js | ||
| const ADDRESSES = require('../helper/coreAssets.json') | ||
| const { staking } = require('../helper/staking') | ||
| const { getUniTVL } = require('../helper/unknownTokens'); | ||
| const { getTridentTVL } = require('../helper/sushi-trident') | ||
| const { sumTokens2 } = require('../helper/unwrapLPs') | ||
| const sdk = require('@defillama/sdk') | ||
|
|
||
| const CHAIN = 'avax' | ||
|
|
||
| // --- FACTORIES / CONTRACTS --- | ||
| const PUMP_FACTORY = '0x26B42c208D8a9d8737A2E5c9C57F4481484d4616' // V2 | ||
| const PUMP_V3 = '0xE749c1cA2EA4f930d1283ad780AdE28625037CeD' // V3/Trident | ||
|
|
||
| // If you later expose staking for other MasterChefs, add here | ||
| const MASTERCHEFS = [ | ||
| '0x40a58fc672F7878F068bD8ED234a47458Ec33879', // SHELL | ||
| '0x56b54a1384d35C63cD95b39eDe9339fEf7df3E42', // KRILL | ||
| '0x06C551B19239fE6a425b3c45Eb8b49d28e8283C6', // PEARL | ||
| ] | ||
|
|
||
| // --- TOKENS (project/local wrappers + protocol tokens) --- | ||
| const TOKENS = { | ||
| bUSDT: '0x3C594084dC7AB1864AC69DFd01AB77E8f65B83B7', // mapped to USDt | ||
| WAVAX_PROXY: "0xAB4fBa02a2905a03adA8BD3d493FB289Dcf84024", // mapped to canonical WAVAX below | ||
| sBWPM: "0x6c960648d5F16f9e12895C28655cc6Dd73B660f7", | ||
| sADOL: "0x6214D13725d458890a8EF39ECB2578BdfCd82170", | ||
| CLAM: "0x1ea53822f9B2a860A7d20C6D2560Fd07db7CFF85", | ||
| PEARL: "0x08c4b51e6Ca9Eb89C255F0a5ab8aFD721420e447", | ||
| KRILL: "0x4ED0A710a825B9FcD59384335836b18C75A34270", | ||
| SHELL: '0xaD4CB79293322c07973ee83Aed5DF66A53214dc6', | ||
| } | ||
|
|
||
|
|
||
| // Map wrapped/local tokens to canonical core assets for pricing/aggregation. | ||
| // - bUSDT → USDt (Tether on Avalanche) | ||
| // - WAVAX proxy (0xAB4f...) → canonical WAVAX (0xB31f...) | ||
| function transformAddress(addr) { | ||
|
||
| const a = addr.toLowerCase() | ||
| if (a === TOKENS.bUSDT.toLowerCase()) | ||
| return `avax:${ADDRESSES.avax.USDt.toLowerCase()}` | ||
| if (a === TOKENS.WAVAX_PROXY.toLowerCase()) | ||
| return `avax:${ADDRESSES.avax.WAVAX.toLowerCase()}` | ||
| return `avax:${a}` | ||
| } | ||
|
|
||
| // ---------- V2 factory TVL (sum token reserves held by each pair) ---------- | ||
| const uniAbis = { | ||
| allPairsLength: 'function allPairsLength() view returns (uint256)', | ||
| allPairs: 'function allPairs(uint256) view returns (address)', | ||
| token0: 'function token0() view returns (address)', | ||
| token1: 'function token1() view returns (address)', | ||
| } | ||
|
|
||
| // ---------- V3 (Trident) TVL: factory → pool list → asset list → sum with transforms ---------- | ||
| const tridentAbis = { | ||
| totalPoolsCount: "uint256:totalPoolsCount", | ||
| getPoolAddress: "function getPoolAddress(uint256 idx) view returns (address pool)", | ||
| getAssets: "address[]:getAssets", | ||
| } | ||
|
|
||
|
|
||
| async function v2FactoryTVLWithBusdtMapping(api) { | ||
| // 1) enumerate all pairs from the V2 factory | ||
| const n = await api.call({ target: PUMP_FACTORY, abi: uniAbis.allPairsLength }) | ||
| const idx = Array.from({ length: Number(n) }, (_, i) => i) | ||
| const pairs = await api.multiCall({ | ||
| abi: uniAbis.allPairs, | ||
| calls: idx.map(i => ({ target: PUMP_FACTORY, params: i })), | ||
| }) | ||
|
|
||
| // 2) get token0/token1 for each pair | ||
| const [token0s, token1s] = await Promise.all([ | ||
| api.multiCall({ abi: uniAbis.token0, calls: pairs.map(p => ({ target: p })) }), | ||
| api.multiCall({ abi: uniAbis.token1, calls: pairs.map(p => ({ target: p })) }), | ||
| ]) | ||
|
|
||
| // 3) for each pair, count reserves via ERC20.balanceOf(pair) | ||
| const tokensAndOwners = [] | ||
| for (let i = 0; i < pairs.length; i++) { | ||
| const t0 = token0s[i]?.toLowerCase() | ||
| const t1 = token1s[i]?.toLowerCase() | ||
| if (!t0 || !t1) continue | ||
| tokensAndOwners.push([t0, pairs[i]]) | ||
| tokensAndOwners.push([t1, pairs[i]]) | ||
| } | ||
|
|
||
| // 4) aggregate with transforms (bUSDT→USDt, WAVAX proxy→WAVAX) | ||
| return sumTokens2({ | ||
| api, | ||
| tokensAndOwners, | ||
| transformAddress, | ||
| // resolveLP: false // V2 pairs counted by reserves; no unwrapping required here | ||
| }) | ||
| } | ||
|
|
||
| async function v3FactoryTVLWithMappings(ts, _block, { [CHAIN]: block }) { | ||
| // 1) number of pools | ||
| const pairLength = (await sdk.api.abi.call({ | ||
| target: PUMP_V3, abi: tridentAbis.totalPoolsCount, chain: CHAIN, block | ||
| })).output | ||
|
|
||
| // 2) pool addresses | ||
| const idxs = Array.from({ length: Number(pairLength) }, (_, i) => i) | ||
| const pools = (await sdk.api.abi.multiCall({ | ||
| abi: tridentAbis.getPoolAddress, chain: CHAIN, | ||
| calls: idxs.map(i => ({ target: PUMP_V3, params: [i] })), block | ||
| })).output.map(r => r.output) | ||
|
|
||
| // 3) assets per pool | ||
| const { output: assetLists } = await sdk.api.abi.multiCall({ | ||
| abi: tridentAbis.getAssets, chain: CHAIN, | ||
| calls: pools.map(p => ({ target: p })), block, | ||
| }) | ||
|
|
||
| // 4) build [token, owner(pool)] tuples | ||
| const toa = [] | ||
| assetLists.forEach(({ output, input: { target: pool } }) => | ||
| output.forEach(token => toa.push([token, pool])) | ||
| ) | ||
|
|
||
| // 5) aggregate with transforms (bUSDT→USDt, WAVAX proxy→WAVAX) | ||
| return sumTokens2({ chain: CHAIN, block, tokensAndOwners: toa, transformAddress }) | ||
| } | ||
|
|
||
| module.exports = { | ||
| misrepresentedTokens: true, | ||
| methodology: ` | ||
| TVL is calculated by summing up all reserves from both PumpSpace V2 and V3 (Trident) factories. | ||
| For accurate valuation, the following transformations are applied: | ||
| - bUSDT is mapped 1:1 to USDt (Tether) so its liquidity and price are aggregated under USDT. | ||
| - WAVAX proxy token (0xAB4f...) is mapped to the canonical Avalanche WAVAX (0xB31f...). | ||
| This ensures all liquidity pools using wrapped variants are properly reflected in total TVL. | ||
| Staking represents single-token staking of SHELL from the Shell MasterChef contract. | ||
| `, | ||
| avax: { | ||
| tvl: sdk.util.sumChainTvls([ | ||
| v2FactoryTVLWithBusdtMapping, | ||
| v3FactoryTVLWithMappings, | ||
| ]), | ||
| staking: sdk.util.sumChainTvls([ | ||
| // If you later add KRILL/PEARL single-asset staking, append similar lines here. | ||
| staking([MASTERCHEFS[0]], [TOKENS.SHELL]), | ||
| ]) | ||
| }, | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we price both of these too, you can just export a balance of the receipt tokens, no need for conversion