Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions dexs/nado-perp/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { FetchOptions, SimpleAdapter } from "../../adapters/types";
import { CHAIN } from "../../helpers/chains";
import { httpGet, httpPost } from "../../utils/fetchURL";

interface IProducts {
perp_products: number[];
}

// Nado (Private Alpha)
// Production API on Ink Mainnet
const gatewayInkUrl = "https://gateway.prod.nado.xyz/v1";
const archiveInkUrl = "https://archive.prod.nado.xyz/v1";

type TURL = {
[s: string]: {
gateway: string;
archive: string;
};
};

const url: TURL = {
[CHAIN.INK]: {
gateway: gatewayInkUrl,
archive: archiveInkUrl,
},
};

const fetchValidSymbols = async (
fetchOptions: FetchOptions
): Promise<number[]> => {
const symbols = await httpGet(`${url[fetchOptions.chain].gateway}/symbols`);
return symbols.map((product: { product_id: number }) => product.product_id);
};

const fetchProducts = async (
fetchOptions: FetchOptions
): Promise<IProducts> => {
const validSymbols = await fetchValidSymbols(fetchOptions);
const allProducts = (
await httpGet(`${url[fetchOptions.chain].gateway}/query?type=all_products`)
).data;
return {
perp_products: allProducts.perp_products
.map((product: { product_id: number }) => product.product_id)
.filter((id: number) => validSymbols.includes(id))
};
};

const computeVolume = async (
timestamp: number,
productIds: number[],
fetchOptions: FetchOptions
) => {
if (!productIds.length) {
return { dailyVolume: undefined };
}

const response = await httpPost(url[fetchOptions.chain].archive, {
market_snapshots: {
interval: {
count: 2,
granularity: 86400,
max_time: timestamp,
},
product_ids: productIds,
},
});

const snapshots = response?.snapshots;
if (!Array.isArray(snapshots) || snapshots.length < 2) {
return { dailyVolume: undefined };
}

const lastCumulativeVolumes: Record<string, string> =
snapshots[0].cumulative_volumes;
const prevCumulativeVolumes: Record<string, string> =
snapshots[1].cumulative_volumes;
const totalVolume = Number(
Object.values(lastCumulativeVolumes).reduce(
(acc, current) => acc + BigInt(current),
BigInt(0)
) / BigInt(10 ** 18)
);
const totalVolumeOneDayAgo = Number(
Object.values(prevCumulativeVolumes).reduce(
(acc, current) => acc + BigInt(current),
BigInt(0)
) / BigInt(10 ** 18)
);
const dailyVolume = totalVolume - totalVolumeOneDayAgo;

return { dailyVolume };
};


const fetch = async (timestamp: number, _: any, fetchOptions: FetchOptions) => {
const products = await fetchProducts(fetchOptions);
const perpVolumes = await computeVolume(timestamp, products.perp_products, fetchOptions);
return { dailyVolume: perpVolumes.dailyVolume };
};


const adapter: SimpleAdapter = {
version: 1,
fetch,
chains: [CHAIN.INK],
start: '2025-11-15',
};

export default adapter;
124 changes: 124 additions & 0 deletions dexs/nado-spot/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { FetchOptions, SimpleAdapter } from "../../adapters/types";
import { CHAIN } from "../../helpers/chains";
import { httpGet, httpPost } from "../../utils/fetchURL";

interface IProducts {
spot_products: number[];
margined_products: number[];
}

// Nado (Private Alpha)
// Production API on Ink Mainnet
const gatewayInkUrl = "https://gateway.prod.nado.xyz/v1";
const archiveInkUrl = "https://archive.prod.nado.xyz/v1";

type TURL = {
[s: string]: {
gateway: string;
archive: string;
};
};

const url: TURL = {
[CHAIN.INK]: {
gateway: gatewayInkUrl,
archive: archiveInkUrl,
},
};

const fetchValidSymbols = async (
fetchOptions: FetchOptions
): Promise<number[]> => {
const symbols = await httpGet(`${url[fetchOptions.chain].gateway}/symbols`);
return symbols.map((product: { product_id: number }) => product.product_id);
};

const fetchProducts = async (
fetchOptions: FetchOptions
): Promise<IProducts> => {
const validSymbols = await fetchValidSymbols(fetchOptions);
const allProducts = (
await httpGet(`${url[fetchOptions.chain].gateway}/query?type=all_products`)
).data;
return {
spot_products: allProducts.spot_products
.map((product: { product_id: number }) => product.product_id)
.filter((id: number) => validSymbols.includes(id) && id > 0),
margined_products: allProducts.spot_products
.map((product: { product_id: number }) => product.product_id)
.filter((id: number) => validSymbols.includes(id) && id > 0),

};
};

