Platform: Code4rena
Start Date: 10/03/2023
Pot Size: $180,500 USDC
Total HM: 6
Participants: 19
Period: 9 days
Judge: GalloDaSballo
Total Solo HM: 2
Id: 221
League: ETH
Rank: 3/19
Findings: 2
Award: $10,743.03
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: unforgiven
Also found by: Franfran, HE1M, bin2chen, rvierdiiev
1968.2509 USDC - $1,968.25
may be missing markAccountCodeHashAsConstructed(), this causes the code corresponding to the address to be invalid
forceDeployOnAddress() can be used to forcefully deploy a contract. The code is as follows:
function forceDeployOnAddress(ForceDeployment calldata _deployment, address _sender) external payable onlySelf { _ensureBytecodeIsKnown(_deployment.bytecodeHash); _storeConstructingByteCodeHashOnAddress(_deployment.newAddress, _deployment.bytecodeHash); //<-----set codehash to Constructing AccountInfo memory newAccountInfo; newAccountInfo.supportedAAVersion = AccountAbstractionVersion.None; // Accounts have sequential nonces by default. newAccountInfo.nonceOrdering = AccountNonceOrdering.Sequential; _storeAccountInfo(_deployment.newAddress, newAccountInfo); if (_deployment.callConstructor) { _constructContract(_sender, _deployment.newAddress, _deployment.input, false); //<----set codehash to Constructed in _constructContract() } //<-------but if don't need callConstructor, codehash will still Constructing emit ContractDeployed(_sender, _deployment.bytecodeHash, _deployment.newAddress); }
This method first sets codeHash
to Constructing
If callConstructor
is required, _constructContract ()
will be called, and codehash
will be set to Constructed
in the _constructContract()
But do nothing if don't need callConstructor
, so codehash
will still Constructing
This will cause the code corresponding to the address to remain invalid.
function forceDeployOnAddress(ForceDeployment calldata _deployment, address _sender) external payable onlySelf { _ensureBytecodeIsKnown(_deployment.bytecodeHash); _storeConstructingByteCodeHashOnAddress(_deployment.newAddress, _deployment.bytecodeHash); AccountInfo memory newAccountInfo; newAccountInfo.supportedAAVersion = AccountAbstractionVersion.None; // Accounts have sequential nonces by default. newAccountInfo.nonceOrdering = AccountNonceOrdering.Sequential; _storeAccountInfo(_deployment.newAddress, newAccountInfo); if (_deployment.callConstructor) { _constructContract(_sender, _deployment.newAddress, _deployment.input, false); - } + }else{ + ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.markAccountCodeHashAsConstructed(_newAddress); + } emit ContractDeployed(_sender, _deployment.bytecodeHash, _deployment.newAddress); }
#0 - c4-judge
2023-03-24T09:20:49Z
GalloDaSballo marked the issue as duplicate of #167
#1 - c4-judge
2023-04-05T12:02:11Z
GalloDaSballo marked the issue as satisfactory
8774.7813 USDC - $8,774.78
fallback lack payable
,will lead to differences from the mainnet, and many existing protocols may not work
DefaultAccount Defined as follows:
DefaultAccount The implementation of the default account abstraction. This is the code that is used by default for all addresses that are not in kernel space and have no contract deployed on them. This address: Contains the minimal implementation of our account abstraction protocol. Note that it supports the built-in paymaster flows. When anyone (except bootloader) calls/delegate calls it, it behaves in the same way as a call to an EOA, i.e. it always returns success = 1, returndatasize = 0 for calls from anyone except for the bootloader.
If there is no code for the address, the DefaultAccount #fallback method will be executed, which is compatible with the behavior of the mainnet
But At present, fallback
is not payable
The code is as follows
contract DefaultAccount is IAccount { using TransactionHelper for *; .. fallback() external { //<--------without payable // fallback of default account shouldn't be called by bootloader under no circumstances assert(msg.sender != BOOTLOADER_FORMAL_ADDRESS); // If the contract is called directly, behave like an EOA } receive() external payable { // If the contract is called directly, behave like an EOA }
which will lead to differences from the mainnet.
For example, the mainnet execution of the method with value will return true, and the corresponding value will be transfer but DefaultAccount.sol will return false
It is quite common for call ()
to with value
. If it is not compatible, many existing protocols may not work
Mainnet code example, executing 0x0 call with value can be successful:
function test() external { vm.deal(address(this),1000); console.log("before value:",address(0x0).balance); (bool result,bytes memory datas) = address(0x0).call{value:10}("abc"); console.log("call result:",result); console.log("after value:",address(0x0).balance); }
$ forge test -vvv [PASS] test() (gas: 42361) Logs: before value: 0 call result: true after value: 10
Simulate DefaultAccount #fallback without payable, it will fail:
contract DefaultAccount { fallback() external { } receive() external payable { } } function test() external { DefaultAccount defaultAccount = new DefaultAccount(); vm.deal(address(this),1000); console.log("before value:",address(defaultAccount).balance); (bool result,bytes memory datas) = address(defaultAccount).call{value:10}("abc"); console.log("call result:",result); console.log("after value:",address(defaultAccount).balance); }
$ forge test -vvv [PASS] test() (gas: 62533) Logs: before value: 0 call result: false after value: 0
- function test() external { + function test() external payable { vm.deal(address(this),1000); console.log("before value:",address(0x0).balance); (bool result,bytes memory datas) = address(0x0).call{value:10}("abc"); console.log("call result:",result); console.log("after value:",address(0x0).balance); }
#0 - GalloDaSballo
2023-03-23T13:29:38Z
Simpler POC, imo clearer
#1 - c4-judge
2023-03-23T13:29:42Z
GalloDaSballo marked the issue as primary issue
#2 - c4-sponsor
2023-03-25T21:28:47Z
vladbochok marked the issue as sponsor confirmed
#3 - GalloDaSballo
2023-04-04T09:31:46Z
The Warden has shown how, due to the lack of the payable
modifer, contracts that sent value as well as a message would revert when triggering the fallback
While this could have been very severe for Smart Contracts, because the contract in question is for Account Abstraction, and that could break only certain transfers, I agree with Medium Severity
#4 - c4-judge
2023-04-04T09:31:51Z
GalloDaSballo marked the issue as selected for report