-
Notifications
You must be signed in to change notification settings - Fork 52
[Certora] canForceDeallocateZero #894
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
bhargavbh
wants to merge
54
commits into
main
Choose a base branch
from
certora/forceDeallocate
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+171
−7
Open
Changes from 45 commits
Commits
Show all changes
54 commits
Select commit
Hold shift + click to select a range
793778d
new rule: canForceDeallocate
bhargavbh a714c93
tuned
bhargavbh fe30416
added assumtpions on penaltyAssets
bhargavbh cce2d4c
can forceDeallocate on zero
bhargavbh e3dc39b
superset of preconditions
bhargavbh bbd341f
superset of preconditions
bhargavbh 33bd54b
reduced assumptions
bhargavbh c8a22d7
reduced assumptions
bhargavbh aa79259
summarised accrueInterestView
bhargavbh ad57c49
realAsset summary approach
bhargavbh cd6242b
created a new spec for forceDeallocate; summaries of deallocate were …
bhargavbh 1988971
tuned
bhargavbh 047169e
reset RemoveMarketLiveness
bhargavbh f860168
cleaned up ForceDeallocate spec
bhargavbh cd93c55
restored realAssetSummary
bhargavbh 5234c3f
cleaned up unnecessary assumptions and formatted
bhargavbh 18eab79
tuned
bhargavbh 16b3517
tuned
bhargavbh 8b54670
improved comments
bhargavbh e6120fc
updated workflow
bhargavbh 9ef2587
transfer and transferFrom are now dispatched
bhargavbh 88abc10
cleanup
bhargavbh 3314589
cleanup
bhargavbh 8010514
added comments and formatted
bhargavbh ba37122
added reference to accrueInterestView revert conditions
bhargavbh dfa29e3
tuned
bhargavbh 445f8ba
tightened totalSupply
bhargavbh db23457
matched the post conditions from reverts spec
bhargavbh 99ca1f1
tuned
bhargavbh 30e5f1c
cleaned up config
bhargavbh 8bd2ae8
need to justify the mismatch in post conditions of newTotalAssets fro…
bhargavbh 6a0c689
updated comments
bhargavbh 19513af
rearranged assumptions
bhargavbh 6395c2c
reomved realAsset summary, was unused
bhargavbh 0369340
added asset link
bhargavbh 9912302
tuned
bhargavbh 5596660
replaced with requireInvariant for the feeRecipient non-zero assumption
bhargavbh 88c4bfd
summarise multicall
bhargavbh 11e9f60
updated comments
bhargavbh deb799f
categorised assumptions in canForceDallocateZero
bhargavbh 91d4067
formatted
bhargavbh ef6e053
tuned
bhargavbh 492b37f
tuned
bhargavbh ac0dbc4
added reference to AccrueInterestReverts.spec
bhargavbh 7779bdb
tuned comment
bhargavbh 98940a5
removed MorphoHarness from scope
bhargavbh d963556
jochen and quentin's comments
bhargavbh 743f494
Merge remote-tracking branch 'origin/main' into certora/forceDeallocate
bhargavbh 6676cd4
minor changes to AccrueInterestReverts.spec; spill over from 897
bhargavbh 6d3f6c1
used invariants on fee!=0 => feeRecipient!=0
bhargavbh 336a964
tuned: minor
bhargavbh 79a0fdb
accrueInterestReview: summarised multicall
bhargavbh 0f96469
used requiredInvariants instead of linking with comments
bhargavbh 1c6335f
Merge remote-tracking branch 'origin/main' into certora/forceDeallocate
QGarchery File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| { | ||
| "files": [ | ||
| "src/VaultV2.sol", | ||
| "certora/helpers/ERC20Helper.sol", | ||
| "lib/morpho-blue/certora/helpers/MorphoHarness.sol", | ||
| "lib/metamorpho/certora/dispatch/ERC20Standard.sol" | ||
| ], | ||
| "link": [ | ||
| "VaultV2:asset=ERC20Standard" | ||
| ], | ||
| "verify": "VaultV2:certora/specs/ForceDeallocate.spec", | ||
| "loop_iter": "3", | ||
| "optimistic_loop": true, | ||
| "optimistic_hashing": true, | ||
| "compiler_map": { | ||
| "VaultV2": "solc-0.8.28", | ||
| "MorphoHarness": "solc-0.8.19", | ||
| "ERC20Standard": "solc-0.8.28", | ||
| "ERC20Helper": "solc-0.8.28" | ||
| }, | ||
| "prover_args": [ | ||
| "-depth 5", | ||
| "-mediumTimeout 20", | ||
| "-timeout 3600" | ||
| ], | ||
| "msg": "Vault V2 ForceDeallocate" | ||
| } | ||
|
QGarchery marked this conversation as resolved.
bhargavbh marked this conversation as resolved.
|
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
| // Copyright (c) 2025 Morpho Association | ||
|
|
||
| import "../helpers/UtilityVault.spec"; | ||
|
|
||
| methods { | ||
| function multicall(bytes[]) external => HAVOC_ALL DELETE; | ||
|
|
||
| function virtualShares() external returns (uint256) envfree; | ||
| function performanceFeeRecipient() external returns (address) envfree; | ||
| function managementFeeRecipient() external returns (address) envfree; | ||
|
|
||
| // `balanceOf` is assumed to not revert and summarized to a bounded value. | ||
| function _.balanceOf(address account) external => summaryBalanceOf() expect(uint256) ALL; | ||
|
|
||
| // Adapter's `deallocate` is assumed to not revert when called and returns 3 distinct ids, with post-conditions on the returned ids and change as specified in summaryDeallocate. | ||
| function _.deallocate(bytes data, uint256 assets, bytes4 selector, address sender) external => summaryDeallocate(data, assets, selector, sender) expect(bytes32[], int256); | ||
|
|
||
| // `accrueInterest` is assumed to not revert; Check the rule accrueInterestRevertConditions in Reverts.spec. | ||
| function accrueInterestView() internal returns (uint256, uint256, uint256) => summaryAccrueInterestView(); | ||
|
|
||
| // Trick to be able to retrieve the value returned by the corresponding contract before it is called, without the value changing between the retrieval and the call. | ||
| function _.canSendShares(address account) external => ghostCanSendShares(calledContract, account) expect(bool); | ||
| function _.canReceiveAssets(address account) external => ghostCanReceiveAssets(calledContract, account) expect(bool); | ||
| } | ||
|
|
||
| ghost ghostCanSendShares(address, address) returns bool; | ||
|
|
||
| ghost ghostCanReceiveAssets(address, address) returns bool; | ||
|
|
||
| // Maximum signed 256-bit integer, used to bound int256 return values. | ||
| definition max_int256() returns int256 = (2 ^ 255) - 1; | ||
|
|
||
| // Returns a value bounded by 10 ^ 35. | ||
| function summaryBalanceOf() returns uint256 { | ||
| uint256 balance; | ||
| require balance < 10 ^ 35, "totalAssets is assumed to be bounded by 10 ^ 35; vault balance is less than totalAssets"; | ||
| return balance; | ||
| } | ||
|
|
||
| // newTotalAssets returned by accrueInterestView is not proven to be < 10 ^ 35. We add it as an an explicit assumption required. | ||
| // In accrueInterestViewRevertConditions in AccrueInterestReverts.spec, we only show that the newTotalAssets is 2 ^ 128, given _totalAssets < 10 ^ 35. | ||
| // The bounds on performanceFeeShares and managementFeeShares are proven in the rule accrueInterestViewRevertConditions in Reverts.spec. | ||
| function summaryAccrueInterestView() returns (uint256, uint256, uint256) { | ||
| uint256 newTotalAssets; | ||
| uint256 performanceFeeShares; | ||
| uint256 managementFeeShares; | ||
| require newTotalAssets < 10 ^ 35, "totalAssets is bounded 10 ^ 35"; | ||
|
bhargavbh marked this conversation as resolved.
Outdated
|
||
| require performanceFeeShares < 2 ^ 236, "see accrueInterestViewRevertConditions in Reverts.spec"; | ||
| require managementFeeShares < 2 ^ 236, "see accrueInterestViewRevertConditions in Reverts.spec"; | ||
| require(performanceFee() != 0 || performanceFeeShares == 0), "see accrueInterestViewRevertConditions in AccrueInterestReverts.spec"; | ||
| require(managementFee() != 0 || managementFeeShares == 0), "see accrueInterestViewRevertConditions in AccrueInterestReverts.spec"; | ||
| return (newTotalAssets, performanceFeeShares, managementFeeShares); | ||
| } | ||
|
|
||
| // Post-conditions on the adapter's deallocate required for the canForceDeallocateZero rule. | ||
| function summaryDeallocate(bytes data, uint256 assets, bytes4 selector, address sender) returns (bytes32[], int256) { | ||
| bytes32[] ids; | ||
| int256 change; | ||
|
|
||
| // for simplicity, we assume the adapter returns exactly 3 ids. | ||
| require ids.length == 3, "simplified adapter to return 3 ids"; | ||
|
|
||
| // the 3 returned ids must be pairwise distinct. | ||
| require ids[0] != ids[1], "ids must be unique"; | ||
| require ids[0] != ids[2], "ids must be unique"; | ||
| require ids[1] != ids[2], "ids must be unique"; | ||
|
|
||
| // Post-conditions on the returned ids and change that ensures forceDeallocate with Zero does not revert: | ||
|
bhargavbh marked this conversation as resolved.
|
||
| require forall uint256 i. i < ids.length => currentContract.caps[ids[i]].allocation > 0; | ||
| require forall uint256 i. i < ids.length => currentContract.caps[ids[i]].allocation <= max_int256(); | ||
|
QGarchery marked this conversation as resolved.
Outdated
|
||
| require forall uint256 i. i < ids.length => currentContract.caps[ids[i]].allocation + change >= 0; | ||
| require forall uint256 i. i < ids.length => currentContract.caps[ids[i]].allocation + change <= max_int256(); | ||
|
|
||
| return (ids, change); | ||
| } | ||
|
|
||
| hook Sload uint256 balance balanceOf[KEY address addr] { | ||
| require balance < 10 ^ 35, "balance is less than totalAssets and totalAssets is assume to bounded by 10 ^ 35"; | ||
|
bhargavbh marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| strong invariant performanceFeeRecipientSetWhenPerformanceFeeIsSet() | ||
|
lilCertora marked this conversation as resolved.
|
||
| performanceFee() != 0 => performanceFeeRecipient() != 0; | ||
|
|
||
| strong invariant managementFeeRecipientSetWhenManagementFeeIsSet() | ||
| managementFee() != 0 => managementFeeRecipient() != 0; | ||
|
|
||
| // forceDeallocate with assets=0 triggers the adapter to update the allocation tracking in caps. | ||
| // We assume the asset token is ERC20Standard. | ||
| // This rule verifies the liveness property that `forceDeallocate()` can be called with assets=0 with the following pre-conditions: | ||
| // 1. The `onBehalf` address passes the sendShares gate check. | ||
| // 2. The vault itself passes the receiveAssets gate check. | ||
| // 3. totalSupply is bounded by 10 ^ 35. | ||
| // 4. Assumptions on the adapter's deallocate as specified in summaryDeallocate. | ||
| // 5. `accrueInterestView()` does not revert. See the accrueInterestViewRevertConditions for its revert conditions in AccrueInterestReverts.spec. | ||
| rule canForceDeallocateZero(env e, address adapter, bytes data, address onBehalf) { | ||
| require totalSupply() < 10 ^ 35, "assume totalSupply is bounded by 10 ^ 35"; | ||
|
|
||
| // ensure that withdraw within forceDeallocate will not revert due to gates. | ||
| require canSendShares(onBehalf), "onBehalf must pass canSendShares check"; | ||
| require canReceiveAssets(currentContract), "vault must pass canReceiveAssets check"; | ||
|
bhargavbh marked this conversation as resolved.
|
||
|
|
||
| // call set up | ||
| require e.msg.value == 0, "forceDeallocate is non-payable"; | ||
| require isAdapter(adapter), "the adapter must be registered in the vault"; | ||
| require onBehalf != 0, "exit requires onBehalf to be non-zero address"; | ||
|
|
||
| // proven invariants | ||
| requireInvariant performanceFeeRecipientSetWhenPerformanceFeeIsSet(); | ||
| requireInvariant managementFeeRecipientSetWhenManagementFeeIsSet(); | ||
| require virtualShares() <= 10 ^ 18, "See virtualSharesBounds in Invariants.spec"; | ||
|
|
||
| // call forceDeallocate with zero requested assets. | ||
| forceDeallocate@withrevert(e, adapter, data, 0, onBehalf); | ||
|
|
||
| assert !lastReverted; | ||
| } | ||
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.