Skip to content

Commit 56832e2

Browse files
committed
feat: add oracle + tests
1 parent 653f6c7 commit 56832e2

File tree

3 files changed

+118
-0
lines changed

3 files changed

+118
-0
lines changed

src/adapters/WstEthOracle.sol

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
pragma solidity 0.8.21;
3+
4+
import {IStETH} from "../interfaces/IStETH.sol";
5+
import {AggregatorV3Interface} from "../interfaces/AggregatorV3Interface.sol";
6+
7+
/// @title WstEthOracle
8+
/// @author Morpho Labs
9+
/// @custom:contact [email protected]
10+
/// @notice wstETH/ETH exchange rate price feed.
11+
contract WstEthOracle is AggregatorV3Interface {
12+
uint256 public constant version = 1;
13+
uint8 public constant DECIMALS = uint8(18);
14+
string public constant description = "wstETH/ETH exchange rate price";
15+
16+
IStETH public immutable ST_ETH;
17+
18+
constructor(address stEth) {
19+
require(stEth != address(0), "WstEthOracle: ZERO_ADDRESS");
20+
ST_ETH = IStETH(stEth);
21+
}
22+
23+
function decimals() external view returns (uint8) {
24+
return DECIMALS;
25+
}
26+
27+
function getRoundData(uint80)
28+
external
29+
view
30+
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
31+
{
32+
return latestRoundData();
33+
}
34+
35+
function latestRoundData()
36+
public
37+
view
38+
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
39+
{
40+
uint256 ethByShares = ST_ETH.getPooledEthByShares(10 ** DECIMALS);
41+
require(ethByShares < type(uint256).max, "WstEthOracle: OVERFLOW");
42+
answer = int256(ethByShares);
43+
}
44+
}

src/interfaces/IStEth.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
pragma solidity >=0.5.0;
3+
4+
interface IStETH {
5+
function getPooledEthByShares(uint256) external view returns (uint256);
6+
}

test/WstEthOracle.sol

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
pragma solidity ^0.8.0;
3+
4+
import "../lib/forge-std/src/Test.sol";
5+
import "../src/adapters/WstEthOracle.sol";
6+
7+
contract ChainlinkOracleTest is Test {
8+
IStETH internal constant ST_ETH = IStETH(0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84);
9+
10+
WstEthOracle internal oracle;
11+
12+
function setUp() public {
13+
vm.createSelectFork(vm.envString("ETH_RPC_URL"));
14+
oracle = new WstEthOracle(address(ST_ETH));
15+
}
16+
17+
function testLastRoundDataUintMax() public {
18+
vm.mockCall(
19+
address(ST_ETH),
20+
abi.encodeWithSelector(ST_ETH.getPooledEthByShares.selector, 10 ** 18),
21+
abi.encode(type(uint256).max)
22+
);
23+
vm.expectRevert("WstEthOracle: OVERFLOW");
24+
oracle.latestRoundData();
25+
}
26+
27+
function testGetRoundDataUintMax() public {
28+
vm.mockCall(
29+
address(ST_ETH),
30+
abi.encodeWithSelector(ST_ETH.getPooledEthByShares.selector, 10 ** 18),
31+
abi.encode(type(uint256).max)
32+
);
33+
vm.expectRevert("WstEthOracle: OVERFLOW");
34+
oracle.getRoundData(1);
35+
}
36+
37+
function testDecimals() public {
38+
assertEq(oracle.decimals(), uint8(18));
39+
}
40+
41+
function testDeployZeroAddress() public {
42+
vm.expectRevert("WstEthOracle: ZERO_ADDRESS");
43+
new WstEthOracle(address(0));
44+
}
45+
46+
function testConfig() public {
47+
assertEq(oracle.description(), "wstETH/ETH exchange rate price");
48+
assertEq(oracle.version(), 1);
49+
}
50+
51+
function testLastRoundData() public {
52+
(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) = oracle.latestRoundData();
53+
assertEq(roundId, 0);
54+
assertEq(answer,int256(ST_ETH.getPooledEthByShares(10 ** 18)));
55+
assertEq(startedAt, 0);
56+
assertEq(updatedAt, 0);
57+
assertEq(answeredInRound, 0);
58+
}
59+
60+
function testGetLastRoundData() public {
61+
(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) = oracle.getRoundData(1);
62+
assertEq(roundId, 0);
63+
assertEq(answer, int256(ST_ETH.getPooledEthByShares(10 ** 18)));
64+
assertEq(startedAt, 0);
65+
assertEq(updatedAt, 0);
66+
assertEq(answeredInRound, 0);
67+
}
68+
}

0 commit comments

Comments
 (0)