Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: BlastYield contract #103

Merged
merged 39 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
b93db9e
feat: BlastYield contract
stonehengeLR Jun 30, 2024
8445190
fix: Compilier error
stonehengeLR Jul 1, 2024
f9fcf92
fix: Compilier error
stonehengeLR Jul 1, 2024
2edb97e
test: BlastYield Tests
stonehengeLR Jul 2, 2024
edbe35f
test: BlastYield Test Fixes
stonehengeLR Jul 3, 2024
fe415c3
fix: Prettier and Mocks
stonehengeLR Jul 3, 2024
b7e2d0d
fix: Increase node version for github actions
stonehengeLR Jul 3, 2024
d1c8d3f
fix: Increase node version for github actions
stonehengeLR Jul 3, 2024
9ca51b7
fix: Update Access Control to OwnableTwoSteps
stonehengeLR Jul 4, 2024
6f67949
feat: Private function to allow both access control and ownable two s…
stonehengeLR Jul 4, 2024
e9cb33e
fix: Remove virtual from _claim
stonehengeLR Jul 4, 2024
cc82877
fix: Rename _owner to _governor
stonehengeLR Jul 4, 2024
e7dcbeb
fix: Fix _claim notice
stonehengeLR Jul 4, 2024
2517444
fix: Pass mockPoints
stonehengeLR Jul 4, 2024
88ee19b
fix: Remove operator role
stonehengeLR Jul 4, 2024
ce33daf
fix: Mock naming
stonehengeLR Jul 4, 2024
591cf53
fix: Get modifier from TestHelpers
stonehengeLR Jul 4, 2024
0ba5d3a
chore: Update solmate to openzeppelin
stonehengeLR Jul 4, 2024
acda72e
fix: Governor comment
stonehengeLR Jul 4, 2024
4143be9
fix: TestHelpers inheritance
stonehengeLR Jul 4, 2024
5946c5f
feat: Split BlastYield into multiple contracts
stonehengeLR Jul 5, 2024
06cebd1
fix: Remove event
stonehengeLR Jul 5, 2024
70b57dd
fix: Remove empty line
stonehengeLR Jul 5, 2024
f80299b
fix: YieldMode GasMode fix
stonehengeLR Jul 5, 2024
96c81ec
fix: Fix blast points notice
stonehengeLR Jul 5, 2024
ed78762
fix: Refix YieldMode GasMode
stonehengeLR Jul 5, 2024
bc6d3e8
fix: Test names
stonehengeLR Jul 5, 2024
3d7fac2
fix: Improve notices
stonehengeLR Jul 5, 2024
666c866
fix: Fix native yield and points tests
stonehengeLR Jul 5, 2024
c01c63d
fix: YieldMode GasMode naming BlastNativeYield_Test
stonehengeLR Jul 5, 2024
68bb39c
fix: Unused variables and imports
stonehengeLR Jul 6, 2024
1d6537f
fix: Remove variable alias
stonehengeLR Jul 6, 2024
0729c1a
fix: Another unused import
stonehengeLR Jul 6, 2024
9183289
fix: Remove variable alias
stonehengeLR Jul 6, 2024
602487e
fix: Fix _claim notice
stonehengeLR Jul 6, 2024
534d053
fix: Fix _claim notice
stonehengeLR Jul 6, 2024
39e688a
fix: Renaming
stonehengeLR Jul 7, 2024
763c63d
fix: Notice length
stonehengeLR Jul 7, 2024
3cfa074
fix: Whitespace formatting
stonehengeLR Jul 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 14.x
node-version: 16.x
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 14.x
node-version: 16.x
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
Expand Down
59 changes: 59 additions & 0 deletions contracts/BlastYield.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IBlast, YieldMode as IBlast__YieldMode, GasMode as IBlast__GasMode} from "./interfaces/IBlast.sol";
import {IBlastPoints} from "./interfaces/IBlastPoints.sol";
import {IERC20Rebasing, YieldMode as IERC20Rebasing__YieldMode} from "./interfaces/IERC20Rebasing.sol";

/**
* @title BlastYield
* @notice This contract is a base contract for future contracts that wish to claim Blast WETH or USDB yield to inherit from.
* @author LooksRare protocol team (👀,💎)
*/
contract BlastYield {
address public immutable WETH;
address public immutable USDB;

/**
* @param _blast Blast precompile
* @param _blastPoints Blast points
* @param _blastPointsOperator Blast points operator
* @param _owner Owner of the contract
0xhiroshi marked this conversation as resolved.
Show resolved Hide resolved
* @param _usdb USDB address
* @param _weth WETH address
*/
constructor(
address _blast,
address _blastPoints,
address _blastPointsOperator,
address _owner,
address _usdb,
address _weth
) {
WETH = _weth;
USDB = _usdb;

IBlast(_blast).configure(IBlast__YieldMode.CLAIMABLE, IBlast__GasMode.CLAIMABLE, _owner);
IBlastPoints(_blastPoints).configurePointsOperator(_blastPointsOperator);
IERC20Rebasing(_weth).configure(IERC20Rebasing__YieldMode.CLAIMABLE);
IERC20Rebasing(_usdb).configure(IERC20Rebasing__YieldMode.CLAIMABLE);
}

/**
* @notice Claim Blast yield. Only callable by contract owner.
0xhiroshi marked this conversation as resolved.
Show resolved Hide resolved
* @param wethReceiver The receiver of WETH.
* @param usdbReceiver The receiver of USDB.
*/
function _claim(address wethReceiver, address usdbReceiver) internal virtual {
0xhiroshi marked this conversation as resolved.
Show resolved Hide resolved
uint256 claimableWETH = IERC20Rebasing(WETH).getClaimableAmount(address(this));
if (claimableWETH != 0) {
IERC20Rebasing(WETH).claim(wethReceiver, claimableWETH);
}

uint256 claimableUSDB = IERC20Rebasing(USDB).getClaimableAmount(address(this));
if (claimableUSDB != 0) {
IERC20Rebasing(USDB).claim(usdbReceiver, claimableUSDB);
}
}
}
77 changes: 77 additions & 0 deletions contracts/interfaces/IBlast.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

enum YieldMode {
AUTOMATIC,
VOID,
CLAIMABLE
}

enum GasMode {
VOID,
CLAIMABLE
}

interface IBlast {
// configure
function configureContract(address contractAddress, YieldMode _yield, GasMode gasMode, address governor) external;

function configure(YieldMode _yield, GasMode gasMode, address governor) external;

// base configuration options
function configureClaimableYield() external;

function configureClaimableYieldOnBehalf(address contractAddress) external;

function configureAutomaticYield() external;

function configureAutomaticYieldOnBehalf(address contractAddress) external;

function configureVoidYield() external;

function configureVoidYieldOnBehalf(address contractAddress) external;

function configureClaimableGas() external;

function configureClaimableGasOnBehalf(address contractAddress) external;

function configureVoidGas() external;

function configureVoidGasOnBehalf(address contractAddress) external;

function configureGovernor(address _governor) external;

function configureGovernorOnBehalf(address _newGovernor, address contractAddress) external;

// claim yield
function claimYield(address contractAddress, address recipientOfYield, uint256 amount) external returns (uint256);

function claimAllYield(address contractAddress, address recipientOfYield) external returns (uint256);

// claim gas
function claimAllGas(address contractAddress, address recipientOfGas) external returns (uint256);

function claimGasAtMinClaimRate(
address contractAddress,
address recipientOfGas,
uint256 minClaimRateBips
) external returns (uint256);

function claimMaxGas(address contractAddress, address recipientOfGas) external returns (uint256);

function claimGas(
address contractAddress,
address recipientOfGas,
uint256 gasToClaim,
uint256 gasSecondsToConsume
) external returns (uint256);

// read functions
function readClaimableYield(address contractAddress) external view returns (uint256);

function readYieldConfiguration(address contractAddress) external view returns (uint8);

function readGasParams(
address contractAddress
) external view returns (uint256 etherSeconds, uint256 etherBalance, uint256 lastUpdated, GasMode);
}
6 changes: 6 additions & 0 deletions contracts/interfaces/IBlastPoints.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IBlastPoints {
function configurePointsOperator(address operator) external;
}
21 changes: 21 additions & 0 deletions contracts/interfaces/IERC20Rebasing.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

enum YieldMode {
AUTOMATIC,
VOID,
CLAIMABLE
}

