|
1 |
| -import { httpGet, } from "../../utils/fetchURL"; |
2 |
| -import { FetchOptions } from "../../adapters/types"; |
| 1 | +import { httpGet, httpPost } from "../../utils/fetchURL"; |
| 2 | +import { FetchOptions, SimpleAdapter } from "../../adapters/types"; |
3 | 3 | import { CHAIN } from "../../helpers/chains";
|
4 | 4 |
|
5 |
| -const dailyEndpoint = "https://api.moonlander.trade/v1/trading-volumes/sum-by-date"; |
| 5 | +const CONFIG = { |
| 6 | + [CHAIN.CRONOS]: { |
| 7 | + chainName: "CRONOS", |
| 8 | + start: "2025-04-29", |
| 9 | + address: "0xE6F6351fb66f3a35313fEEFF9116698665FBEeC9", |
| 10 | + excludeFilters: [ |
| 11 | + { |
| 12 | + pairBase: "0xbad4ccc91ef0dfffbcab1402c519601fbaf244ef", // 500BTC pair address |
| 13 | + excludeStartTime: "2025-07-08T09:00:00.000Z", // Jul 8 5:00pm HKT |
| 14 | + excludeEndTime: "2025-07-29T09:00:00.000Z", // Jul 29 5:00pm HKT |
| 15 | + }, |
| 16 | + ], |
| 17 | + }, |
| 18 | + [CHAIN.CRONOS_ZKEVM]: { |
| 19 | + chainName: "CRONOS_ZKEVM", |
| 20 | + start: "2024-12-17", |
| 21 | + address: "0x02ae2e56bfDF1ee4667405eE7e959CD3fE717A05", |
| 22 | + excludeFilters: [], // No need to remove zkEVM's |
| 23 | + }, |
| 24 | +}; |
| 25 | + |
| 26 | +const pairsAbi = |
| 27 | + "function pairsV4() view returns ((string name, address base, uint16 basePosition, uint8 pairType, uint8 status, uint256 maxLongOiUsd, uint256 maxShortOiUsd, uint256 fundingFeePerSecondP, uint256 minFundingFeeR, uint256 maxFundingFeeR, (uint256 notionalUsd, uint16 tier, uint16 maxLeverage, uint16 initialLostP, uint16 liqLostP)[] leverageMargins, uint16 slippageConfigIndex, uint16 slippagePosition, (string name, uint256 onePercentDepthAboveUsd, uint256 onePercentDepthBelowUsd, uint16 slippageLongP, uint16 slippageShortP, uint16 index, uint8 slippageType, bool enable, uint256 longThresholdUsd, uint256 shortThresholdUsd) slippageConfig, uint16 feeConfigIndex, uint16 feePosition, (string name, uint16 index, uint16 openFeeP, uint16 closeFeeP, bool enable, uint24 shareP, uint24 minCloseFeeP) feeConfig, uint40 longHoldingFeeRate, uint40 shortHoldingFeeRate)[])"; |
| 28 | +const marketInfoAbi = |
| 29 | + "function getMarketInfos(address[] pairBases) view returns ((address pairBase, uint256 longQty, uint256 shortQty, uint128 lpLongAvgPrice, uint128 lpShortAvgPrice, int256 fundingFeeRate)[])"; |
| 30 | +const priceAbi = "function getPrice(address token) view returns (uint256)"; |
6 | 31 |
|
7 |
| -const chains: { [key: string]: string } = { |
8 |
| - // [CHAIN.CRONOS]: "CRONOS", |
9 |
| - [CHAIN.CRONOS_ZKEVM]: "CRONOS_ZKEVM", |
| 32 | +const BASE_API_URL = "https://api.moonlander.trade/v1/defillama"; |
| 33 | +const VOLUME_ENDPOINT = `${BASE_API_URL}/volume`; |
| 34 | +const FEES_ENDPOINT = `${BASE_API_URL}/fee`; |
| 35 | + |
| 36 | +const getDailyVolumeData = async ({ chain, startTime, endTime }: any) => { |
| 37 | + const requestBody = { |
| 38 | + chains: [CONFIG[chain].chainName], |
| 39 | + startTime: startTime.toISOString(), |
| 40 | + endTime: endTime.toISOString(), |
| 41 | + exclusionFilters: CONFIG[chain].excludeFilters, |
| 42 | + }; |
| 43 | + |
| 44 | + return await httpPost(VOLUME_ENDPOINT, requestBody); |
10 | 45 | };
|
11 | 46 |
|
12 |
| -const getDailyUri = ({ chain, startTime, endTime, }: any) => { |
13 |
| - return `${dailyEndpoint}?chains=${chains[chain]}&startTime=${startTime.toISOString()}&endTime=${endTime.toISOString()}`; |
| 47 | +const getFeesUri = ({ chain, startTime, endTime }: any) => { |
| 48 | + return `${FEES_ENDPOINT}?block_chain=${ |
| 49 | + CONFIG[chain].chainName |
| 50 | + }&startDate=${startTime.toISOString()}&endDate=${endTime.toISOString()}`; |
14 | 51 | };
|
15 | 52 |
|
16 |
| -interface APIResponse { |
| 53 | +const getOpenInterest = async ({ |
| 54 | + chain, |
| 55 | + api, |
| 56 | +}: Pick<FetchOptions, "chain" | "api">) => { |
| 57 | + const pairs = await api.call({ |
| 58 | + target: CONFIG[chain].address, |
| 59 | + abi: pairsAbi, |
| 60 | + }); |
| 61 | + const pairBases = pairs.map((pair: any) => pair.base); |
| 62 | + |
| 63 | + const pairsMarketInfo = await api.call({ |
| 64 | + target: CONFIG[chain].address, |
| 65 | + abi: marketInfoAbi, |
| 66 | + params: [pairBases], |
| 67 | + }); |
| 68 | + |
| 69 | + const pairPrices = await api.multiCall({ |
| 70 | + abi: priceAbi, |
| 71 | + calls: pairs.map((pair: any) => ({ |
| 72 | + target: CONFIG[chain].address, |
| 73 | + params: pair.base, |
| 74 | + })), |
| 75 | + permitFailure: true, |
| 76 | + }); |
| 77 | + |
| 78 | + let totalLongOIUsd = 0, |
| 79 | + totalShortOIUsd = 0; |
| 80 | + |
| 81 | + pairPrices.forEach((pairPrice, index) => { |
| 82 | + const longOI = pairsMarketInfo[index].longQty / 1e10; // qty is 10 decimals |
| 83 | + const shortOI = pairsMarketInfo[index].shortQty / 1e10; // qty is 10 decimals |
| 84 | + |
| 85 | + const longOIUsd = longOI * (pairPrice / 1e18); // convert to USD, price is 18 decimals |
| 86 | + const shortOIUsd = shortOI * (pairPrice / 1e18); // convert to USD, price is 18 decimals |
| 87 | + |
| 88 | + totalLongOIUsd += longOIUsd; |
| 89 | + totalShortOIUsd += shortOIUsd; |
| 90 | + }); |
| 91 | + |
| 92 | + return { |
| 93 | + longOpenInterestAtEnd: totalLongOIUsd, |
| 94 | + shortOpenInterestAtEnd: totalShortOIUsd, |
| 95 | + openInterestAtEnd: totalLongOIUsd + totalShortOIUsd, |
| 96 | + }; |
| 97 | +}; |
| 98 | + |
| 99 | +interface DailyDateAPIResponse { |
17 | 100 | vol: string;
|
18 | 101 | usdVol: string;
|
19 | 102 | }
|
20 | 103 |
|
21 |
| -async function fetch({ startTimestamp, endTimestamp, chain }: FetchOptions) { |
22 |
| - const dailyData: APIResponse = await httpGet(getDailyUri({ |
| 104 | +interface FeesAPIResponse { |
| 105 | + dailyFeeAmountUsd: number; |
| 106 | +} |
| 107 | + |
| 108 | +async function fetch({ |
| 109 | + startTimestamp, |
| 110 | + endTimestamp, |
| 111 | + chain, |
| 112 | + api, |
| 113 | +}: FetchOptions) { |
| 114 | + const dailyData: DailyDateAPIResponse = await getDailyVolumeData({ |
23 | 115 | chain,
|
24 | 116 | startTime: new Date(startTimestamp * 1000),
|
25 | 117 | endTime: new Date(endTimestamp * 1000),
|
26 |
| - })); |
| 118 | + }); |
| 119 | + |
| 120 | + const dailyFeesData: FeesAPIResponse = await httpGet( |
| 121 | + getFeesUri({ |
| 122 | + chain, |
| 123 | + startTime: new Date(startTimestamp * 1000), |
| 124 | + endTime: new Date(endTimestamp * 1000), |
| 125 | + }) |
| 126 | + ); |
| 127 | + |
| 128 | + const { shortOpenInterestAtEnd, longOpenInterestAtEnd, openInterestAtEnd } = |
| 129 | + await getOpenInterest({ chain, api }); |
27 | 130 |
|
28 | 131 | return {
|
29 | 132 | dailyVolume: dailyData.usdVol,
|
| 133 | + shortOpenInterestAtEnd, |
| 134 | + longOpenInterestAtEnd, |
| 135 | + openInterestAtEnd, |
| 136 | + dailyFees: dailyFeesData.dailyFeeAmountUsd, |
30 | 137 | };
|
31 | 138 | }
|
32 | 139 |
|
33 |
| -const startTimestamps: { [chain: string]: string } = { |
34 |
| - // [CHAIN.CRONOS]: '2025-04-29', |
35 |
| - [CHAIN.CRONOS_ZKEVM]: '2024-12-17', |
36 |
| -}; |
37 |
| -const adapter: any = {} |
38 |
| - |
39 |
| -Object.keys(chains).forEach((chain) => adapter[chain] = { fetch, start: startTimestamps[chain], }) |
40 |
| - |
41 |
| -export default { |
42 |
| - adapter, |
| 140 | +const adapter: SimpleAdapter = { |
43 | 141 | version: 2,
|
44 |
| -} |
| 142 | + adapter: Object.fromEntries( |
| 143 | + Object.entries(CONFIG).map(([chain, config]) => [ |
| 144 | + chain, |
| 145 | + { |
| 146 | + fetch: (options: FetchOptions) => fetch({ ...options, chain }), |
| 147 | + start: config.start, |
| 148 | + }, |
| 149 | + ]) |
| 150 | + ), |
| 151 | +}; |
45 | 152 |
|
| 153 | +export default adapter; |
0 commit comments