Skip to content
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: invariant basic properties #47

Merged
merged 28 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3c1a9b5
test: add basic invariant properties
0xRaccoon Oct 23, 2024
a5d360c
fix: add mising import
0xRaccoon Oct 23, 2024
716ef73
fix: add missing admin set
0xRaccoon Oct 23, 2024
387a085
test: add invariant test properties
0xRaccoon Oct 23, 2024
47df96a
test: update PROPERTIES.md
0xRaccoon Oct 23, 2024
261d610
test: remove test properties constraints
0xRaccoon Oct 24, 2024
4805dc8
fix: updated tests and partially tackled pr comments
0xRaccoon Oct 24, 2024
08ef593
fix: linter
0xRaccoon Oct 24, 2024
7963817
fix: linter
0xRaccoon Oct 24, 2024
5b8ec7d
fix: linter
0xRaccoon Oct 24, 2024
03a3f5f
fix: addressed pr comments
0xRaccoon Oct 25, 2024
973814b
fix: addressed pr comments
0xRaccoon Oct 25, 2024
cbff194
fix: rollback foundry fmt exclude removal
0xRaccoon Oct 25, 2024
9b994ea
fix: remove yarn.lock
0xRaccoon Oct 25, 2024
59f07c4
fix: properties
0xRaccoon Oct 25, 2024
345e961
fix: properties test
0xRaccoon Oct 25, 2024
4f02578
fix: properties test
0xRaccoon Oct 25, 2024
38cf7c3
fix: addressed pr comments
0xRaccoon Oct 29, 2024
7224964
Merge branch 'test/invariants' of github-defi:allo-protocol/allo-v2.1…
0xRaccoon Oct 29, 2024
c8b5d6e
fix: tests
0xRaccoon Oct 29, 2024
5ea6141
test: add admin assertions
0xRaccoon Oct 29, 2024
9c5ac9a
fix: assertion text
0xRaccoon Oct 29, 2024
857580c
fix: invariant properties
0xRaccoon Oct 29, 2024
3b6a0a7
fix: invariant properties assertions
0xRaccoon Oct 29, 2024
d50ac94
fix: invariant tests
0xRaccoon Oct 29, 2024
74a3805
fix: invariant properties test
0xRaccoon Oct 29, 2024
517a9a3
fix: invariant properties
0xRaccoon Oct 29, 2024
eb5636b
test: fix invariant test assertion
0xRaccoon Oct 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 5 additions & 24 deletions test/invariant/fuzz/Setup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,13 @@ contract Setup is Actors {
registry = new Registry();

// Deploy the proxy, pointing to the implementation
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
implementation,
proxyOwner,
""
);
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(implementation, proxyOwner, "");

allo = Allo(payable(address(proxy)));

// Initialize
vm.prank(protocolDeployer);
allo.initialize(
protocolDeployer,
address(registry),
payable(treasury),
percentFee,
baseFee,
forwarder
);
allo.initialize(protocolDeployer, address(registry), payable(treasury), percentFee, baseFee, forwarder);

