Summary
When contracts are deployed, airdrop and staking vaults are first created, then the loveToken contract is created with the addresses of the vaults, and finally the vaults are initialized with the addresses of the airdrop and staking contracts. In this initialization, the loveToken contract mints tokens for the vaults, with full approval for the airdrop and staking contracts.
A bad actor could frontrun this initialization, and pass addresses of malicious airdrop and staking contracts, with the ability to steal all the vaults tokens.
Vulnerability Details
The sequence of deploy is as follows:
function testClaimRewardsWithoutSoulmate() public {
airdropVault = new Vault();
stakingVault = new Vault();
soulmateContract = new Soulmate();
loveToken = new LoveToken(
ISoulmate(address(soulmateContract)),
address(airdropVault),
address(stakingVault)
);
stakingContract = new Staking(
ILoveToken(address(loveToken)),
ISoulmate(address(soulmateContract)),
IVault(address(stakingVault))
);
airdropContract = new Airdrop(
ILoveToken(address(loveToken)),
ISoulmate(address(soulmateContract)),
IVault(address(airdropVault))
);
@>
airdropVault.initVault(
ILoveToken(address(loveToken)),
address(airdropContract)
);
stakingVault.initVault(
ILoveToken(address(loveToken)),
address(stakingContract)
);
}
Impact
Love tokens can be stolen from the vaults.
Tools Used
Foundry, Manual review
Recommendations
Add owner access control in Vault:initVault()
+error Vault__NotTheOwner();
+constructor() {
+ owner = msg.sender;
+}
function initVault(ILoveToken loveToken, address managerContract) public {
+ if (msg.sender != owner) revert Vault__NotTheOwner();
if (vaultInitialize) revert Vault__AlreadyInitialized();
loveToken.initVault(managerContract);
vaultInitialize = true;
}