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

add fxusd nav adapter #95

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ out = "out"
libs = ["lib"]
optimizer_runs = 999999 # Etherscan does not support verifying contracts with more optimizer runs.
via_ir = true
evm_version = "paris"
evm_version = "shanghai" # use shanghai to pass tests in FxUSDNetAssetValueChainlinkAdapterTest

[profile.default.fmt]
wrap_comments = true
Expand Down
38 changes: 38 additions & 0 deletions src/fxusd-nav-adapter/FxUSDNetAssetValueChainlinkAdapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.21;

import {IFxUSD} from "./interfaces/IFxUSD.sol";
import {MinimalAggregatorV3Interface} from "./interfaces/MinimalAggregatorV3Interface.sol";

/// @title FxUSDNetAssetValueChainlinkAdapter
/// @author Aladdin DAO
/// @custom:contact [email protected]
/// @notice fxUSD net asset value price feed.
/// @dev This contract should only be deployed on Ethereum and used as a price feed for Morpho oracles.
contract FxUSDNetAssetValueChainlinkAdapter is MinimalAggregatorV3Interface {
/// @inheritdoc MinimalAggregatorV3Interface
// @dev The calculated price has 18 decimals precision, whatever the value of `decimals`.
uint8 public constant decimals = 18;

/// @notice The description of the price feed.
string public constant description = "fxUSD net asset value";

/// @notice The address of fxUSD on Ethereum.
IFxUSD public immutable fxUSD;

constructor(IFxUSD _fxUSD) {
fxUSD = _fxUSD;
}

/// @inheritdoc MinimalAggregatorV3Interface
/// @dev Returns zero for roundId, startedAt, updatedAt and answeredInRound.
/// @dev Silently overflows if `nav`'s return value is greater than `type(int256).max`.
function latestRoundData()
external
view
returns (uint80, int256, uint256, uint256, uint80)
{
// It is assumed that `fxUSD.nav()` returns a price with 18 decimals precision.
return (0, int256(fxUSD.nav()), 0, 0, 0);
}
}
7 changes: 7 additions & 0 deletions src/fxusd-nav-adapter/interfaces/IFxUSD.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

interface IFxUSD {
/// @notice Return the nav of fxUSD.
function nav() external view returns (uint256);
}
17 changes: 17 additions & 0 deletions src/fxusd-nav-adapter/interfaces/MinimalAggregatorV3Interface.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;

/// @dev Inspired by
/// https://github.com/smartcontractkit/chainlink/blob/master/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol
/// @dev This is the minimal feed interface required by `MorphoChainlinkOracleV2`.
interface MinimalAggregatorV3Interface {
/// @notice Returns the precision of the feed.
function decimals() external view returns (uint8);

/// @notice Returns Chainlink's `latestRoundData` return values.
/// @notice Only the `answer` field is used by `MorphoChainlinkOracleV2`.
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
46 changes: 46 additions & 0 deletions test/FxUSDNetAssetValueChainlinkAdapterTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "./helpers/Constants.sol";
import "../lib/forge-std/src/Test.sol";
import {MorphoChainlinkOracleV2} from "../src/morpho-chainlink/MorphoChainlinkOracleV2.sol";
import "../src/fxusd-nav-adapter/FxUSDNetAssetValueChainlinkAdapter.sol";

contract FxUSDNetAssetValueChainlinkAdapterTest is Test {
IFxUSD internal constant fxUSD = IFxUSD(0x085780639CC2cACd35E474e71f4d000e2405d8f6);

FxUSDNetAssetValueChainlinkAdapter internal adapter;
MorphoChainlinkOracleV2 internal morphoOracle;

function setUp() public {
vm.createSelectFork(vm.envString("ETH_RPC_URL"));
require(block.chainid == 1, "chain isn't Ethereum");
adapter = new FxUSDNetAssetValueChainlinkAdapter(fxUSD);
morphoOracle = new MorphoChainlinkOracleV2(
vaultZero, 1, AggregatorV3Interface(address(adapter)), feedZero, 18, vaultZero, 1, feedZero, feedZero, 18
);
}

function testDecimals() public {
assertEq(adapter.decimals(), uint8(18));
}

function testDescription() public {
assertEq(adapter.description(), "fxUSD net asset value");
}

function testLatestRoundData() public {
(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) =
adapter.latestRoundData();
assertEq(roundId, 0);
assertEq(uint256(answer), fxUSD.nav());
assertEq(startedAt, 0);
assertEq(updatedAt, 0);
assertEq(answeredInRound, 0);
}

function testOracleFxUSDNav() public {
(, int256 expectedPrice,,,) = adapter.latestRoundData();
assertEq(morphoOracle.price(), uint256(expectedPrice) * 10 ** (36 + 18 - 18 - 18));
}
}