Skip to content

Commit

Permalink
test: BlastYield Tests
Browse files Browse the repository at this point in the history
  • Loading branch information
stonehengeLR committed Jul 2, 2024
1 parent f9fcf92 commit 2edb97e
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 0 deletions.
3 changes: 3 additions & 0 deletions contracts/BlastYield.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {IERC20Rebasing, YieldMode as IERC20Rebasing__YieldMode} from "./interfac
contract BlastYield is AccessControl {
address public immutable WETH;
address public immutable USDB;
bytes32 private constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");

/**
* @param _blast Blast precompile
Expand All @@ -33,6 +34,8 @@ contract BlastYield is AccessControl {
address _weth
) {
_grantRole(DEFAULT_ADMIN_ROLE, _owner);
_grantRole(OPERATOR_ROLE, _owner);
_grantRole(OPERATOR_ROLE, _blastPointsOperator);

WETH = _weth;
USDB = _usdb;
Expand Down
77 changes: 77 additions & 0 deletions test/foundry/BlastYield.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

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 BlastYield_Test is Test {
MockWETH private weth;
MockERC20 private usdb;
MockYield private mockYield;
MockPoints private mockPoints;
BlastYield private blastYield;

address public owner = address(69);
address public operator = address(420);
address private constant TREASURY = address(69420);
address internal constant BLAST = 0x4300000000000000000000000000000000000002;
address internal constant BLAST_POINTS = 0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800;

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

function test_setUpState() public {
assertEq(blastYield.WETH(), address(weth));
assertEq(blastYield.USDB(), address(usdb));
assertTrue(blastYield.hasRole(bytes32(0), owner));
assertTrue(blastYield.hasRole(keccak256("OPERATOR_ROLE"), owner));
assertTrue(blastYield.hasRole(keccak256("OPERATOR_ROLE"), operator));

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

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

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

function test_claim() public {
vm.startPrank(owner);
blastYield.claim(TREASURY, TREASURY);

vm.stopPrank();

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

function test_claim_RevertIf_NotOwner() public {
vm.expectRevert(
abi.encodePacked(
"AccessControl: account 0xb4c79dab8f259c7aee6e5b2aa729821864227e84 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000"
)
);
blastYield.claim(TREASURY, TREASURY);
}
}
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 {
mapping(address => 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 {}
}
80 changes: 80 additions & 0 deletions test/mock/MockBlastWETH.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

contract MockWETH {
string public name = "Wrapped Ether";
string public symbol = "WETH";
uint8 public decimals = 18;

event Approval(address indexed src, address indexed guy, uint256 wad);
event Transfer(address indexed src, address indexed dst, uint256 wad);
event Deposit(address indexed dst, uint256 wad);
event Withdrawal(address indexed src, uint256 wad);

mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;

mapping(address => YieldMode) public yieldMode;

receive() external payable {
deposit();
}

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) {
balanceOf[receiver] += amount;
return amount;
}

function deposit() public payable {
balanceOf[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}

function withdraw(uint256 wad) public {
require(balanceOf[msg.sender] >= wad);
balanceOf[msg.sender] -= wad;
payable(msg.sender).transfer(wad);
emit Withdrawal(msg.sender, wad);
}

function totalSupply() public view returns (uint256) {
return address(this).balance;
}

function approve(address guy, uint256 wad) public returns (bool) {
allowance[msg.sender][guy] = wad;
emit Approval(msg.sender, guy, wad);
return true;
}

function transfer(address dst, uint256 wad) public returns (bool) {
return transferFrom(msg.sender, dst, wad);
}

function transferFrom(address src, address dst, uint256 wad) public returns (bool) {
require(balanceOf[src] >= wad);

if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) {
require(allowance[src][msg.sender] >= wad);
allowance[src][msg.sender] -= wad;
}

balanceOf[src] -= wad;
balanceOf[dst] += wad;

emit Transfer(src, dst, wad);

return true;
}
}
18 changes: 18 additions & 0 deletions test/mock/MockBlastYield.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

contract MockYield {
struct Config {
YieldMode yieldMode;
GasMode gasMode;
address governor;
}

mapping(address => Config) public config;

function configure(YieldMode _yield, GasMode _gasMode, address _governor) external {
config[msg.sender] = Config(_yield, _gasMode, _governor);
}
}

0 comments on commit 2edb97e

Please sign in to comment.