In the _distribute
function, the call to the proxy address can throw a false positive if the proxy is not deployed.
If the bytecode of the proxy address is empty the call to that address will not revert. In the case a contest is expired, the address of the proxy holds tokens and the proxy has never been deployed, the call to the proxy address will not revert, givin the executor the false impression that the distribution process has been completed.
The distributionByOwner
function is used to distribute tokens to the winner of a contest. The function calls the _distribute
function, which calls the proxy address. If the proxy address has never been deployed, the call will not revert. And the executor will assume it has been distributed.
Note: the snippet shows only the relevant code for the test. Full test file can be found here.
function test_PoC_L05() public {
bytes32 contestId = keccak256("a contest id");
uint256 closeTime = block.timestamp + 1 weeks;
bytes memory data = createData();
address empty = makeAddr("empty");
vm.startPrank(owner);
address implementation = address(new Distributor(address(factory), stadium));
factory.setContest(organizer, contestId, closeTime, implementation);
uint256 expiration = closeTime + 1 weeks;
vm.warp(expiration);
tokenA.mint(empty, 1000e18);
//Distribution does not revert.
factory.distributeByOwner(empty, organizer, contestId, implementation, data);
// Empty address has 1000e18 tokens.
assertEq(tokenA.balanceOf(empty), 1000e18);
}
Low. The issue can be fixed by using the deployProxyAndDistributeByOwner
function.
None.
Verify that the proxy address bytecode is not empty before calling it.
function _distribute(address proxy, bytes calldata data) internal {
+ if (proxy.code.length == 0) revert ProxyFactory__ProxyNotDeployed();
(bool success,) = proxy.call(data);
if (!success) revert ProxyFactory__DelegateCallFailed();
emit Distributed(proxy, data);
}