feat: Read router fees from on-chain FeeCalculator#250
Conversation
| /// This is the calldata convention between Fynd and the router (`clientFeeBps`), independent | ||
| /// of the FeeCalculator's internal precision. The contract scales `clientFeeBps` into its own | ||
| /// fee units by `max_fee_units / LEGACY_BPS_DENOMINATOR`. | ||
| pub const LEGACY_BPS_DENOMINATOR: u64 = 10_000; |
There was a problem hiding this comment.
note that when we resolve this mismatch between the router and the fee calculator (and do a new router deployment) Fynd won't be able to automatically update, the clients will need to update their Fynd version
Fynd now reads router fees from the FeeCalculator contract at startup and refreshes them every 5 minutes via a background fetcher, replacing the previously hardcoded fee values. Done: - Resolve the FeeCalculator address from the Tycho Router, then read the default router fees, per-client fee overrides, and the fee-unit precision scale (MAX_FEE_BPS). - Apply fees per order by client address, falling back to the default fee when the client has no override. Uses the client fee receiver to identify the client if set, if not it uses the order sender. - Encode using only fetched on-chain values; return an error instead of encoding when fees are not yet loaded. - Add RouterFees/FeeRates types and a SharedRouterFees handle shared between the encoder and the fetcher. Took 1 hour 34 minutes Took 19 seconds Took 11 seconds
56c57d9 to
6e06eda
Compare
No API Breaking Changes DetectedThe PR title signals breaking changes, but |
Troshchk
left a comment
There was a problem hiding this comment.
Thank you @dianacarvalho1! 💫
I left a couple of questions for my understanding
louise-poole
left a comment
There was a problem hiding this comment.
Just some small comments - otherwise looks good! 👍
| println!( | ||
| "mainnet router fees: max_fee_units={}, default_on_output={}, \ | ||
| default_on_client_fee={}, custom_clients={}", | ||
| fees.max_fee_units(), | ||
| default_rates.on_output(), | ||
| default_rates.on_client_fee(), | ||
| fees.custom_client_count(), | ||
| ); |
There was a problem hiding this comment.
We don't usually have a println in our tests... couldt hese not just be asserts?
There was a problem hiding this comment.
I didn't want to have asserts because the contract fees might actually change for real and then we have a broken test 😕
- Track the last successful fetch time on RouterFees - Gate Solver::wait_until_ready and GET /v1/health on router fees having been loaded at least once. - Return 503 from /v1/health when the last successful fetch is over an hour old, so a liveness probe restarts the service. Took 41 minutes
tamaralipows
left a comment
There was a problem hiding this comment.
Thank you! I've double checked the logic and it all looks perfect. Glad this has also been added to the health check
| for (client, fees) in page.clients.into_iter().zip(page.fees) { | ||
| // Resolve each field against the defaults here, mirroring | ||
| // FeeCalculator._getFeeInfo, so the stored pair is the effective rate. | ||
| let on_output = if fees.hasCustomFeeOnOutput { | ||
| fees.feeBpsOnOutput | ||
| } else { | ||
| default_fee_on_output | ||
| }; | ||
| let on_client_fee = if fees.hasCustomFeeOnClientFee { | ||
| fees.feeBpsOnClientFee | ||
| } else { | ||
| default_fee_on_client_fee | ||
| }; | ||
| custom_fees | ||
| .insert(Bytes::from(client.as_slice().to_vec()), (on_output, on_client_fee)); | ||
| } |
Took 2 minutes
Add RouterFees::fallback() (0.1 bps on output) and initialise SharedRouterFees with it, so encoding never fails when on-chain fees haven't been fetched yet. Drop the router fee readiness and staleness checks from /v1/health, plus the now-dead ROUTER_FEE_STALE_THRESHOLD and fetch-timestamp tracking. Took 12 minutes
Took 20 seconds
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub. |
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
Fynd now reads router fees from the FeeCalculator contract at startup and refreshes them every 5 minutes via a background fetcher, replacing the previously hardcoded fee values.
Done: