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 all 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
58 changes: 58 additions & 0 deletions contracts/BlastERC20RebasingYield.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

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

/**
* @title BlastERC20RebasingYield
* @notice This contract is a base contract for inheriting functions to claim Blast WETH or USDB yield
* @author LooksRare protocol team (👀,💎)
*/
contract BlastERC20RebasingYield is BlastNativeYield {
address public immutable WETH;
address public immutable USDB;

/**
* @param _blast Blast precompile
* @param _blastPoints Blast points
* @param _blastPointsOperator Blast points operator
* @param _governor The address that’s allowed to claim the contract’s yield and gas
* @param _usdb USDB address
* @param _weth WETH address
*/
constructor(
address _blast,
address _blastPoints,
address _blastPointsOperator,
address _governor,
address _usdb,
address _weth
) BlastNativeYield(_blast, _blastPoints, _blastPointsOperator, _governor) {
WETH = _weth;
USDB = _usdb;

IERC20Rebasing(_weth).configure(YieldMode.CLAIMABLE);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to confirm, for all weth and usdb holding contracts, the yield mode will always be claimable, never automatic?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine to leave it as the default and if we want to change it we can do it after - we've never had any contracts with a claim mode that's not CLAIMABLE

IERC20Rebasing(_usdb).configure(YieldMode.CLAIMABLE);
}

/**
* @notice Claim Blast yield. Guarding of the function is dependent on the inherited contract.
0xhiroshi marked this conversation as resolved.
Show resolved Hide resolved
* Inheriting does not allow claiming by default.
* A public or external function is required in the child contract to access the _claim function.
* @param wethReceiver The receiver of WETH.
* @param usdbReceiver The receiver of USDB.
*/
function _claim(address wethReceiver, address usdbReceiver) internal {
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);
}
}
}
28 changes: 28 additions & 0 deletions contracts/BlastNativeYield.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IBlast, YieldMode, GasMode} from "./interfaces/IBlast.sol";
import {BlastPoints} from "./BlastPoints.sol";

/**
0xhiroshi marked this conversation as resolved.
Show resolved Hide resolved
* @title BlastNativeYield
* @notice This contract is a base contract for inheriting functions to claim native yield and for those that wish to recieve Blast points
* @author LooksRare protocol team (👀,💎)
*/
contract BlastNativeYield is BlastPoints {
/**
* @param _blast Blast precompile
* @param _blastPoints Blast points
* @param _blastPointsOperator Blast points operator
* @param _governor The address that’s allowed to claim the contract’s yield and gas
*/
constructor(
address _blast,
address _blastPoints,
address _blastPointsOperator,
address _governor
) BlastPoints(_blastPoints, _blastPointsOperator) {
IBlast(_blast).configure(YieldMode.CLAIMABLE, GasMode.CLAIMABLE, _governor);
}
}
20 changes: 20 additions & 0 deletions contracts/BlastPoints.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IBlastPoints} from "./interfaces/IBlastPoints.sol";

/**
* @title BlastPoints
* @notice This contract is a base for future contracts that wish to be recipients of Blast points to inherit from
* @author LooksRare protocol team (👀,💎)
*/
contract BlastPoints {
/**
* @param _blastPoints Blast points
* @param _blastPointsOperator Blast points operator
*/
constructor(address _blastPoints, address _blastPointsOperator) {
IBlastPoints(_blastPoints).configurePointsOperator(_blastPointsOperator);
}
}
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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,12 @@
"hardhat-gas-reporter": "^1.0.8",
"husky": "^7.0.4",
"merkletreejs": "^0.2.31",
"@openzeppelin/contracts": "^5.0.2",
"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"
Expand Down
85 changes: 85 additions & 0 deletions test/foundry/BlastERC20RebasingYield.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IOwnableTwoSteps} from "../../contracts/interfaces/IOwnableTwoSteps.sol";
import {OwnableTwoSteps} from "../../contracts/OwnableTwoSteps.sol";
import {BlastERC20RebasingYield} from "../../contracts/BlastERC20RebasingYield.sol";
import {TestHelpers} from "./utils/TestHelpers.sol";
import {YieldMode} from "../../contracts/interfaces/IERC20Rebasing.sol";

import {MockBlastERC20} from "../mock/MockBlastERC20.sol";
import {MockBlastPoints} from "../mock/MockBlastPoints.sol";
import {MockBlastWETH} from "../mock/MockBlastWETH.sol";
import {MockBlastYield} from "../mock/MockBlastYield.sol";

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

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

contract BlastERC20RebasingYield_Test is TestHelpers {
MockBlastWETH private weth;
MockBlastERC20 private usdb;
MockBlastYield private mockBlastYield;
MockBlastPoints private mockBlastPoints;
BlastERC20RebasingYieldGuarded private blastERC20RebasingYieldGuarded;

address public owner = address(69);
address public operator = address(420);
address public user1 = address(1);
address private constant TREASURY = address(69420);

function setUp() public {
weth = new MockBlastWETH();
usdb = new MockBlastERC20("USDB", "USDB");
mockBlastYield = new MockBlastYield();
mockBlastPoints = new MockBlastPoints();
blastERC20RebasingYieldGuarded = new BlastERC20RebasingYieldGuarded(
address(mockBlastYield),
address(mockBlastPoints),
operator,
owner,
address(usdb),
address(weth)
);
}

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

YieldMode wethYieldMode = weth.yieldMode(address(blastERC20RebasingYieldGuarded));
assertEq(uint8(wethYieldMode), uint8(YieldMode.CLAIMABLE));

YieldMode usdbYieldMode = usdb.yieldMode(address(blastERC20RebasingYieldGuarded));
assertEq(uint8(usdbYieldMode), uint8(YieldMode.CLAIMABLE));
}

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

assertEq(weth.balanceOf(address(blastERC20RebasingYieldGuarded)), 0);
assertEq(usdb.balanceOf(address(blastERC20RebasingYieldGuarded)), 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);
blastERC20RebasingYieldGuarded.claim(TREASURY, TREASURY);
}
}
32 changes: 32 additions & 0 deletions test/foundry/BlastNativeYield.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {BlastNativeYield} from "../../contracts/BlastNativeYield.sol";
import {TestHelpers} from "./utils/TestHelpers.sol";
import {YieldMode, GasMode} from "../../contracts/interfaces/IBlast.sol";

import {MockBlastPoints} from "../mock/MockBlastPoints.sol";
import {MockBlastYield} from "../mock/MockBlastYield.sol";

contract BlastNativeYield_Test is TestHelpers {
MockBlastYield private mockBlastYield;
MockBlastPoints private mockBlastPoints;
BlastNativeYield private blastNativeYield;

address public owner = address(69);
address public operator = address(420);

function setUp() public {
mockBlastPoints = new MockBlastPoints();
mockBlastYield = new MockBlastYield();
blastNativeYield = new BlastNativeYield(address(mockBlastYield), address(mockBlastPoints), operator, owner);
}

function test_setUpState() public {
(YieldMode yieldMode, GasMode gasMode, address governor) = mockBlastYield.config(address(blastNativeYield));
assertEq(uint8(yieldMode), uint8(YieldMode.CLAIMABLE));
assertEq(uint8(gasMode), uint8(GasMode.CLAIMABLE));
assertEq(governor, owner);
}
}
Loading
Loading