11import { FetchOptions , SimpleAdapter } from "../../adapters/types" ;
22import { CHAIN } from "../../helpers/chains" ;
33
4- // shMonad liquid staking contract on Monad
54const SHMONAD_CONTRACT = "0x1B68626dCa36c7fE922fD2d55E4f631d962dE19c" ;
65
7- // contract functions
8- const getAtomicCapitalAbi = "function getAtomicCapital() view returns (uint256 allocated, uint256 distributed)" ;
9- const getCurrentAssetsAbi = "function getCurrentAssets() view returns (uint256)" ;
10- const getWorkingCapitalAbi = "function getWorkingCapital() view returns (uint256 staked, uint256 reserved)" ;
11- const totalSupplyAbi = "function totalSupply() view returns (uint256)" ;
12-
13- /**
14- * @notice Retrieves the rolling global liabilities tracked by StakeTracker.
15- * @return rewardsPayable The MON amount reserved for rewards payouts.
16- * @return redemptionsPayable The MON amount pending redemption settlement.
17- * @return totalZeroYieldPayable The MON amount earmarked for zero-yield obligations (e.g., commission accruals).
18- * https://github.com/FastLane-Labs/fastlane-contracts/blob/485e9305b251089b94f62ef22b7ab9a74e0d32c3/src/shmonad/Storage.sol#L157
19- */
20- const globalLiabilitiesAbi = "function globalLiabilities() view returns (uint128 rewardsPayable, uint128 redemptionsPayable, uint128 totalZeroYieldPayable)" ;
21-
22- /**
23- * Calculate Equity (Net Asset Value) using the formula described in the documentation:
24- * Equity = (Total Assets) - (Total Liabilities)
25- * Total Assets = staked + reserved + allocated + currentAssets
26- * Total Liabilities = rewardsPayable + redemptionsPayable + totalZeroYieldPayable
27- */
28-
29- // https://docs.shmonad.xyz/exchange-rate
30- // Equity = What the Protocol Owns − What the Protocol Owes
31- // Equity = Total Staked MON + Available MON − (Pending Withdrawals + Validator Rewards Payable + Zero-Yield Deposits)
32-
33- function calculateEquity (
34- workingCapital : { staked : bigint ; reserved : bigint } ,
35- atomicCapital : { allocated : bigint ; distributed : bigint } ,
36- currentAssets : bigint ,
37- liabilities : { rewardsPayable : bigint ; redemptionsPayable : bigint ; totalZeroYieldPayable : bigint }
38- ) : bigint {
39- const totalAssets =
40- workingCapital . staked +
41- workingCapital . reserved +
42- atomicCapital . allocated +
43- currentAssets ;
44-
45- const totalLiabilities =
46- liabilities . rewardsPayable +
47- liabilities . redemptionsPayable +
48- liabilities . totalZeroYieldPayable ;
49-
50- return totalAssets - totalLiabilities ;
51- }
52-
53- const getFetch = ( ) => {
54- return async ( options : FetchOptions ) => {
55- const dailyFees = options . createBalances ( ) ;
56- const dailyProtocolRevenue = options . createBalances ( ) ;
57- const dailySupplySideRevenue = options . createBalances ( ) ;
58-
59- try {
60- // FETCH CONTRACT STATE AT START OF PERIOD
61- const [ workingBefore , atomicBefore , currentBefore , liabilitiesBefore , supplyBefore ] = await Promise . all ( [
62- options . fromApi . call ( { target : SHMONAD_CONTRACT , abi : getWorkingCapitalAbi } ) ,
63- options . fromApi . call ( { target : SHMONAD_CONTRACT , abi : getAtomicCapitalAbi } ) ,
64- options . fromApi . call ( { target : SHMONAD_CONTRACT , abi : getCurrentAssetsAbi } ) ,
65- options . fromApi . call ( { target : SHMONAD_CONTRACT , abi : globalLiabilitiesAbi } ) ,
66- options . fromApi . call ( { target : SHMONAD_CONTRACT , abi : totalSupplyAbi } ) ,
67- ] ) ;
68-
69- // FETCH CONTRACT STATE AT END OF PERIOD
70- const [ workingAfter , atomicAfter , currentAfter , liabilitiesAfter , supplyAfter ] = await Promise . all ( [
71- options . toApi . call ( { target : SHMONAD_CONTRACT , abi : getWorkingCapitalAbi } ) ,
72- options . toApi . call ( { target : SHMONAD_CONTRACT , abi : getAtomicCapitalAbi } ) ,
73- options . toApi . call ( { target : SHMONAD_CONTRACT , abi : getCurrentAssetsAbi } ) ,
74- options . toApi . call ( { target : SHMONAD_CONTRACT , abi : globalLiabilitiesAbi } ) ,
75- options . toApi . call ( { target : SHMONAD_CONTRACT , abi : totalSupplyAbi } ) ,
76- ] ) ;
77-
78- // CONVERT TO BIGINT
79- // Helper function to safely convert contract return objects to BigInt structure.
80- const toBigInt = ( obj : any ) => ( {
81- staked : BigInt ( obj ?. staked || 0 ) ,
82- reserved : BigInt ( obj ?. reserved || 0 ) ,
83- allocated : BigInt ( obj ?. allocated || 0 ) ,
84- distributed : BigInt ( obj ?. distributed || 0 ) ,
85- rewardsPayable : BigInt ( obj ?. rewardsPayable || 0 ) ,
86- redemptionsPayable : BigInt ( obj ?. redemptionsPayable || 0 ) ,
87- totalZeroYieldPayable : BigInt ( obj ?. totalZeroYieldPayable || 0 ) ,
88- } ) ;
89-
90- const startPeriodState = {
91- working : toBigInt ( workingBefore ) ,
92- atomic : toBigInt ( atomicBefore ) ,
93- current : BigInt ( currentBefore || 0 ) ,
94- liabilities : toBigInt ( liabilitiesBefore ) ,
95- supply : BigInt ( supplyBefore || 0 )
96- } ;
97-
98- const endPeriodState = {
99- working : toBigInt ( workingAfter ) ,
100- atomic : toBigInt ( atomicAfter ) ,
101- current : BigInt ( currentAfter || 0 ) ,
102- liabilities : toBigInt ( liabilitiesAfter ) ,
103- supply : BigInt ( supplyAfter || 0 )
104- } ;
105-
106- // CALCULATE EQUITY
107- const equityBefore = calculateEquity (
108- startPeriodState . working ,
109- startPeriodState . atomic ,
110- startPeriodState . current ,
111- startPeriodState . liabilities
112- ) ;
113- const equityAfter = calculateEquity (
114- endPeriodState . working ,
115- endPeriodState . atomic ,
116- endPeriodState . current ,
117- endPeriodState . liabilities
118- ) ;
119-
120- let totalRewards = 0n ;
121-
122- if ( startPeriodState . supply > 0n && endPeriodState . supply > 0n ) {
123- // Calculate exchange rates (scaled by 10^18 to maintain precision during division)
124- const exchangeRateBefore = equityBefore * 10n ** 18n / startPeriodState . supply ;
125- const exchangeRateAfter = equityAfter * 10n ** 18n / endPeriodState . supply ;
126-
127- if ( exchangeRateAfter > exchangeRateBefore ) {
128- // Unscale the result
129- totalRewards = ( ( exchangeRateAfter - exchangeRateBefore ) * startPeriodState . supply ) / 10n ** 18n ;
130- }
131- }
132-
133- if ( totalRewards > 0n ) {
134- dailyFees . addGasToken ( totalRewards ) ;
135-
136- // Protocol revenue = 5%
137- const protocolRevenue = ( totalRewards * 5n ) / 100n ;
138- dailyProtocolRevenue . addGasToken ( protocolRevenue ) ;
139-
140- // Supply-side revenue = 95%
141- const supplySideRevenue = totalRewards - protocolRevenue ;
142- dailySupplySideRevenue . addGasToken ( supplySideRevenue ) ;
143- }
6+ const ABIS = {
7+ totalAssets : "uint256:totalAssets" ,
8+ totalSupply : "uint256:totalSupply" ,
9+ } ;
14410
145- return {
146- dailyFees,
147- dailyRevenue : dailyFees ,
148- dailyProtocolRevenue,
149- dailySupplySideRevenue,
150- dailyHoldersRevenue : dailySupplySideRevenue ,
151- } ;
152- } catch ( error ) {
153- console . error ( "Error fetching shMonad fee data:" , error ) ;
154- return {
155- dailyFees,
156- dailyRevenue : dailyFees ,
157- dailyProtocolRevenue,
158- dailySupplySideRevenue,
159- } ;
160- }
11+ const PROTOCOL_FEE = 0.05 ; // 5% protocol revenue
12+
13+ const fetch = async ( options : FetchOptions ) => {
14+ const dailyFees = options . createBalances ( ) ;
15+ const dailyProtocolRevenue = options . createBalances ( ) ;
16+ const dailySupplySideRevenue = options . createBalances ( ) ;
17+
18+ const [ assetsBefore , supplyBefore ] = await Promise . all ( [
19+ options . fromApi . call ( { target : SHMONAD_CONTRACT , abi : ABIS . totalAssets } ) ,
20+ options . fromApi . call ( { target : SHMONAD_CONTRACT , abi : ABIS . totalSupply } ) ,
21+ ] ) ;
22+
23+ const [ assetsAfter , supplyAfter ] = await Promise . all ( [
24+ options . toApi . call ( { target : SHMONAD_CONTRACT , abi : ABIS . totalAssets } ) ,
25+ options . toApi . call ( { target : SHMONAD_CONTRACT , abi : ABIS . totalSupply } ) ,
26+ ] ) ;
27+
28+ // Compute exchange rate
29+ const rateBefore = ( assetsBefore * 1e18 ) / supplyBefore ;
30+ const rateAfter = ( assetsAfter * 1e18 ) / supplyAfter ;
31+ const netRewards = ( ( rateAfter - rateBefore ) * supplyBefore ) / 1e18 ;
32+
33+ const grossRewards = netRewards / ( 1 - PROTOCOL_FEE ) ;
34+
35+ dailyFees . addGasToken ( grossRewards ) ;
36+ dailyProtocolRevenue . addGasToken ( grossRewards * PROTOCOL_FEE ) ;
37+ dailySupplySideRevenue . addGasToken ( grossRewards * ( 1 - PROTOCOL_FEE ) ) ;
38+
39+ return {
40+ dailyFees,
41+ dailyRevenue : dailyProtocolRevenue ,
42+ dailyProtocolRevenue,
43+ dailySupplySideRevenue,
44+ dailyHoldersRevenue : dailySupplySideRevenue ,
16145 } ;
16246} ;
16347
16448const adapter : SimpleAdapter = {
16549 version : 2 ,
16650 adapter : {
16751 [ CHAIN . MONAD ] : {
168- fetch : getFetch ( ) ,
52+ fetch,
16953 start : "2024-11-01" ,
17054 } ,
17155 } ,
17256 methodology : {
173- Fees : "Total fees calculated from exchange rate appreciation (equity per shMON token). Includes staking rewards from Monad validators, MEV, and atomic unstake fees. Equity is calculated as Total Assets minus detailed Liabilities (rewards payable, pending redemptions, and zero-yield obligations)." ,
174- Revenue : "Protocol revenue = 5% of total equity growth." ,
175- UserFees : "Users pay atomic unstake fees for instant withdrawals." ,
176- ProtocolRevenue : "5% commission on staking and MEV revenue." ,
177- SupplySideRevenue : "95% of staking/MEV + 100% atomic fees accrue to shMON holders." ,
178- HoldersRevenue : "All supply-side revenue accrues to shMON holders via increasing MON value per shMON token." ,
57+ Fees : "Fees calculated from exchange rate appreciation (totalAssets / totalSupply), following ERC-4626 standard." ,
58+ Revenue : "5% of yield goes to protocol, deducted before distributing to shMON holders." ,
59+ ProtocolRevenue : "5% performance fee collected by the protocol." ,
60+ SupplySideRevenue : "95% of yield increases shMON value for holders." ,
61+ HoldersRevenue : "Yield accrues to holders through higher MON per shMON token." ,
17962 } ,
18063} ;
18164
182- export default adapter ;
65+ export default adapter ;
0 commit comments