interface IERC20Rebasing {
// changes the yield mode of the caller and update the balance
// to reflect the configuration
function configure(YieldMode) external returns (uint256);

// "claimable" yield mode accounts can call this this claim their yield
// to another address
function claim(address recipient, uint256 amount) external returns (uint256);

// read the claimable amount for an account
function getClaimableAmount(address account) external view returns (uint256);
}
2 changes: 1 addition & 1 deletion hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const config: HardhatUserConfig = {
solidity: {
compilers: [
{
version: "0.8.17",
version: "0.8.20",
settings: { optimizer: { enabled: true, runs: 888888 } },
},
{
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,16 @@
"husky": "^7.0.4",
"merkletreejs": "^0.2.31",
"prettier": "^2.3.2",
"prettier-plugin-solidity": "^1.1.1",
"prettier-plugin-solidity": "^1.3.0",
"release-it": "^15.0.0",
"solhint": "^3.3.7",
"solidity-coverage": "^0.7.21",
"solmate": "^6.6.1",
"ts-node": "^10.1.0",
"typechain": "^5.1.2",
"typescript": "^4.5.2"
},
"dependencies": {
"@openzeppelin/contracts": "^5.0.2"
0xhiroshi marked this conversation as resolved.
Show resolved Hide resolved
}
}
98 changes: 98 additions & 0 deletions test/foundry/BlastYield.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IOwnableTwoSteps} from "../../contracts/interfaces/IOwnableTwoSteps.sol";
import {OwnableTwoSteps} from "../../contracts/OwnableTwoSteps.sol";
import {BlastYield} from "../../contracts/BlastYield.sol";
import {Test} from "../../lib/forge-std/src/Test.sol";
import {YieldMode as IBlast__YieldMode, GasMode as IBlast__GasMode} from "../../contracts/interfaces/IBlast.sol";
import {YieldMode as IERC20Rebasing__YieldMode} from "../../contracts/interfaces/IERC20Rebasing.sol";

import {MockERC20} from "../mock/MockBlastERC20.sol";
import {MockPoints} from "../mock/MockBlastPoints.sol";
import {MockWETH} from "../mock/MockBlastWETH.sol";
import {MockYield} from "../mock/MockBlastYield.sol";

contract BlastYieldOwnableTwoSteps is BlastYield, OwnableTwoSteps {
constructor(
address _blast,
address _blastPoints,
address _blastPointsOperator,
address _owner,
address _usdb,
address _weth
) BlastYield(_blast, _blastPoints, _blastPointsOperator, _owner, _usdb, _weth) OwnableTwoSteps(_owner) {}

function claim(address wethReceiver, address usdbReceiver) public onlyOwner {
_claim(wethReceiver, usdbReceiver);
}
}

contract BlastYieldOwnableTwoSteps_Test is Test {
MockWETH private weth;
MockERC20 private usdb;
MockYield private mockYield;
MockPoints private mockPoints;
BlastYieldOwnableTwoSteps private blastYieldOwnableTwoSteps;

address public owner = address(69);
address public operator = address(420);
address public user1 = address(1);
address private constant TREASURY = address(69420);
address internal constant BLAST_POINTS = 0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800;
0xhiroshi marked this conversation as resolved.
Show resolved Hide resolved
bytes32 private constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
0xhiroshi marked this conversation as resolved.
Show resolved Hide resolved

function setUp() public {
vm.etch(BLAST_POINTS, address(new MockPoints()).code);
weth = new MockWETH();
usdb = new MockERC20("USDB", "USDB");
mockYield = new MockYield();
blastYieldOwnableTwoSteps = new BlastYieldOwnableTwoSteps(
address(mockYield),
BLAST_POINTS,
operator,
owner,
address(usdb),
address(weth)
);
}

function test_setUpState() public {
assertEq(blastYieldOwnableTwoSteps.WETH(), address(weth));
assertEq(blastYieldOwnableTwoSteps.USDB(), address(usdb));

(IBlast__YieldMode yieldMode, IBlast__GasMode gasMode, address governor) = mockYield.config(
address(blastYieldOwnableTwoSteps)
);
assertEq(uint8(yieldMode), uint8(IBlast__YieldMode.CLAIMABLE));
assertEq(uint8(gasMode), uint8(IBlast__GasMode.CLAIMABLE));
assertEq(governor, owner);

IERC20Rebasing__YieldMode wethYieldMode = weth.yieldMode(address(blastYieldOwnableTwoSteps));
assertEq(uint8(wethYieldMode), uint8(IERC20Rebasing__YieldMode.CLAIMABLE));

IERC20Rebasing__YieldMode usdbYieldMode = usdb.yieldMode(address(blastYieldOwnableTwoSteps));
assertEq(uint8(usdbYieldMode), uint8(IERC20Rebasing__YieldMode.CLAIMABLE));
}

function test_claim() public asPrankedUser(owner) {
blastYieldOwnableTwoSteps.claim(TREASURY, TREASURY);

assertEq(weth.balanceOf(address(blastYieldOwnableTwoSteps)), 0);
assertEq(usdb.balanceOf(address(blastYieldOwnableTwoSteps)), 0);
assertEq(weth.balanceOf(TREASURY), 1 ether);
assertEq(usdb.balanceOf(TREASURY), 1 ether);
}

function test_claim_RevertIf_NotOwner() public asPrankedUser(user1) {
vm.expectRevert(IOwnableTwoSteps.NotOwner.selector);
blastYieldOwnableTwoSteps.claim(TREASURY, TREASURY);
}

modifier asPrankedUser(address user) {
0xhiroshi marked this conversation as resolved.
Show resolved Hide resolved
vm.startPrank(user);
_;
vm.stopPrank();
}
}
30 changes: 30 additions & 0 deletions test/mock/MockBlastERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

import {YieldMode} from "../../../contracts/interfaces/IERC20Rebasing.sol";

contract MockERC20 is ERC20 {
0xhiroshi marked this conversation as resolved.
Show resolved Hide resolved
mapping(address _contract => YieldMode) public yieldMode;

constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) {}

function mint(address to, uint256 amount) public {
_mint(to, amount);
}

function configure(YieldMode _yieldMode) external returns (uint256) {
yieldMode[msg.sender] = _yieldMode;
return uint256(_yieldMode);
}

function getClaimableAmount(address) external pure returns (uint256) {
return 1 ether;
}

function claim(address receiver, uint256 amount) external returns (uint256) {
_mint(receiver, amount);
return amount;
}
}
6 changes: 6 additions & 0 deletions test/mock/MockBlastPoints.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract MockPoints {
function configurePointsOperator(address operator) external {}
}
Loading
Loading