You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When testing systems which involve multiple deployments of the same contract, it may be desirable to deploy additional copies during the tests. For invariant testing, if a contract is more complex it is often necessary to wrap it in a handler contract. So if we want to deploy another copy of the contract we must also deploy another handler, which may require access to cheat codes such as deal.
However, it appears that cheats are not available to contracts deployed after the setUp function. Some copies of the handler, those which are deployed during setUp, do have cheat code access, but others deployed later from the same code are prevented from behaving the same way. The expected behavior is that my handler contracts behave the same regardless of when they were deployed, unless deployment time has some bearing on the underlying contract's execution.
The only way around this issue that I could find was to pre-deploy additional copies of the handler in question in the setUp function, despite not needing them until their underlying contracts are deployed. However, doing so muddles up the setUp function, adds additional overhead to the process of adding a new deployment and limits the number of deployments I can have to however many I pre-deployed. The helper function I use for targeting a newly deployed handler goes from this:
function addStakedToken(
StakedToken newStakedToken
) external onlySafetyModuleHandler {
IERC20 underlying = newStakedToken.getUnderlyingToken();
StakedTokenHandler newStakedTokenHandler =newStakedTokenHandler(newStakedToken, stakers);
// Add staked token and its handler to lists
stakedTokens.push(newStakedToken);
stakedTokenHandlers.push(newStakedTokenHandler);
// Register new target and exclude base contractstargetContract(address(newStakedTokenHandler));
excludeContract(address(newStakedToken));
excludeContract(address(underlying));
}
to this:
function addStakedToken(
StakedToken newStakedToken
) external onlySafetyModuleHandler {
IERC20 underlying = newStakedToken.getUnderlyingToken();
// Use pre-deployed StakedTokenHandler// since deploying a new one doesn't give it access to cheats
StakedTokenHandler newStakedTokenHandler;
// Two StakedTokenHandlers are already deployed in setUp()if (numStakedTokenHandlers ==2)
newStakedTokenHandler = stakedTokenHandler3;
elseif (numStakedTokenHandlers ==3)
newStakedTokenHandler = stakedTokenHandler4;
elseif (numStakedTokenHandlers ==4)
newStakedTokenHandler = stakedTokenHandler5;
elseif (numStakedTokenHandlers ==5)
newStakedTokenHandler = stakedTokenHandler6;
elseif (numStakedTokenHandlers ==6)
newStakedTokenHandler = stakedTokenHandler7;
elseif (numStakedTokenHandlers ==7)
newStakedTokenHandler = stakedTokenHandler8;
elseif (numStakedTokenHandlers ==8)
newStakedTokenHandler = stakedTokenHandler9;
elserevert("too many staked token handlers");
numStakedTokenHandlers++;
newStakedTokenHandler.setStakedToken(newStakedToken);
// Add staked token and its handler to lists
stakedTokens.push(newStakedToken);
stakedTokenHandlers.push(newStakedTokenHandler);
// Register new target and exclude base contractstargetContract(address(newStakedTokenHandler));
excludeContract(address(newStakedToken));
excludeContract(address(underlying));
}
For additional context, here is the output for the failing test, where 0x1aF7f588A501EA2B5bB3feeFA744892aA2CF00e6 is the address of the new handler contract deployed during SafetyModuleHandler.addStakingToken on the fourth line from the bottom:
Note: looking at the traces from the failed test, I can't tell whether calls to vm.<cheatcode> work correctly, such as vm.startPrank. In the following trace snippet, which is the last call in the traces and the first call to the newly deployed handler, it appears that vm.startPrank, vm.stopPrank, vm.expectRevert and vm.assume executed as usual:
The only cheat code used in the handler contract which is not a call to vm is deal(address token, address to, uint256 give), which is only used in StakedTokenHandler.dealUnderlying. But I still get the same no cheats available for 0x1aF7f588A501EA2B5bB3feeFA744892aA2CF00e6 error after commenting that line out. And in another failed test with the same reason, and without deal commented out, I get the following trace for dealUnderlying at the end of the call sequence:
@webthethird I assume you are forking in these tests. In this mode, we disallow cheatcode usage from anything other than the caller and the test contract as a safety precaution. You can allow other addresses to use cheats with vm.allowCheatcodes(address). Does this help?
@onbjerg Yes I am forking, good catch. Your solution definitely seems promising! I'm working on something else at the moment, but I'll close this issue for now and re-open it if vm.allowCheatcodes doesn't work.
Thanks!
Component
Forge
Have you ensured that all of these are up to date?
What version of Foundry are you on?
forge 0.2.0 (1978a03 2023-12-21T09:45:30.854629898Z)
What command(s) is the bug in?
forge test
Operating System
Linux
Describe the bug
When testing systems which involve multiple deployments of the same contract, it may be desirable to deploy additional copies during the tests. For invariant testing, if a contract is more complex it is often necessary to wrap it in a handler contract. So if we want to deploy another copy of the contract we must also deploy another handler, which may require access to cheat codes such as
deal
.However, it appears that cheats are not available to contracts deployed after the
setUp
function. Some copies of the handler, those which are deployed duringsetUp
, do have cheat code access, but others deployed later from the same code are prevented from behaving the same way. The expected behavior is that my handler contracts behave the same regardless of when they were deployed, unless deployment time has some bearing on the underlying contract's execution.The only way around this issue that I could find was to pre-deploy additional copies of the handler in question in the
setUp
function, despite not needing them until their underlying contracts are deployed. However, doing so muddles up thesetUp
function, adds additional overhead to the process of adding a new deployment and limits the number of deployments I can have to however many I pre-deployed. The helper function I use for targeting a newly deployed handler goes from this:to this:
For additional context, here is the output for the failing test, where
0x1aF7f588A501EA2B5bB3feeFA744892aA2CF00e6
is the address of the new handler contract deployed duringSafetyModuleHandler.addStakingToken
on the fourth line from the bottom:Note: looking at the traces from the failed test, I can't tell whether calls to
vm.<cheatcode>
work correctly, such asvm.startPrank
. In the following trace snippet, which is the last call in the traces and the first call to the newly deployed handler, it appears thatvm.startPrank
,vm.stopPrank
,vm.expectRevert
andvm.assume
executed as usual:The only cheat code used in the handler contract which is not a call to
vm
isdeal(address token, address to, uint256 give)
, which is only used inStakedTokenHandler.dealUnderlying
. But I still get the sameno cheats available for 0x1aF7f588A501EA2B5bB3feeFA744892aA2CF00e6
error after commenting that line out. And in another failed test with the same reason, and withoutdeal
commented out, I get the following trace fordealUnderlying
at the end of the call sequence:With these traces, it is unclear which cheat code was unavailable.
The text was updated successfully, but these errors were encountered: