Beginner FriendlyDeFiFoundry
100 EXP
View results
Submission Details
Severity: high
Invalid

`Deploy` contract owns `MerkleAirdrop`, preventing protocol team from claiming fees

Description:
Upon deployment, the Deploy contract inadvertently becomes the owner of the MerkleAirdrop contract as it inherits from Ownable. This ownership change prevents the protocol team from calling the claimFees function to claim fees, as the claimFees function is restricted to the owner of the contract.

Impact:
This issue significantly impacts the protocol team's ability to manage and operate the MerkleAirdrop contract effectively. Without the ability to claim fees, the ether gathered in fee remains stuck inside the contract forever.

Proof of Code:

  1. Make the following changes to MerkleAirdrop.t.sol-

Changes
// ...
+ import { Deploy } from "../script/Deploy.s.sol";
+ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
contract MerkleAirdropTest is Test {
// ...
+ MerkleAirdrop public airdropDeployed;
+ Deploy deployer;
// ...
function setUp() public {
token = new AirdropToken();
+ deployer = new Deploy();
+ airdropDeployed = deployer.deployMerkleDropper(merkleRoot, token);
// ...
}
  1. Add the following test and run forge test --zksync --mt testDeployerIsOwnerOfAirdrop and forge test --zksync --mt testOwnerCannotClaimFees

POC
function testDeployerIsOwnerOfAirdrop() public {
console.log("Address of Owner", airdropDeployed.owner());
console.log("Address of Deployer", address(deployer));
assertEq(address(deployer), airdropWithWrongRoot.owner());
}
function testOwnerCannotClaimFees() public {
vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, address(this)));
airdropWithWrongRoot.claimFees();
}
Console Output
Logs:
Address of Owner 0x2e234DAe75C793f67A35089C9d99245E1C58470b
Address of Deployer 0x2e234DAe75C793f67A35089C9d99245E1C58470b

Recommended Mitigation:
To resolve this issue, the ownership should be transferred from the deployer to the account that runs the Deploy script using the transferOwnership method. This mitigation ensures that the protocol team can regain control over the MerkleAirdrop contract and access the claimFees function.

Updated function
contract Deploy is Script {
// ...
function run() public {
vm.startBroadcast();
MerkleAirdrop airdrop = deployMerkleDropper(s_merkleRoot, IERC20(s_zkSyncUSDC));
IERC20(0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4).transfer(address(airdrop), s_amountToAirdrop);
// transfer the ownership to the EOA running the script
+ airdrop.transferOwnership(msg.sender);
vm.stopBroadcast();
}
// ...
}

Tools Used: Manual review and Foundry for POC

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.