const computeVolume = async (
timestamp: number,
productIds: number[],
fetchOptions: FetchOptions
) => {
if (!productIds.length) {
return {
dailyVolume: undefined,
};
}

const response = await httpPost(url[fetchOptions.chain].archive, {
market_snapshots: {
interval: {
count: 2,
granularity: 86400,
max_time: timestamp,
},
product_ids: productIds,
},
});

const snapshots = response?.snapshots;
if (!Array.isArray(snapshots) || snapshots.length < 2) {
return {
dailyVolume: undefined,
};
}

const lastCumulativeVolumes: Record<string, string> =
snapshots[0].cumulative_volumes;
const prevCumulativeVolumes: Record<string, string> =
snapshots[1].cumulative_volumes;
const totalVolume = Number(
Object.values(lastCumulativeVolumes).reduce(
(acc, current) => acc + BigInt(current),
BigInt(0)
) / BigInt(10 ** 18)
);
const totalVolumeOneDayAgo = Number(
Object.values(prevCumulativeVolumes).reduce(
(acc, current) => acc + BigInt(current),
BigInt(0)
) / BigInt(10 ** 18)
);
const dailyVolume = totalVolume - totalVolumeOneDayAgo;

return { dailyVolume };
};


const fetch = async (timestamp: number, _: any, fetchOptions: FetchOptions) => {
const products = await fetchProducts(fetchOptions);

const [spotVolumes, marginedVolumes] = await Promise.all([
computeVolume(timestamp, products.spot_products, fetchOptions),
computeVolume(timestamp, products.margined_products, fetchOptions),
]);
const dailyVolume = (spotVolumes.dailyVolume ?? 0) + (marginedVolumes.dailyVolume ?? 0);
return { dailyVolume };
};


const adapter: SimpleAdapter = {
version: 1,
fetch,
chains: [CHAIN.INK],
start: '2025-11-15',
};

export default adapter;
128 changes: 128 additions & 0 deletions fees/nado.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { CHAIN } from "../helpers/chains";
import { Adapter, FetchOptions, FetchResultFees } from "../adapters/types";
import { httpPost } from "../utils/fetchURL";

interface MarketSnapshots {
interval: {
count: number;
granularity: number;
max_time: number;
};
}

interface QueryBody {
market_snapshots: MarketSnapshots;
}

interface IData {
[s: string]: string;
}

interface Snapshot {
[s: string]: IData;
}

interface Response {
snapshots: Snapshot[];
}

// Nado (Private Alpha)
// Production API on Ink Mainnet
const archiveInkUrl = "https://archive.prod.nado.xyz/v1";

type TURL = {
[s: string]: string;
};

const url: TURL = {
[CHAIN.INK]: archiveInkUrl,
};

const query = async (
max_time: number,
fetchOptions: FetchOptions
): Promise<Response> => {
const body: QueryBody = {
market_snapshots: {
interval: {
count: 2,
granularity: 86400,
max_time: max_time,
},
},
};

const response = await httpPost(url[fetchOptions.chain], body);
return response;
};

const sumAllProductStats = (stat_map: IData): number => {
let stat_sum = 0;
for (const v of Object.values(stat_map)) {
stat_sum += parseInt(v);
}
return stat_sum / 1e18;
};

const get24hrStat = async (
field: string,
max_time: number,
fetchOptions: FetchOptions
): Promise<number> => {
const response = await query(max_time, fetchOptions);
const cur_res: Snapshot = response.snapshots[0];
const past_res: Snapshot = response.snapshots[1];
return (
sumAllProductStats(cur_res[field]) - sumAllProductStats(past_res[field])
);
};

const get24hrFees = async (
max_time: number,
fetchOptions: FetchOptions
): Promise<number> => {
const fees = await get24hrStat(
"cumulative_taker_fees",
max_time,
fetchOptions
);
const sequencer_fees = await get24hrStat(
"cumulative_sequencer_fees",
max_time,
fetchOptions
);
return fees - sequencer_fees;
};

const get24hrRevenue = async (
max_time: number,
fetchOptions: FetchOptions
): Promise<number> => {
const fees = await get24hrFees(max_time, fetchOptions);
const rebates = await get24hrStat(
"cumulative_maker_fees",
max_time,
fetchOptions
);
return fees + rebates;
};

const fetch = async (
timestamp: number,
_: any,
fetchOptions: FetchOptions
): Promise<FetchResultFees> => {
const dailyFees = await get24hrFees(timestamp, fetchOptions);
const dailyRevenue = await get24hrRevenue(timestamp, fetchOptions);

return { dailyFees, dailyRevenue };
};

const adapter: Adapter = {
version: 1,
fetch,
chains: [CHAIN.INK],
start: '2025-11-15',
};

export default adapter;
Loading