Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
52 changes: 52 additions & 0 deletions src/feeds/PriceFeedNoStale.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "src/interfaces/IChainlinkFeed.sol";

contract PriceFeedNoStale {
IChainlinkFeed public immutable feed;
string public description;

constructor(
address _feed
) {
feed = IChainlinkFeed(_feed);
require(feed.decimals() == 18, "Wrong Decimals");
description = feed.description();
}

/**
* @notice Retrieves the latest round data for the asset token price feed
* @return roundId Will return 0
* @return usdPrice The latest asset price in USD with 18 decimals
* @return startedAt Will return 0
* @return updatedAt Will return block.timestamp
* @return answeredInRound Will return 0
*/
function latestRoundData()
public
view
returns (
uint80 roundId,
int256 usdPrice,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
{
return (0, feed.latestAnswer(), 0, block.timestamp, 0 );
}

/**
* @notice Returns the latest price only
* @dev Unlike chainlink oracles, the latestAnswer will always be the same as in the latestRoundData
* @return int256 Returns the last finalized price of the chainlink oracle
*/
function latestAnswer() external view returns (int256) {
return feed.latestAnswer();
}

function decimals() external pure returns (uint8) {
return 18;
}
}
2 changes: 1 addition & 1 deletion test/feedForkTests/ChainlinkBridgeAssetBase.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import "src/feeds/ChainlinkBasePriceFeed.sol";
import {ChainlinkBridgeAssetFeed} from "src/feeds/ChainlinkBridgeAssetFeed.sol";

abstract contract ChainlinkBridgeAssetBase is Test {
ChainlinkBridgeAssetFeed feed;
ChainlinkBridgeAssetFeed internal feed;
ChainlinkBasePriceFeed collateralToBridgeAssetFeed; // main coin1 feed
ChainlinkBasePriceFeed bridgeAssetToUsdFeed; // main coin2 feed

Expand Down
41 changes: 41 additions & 0 deletions test/feedForkTests/SINV.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import "forge-std/Test.sol";
import {ERC4626Feed, IERC4626} from "src/feeds/ERC4626Feed.sol";
import {ChainlinkBasePriceFeed} from "src/feeds/ChainlinkBasePriceFeed.sol";
import {PriceFeedNoStale} from "src/feeds/PriceFeedNoStale.sol";
import "forge-std/console2.sol";


contract SINVFeedTest is Test {
ERC4626Feed vaultFeed;
PriceFeedNoStale priceFeed;

IERC4626 sInv = IERC4626(0x08d23468A467d2bb86FaE0e32F247A26C7E2e994);
ChainlinkBasePriceFeed invToUsd = ChainlinkBasePriceFeed(0x54F1E4EB93c5b5F4C12776c96e08a49A9928FE84);

function setUp() public {
string memory url = vm.rpcUrl("mainnet");
vm.createSelectFork(url);
vaultFeed = new ERC4626Feed(address(sInv), address(invToUsd));
priceFeed = new PriceFeedNoStale(address(vaultFeed));
}

function test_sINV_Feed() public {
uint256 exchangeRate = sInv.previewRedeem(1e18);
uint256 invPrice = uint(invToUsd.latestAnswer());
uint256 sInvToUsd = exchangeRate * invPrice / 1e18;
assertEq(sInvToUsd, uint(vaultFeed.latestAnswer()));
assertEq(sInvToUsd, uint(priceFeed.latestAnswer()));
console2.log(uint(vaultFeed.latestAnswer()));
}

function test_feedNoStale() public {
assertEq(vaultFeed.latestAnswer(), priceFeed.latestAnswer());
(,int price, , uint updateAt,) = priceFeed.latestRoundData();
assertEq(updateAt, block.timestamp);
assertEq(price, vaultFeed.latestAnswer());
console2.log(priceFeed.description());
}
}
50 changes: 50 additions & 0 deletions test/feedForkTests/WOETH.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import "forge-std/Test.sol";
import {ERC4626Feed, IERC4626} from "src/feeds/ERC4626Feed.sol";
import {ChainlinkBridgeAssetFeed} from "src/feeds/ChainlinkBridgeAssetFeed.sol";
import {ChainlinkBridgeAssetBase} from "test/feedForkTests/ChainlinkBridgeAssetBase.t.sol";
import {ChainlinkBasePriceFeed} from "src/feeds/ChainlinkBasePriceFeed.sol";
import {PriceFeedNoStale} from "src/feeds/PriceFeedNoStale.sol";
import "forge-std/console2.sol";


contract WOETHFeedTest is ChainlinkBridgeAssetBase {
ERC4626Feed vaultFeed;
ChainlinkBasePriceFeed ethWrapper;
ChainlinkBasePriceFeed oEthToEthWrapper;
PriceFeedNoStale feedNoStale;

address oEthToEth = 0x703118C4CbccCBF2AB31913e0f8075fbbb15f563;
address wOeth = 0xDcEe70654261AF21C44c093C300eD3Bb97b78192;
address ethToUsd = 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419;

function setUp() public {
string memory url = vm.rpcUrl("mainnet");
vm.createSelectFork(url);
oEthToEthWrapper = new ChainlinkBasePriceFeed(address(this), oEthToEth, address(0), 86400);
vaultFeed = new ERC4626Feed(wOeth, address(oEthToEthWrapper));
ethWrapper = new ChainlinkBasePriceFeed(address(this),ethToUsd, address(0), 3600);
init(address(vaultFeed), address(ethWrapper), true);
feedNoStale = new PriceFeedNoStale(address(feed));
}

function test_woEth() public {
uint256 woEthToOEth = IERC4626(wOeth).previewRedeem(1e18);
uint256 oEthToEthPrice = uint(oEthToEthWrapper.latestAnswer());
uint256 ethToUsdPrice = uint(ethWrapper.latestAnswer());
uint256 woEthToEthPrice = woEthToOEth * oEthToEthPrice / 1e18;
uint256 woEthToUsdPrice = woEthToEthPrice * ethToUsdPrice / 1e18;
assertEq(woEthToUsdPrice, uint(feed.latestAnswer()));
console2.log(uint(feed.latestAnswer()));
}

function test_feedNoStale() public {
assertEq(feed.latestAnswer(), feedNoStale.latestAnswer());
(,int price, , uint updateAt,) = feedNoStale.latestRoundData();
assertEq(updateAt, block.timestamp);
assertEq(price, feed.latestAnswer());
console2.log(feedNoStale.description());
}
}