mirror of
https://github.com/alexgo-io/DefiLlama-Adapters.git
synced 2026-01-12 16:53:02 +08:00
244 lines
7.7 KiB
JavaScript
244 lines
7.7 KiB
JavaScript
const ADDRESSES = require('./helper/coreAssets.json')
|
|
const { getBlock, blockQuery } = require("./helper/http");
|
|
|
|
const supertokensQuery = ({ first = 1000, id_gt = "" } = {}) => `
|
|
query get_supertokens($block: Int) {
|
|
tokens(
|
|
first: ${first},
|
|
block: { number: $block },
|
|
where: { isSuperToken: true${id_gt ? `, id_gt: "${id_gt}"` : ""} },
|
|
orderBy: id,
|
|
orderDirection: asc
|
|
) {
|
|
id
|
|
underlyingAddress
|
|
name
|
|
underlyingToken { name decimals symbol id }
|
|
symbol
|
|
decimals
|
|
isSuperToken
|
|
isNativeAssetSuperToken
|
|
isListed
|
|
}
|
|
}`;
|
|
|
|
const blacklistedSuperTokens = new Set(
|
|
["0x441bb79f2da0daf457bad3d401edb68535fb3faa"].map((i) => i.toLowerCase())
|
|
);
|
|
|
|
// Fetch and paginate all SuperTokens at a given block
|
|
async function fetchAllSuperTokens(graphUrl, blockForQuery) {
|
|
const PAGE_SIZE = 1000;
|
|
let lastId = "";
|
|
const allTokens = [];
|
|
let hasMore = true
|
|
while (hasMore) {
|
|
const query = supertokensQuery({ first: PAGE_SIZE, id_gt: lastId });
|
|
const res = await blockQuery(graphUrl, query, {
|
|
api: { getBlock: () => blockForQuery, block: blockForQuery },
|
|
});
|
|
const tokens = res.tokens;
|
|
if (!tokens?.length) {
|
|
hasMore = false
|
|
break;
|
|
}
|
|
allTokens.push(...tokens);
|
|
if (tokens.length < PAGE_SIZE) {
|
|
hasMore = false
|
|
break;
|
|
}
|
|
lastId = tokens[tokens.length - 1].id;
|
|
}
|
|
return allTokens;
|
|
}
|
|
|
|
// ALEPH custom locker address used on Base and Avalanche
|
|
const ALEPH_LOCKER = "0xb6e45ADfa0C7D70886bBFC990790d64620F1BAE8".toLowerCase();
|
|
// ALEPH SuperToken address (same on Base and Avalanche)
|
|
const ALEPH_SUPERTOKEN = "0xc0Fbc4967259786C743361a5885ef49380473dCF".toLowerCase();
|
|
|
|
// MIVA token locker addresses (on xDai/Gnosis). Circulating = totalSupply - sum(locker balances)
|
|
const MIVA_LOCKERS = [
|
|
"0x50e39b354c90146de80a577e13129bb0ba36ee45",
|
|
"0x791B3A48D2dca38871C9900783653b15aCae0Aea",
|
|
"0x6A0491132aF4d0925F857A5000bb21e5C5C195EA",
|
|
"0x8F00FC7756C9E901963B723AD1821E5EB8C69C02",
|
|
"0x10DAF0DF6Ec9bEF452F3073A56f6adB4B1809222",
|
|
"0xa2eac044fe1e004cAaC4E8C4164a39F4Cc522b6f",
|
|
"0x89Abea6823cfd903fB503A1DB17a7ce890A3232e",
|
|
"0xFd989d6E3244cFb5470597E7B93E4430CC29EfE9",
|
|
"0x867e84EB2789c95eEF6d6991cC4bC6B48e1519b8",
|
|
"0x16daae140FbC2F854Cf61af0512Bd8CD627d0B8e",
|
|
"0xA298D0b6B9216f7d9EB252DeA06280b748eFe8E5",
|
|
"0x1d9896F00fd51df839B2F5B7fFdD0bD60b471CeF",
|
|
"0xDfdec8DF5cfF5DaAb3ec635E477517AC92251dfD",
|
|
"0xeCD2D1bB2776f00AD15F976F349A1ab01F8ce398",
|
|
"0x5B339241312024382C9768b3598f60eCF34Ae779",
|
|
];
|
|
|
|
// Main function for all chains to get balances of superfluid tokens
|
|
async function getChainBalances(allTokens, chain, block, isVesting, api) {
|
|
// Init empty balances
|
|
let balances = {};
|
|
|
|
// Abi MultiCall to get supertokens supplies
|
|
const supply = await api.multiCall({
|
|
abi: "erc20:totalSupply", // abi['totalSupply'],
|
|
calls: allTokens.map(token => token.id),
|
|
});
|
|
|
|
for (let i = 0; i < supply.length; i++) {
|
|
const totalSupply = supply[i];
|
|
const {
|
|
id,
|
|
underlyingAddress,
|
|
underlyingToken,
|
|
decimals,
|
|
name,
|
|
symbol,
|
|
isNativeAssetSuperToken,
|
|
} = allTokens[i];
|
|
|
|
// Accumulate to balances, the balance for tokens on mainnet or sidechain
|
|
let prefixedUnderlyingAddress = underlyingAddress;
|
|
if (
|
|
underlyingAddress &&
|
|
blacklistedSuperTokens.has(underlyingAddress.toLowerCase())
|
|
)
|
|
continue;
|
|
|
|
// ALEPH custom logic (Base and Avalanche): no underlying; circulating = totalSupply - locker balance
|
|
if (
|
|
(chain === 'base' || chain === 'avax') &&
|
|
id.toLowerCase() === ALEPH_SUPERTOKEN
|
|
) {
|
|
const lockerHolding = await api.call({ abi: 'erc20:balanceOf', target: id, params: [ALEPH_LOCKER] });
|
|
const circulating = Math.max(0, totalSupply - lockerHolding);
|
|
api.add(id, circulating);
|
|
continue;
|
|
}
|
|
|
|
// MIVA token special logic (Gnosis/xDai): circulating = totalSupply - sum(locker balances)
|
|
if (symbol && symbol.toUpperCase() === 'MIVA') {
|
|
const lockerHoldings = await api.multiCall({
|
|
abi: 'erc20:balanceOf',
|
|
calls: MIVA_LOCKERS.map((locker) => ({ target: id, params: [locker] })),
|
|
});
|
|
const totalLocked = lockerHoldings.reduce((sum, v) => sum + (Number(v) || 0), 0);
|
|
const circulating = Math.max(0, totalSupply - totalLocked);
|
|
api.add(id, circulating);
|
|
continue;
|
|
}
|
|
|
|
if (isNativeAssetSuperToken) {
|
|
// For native asset SuperTokens (like ETHx), use the chain's native token
|
|
api.add(ADDRESSES.null, totalSupply);
|
|
continue;
|
|
}
|
|
|
|
if (underlyingToken) {
|
|
// For wrapped tokens (default), convert to underlying units
|
|
const underlyingDecimals = (underlyingToken || { decimals: 18 }).decimals;
|
|
const underlyingTokenBalance =
|
|
(totalSupply * 10 ** underlyingDecimals) / 10 ** decimals;
|
|
api.add(prefixedUnderlyingAddress, underlyingTokenBalance);
|
|
continue;
|
|
}
|
|
|
|
// For pure SuperTokens (no underlying), use the SuperToken's own address
|
|
api.add(id, totalSupply);
|
|
}
|
|
}
|
|
|
|
async function retrieveSupertokensBalances(
|
|
chain,
|
|
block,
|
|
isVesting,
|
|
api,
|
|
graphUrl
|
|
) {
|
|
let blockNum;
|
|
try {
|
|
blockNum = await getBlock(api.timestamp, chain, { [chain]: block });
|
|
} catch (e) {
|
|
// If block lookup fails for this chain/date (e.g. pre-genesis), skip it
|
|
return;
|
|
}
|
|
|
|
// Ensure we don't query subgraphs before their start block set in the manifest (pre protocol deployment)
|
|
const subgraphStartBlocks = {
|
|
scroll: 2_575_000,
|
|
degen: 26188017,
|
|
base: 1000000,
|
|
ethereum: 15870000,
|
|
celo: 16393000,
|
|
bsc: 18800000,
|
|
avax: 14700000,
|
|
arbitrum: 7600000,
|
|
optimism: 4300000,
|
|
polygon: 11650500,
|
|
xdai: 14820000,
|
|
};
|
|
const minStart = subgraphStartBlocks[chain] || 0;
|
|
// If requested block is before the subgraph started indexing ( protocol deployment ), return 0 for this chain
|
|
if (minStart && blockNum < minStart) return;
|
|
const blockForQuery = (blockNum || 0);
|
|
|
|
const allTokens = await fetchAllSuperTokens(graphUrl, blockForQuery);
|
|
const filteredTokens = allTokens.filter((t) => t.isSuperToken);
|
|
await getChainBalances(filteredTokens, chain, block, isVesting, api);
|
|
return api.getBalances();
|
|
}
|
|
|
|
/**
|
|
* List of subgraphs can be retrieved from https://docs.superfluid.finance/docs/technical-reference/subgraph
|
|
*/
|
|
const subgraphEndpoints = {
|
|
arbitrum: {
|
|
graph: "https://arbitrum-one.subgraph.x.superfluid.dev",
|
|
},
|
|
avax: {
|
|
graph: "https://avalanche-c.subgraph.x.superfluid.dev",
|
|
},
|
|
base: {
|
|
graph: "https://base-mainnet.subgraph.x.superfluid.dev",
|
|
},
|
|
bsc: {
|
|
graph: "https://bsc-mainnet.subgraph.x.superfluid.dev",
|
|
},
|
|
// degen: {
|
|
// graph: "https://degenchain.subgraph.x.superfluid.dev",
|
|
// },
|
|
ethereum: {
|
|
graph: "https://eth-mainnet.subgraph.x.superfluid.dev",
|
|
},
|
|
optimism: {
|
|
graph: "https://optimism-mainnet.subgraph.x.superfluid.dev",
|
|
},
|
|
polygon: {
|
|
graph: "https://polygon-mainnet.subgraph.x.superfluid.dev",
|
|
},
|
|
scroll: {
|
|
graph: "https://scroll-mainnet.subgraph.x.superfluid.dev",
|
|
},
|
|
xdai: {
|
|
graph: "https://xdai-mainnet.subgraph.x.superfluid.dev",
|
|
},
|
|
celo: {
|
|
graph: "https://celo-mainnet.subgraph.x.superfluid.dev",
|
|
},
|
|
};
|
|
|
|
module.exports = {
|
|
methodology: `TVL is the value of SuperTokens in circulation. SuperTokens are Superfluid protocol's extension of the ERC20 token standard with additional functionalities like Money Streaming or Distributions. More on SuperTokens here: https://docs.superfluid.finance/docs/concepts/overview/super-tokens`,
|
|
// hallmarks: [[1644278400, "Fake ctx hack"]],
|
|
};
|
|
|
|
Object.keys(subgraphEndpoints).forEach((chain) => {
|
|
const { graph } = subgraphEndpoints[chain];
|
|
module.exports[chain] = {
|
|
tvl: async (api, _b, { [chain]: block }) =>
|
|
retrieveSupertokensBalances(chain, block, false, api, graph),
|
|
}
|
|
});
|