Skip to content

Commit c5d53c5

Browse files
feat: Narwhal fee (#4921)
* draft: implement initial version * fix: correct calculation following hard-coded USDC * chore: change start time of fee for testing * fix: correct number calculation * chore: remove redundant comments * chore: remove redundant paragraph * fix --------- Co-authored-by: Eden <[email protected]>
1 parent fc022dd commit c5d53c5

File tree

1 file changed

+90
-0
lines changed

1 file changed

+90
-0
lines changed

dexs/narwhal-finance.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { FetchOptions, SimpleAdapter } from '../adapters/types';
2+
import { CHAIN } from '../helpers/chains';
3+
import BigNumber from 'bignumber.js';
4+
5+
const ADDRESS_TRADING_USDC = '0x3556d16519e3407AD43d5d7b3011bB095553d77a';
6+
7+
// Constants from contract
8+
const DENOMINATOR = BigNumber(10 ** 18);
9+
const USDC_DECIMALS = BigNumber(1e6);
10+
11+
// Event definitions based on the contract
12+
// OpenTrade struct: { base: TradeBase, openPrice, lastUpdateTime }
13+
// TradeBase struct: { trader, pairIndex, margin, long, leverage, tp, sl }
14+
const openEventAbi = 'event Open(uint256 orderId, ((address trader, uint256 pairIndex, uint256 margin, bool long, uint256 leverage, uint256 tp, uint256 sl) base, uint256 openPrice, uint256 lastUpdateTime) t, uint256 fee)';
15+
const closeEventAbi = 'event Close(uint256 orderId, uint256 closePrice, uint256 _closeMargin, int256 fundingFee, uint256 rolloverFee, uint256 closeFee, uint256 afterFee, uint8 s)';
16+
17+
const fetch = async (options: FetchOptions) => {
18+
const dailyFees = options.createBalances();
19+
const dailyVolume = options.createBalances();
20+
21+
const [openLogs, closeLogs] = await Promise.all([
22+
options.getLogs({
23+
target: ADDRESS_TRADING_USDC,
24+
eventAbi: openEventAbi,
25+
}),
26+
options.getLogs({
27+
target: ADDRESS_TRADING_USDC,
28+
eventAbi: closeEventAbi,
29+
}),
30+
]);
31+
32+
const leverageByOrder = new Map();
33+
34+
openLogs.forEach((log: any) => {
35+
const orderId = log.orderId.toString();
36+
const margin = BigNumber(log.t.base.margin);
37+
const leverage = BigNumber(log.t.base.leverage);
38+
leverageByOrder.set(orderId, leverage);
39+
40+
const fee = BigNumber(log.fee);
41+
const feeUSD = fee.dividedBy(USDC_DECIMALS);
42+
const size = margin.multipliedBy(leverage).dividedBy(DENOMINATOR);
43+
const sizeUSD = size.dividedBy(USDC_DECIMALS);
44+
45+
dailyVolume.addUSDValue(sizeUSD.toNumber());
46+
dailyFees.addUSDValue(feeUSD.toNumber());
47+
});
48+
49+
closeLogs.forEach((log: any) => {
50+
const orderId = log.orderId.toString();
51+
const closeMargin = BigNumber(log._closeMargin);
52+
const rolloverFee = BigNumber(log.rolloverFee);
53+
const closeFee = BigNumber(log.closeFee);
54+
55+
const leverage = leverageByOrder.get(orderId);
56+
57+
if (leverage) {
58+
const size = closeMargin.multipliedBy(leverage).dividedBy(DENOMINATOR);
59+
const sizeUSD = size.dividedBy(USDC_DECIMALS);
60+
dailyVolume.addUSDValue(sizeUSD.toNumber());
61+
} else {
62+
console.warn("unknown orderId for event Close", orderId);
63+
}
64+
65+
const totalCloseFees = rolloverFee.plus(closeFee);
66+
const totalCloseFeeUSD = totalCloseFees.dividedBy(USDC_DECIMALS);
67+
dailyFees.addUSDValue(totalCloseFeeUSD.toNumber());
68+
});
69+
70+
return {
71+
dailyVolume,
72+
dailyFees,
73+
};
74+
};
75+
76+
const adapter: SimpleAdapter = {
77+
version: 2,
78+
adapter: {
79+
[CHAIN.MONAD]: {
80+
fetch,
81+
start: '2025-11-11',
82+
},
83+
},
84+
methodology: {
85+
Volume: 'Volume is calculated by summing the notional position sizes: (margin * leverage) for both Open and Close events',
86+
Fees: 'Fees include: (1) Open fees from Open events, (2) Close fees, rollover fees, and positive funding fees from Close events. All fees are in USDC.',
87+
},
88+
};
89+
90+
export default adapter;

0 commit comments

Comments
 (0)