// Deploy base strategy
strategy_directAllocation = new DirectAllocationStrategy(address(allo));
Expand All @@ -65,20 +54,12 @@ contract Setup is Actors {
token = ERC20(address(new FuzzERC20()));

// Create profile for 4 addresses
for (uint i; i < 4; i++) {
for (uint256 i; i < 4; i++) {
bytes32 _id = registry.createProfile(
0,
"a",
Metadata({protocol: i + 1, pointer: ""}),
_ghost_actors[i],
new address[](0)
0, "a", Metadata({protocol: i + 1, pointer: ""}), _ghost_actors[i], new address[](0)
);

_addAnchorToActor(
_ghost_actors[i],
registry.getProfileById(_id).anchor,
_id
);
_addAnchorToActor(_ghost_actors[i], registry.getProfileById(_id).anchor, _id);
}
}
}
169 changes: 37 additions & 132 deletions test/invariant/fuzz/handlers/HandlerAllo.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ contract HandlerAllo is Setup {

function handler_createPool(uint256 _msgValue) public {
// Get the profile ID
IRegistry.Profile memory profile = registry.getProfileByAnchor(
_ghost_anchorOf[msg.sender]
);
IRegistry.Profile memory profile = registry.getProfileByAnchor(_ghost_anchorOf[msg.sender]);

// Avoid EOA
if (profile.anchor == address(0)) return;
Expand Down Expand Up @@ -46,11 +44,7 @@ contract HandlerAllo is Setup {
}
}

function handler_updatePoolMetadata(
uint256 _idSeed,
uint256 _metadataProtocol,
string calldata _data
) public {
function handler_updatePoolMetadata(uint256 _idSeed, uint256 _metadataProtocol, string calldata _data) public {
// Needs at least one pool
if (ghost_poolIds.length == 0) return;

Expand All @@ -59,91 +53,56 @@ contract HandlerAllo is Setup {
uint256 poolId = ghost_poolIds[_idSeed];

// Get the profile ID
IRegistry.Profile memory profile = registry.getProfileByAnchor(
_ghost_anchorOf[msg.sender]
);
IRegistry.Profile memory profile = registry.getProfileByAnchor(_ghost_anchorOf[msg.sender]);

// Avoid EOA
if (profile.anchor == address(0)) return;

Metadata memory metadata = Metadata({
protocol: _metadataProtocol,
pointer: _data
});
Metadata memory metadata = Metadata({protocol: _metadataProtocol, pointer: _data});

// Update the pool metadata - will revert on wrong anchor
targetCall(
address(allo),
0,
abi.encodeCall(allo.updatePoolMetadata, (poolId, metadata))
);
targetCall(address(allo), 0, abi.encodeCall(allo.updatePoolMetadata, (poolId, metadata)));
}

function handler_updatePercentFee(uint256 _newPercentFee) public {
_newPercentFee = bound(_newPercentFee, 0, 1e18);

// Update the percent fee - will revert if caller is not the owner
targetCall(
address(allo),
0,
abi.encodeCall(allo.updatePercentFee, (_newPercentFee))
);
targetCall(address(allo), 0, abi.encodeCall(allo.updatePercentFee, (_newPercentFee)));
}

function handler_updateBaseFee(uint256 _newBaseFee) public {
// Update the base fee - will revert if caller is not the owner
targetCall(
address(allo),
0,
abi.encodeCall(allo.updateBaseFee, (_newBaseFee))
);
targetCall(address(allo), 0, abi.encodeCall(allo.updateBaseFee, (_newBaseFee)));
}

function handler_updateRegistry(address _newRegistry) public {
// Update the registry - will revert if caller is not the owner or if the new registry is zero
targetCall(
address(allo),
0,
abi.encodeCall(allo.updateRegistry, (_newRegistry))
);
targetCall(address(allo), 0, abi.encodeCall(allo.updateRegistry, (_newRegistry)));
}

function handler_updateTreasury(address _newTreasury) public {
// Update the treasury - will revert if caller is not the owner or if the new treasury is zero
targetCall(
address(allo),
0,
abi.encodeCall(allo.updateTreasury, (payable(_newTreasury)))
);
targetCall(address(allo), 0, abi.encodeCall(allo.updateTreasury, (payable(_newTreasury))));
}

function handler_updateTrustedForwarder(address _newForwarder) public {
// Update the trusted forwarder - will revert if caller is not the owner or if the new forwarder is zero
targetCall(
address(allo),
0,
abi.encodeCall(Allo.updateTrustedForwarder, (_newForwarder))
);
targetCall(address(allo), 0, abi.encodeCall(Allo.updateTrustedForwarder, (_newForwarder)));
}

function handler_addPoolManagers(
uint256 _idSeed,
uint256 _numberOfManagers
) public {
function handler_addPoolManagers(uint256 _idSeed, uint256 _numberOfManagers) public {
uint256 _poolId = _pickPoolId(_idSeed);
_numberOfManagers = bound(_numberOfManagers, 0, _ghost_actors.length);

// Gather the managers
address[] memory _managers = new address[](_numberOfManagers);
for (uint256 i; i < _numberOfManagers; i++)
for (uint256 i; i < _numberOfManagers; i++) {
_managers[i] = _ghost_actors[i];
}

// Add pool managers - will revert if caller is not the pool admin of the pool id
(bool _succ, ) = targetCall(
address(allo),
0,
abi.encodeCall(allo.addPoolManagers, (_poolId, _managers))
);
(bool _succ,) = targetCall(address(allo), 0, abi.encodeCall(allo.addPoolManagers, (_poolId, _managers)));

if (_succ) {
for (uint256 _i; _i < _managers.length; ++_i) {
Expand All @@ -157,11 +116,7 @@ contract HandlerAllo is Setup {
address[] memory _managers = ghost_poolManagers[_poolId];

// Remove pool managers - will revert if caller is not a pool admin of the pool id
(bool _succ, ) = targetCall(
address(allo),
0,
abi.encodeCall(allo.removePoolManagers, (_poolId, _managers))
);
(bool _succ,) = targetCall(address(allo), 0, abi.encodeCall(allo.removePoolManagers, (_poolId, _managers)));

if (_succ) {
delete ghost_poolManagers[_poolId];
Expand All @@ -170,11 +125,7 @@ contract HandlerAllo is Setup {

function handler_recoverFunds(address _recipient) public {
// Recover funds - will revert if caller is not the owner
targetCall(
address(allo),
0,
abi.encodeCall(allo.recoverFunds, (address(token), _recipient))
);
targetCall(address(allo), 0, abi.encodeCall(allo.recoverFunds, (address(token), _recipient)));
}

function handler_registerRecipient(
Expand All @@ -184,40 +135,28 @@ contract HandlerAllo is Setup {
uint256 _msgValue
) public {
uint256 _poolId = _pickPoolId(_idSeed);
_numberOfRecipients = bound(
_numberOfRecipients,
0,
_ghost_actors.length
);
_numberOfRecipients = bound(_numberOfRecipients, 0, _ghost_actors.length);

// Gather the recipients
address[] memory _recipientAddresses = new address[](
_numberOfRecipients
);
for (uint256 i; i < _numberOfRecipients; i++)
address[] memory _recipientAddresses = new address[](_numberOfRecipients);
for (uint256 i; i < _numberOfRecipients; i++) {
_recipientAddresses[i] = _ghost_actors[i];
}

// Register recipient
(bool succ, ) = targetCall(
address(allo),
_msgValue,
abi.encodeCall(
allo.registerRecipient,
(_poolId, _recipientAddresses, _data)
)
(bool succ,) = targetCall(
address(allo), _msgValue, abi.encodeCall(allo.registerRecipient, (_poolId, _recipientAddresses, _data))
);

// todo: double-check there is no way a recipient is registered twice
if (succ)
for (uint256 i; i < _recipientAddresses.length; i++)
if (succ) {
for (uint256 i; i < _recipientAddresses.length; i++) {
ghost_recipients[_poolId].push(_recipientAddresses[i]);
}
}
}

function handler_fundPool(
uint256 _idSeed,
uint256 _amount,
uint256 _msgValue
) public {
function handler_fundPool(uint256 _idSeed, uint256 _amount, uint256 _msgValue) public {
uint256 _poolId = _pickPoolId(_idSeed);
uint256 _previousBalance = token.balanceOf(address(msg.sender));

Expand All @@ -230,20 +169,13 @@ contract HandlerAllo is Setup {
}

// Fund pool - will revert if the amount is zero or if pool token is native and message value is != amount
targetCall(
address(allo),
_msgValue,
abi.encodeCall(allo.fundPool, (_poolId, _amount))
);
targetCall(address(allo), _msgValue, abi.encodeCall(allo.fundPool, (_poolId, _amount)));
}

// _seedAmounts at 50 as it is not likely we'll handle 50 actors at the same time (update if so)
function handler_allocate(
uint256 _idSeed,
uint256[50] memory _seedAmounts,
bytes memory _data,
uint256 _msgValue
) public {
function handler_allocate(uint256 _idSeed, uint256[50] memory _seedAmounts, bytes memory _data, uint256 _msgValue)
public
{
uint256 _poolId = _pickPoolId(_idSeed);

address[] memory _recipients = ghost_recipients[_poolId];
Expand All @@ -254,52 +186,27 @@ contract HandlerAllo is Setup {
_amounts[i] = _seedAmounts[i];

if (_amounts[i] > 0) {
FuzzERC20(address(token)).mint(
address(msg.sender),
_amounts[i]
);
FuzzERC20(address(token)).mint(address(msg.sender), _amounts[i]);
}
}

// Allocate - allocate to a recipient or multiple recipients
targetCall(
address(allo),
_msgValue,
abi.encodeCall(
allo.allocate,
(_poolId, _recipients, _amounts, _data)
)
);
targetCall(address(allo), _msgValue, abi.encodeCall(allo.allocate, (_poolId, _recipients, _amounts, _data)));
}

function handler_distribute(
uint256 _idSeed,
address[] memory _recipientIds,
bytes memory _data
) public {
function handler_distribute(uint256 _idSeed, address[] memory _recipientIds, bytes memory _data) public {
uint256 _poolId = _pickPoolId(_idSeed);

// Distribute - distribute to a recipient or multiple recipients
targetCall(
address(allo),
0,
abi.encodeCall(
allo.distribute,
(_poolId, ghost_recipients[_idSeed], _data)
)
);
targetCall(address(allo), 0, abi.encodeCall(allo.distribute, (_poolId, ghost_recipients[_idSeed], _data)));
}

function handler_changeAdmin(uint256 _seed, uint256 _seedAdmin) public {
uint256 _poolId = _pickPoolId(_seed);
address _newAdmin = _ghost_actors[_seedAdmin % _ghost_actors.length];

// Change admin - will revert if caller is not the pool admin
(bool success,) = targetCall(
address(allo),
0,
abi.encodeCall(allo.changeAdmin, (_poolId, _newAdmin))
);
(bool success,) = targetCall(address(allo), 0, abi.encodeCall(allo.changeAdmin, (_poolId, _newAdmin)));
if (success) {
ghost_poolAdmins[_poolId] = _newAdmin;
}
Expand All @@ -315,9 +222,7 @@ contract HandlerAllo is Setup {
return ghost_poolIds[_idSeed % ghost_poolIds.length];
}

function _pickPoolId(
uint256[] memory _seeds
) internal view returns (uint256[] memory) {
function _pickPoolId(uint256[] memory _seeds) internal view returns (uint256[] memory) {
uint256[] memory _poolIds = new uint256[](_seeds.length);

for (uint256 _i; _i < _seeds.length; ++_i) {
Expand Down
7 changes: 6 additions & 1 deletion test/invariant/fuzz/handlers/HandlerRegistry.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,16 @@ contract HandlerRegistry is Setup {
function handler_updateProfileName(string memory _newName) public {
// Get the profile ID
IRegistry.Profile memory profile = registry.getProfileByAnchor(_ghost_anchorOf[msg.sender]);
address _owner = registry.getProfileById(profile.id).owner;

// will not succeed if no profile
(bool succ, bytes memory ret) = targetCall(
address(registry), 0, abi.encodeWithSelector(registry.updateProfileName.selector, profile.id, _newName)
);

if (succ) {
_ghost_anchorOf[_owner] = abi.decode(ret, (address));
}
}

function handler_updateProfileMetadata(uint256 _newProtocol, string memory _newPtr) public {
Expand Down Expand Up @@ -85,13 +90,13 @@ contract HandlerRegistry is Setup {

// Get the profile ID
IRegistry.Profile memory profile = registry.getProfileById(_profileId);
address _previousActor = registry.getProfileById(profile.id).owner;

(bool succ, bytes memory ret) = targetCall(
address(registry), 0, abi.encodeWithSelector(registry.acceptProfileOwnership.selector, profile.id)
);

if (succ) {
address _previousActor = _ghost_profileIdToActor[profile.id];
_removeAnchorFromActor(_previousActor, profile.id);
_addAnchorToActor(msg.sender, profile.anchor, profile.id);
delete _ghost_pendingOwnershipChange[_profileSeed % _ghost_pendingOwnershipChange.length];
Expand Down
9 changes: 2 additions & 7 deletions test/invariant/fuzz/handlers/HandlerStrategy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,8 @@ contract HandlerStrategy is HandlerAllo {
IAllo.Pool memory _pool = allo.getPool(ghost_poolIds[_poolSeed]);

// Withdraw
(bool succ, ) = targetCall(
address(allo),
0,
abi.encodeCall(
strategy_directAllocation.withdraw,
(_pool.token, _amount, _recipient)
)
(bool succ,) = targetCall(
address(allo), 0, abi.encodeCall(strategy_directAllocation.withdraw, (_pool.token, _amount, _recipient))
);
}
}
Loading
Loading