-
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.
Open
Changes from 20 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,25 @@ | ||
| { | ||
| "files": [ | ||
| "src/VaultV2.sol", | ||
| "lib/morpho-blue/certora/helpers/MorphoHarness.sol" | ||
| ], | ||
| "parametric_contracts": [ | ||
| "VaultV2" | ||
| ], | ||
| "verify": "VaultV2:certora/specs/ForceDeallocate.spec", | ||
| "loop_iter": "3", | ||
| "optimistic_loop": true, | ||
| "optimistic_hashing": true, | ||
| "compiler_map": { | ||
| "VaultV2": "solc-0.8.28", | ||
| "MorphoMarketV1AdapterV2": "solc-0.8.28", | ||
| "MorphoHarness": "solc-0.8.19", | ||
| "Utils": "solc-0.8.28" | ||
| }, | ||
| "prover_args": [ | ||
| "-depth 5", | ||
| "-mediumTimeout 20", | ||
| "-timeout 3600" | ||
| ], | ||
| "msg": "Vault V2 ForceDeallocate" | ||
| } |
|
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,115 @@ | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
| // Copyright (c) 2025 Morpho Association | ||
|
|
||
| import "../helpers/UtilityVault.spec"; | ||
|
|
||
| methods { | ||
| function virtualShares() external returns (uint256) envfree; | ||
| function lastUpdate() external returns (uint64) envfree; | ||
|
|
||
| // assume safeTransfer and safeTransferFrom do not revert. | ||
|
bhargavbh marked this conversation as resolved.
Outdated
|
||
| function SafeERC20Lib.safeTransferFrom(address, address, address, uint256) internal => NONDET; | ||
| function SafeERC20Lib.safeTransfer(address, address, uint256) internal => NONDET; | ||
|
QGarchery marked this conversation as resolved.
Outdated
|
||
|
|
||
| // `balanceOf` is summarized to a bounded value. | ||
|
QGarchery marked this conversation as resolved.
Outdated
|
||
| function _.balanceOf(address account) external => summaryBalanceOf() expect(uint256); | ||
|
|
||
| // `deallocate` is the core adapter callback. It is summarized with structural constraints from the MorphoMarketV1AdapterV2 implementation; | ||
|
bhargavbh marked this conversation as resolved.
Outdated
|
||
| function _.deallocate(bytes data, uint256 assets, bytes4 selector, address sender) external with(env e) => summaryDeallocate(e, data, assets, selector, sender) expect(bytes32[], int256); | ||
|
|
||
| // `realAssets` is summarized to a bounded value; see summaryRealAssets. | ||
|
bhargavbh marked this conversation as resolved.
Outdated
|
||
| function _.realAssets() external => summaryRealAssets() expect(uint256); | ||
|
|
||
| // 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); | ||
| function _.canReceiveShares(address account) external => ghostCanReceiveShares(calledContract, account) expect(bool); | ||
| } | ||
|
|
||
| ghost ghostCanSendShares(address, address) returns bool; | ||
|
|
||
| ghost ghostCanReceiveAssets(address, address) returns bool; | ||
|
|
||
| ghost ghostCanReceiveShares(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 2^128. | ||
| function summaryBalanceOf() returns uint256 { | ||
| uint256 balance; | ||
| require balance < 2 ^ 128, "totalAssets is bounded by 2 ^ 128; vault balance is less than totalAssets"; | ||
| return balance; | ||
| } | ||
|
|
||
| // Returns a value bounded by 2^128 | ||
| function summaryRealAssets() returns uint256 { | ||
| uint256 realAssets; | ||
| require realAssets < 2 ^ 128, "totalAssets is bounded by 2 ^ 128; realAssets from each adater is less than totalAssets"; | ||
| return realAssets; | ||
| } | ||
|
|
||
| // This summary models the post-conditions of the adapter's deallocate which are required for the canForceDallocateZero rule to hold. | ||
| function summaryDeallocate(env e, bytes data, uint256 assets, bytes4 selector, address sender) returns (bytes32[], int256) { | ||
| bytes32[] ids; | ||
| int256 change; | ||
|
|
||
| // the adapter returns exactly 3 market ids. | ||
| require ids.length == 3, "see IdsMorphoMarketV1Adapter"; | ||
|
bhargavbh marked this conversation as resolved.
Outdated
|
||
|
|
||
| // the 3 returned market 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"; | ||
|
|
||
| // each returned id must correspond to a market with a positive allocation in the vault. | ||
| require forall uint256 i. i < ids.length => currentContract.caps[ids[i]].allocation > 0, "assume that the allocation is positive"; | ||
|
|
||
| // allocation values are bounded. | ||
| require forall uint256 i. i < ids.length => currentContract.caps[ids[i]].allocation < 2 ^ 20 * 2 ^ 128, "market v1 fits total supply assets on 128 bits, and assume at most 2^20 markets"; | ||
|
|
||
| // the net change is bounded by 2^128 (Morpho V1 supply assets fit in 128 bits). | ||
| require change < 2 ^ 128, "market v1 fits total supply assets on 128 bits"; | ||
|
|
||
|
bhargavbh marked this conversation as resolved.
Outdated
|
||
| // after applying `change`, no allocation may go negative. | ||
| require forall uint256 i. i < ids.length => currentContract.caps[ids[i]].allocation + change >= 0, "see changeForAllocateOrDeallocateIsBoundedByAllocation"; | ||
|
|
||
| return (ids, change); | ||
| } | ||
|
|
||
| // forceDeallocate with assets=0 triggers the adapter to update the allocation tracking in caps. | ||
| // This rule verifies the liveness property that `forceDeallocate()` can be called with assets=0 with the following pre-conditions: | ||
| // 1. The adapter is registered in the vault. | ||
| // 2. No ETH is sent with the call. | ||
|
bhargavbh marked this conversation as resolved.
Outdated
|
||
| // 3. The `onBehalf` address passes the sendShares gate check. | ||
| // 4. The vault itself passes the receiveAssets gate check. | ||
| // 5. Total shares do not overflow uint256 when virtual shares are included. | ||
| // 6. `onBehalf` is not the zero address. | ||
| // 7. Interest has already been accrued at the vault level for this block (i.e. lastUpdate == block.timestamp). | ||
| rule canForceDeallocateZero(env e, address adapter, bytes data, address onBehalf) { | ||
|
|
||
| // the adapter must be registered in the vault. | ||
| require isAdapter(adapter); | ||
|
|
||
| // forceDeallocate is non-payable. | ||
| require e.msg.value == 0; | ||
|
|
||
| // gate checks that withdraw within forceDeallocate will not revert. | ||
| require canSendShares(onBehalf); | ||
| require canReceiveAssets(currentContract); | ||
|
|
||
| // prevent totalSupply + virtualShares from overflowing, which would cause an arithmetic revert. | ||
| require totalSupply() + virtualShares() <= max_uint256; | ||
|
|
||
| // vault's exit logic requires onBehalf to be non-zero address. | ||
| require(onBehalf != 0, "onBehalf can't be the zero address"); | ||
|
|
||
| // interest must have been accrued for this block so that the vault's internal lastUpdate matches the current timestamp. | ||
| // this is a fair assumption because accrueInterest() can be called permissionlessly. | ||
|
QGarchery marked this conversation as resolved.
Outdated
|
||
| require(currentContract.lastUpdate() == e.block.timestamp, "assume interest has been accrued at the Vault level"); | ||
|
|
||
| // 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.