Skip to content

Commit 7761fe8

Browse files
authored
fix: address 142 (#16501)
Currently the rollup don't need to have the registry in its config, and if it did and were to be updatable seems like it poses the same issue. I think the sanest solution here will be to take the alternative route and ensure that governance can simple claw back the funds. However, I don't think that gov need to be constrained on where they can put them for the simple reason that they already have the power of the issuer, so they could already make funds if they wanted to.
2 parents b472add + 77fedd0 commit 7761fe8

File tree

5 files changed

+62
-0
lines changed

5 files changed

+62
-0
lines changed

l1-contracts/src/governance/RewardDistributor.sol

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pragma solidity >=0.8.27;
55
import {IRegistry} from "@aztec/governance/interfaces/IRegistry.sol";
66
import {IRewardDistributor} from "@aztec/governance/interfaces/IRewardDistributor.sol";
77
import {Errors} from "@aztec/governance/libraries/Errors.sol";
8+
import {Ownable} from "@oz/access/Ownable.sol";
89
import {IERC20} from "@oz/token/ERC20/IERC20.sol";
910
import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol";
1011

@@ -28,6 +29,12 @@ contract RewardDistributor is IRewardDistributor {
2829
ASSET.safeTransfer(_to, _amount);
2930
}
3031

32+
function recover(address _asset, address _to, uint256 _amount) external override(IRewardDistributor) {
33+
address owner = Ownable(address(REGISTRY)).owner();
34+
require(msg.sender == owner, Errors.RewardDistributor__InvalidCaller(msg.sender, owner));
35+
IERC20(_asset).safeTransfer(_to, _amount);
36+
}
37+
3138
function canonicalRollup() public view override(IRewardDistributor) returns (address) {
3239
return address(REGISTRY.getCanonicalRollup());
3340
}

l1-contracts/src/governance/interfaces/IRewardDistributor.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ pragma solidity >=0.8.27;
33

44
interface IRewardDistributor {
55
function claim(address _to, uint256 _amount) external;
6+
function recover(address _asset, address _to, uint256 _amount) external;
67
function canonicalRollup() external view returns (address);
78
}

l1-contracts/test/benchmark/happy.t.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ contract FakeCanonical is IRewardDistributor {
9595
}
9696

9797
function updateRegistry(IRegistry _registry) external {}
98+
99+
function recover(address _asset, address _to, uint256 _amount) external {}
98100
}
99101

100102
contract BenchmarkRollupTest is FeeModelTestPoints, DecoderBase {

l1-contracts/test/compression/PreHeating.t.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ contract FakeCanonical is IRewardDistributor {
9292
}
9393

9494
function updateRegistry(IRegistry _registry) external {}
95+
96+
function recover(address _asset, address _to, uint256 _amount) external {}
9597
}
9698

9799
/**
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity >=0.8.27;
3+
4+
import {RewardDistributorBase} from "./Base.t.sol";
5+
6+
import {Errors} from "@aztec/governance/libraries/Errors.sol";
7+
import {Ownable} from "@oz/access/Ownable.sol";
8+
import {TestERC20} from "@aztec/mock/TestERC20.sol";
9+
10+
contract RecoverTest is RewardDistributorBase {
11+
address internal caller;
12+
13+
function test_WhenCallerIsNotOwner(address _caller) external {
14+
// it reverts
15+
address owner = Ownable(address(registry)).owner();
16+
vm.assume(_caller != owner);
17+
18+
vm.expectRevert(abi.encodeWithSelector(Errors.RewardDistributor__InvalidCaller.selector, _caller, owner));
19+
vm.prank(_caller);
20+
rewardDistributor.recover(address(token), _caller, 1e18);
21+
}
22+
23+
modifier whenCallerIsOwner() {
24+
caller = Ownable(address(registry)).owner();
25+
_;
26+
}
27+
28+
function test_GivenBalanceGt0(uint256 _balance, uint256 _amount) external whenCallerIsOwner {
29+
// it transfers the requested amount
30+
31+
uint256 balance = bound(_balance, 1, type(uint256).max);
32+
uint256 amount = bound(_amount, 1, balance);
33+
token.mint(address(rewardDistributor), balance);
34+
35+
uint256 callerBalance = token.balanceOf(caller);
36+
vm.prank(caller);
37+
rewardDistributor.recover(address(token), caller, amount);
38+
39+
assertEq(token.balanceOf(caller), callerBalance + amount);
40+
assertEq(token.balanceOf(address(rewardDistributor)), balance - amount);
41+
42+
TestERC20 token2 = new TestERC20("Token2", "T2", address(this));
43+
token2.mint(address(rewardDistributor), balance);
44+
45+
vm.prank(caller);
46+
rewardDistributor.recover(address(token2), caller, amount);
47+
assertEq(token2.balanceOf(caller), amount);
48+
assertEq(token2.balanceOf(address(rewardDistributor)), balance - amount);
49+
}
50+
}

0 commit comments

Comments
 (0)