-
Notifications
You must be signed in to change notification settings - Fork 1
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
test: remaining protocol properties #26
Changes from 15 commits
e998667
179fba2
6b67194
96ecaad
3adeba5
5e7eb87
403dc70
dea79ce
7bba3f0
cd2b64b
231c6b5
e97fda0
b081760
8a597a9
e26b9c3
c681a7c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.25; | ||
|
||
import { StdUtils } from "forge-std/StdUtils.sol"; | ||
|
||
/// @notice helper for tracking actors, taking advantage of the fuzzer already using several `msg.sender`s | ||
contract Actors is StdUtils { | ||
mapping(address => bool) private _isActor; | ||
address[] private _actors; | ||
address private _currentActor; | ||
|
||
/// @notice register an actor if it's not already registered | ||
/// usually called with msg.sender as a parameter, to track the actors | ||
/// already provided by the fuzzer | ||
modifier withActor(address who) { | ||
addActor(who); | ||
_currentActor = who; | ||
_; | ||
} | ||
|
||
function addActor(address who) internal { | ||
if (!_isActor[who]) { | ||
_isActor[who] = true; | ||
_actors.push(who); | ||
} | ||
} | ||
|
||
/// @notice get the currently configured actor, should equal msg.sender | ||
function currentActor() internal view returns (address) { | ||
return _currentActor; | ||
} | ||
|
||
/// @notice get one of the actors by index, useful to get another random | ||
/// actor than the one set as currentActor, to perform operations between them | ||
function getActorByRawIndex(uint256 rawIndex) internal view returns (address) { | ||
return _actors[bound(rawIndex, 0, _actors.length - 1)]; | ||
} | ||
Comment on lines
+28
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a NIT and really not important since it is a helper. But we could keep sticking to the |
||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,10 @@ | ||||||
// SPDX-License-Identifier: AGPL-3 | ||||||
pragma solidity ^0.8.25; | ||||||
|
||||||
import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; | ||||||
|
||||||
contract OptimismSuperchainERC20ForToBProperties is OptimismSuperchainERC20 { | ||||||
/// @notice This is used by CryticERC20ExternalBasicProperties to know | ||||||
// which properties to test | ||||||
bool public constant isMintableOrBurnable = true; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can't do this 😭 it's called by that name by I'll add a comment clarifying the reason |
||||||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.25; | ||
|
||
import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; | ||
import { ProtocolHandler } from "../handlers/Protocol.t.sol"; | ||
|
||
contract ProtocolGuided is ProtocolHandler { | ||
/// @notice deploy a new supertoken with deploy salt determined by params, to the given (of course mocked) chainId | ||
/// @custom:property-id 14 | ||
/// @custom:property supertoken total supply starts at zero | ||
function fuzz_deployNewSupertoken( | ||
TokenDeployParams memory params, | ||
uint256 chainId | ||
) | ||
external | ||
validateTokenDeployParams(params) | ||
{ | ||
chainId = bound(chainId, 0, MAX_CHAINS - 1); | ||
OptimismSuperchainERC20 supertoken = _deploySupertoken( | ||
remoteTokens[params.remoteTokenIndex], | ||
WORDS[params.nameIndex], | ||
WORDS[params.symbolIndex], | ||
DECIMALS[params.decimalsIndex], | ||
chainId | ||
); | ||
// 14 | ||
assert(supertoken.totalSupply() == 0); | ||
} | ||
|
||
/// @custom:property-id 6 | ||
/// @custom:property calls to sendERC20 succeed as long as caller has enough balance | ||
/// @custom:property-id 22 | ||
/// @custom:property sendERC20 decreases sender balance in source chain and increases receiver balance in | ||
/// destination chain exactly by the input amount | ||
/// @custom:property-id 23 | ||
/// @custom:property sendERC20 decreases total supply in source chain and increases it in destination chain exactly | ||
/// by the input amount | ||
function fuzz_bridgeSupertoken( | ||
uint256 fromIndex, | ||
uint256 recipientIndex, | ||
uint256 destinationChainId, | ||
uint256 amount | ||
) | ||
public | ||
withActor(msg.sender) | ||
{ | ||
destinationChainId = bound(destinationChainId, 0, MAX_CHAINS - 1); | ||
fromIndex = bound(fromIndex, 0, allSuperTokens.length - 1); | ||
address recipient = getActorByRawIndex(recipientIndex); | ||
OptimismSuperchainERC20 sourceToken = OptimismSuperchainERC20(allSuperTokens[fromIndex]); | ||
OptimismSuperchainERC20 destinationToken = | ||
MESSENGER.crossChainMessageReceiver(address(sourceToken), destinationChainId); | ||
// TODO: when implementing non-atomic bridging, allow for the token to | ||
// not yet be deployed and funds be recovered afterwards. | ||
require(address(destinationToken) != address(0)); | ||
uint256 sourceBalanceBefore = sourceToken.balanceOf(currentActor()); | ||
uint256 sourceSupplyBefore = sourceToken.totalSupply(); | ||
uint256 destinationBalanceBefore = destinationToken.balanceOf(recipient); | ||
uint256 destinationSupplyBefore = destinationToken.totalSupply(); | ||
|
||
vm.prank(currentActor()); | ||
try sourceToken.sendERC20(recipient, amount, destinationChainId) { | ||
uint256 sourceBalanceAfter = sourceToken.balanceOf(currentActor()); | ||
uint256 destinationBalanceAfter = destinationToken.balanceOf(recipient); | ||
// no free mint | ||
assert(sourceBalanceBefore + destinationBalanceBefore == sourceBalanceAfter + destinationBalanceAfter); | ||
// 22 | ||
assert(sourceBalanceBefore - amount == sourceBalanceAfter); | ||
assert(destinationBalanceBefore + amount == destinationBalanceAfter); | ||
uint256 sourceSupplyAfter = sourceToken.totalSupply(); | ||
uint256 destinationSupplyAfter = destinationToken.totalSupply(); | ||
// 23 | ||
assert(sourceSupplyBefore - amount == sourceSupplyAfter); | ||
assert(destinationSupplyBefore + amount == destinationSupplyAfter); | ||
} catch { | ||
// 6 | ||
assert(address(destinationToken) == address(sourceToken) || sourceBalanceBefore < amount); | ||
} | ||
} | ||
|
||
/// @custom:property-id 8 | ||
/// @custom:property calls to sendERC20 with a value of zero dont modify accounting | ||
// @notice is a subset of fuzz_bridgeSupertoken, so we'll just call it | ||
// instead of re-implementing it. Keeping the function for visibility of the property. | ||
function fuzz_sendZeroDoesNotModifyAccounting( | ||
uint256 fromIndex, | ||
uint256 recipientIndex, | ||
uint256 destinationChainId | ||
) | ||
external | ||
{ | ||
fuzz_bridgeSupertoken(fromIndex, recipientIndex, destinationChainId, 0); | ||
} | ||
|
||
/// @custom:property-id 9 | ||
/// @custom:property calls to relayERC20 with a value of zero dont modify accounting | ||
/// @custom:property-id 7 | ||
/// @custom:property calls to relayERC20 always succeed as long as the cross-domain caller is valid | ||
function fuzz_relayZeroDoesNotModifyAccounting( | ||
uint256 fromIndex, | ||
uint256 recipientIndex | ||
) | ||
external | ||
withActor(msg.sender) | ||
{ | ||
fromIndex = bound(fromIndex, 0, allSuperTokens.length - 1); | ||
address recipient = getActorByRawIndex(recipientIndex); | ||
OptimismSuperchainERC20 token = OptimismSuperchainERC20(allSuperTokens[fromIndex]); | ||
uint256 balanceSenderBefore = token.balanceOf(currentActor()); | ||
uint256 balanceRecipientBefore = token.balanceOf(recipient); | ||
uint256 supplyBefore = token.totalSupply(); | ||
|
||
MESSENGER.setCrossDomainMessageSender(address(token)); | ||
vm.prank(address(MESSENGER)); | ||
try token.relayERC20(currentActor(), recipient, 0) { | ||
MESSENGER.setCrossDomainMessageSender(address(0)); | ||
} catch { | ||
// should not revert because of 7, and if it *does* revert, I want the test suite | ||
// to discard the sequence instead of potentially getting another | ||
// error due to the crossDomainMessageSender being manually set | ||
assert(false); | ||
} | ||
uint256 balanceSenderAfter = token.balanceOf(currentActor()); | ||
uint256 balanceRecipeintAfter = token.balanceOf(recipient); | ||
uint256 supplyAfter = token.totalSupply(); | ||
assert(balanceSenderBefore == balanceSenderAfter); | ||
assert(balanceRecipientBefore == balanceRecipeintAfter); | ||
assert(supplyBefore == supplyAfter); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I'd make this public jic you need to loop for the whole array