Description
In CrimeMoney contract, only address with "moneyshelf" role can mint or burn the token, which is originally the MoneyShelf contract, granted in Deployer.s.sol script. But after migration, MoneyVault is not granted as "moneyshelf" role.
Impact
After migration, MoneyVault cannot burn CrimeMoney, so godfather cannot withdraw USDC from it.
Proof of Concept
function test_migrateAndWithdraw() public {
address alice = makeAddr("alice");
assertEq(address(kernel.getModuleForKeycode(Keycode.wrap("MONEY"))), address(moneyShelf));
joinGangGodFather();
joinGang(address(this));
vm.prank(godFather);
usdc.transfer(alice, 100e6);
vm.startPrank(alice);
usdc.approve(address(moneyShelf), 100e6);
laundrette.depositTheCrimeMoneyInATM(alice, alice, 100e6);
vm.stopPrank();
assertEq(usdc.balanceOf(alice), 0);
assertEq(usdc.balanceOf(address(moneyShelf)), 100e6);
assertEq(crimeMoney.balanceOf(alice), 100e6);
EmergencyMigration migration = new EmergencyMigration();
MoneyVault moneyVault = migration.migrate(kernel, usdc, crimeMoney, moneyShelf, laundrette);
assertEq(usdc.balanceOf(address(moneyVault)), 0);
vm.prank(godFather);
vm.expectRevert();
laundrette.withdrawMoney(alice, godFather, 100e6);
}
Recommendations
grant "moneyshelf" role to new MoneyVault contract. But since admin role is already transferred to laundrette, we can add a function in laundrette to grant role:
in laundrette:
++ function grantRoleMoneyShelf(address _moneyShelf) external isGodFather {
++ kernel.grantRole(Role.wrap("moneyshelf"), _moneyShelf);
++ }
in EmergencyMigration.s.sol
++ laundrette.grantRoleMoneyShelf(address(moneyVault));