pragma solidity ^0.8.26;
import "../../../shared/TestAccountExecution_Base.t.sol";
import {MockDelegateTargetEmpty} from "contracts/mocks/MockDelgateTargetEmpty.sol";
contract TestAccountExecution_TryExecuteSingle is TestAccountExecution_Base {
MockDelegateTarget delegateTarget;
MockDelegateTargetEmpty delegateTargetEmpty;
function setUp() public {
setUpTestAccountExecution_Base();
delegateTarget = new MockDelegateTarget();
delegateTargetEmpty = new MockDelegateTargetEmpty();
}
function test_ExecuteDelegateCall_Success() public {
(bool res, ) = payable(address(BOB_ACCOUNT)).call{ value: 2 ether}("");
assertEq(res, true, "Funding BOB_ACCOUNT should succeed");
assertEq(counter.getNumber(), 0, "Counter should start at 0");
bytes memory sendValue =
abi.encodeWithSelector("");
Execution[] memory execution = new Execution[](1);
execution[0] = Execution(address(counter), 0, abi.encodeWithSelector(Counter.incrementNumber.selector));
PackedUserOperation[] memory userOps = buildPackedUserOperation(BOB, BOB_ACCOUNT, EXECTYPE_DEFAULT, execution, address(VALIDATOR_MODULE));
bytes memory userOpCalldata = abi.encodeCall(
Nexus.execute,
(
ModeLib.encode(
CALLTYPE_DELEGATECALL, EXECTYPE_DEFAULT, MODE_DEFAULT, ModePayload.wrap(0x00)
),
abi.encodePacked(address(delegateTargetEmpty), sendValue)
)
);
userOps[0].callData = userOpCalldata;
bytes32 userOpHash = ENTRYPOINT.getUserOpHash(userOps[0]);
userOps[0].signature = signMessage(BOB, userOpHash);
ENTRYPOINT.handleOps(userOps, payable(address(BOB.addr)));
}
function test_TryExecuteDelegateCall_Success() public {
(bool res, ) = payable(address(BOB_ACCOUNT)).call{ value: 2 ether}("");
assertEq(res, true, "Funding BOB_ACCOUNT should succeed");
assertEq(counter.getNumber(), 0, "Counter should start at 0");
bytes memory sendValue =
abi.encodeWithSelector("");
Execution[] memory execution = new Execution[](1);
execution[0] = Execution(address(counter), 0, abi.encodeWithSelector(Counter.incrementNumber.selector));
PackedUserOperation[] memory userOps = buildPackedUserOperation(BOB, BOB_ACCOUNT, EXECTYPE_TRY, execution, address(VALIDATOR_MODULE));
bytes memory userOpCalldata = abi.encodeCall(
Nexus.execute,
(
ModeLib.encode(
CALLTYPE_DELEGATECALL, EXECTYPE_TRY, MODE_DEFAULT, ModePayload.wrap(0x00)
),
abi.encodePacked(address(delegateTargetEmpty), sendValue)
)
);
userOps[0].callData = userOpCalldata;
bytes32 userOpHash = ENTRYPOINT.getUserOpHash(userOps[0]);
userOps[0].signature = signMessage(BOB, userOpHash);
ENTRYPOINT.handleOps(userOps, payable(address(BOB.addr)));
}
}
Short term, implement a contract existence check before each delegatecall.
function _handleDelegateCallExecutionAndReturnData(bytes calldata executionCalldata, ExecType execType) internal returns(bytes[] memory returnData) {
(address delegate, bytes calldata callData) = executionCalldata.decodeDelegateCall();
returnData = new bytes[](1);
bool success;
+ bool res;
+ assembly {
+ res := gt(extcodesize(newImplementation), 0)
+ }
+ require(res, InvalidImplementationAddress());
if (execType == EXECTYPE_DEFAULT) {
returnData[0] = _executeDelegatecall(delegate, callData);
}
else if (execType == EXECTYPE_TRY) {
(success, returnData[0]) = _tryExecuteDelegatecall(delegate, callData);
if (!success) emit TryDelegateCallUnsuccessful(0, returnData[0]);
}
else revert UnsupportedExecType(execType);
}
function _handleDelegateCallExecution(bytes calldata executionCalldata, ExecType execType) internal {
(address delegate, bytes calldata callData) = executionCalldata.decodeDelegateCall();
+ bool res;
+ assembly {
+ res := gt(extcodesize(newImplementation), 0)
+ }
+ require(res, InvalidImplementationAddress());
if (execType == EXECTYPE_DEFAULT) _executeDelegatecall(delegate, callData);
else if (execType == EXECTYPE_TRY) _tryExecuteDelegatecall(delegate, callData);
else revert UnsupportedExecType(execType);
}