@@ -47,8 +47,8 @@ interface ArmConfig {
4747 symbol1 : string
4848 token0 : string
4949 token1 : string
50- // ERC4626 vault token whose rate needs converting (e.g. sUSDe)
51- // When set, rates are adjusted by the vault's convertToAssets rate
50+ // ERC4626 vault token (e.g. sUSDe). When set, traderates are raw spread rates
51+ // and the vault's convertToAssets rate must be applied for output estimation.
5252 vaultToken ?: string
5353}
5454
@@ -95,7 +95,7 @@ registerArm({
9595// Cache traderates per ARM address
9696const traderateCache = new Map < string , { traderate0 : bigint ; traderate1 : bigint } > ( )
9797
98- // Cache vault exchange rate (e.g. sUSDe → USDe)
98+ // Cache vault exchange rate (e.g. sUSDe → USDe via convertToAssets )
9999const vaultRateCache = new Map < string , bigint > ( )
100100const ONE = 10n ** 18n
101101
@@ -116,8 +116,8 @@ async function getVaultRate(ctx: RendererCtx, block: RendererBlock, vaultToken:
116116}
117117
118118async function getTraderates (
119- ctx : Parameters < Parameters < typeof registerEventRenderer > [ 1 ] > [ 0 ] [ 'ctx' ] ,
120- block : Parameters < Parameters < typeof registerEventRenderer > [ 1 ] > [ 0 ] [ 'log' ] [ 'block' ] ,
119+ ctx : RendererCtx ,
120+ block : RendererBlock ,
121121 armAddress : string ,
122122) : Promise < { traderate0 : bigint ; traderate1 : bigint } | undefined > {
123123 const cached = traderateCache . get ( armAddress )
@@ -133,7 +133,7 @@ async function getTraderates(
133133 }
134134}
135135
136- // TraderateChanged renderer
136+ // TraderateChanged renderer — shows raw ARM spread rates
137137registerEventRenderer ( TraderateChanged . topic , async ( params ) => {
138138 const arm = armConfigs . get ( params . log . address . toLowerCase ( ) )
139139 if ( ! arm ) return defaultEventRenderer ( params )
@@ -146,18 +146,9 @@ registerEventRenderer(TraderateChanged.topic, async (params) => {
146146 traderate1 : data . traderate1 ,
147147 } )
148148
149- // For vault tokens (e.g. sUSDe), adjust rates to show USDe-equivalent value
150- let sellRate = 10n ** 72n / data . traderate0 // 1e36 precision
151- let buyRate = data . traderate1 // 1e36 precision
152- let rateLabel = `${ arm . symbol1 } /${ arm . symbol0 } `
153-
154- if ( arm . vaultToken ) {
155- const vaultRate = await getVaultRate ( params . ctx , params . log . block , arm . vaultToken )
156- // Multiply by vault rate to convert sUSDe amounts to USDe-equivalent
157- sellRate = ( sellRate * vaultRate ) / ONE
158- buyRate = ( buyRate * vaultRate ) / ONE
159- rateLabel = `${ arm . symbol0 } /${ arm . symbol0 } ` // e.g. USDe/USDe
160- }
149+ const sellRate = 10n ** 72n / data . traderate0 // 1e36 precision
150+ const buyRate = data . traderate1 // 1e36 precision
151+ const rateLabel = `${ arm . symbol1 } /${ arm . symbol0 } `
161152
162153 renderEventDiscordEmbed ( params , {
163154 fields : [
@@ -210,33 +201,37 @@ registerEventRenderer(erc20Abi.events.Transfer.topic, async (params) => {
210201 ]
211202
212203 if ( rates ) {
213- // traderate0 is inverted: sell price = 1e72 / traderate0 (in 1e36 precision)
214- // traderate1 is direct: buy price = traderate1 (in 1e36 precision)
204+ // Traderates are raw spread rates (~1.0). For vault tokens (e.g. sUSDe),
205+ // the ARM contract applies the vault conversion internally during swaps,
206+ // so we must apply it here too for output estimation.
207+ //
208+ // traderate0: for 1 token0 from trader, how many token1 the pool sends (raw)
209+ // traderate1: for 1 token1 from trader, how many token0 the pool sends (raw)
215210 let outAmount : bigint
216- let rate : number
211+
217212 if ( isToken0In ) {
218- // Selling token0 → receive token1 at sell price
219- const sellRate = 10n ** 72n / rates . traderate0
220- outAmount = ( data . value * sellRate ) / 10n ** 36n
221- rate = Number ( sellRate ) / 1e36
213+ // token0 → token1: outAmount = inAmount * traderate0 / 1e36
214+ // For vault: actual token1 out = raw output / vaultRate (fewer vault tokens)
215+ outAmount = ( data . value * rates . traderate0 ) / 10n ** 36n
216+ if ( arm . vaultToken ) {
217+ const vaultRate = await getVaultRate ( params . ctx , params . log . block , arm . vaultToken )
218+ outAmount = ( outAmount * ONE ) / vaultRate
219+ }
222220 } else {
223- // Selling token1 → receive token0 at buy price
224- outAmount = rates . traderate1 > 0n ? ( data . value * 10n ** 36n ) / rates . traderate1 : 0n
225- rate = Number ( rates . traderate1 ) / 1e36
226- }
227-
228- // For vault tokens, convert output to underlying-equivalent for rate display
229- if ( arm . vaultToken ) {
230- const vaultRate = await getVaultRate ( params . ctx , params . log . block , arm . vaultToken )
231- if ( isToken0In ) {
232- // Output is in vault token (sUSDe), convert to USDe-equivalent for rate
233- rate = ( rate * Number ( vaultRate ) ) / 1e18
234- } else {
235- // Input is vault token (sUSDe), adjust rate
236- rate = ( rate * 1e18 ) / Number ( vaultRate )
221+ // token1 → token0: outAmount = inAmount * traderate1 / 1e36
222+ // For vault: actual token0 out = inAmount * vaultRate * traderate1 / 1e36 / 1e18
223+ outAmount = ( data . value * rates . traderate1 ) / 10n ** 36n
224+ if ( arm . vaultToken ) {
225+ const vaultRate = await getVaultRate ( params . ctx , params . log . block , arm . vaultToken )
226+ outAmount = ( outAmount * vaultRate ) / ONE
237227 }
238228 }
239229
230+ // Rate = token0/token1, derived from actual in/out amounts
231+ const rate = isToken0In
232+ ? Number ( data . value ) / Number ( outAmount )
233+ : Number ( outAmount ) / Number ( data . value )
234+
240235 fields . push ( {
241236 name : formatAmount ( outAmount , 18 , { maximumFractionDigits : 6 } ) ,
242237 value : `${ outSymbol } out` ,
0 commit comments