From cb270ef583cde0c4c8127ad1b0946c6a3e015123 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Mon, 30 Sep 2024 17:08:12 +0000 Subject: [PATCH] feat: sysstia --- l1-contracts/src/governance/Sysstia.sol | 44 +++++++++++++++ .../src/governance/interfaces/ISysstia.sol | 6 +++ .../src/governance/libraries/Errors.sol | 2 + .../test/governance/sysstia/Base.t.sol | 23 ++++++++ .../test/governance/sysstia/claim.t.sol | 53 +++++++++++++++++++ .../test/governance/sysstia/claim.tree | 9 ++++ 6 files changed, 137 insertions(+) create mode 100644 l1-contracts/src/governance/Sysstia.sol create mode 100644 l1-contracts/src/governance/interfaces/ISysstia.sol create mode 100644 l1-contracts/test/governance/sysstia/Base.t.sol create mode 100644 l1-contracts/test/governance/sysstia/claim.t.sol create mode 100644 l1-contracts/test/governance/sysstia/claim.tree diff --git a/l1-contracts/src/governance/Sysstia.sol b/l1-contracts/src/governance/Sysstia.sol new file mode 100644 index 00000000000..db65ca969e5 --- /dev/null +++ b/l1-contracts/src/governance/Sysstia.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Aztec Labs. +pragma solidity >=0.8.27; + +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; + +import {IRegistry} from "@aztec/governance/interfaces/IRegistry.sol"; +import {ISysstia} from "@aztec/governance/interfaces/ISysstia.sol"; + +import {Errors} from "@aztec/governance/libraries/Errors.sol"; + +contract Sysstia is ISysstia { + using SafeERC20 for IERC20; + + // This value is pulled out my ass. Don't take it seriously + uint256 public constant BLOCK_REWARD = 50e18; + + IERC20 public immutable TST; + IRegistry public immutable REGISTRY; + + constructor(IERC20 _tst, IRegistry _registry) { + TST = _tst; + REGISTRY = _registry; + } + + /** + * @notice Simple claim of a block reward + * Note especially that it can be called any number of times. + * Essentially a placeholder until more nuanced logic is designed. + * + * @dev Does not check if the tokens are actually available first. + * + * @param _to - The address to receive the reward + * + * @return - the amount claimed + */ + function claim(address _to) external override(ISysstia) returns (uint256) { + address canonical = REGISTRY.getRollup(); + require(msg.sender == canonical, Errors.Sysstia__InvalidCaller(msg.sender, canonical)); + TST.safeTransfer(_to, BLOCK_REWARD); + return BLOCK_REWARD; + } +} diff --git a/l1-contracts/src/governance/interfaces/ISysstia.sol b/l1-contracts/src/governance/interfaces/ISysstia.sol new file mode 100644 index 00000000000..2d640cd09d3 --- /dev/null +++ b/l1-contracts/src/governance/interfaces/ISysstia.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.8.27; + +interface ISysstia { + function claim(address _to) external returns (uint256); +} diff --git a/l1-contracts/src/governance/libraries/Errors.sol b/l1-contracts/src/governance/libraries/Errors.sol index be84e159db3..80a2c29e8f2 100644 --- a/l1-contracts/src/governance/libraries/Errors.sol +++ b/l1-contracts/src/governance/libraries/Errors.sol @@ -15,4 +15,6 @@ library Errors { error Registry__RollupAlreadyRegistered(address rollup); // 0x3c34eabf error Registry__RollupNotRegistered(address rollup); // 0xa1fee4cf + + error Sysstia__InvalidCaller(address caller, address canonical); // 0xb95e39f6 } diff --git a/l1-contracts/test/governance/sysstia/Base.t.sol b/l1-contracts/test/governance/sysstia/Base.t.sol new file mode 100644 index 00000000000..3a6a41b1dd8 --- /dev/null +++ b/l1-contracts/test/governance/sysstia/Base.t.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.27; + +import {Test} from "forge-std/Test.sol"; + +import {IMintableERC20} from "@aztec/governance/interfaces/IMintableERC20.sol"; + +import {Registry} from "@aztec/governance/Registry.sol"; +import {Sysstia} from "@aztec/governance/Sysstia.sol"; + +import {TestERC20} from "@aztec/mock/TestERC20.sol"; + +contract SysstiaBase is Test { + IMintableERC20 internal token; + Registry internal registry; + Sysstia internal sysstia; + + function setUp() public { + token = IMintableERC20(address(new TestERC20())); + registry = new Registry(address(this)); + sysstia = new Sysstia(token, registry); + } +} diff --git a/l1-contracts/test/governance/sysstia/claim.t.sol b/l1-contracts/test/governance/sysstia/claim.t.sol new file mode 100644 index 00000000000..912471f7c22 --- /dev/null +++ b/l1-contracts/test/governance/sysstia/claim.t.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.27; + +import {SysstiaBase} from "./Base.t.sol"; + +import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol"; + +import {Errors} from "@aztec/governance/libraries/Errors.sol"; + +contract ClaimTest is SysstiaBase { + address internal caller; + + function test_WhenCallerIsNotCanonical(address _caller) external { + // it reverts + vm.assume(_caller != address(0xdead)); + + vm.expectRevert( + abi.encodeWithSelector(Errors.Sysstia__InvalidCaller.selector, _caller, address(0xdead)) + ); + vm.prank(_caller); + sysstia.claim(_caller); + } + + modifier whenCallerIsCanonical() { + caller = address(0xdead); + _; + } + + function test_GivenBalanceSmallerThanReward() external whenCallerIsCanonical { + // it reverts + uint256 needed = sysstia.BLOCK_REWARD(); + vm.prank(caller); + vm.expectRevert( + abi.encodeWithSelector( + IERC20Errors.ERC20InsufficientBalance.selector, address(sysstia), 0, needed + ) + ); + sysstia.claim(caller); + } + + function test_GivenBalanceLargerOrEqualReward(uint256 _balance) external whenCallerIsCanonical { + // it transfers block reward of asset + // it returns block reward value + uint256 balance = bound(_balance, sysstia.BLOCK_REWARD(), type(uint256).max); + token.mint(address(sysstia), balance); + + uint256 callerBalance = token.balanceOf(caller); + vm.prank(caller); + sysstia.claim(caller); + + assertEq(token.balanceOf(caller), callerBalance + sysstia.BLOCK_REWARD()); + } +} diff --git a/l1-contracts/test/governance/sysstia/claim.tree b/l1-contracts/test/governance/sysstia/claim.tree new file mode 100644 index 00000000000..4d7d92aea21 --- /dev/null +++ b/l1-contracts/test/governance/sysstia/claim.tree @@ -0,0 +1,9 @@ +ClaimTest +├── when caller is not canonical +│ └── it reverts +└── when caller is canonical + ├── given balance smaller than reward + │ └── it reverts + └── given balance larger or equal reward + ├── it transfers block reward of asset + └── it returns block reward value \ No newline at end of file