Repository navigation
feat: price swETH from its own exchange rate - #198
Merged
Merged
Conversation
Redstone abandoned the swETH adapter this router prices through — its last update was 2025-05-30 — so every cellar holding swETH reverts in totalAssets() with RedstoneEthPriceFeedExtension__STALE_PRICE. Turbo swETH is frozen as a result. Chainlink has no swETH feed, and the only Uniswap v3 pool holds about 0.5 swETH, far too thin for a TWAP. swETH does expose its own ETH exchange rate through the IRateProvider getRate() interface, and Swell is still actively repricing it. SwEthExtension is a line-for-line derivation of the audited weEthExtension with weETH replaced by swETH. After normalising the renames (contract name, error names, token identifier and address) the executable code is identical to the original, verified mechanically by diff. It is deliberately asset-bound rather than a generic rate-provider extension: a generic version takes an arbitrary address and selector, which removes exactly the compile-time guarantee that makes the audited original safe, and only one asset needs it. Tests mirror the existing eETH extension suite — pricing against getRate(), the wrong-asset guard, and the missing-WETH dependency — plus one integration test against the live PriceRouter. That test performs the same owner-only, timelocked edit the Safe will execute and asserts Turbo swETH values again (~2.26 WETH). The 7-day timelock is simulated by setting the edit's due-time rather than warping, since warping would stale the live WETH/USD feed. Mutation-tested: a 10x price error fails both pricing tests via the router's own BadAnswer guard, and removing the asset guard fails the wrong-asset test. Tests pin block 26007554 and so need an archive-capable MAINNET_RPC_URL, as the rest of this suite does. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Hi, I am trying to withdraw my funds from Turbo sweth vault but I get an error whenever I call redeem function. If that proposal is signed and deployed will I be able to redeem? |
Contributor
Author
|
yes the admin multisig needs to approve the transactions and then you'll be able to redeem |
|
I can see only 2 signed, do you think there's any chance we can get the other 2? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Redstone abandoned the swETH adapter this PriceRouter prices through; its last update was 2025-05-30. Every cellar holding swETH now reverts in
totalAssets()withRedstoneEthPriceFeedExtension__STALE_PRICE()(0x2cfb7afb), so Turbo swETH (0xd33dAd974b938744dAC81fE00ac67cb5AA13958E) is frozen.Part of the wider remediation for the withdrawal outage. rsETH, ezETH and weETH have the same Redstone problem, but each has a live Chainlink feed, so they are being repointed with existing code. swETH has no Chainlink feed, and all three swETH/WETH Uniswap v3 pools are too thin for a TWAP: the 0.01% pool that
ChangingSwethPricing.t.sol(Macro audit 9) priced swETH against in 2023 now holds 0.003 swETH, the 0.3% pool is empty, and the deepest, 0.05%, holds 0.62 swETH. It needs this extension.What
SwEthExtensionprices swETH from its own ETH exchange rate. swETH exposes the IRateProvidergetRate()interface, and Swell is still actively repricing it.It is a line-for-line derivation of the audited
weEthExtension, with weETH replaced by swETH. After normalising the renames (contract name, error names, token name and address), the executable code is identical to the original. That was checked mechanically by diffing the two files, not by reading them.It is deliberately fixed to swETH rather than a generic rate-provider extension. A generic version takes an arbitrary address and function selector, which gives up the compile-time guarantee that makes the audited original safe, and only one asset needs it.
Tests
Mirrors the existing
eETHExtension.t.solsuite:testAddSwEthExtension— prices swETH againstgetRate()testUsingExtensionWithWrongAsset— the asset guardtestAddingSwEthWithoutPricingWeth— the WETH dependencyPlus one integration test the originals don't have:
testRepointingSwEthUnfreezesTurboSwEth— runs against the live PriceRouter, through the same owner-only, 7-day-timelockedstartEditAsset/completeEditAssetedit the Safe will execute, and asserts Turbo swETH values again (~2.26 WETH). The timelock is simulated by setting the edit's due time rather than warping, because warping would make the live WETH/USD feed look stale and fail for the wrong reason.Written test-first: the suite failed because the contract did not exist, then passed. Mutation-tested, meaning the code was broken on purpose to confirm the tests notice. A 10x price error fails both pricing tests through the router's own
BadAnswerguard, and removing the asset guard fails the wrong-asset test.Verification notes
main.setUp()error —Could not instantiate forked environment with fork url— meaning CI's archive RPC (theALCHEMY_API_KEYsecret) can't create forks. Every CI run in this repo has failed this way since at least 2026-08-20, including all runs on feat: keeper-driven share price oracle for networks without Chainlink Automation #197. No test ran an assertion, so CI provides no signal on this change either way. The key needs rotating.26007554and so also need an archive-capableMAINNET_RPC_URL, like the rest of the suite.Deployment
Deployed at
0xad84a81157fe6804fc8e13658452a60793d123fbvia the canonical CREATE2 deployer at salt 0, from this branch's head852c3a0(tx0x4014fbe9f3d70296d74f4db8efa7f8b1e89b4ff45caedcd4a947eebb354caff6). The address commits to the exact initcode, so anyone can reproduce it from source.priceRouter()returns the live router andgetPriceInUSD(swETH)returns $2,960.93.The contract alone changes no pricing. To use it, the PriceRouter owner (the 4-of-7 Safe
0x7340D1FeCD4B64A4ac34f826B21c945d44d7407F) would deploy the extension and run the timelockedstartEditAsset(swETH, (3, <extension>), ""), thencompleteEditAssetseven days later.cancelEditAssetcan withdraw a pending edit.Generated with Claude Code