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 external oracle tester contract test #1081

Merged
merged 4 commits into from
Sep 12, 2024
Merged
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
9 changes: 8 additions & 1 deletion protocol/contracts/libraries/Oracle/LibUsdOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,15 @@ library LibUsdOracle {
// Non-zero addresses are enforced in verifyOracleImplementation, this is just an extra check.
if (oracleImpl.target == address(0)) return 0;

// if `isMillion` is enabled, append the boolean into the oracleImpl,
// such that the oracle implementation can use the data.
bytes memory oracleImplData = oracleImpl.data;
if (isMillion) {
oracleImplData = abi.encodePacked(oracleImpl.data, isMillion);
}

(bool success, bytes memory data) = oracleImpl.target.staticcall(
abi.encodeWithSelector(oracleImpl.selector, tokenDecimals, lookback, oracleImpl.data)
abi.encodeWithSelector(oracleImpl.selector, tokenDecimals, lookback, oracleImplData)
);

if (!success) return 0;
Expand Down
20 changes: 9 additions & 11 deletions protocol/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ test = 'test'
script = 'script'
no_match_test = "testDiff"
out = 'out'
libs = [
'node_modules',
'lib'
]
libs = ['node_modules', 'lib']
cache = true
cache_path = 'cache'
force = false
Expand All @@ -34,18 +31,19 @@ bytecode_hash = 'ipfs'
verbosity = 0
ffi = true
fs_permissions = [
{ access = "read", path = "../"},
{ access = "read", path = "./out" }
{ access = "read", path = "../" },
{ access = "read", path = "./out" },
]
ignored_warnings_from = [
"test",
"contracts/interfaces/IMockFBeanstalk.sol",
"contracts/mocks/"
"contracts/mocks/",
]
gas_reports = ['*']
# Cache to `$HOME/.foundry/cache/<chain id>/<block number>`.
# Cache to `$HOME/.foundry/cache/<chain id>/<block number>`.
no_storage_caching = false

no_match_contract = "ReseedStateTest"

[profile.differential]
match_test = "testDiff"
no_match_test = "a^"
Expand All @@ -55,9 +53,9 @@ chains = 'all'
endpoints = 'all'

[rpc_endpoints]
mainnet = "${FORKING_RPC}"
mainnet = "${FORKING_RPC}"

[invariant]
runs = 4
depth = 2048
fail_on_revert = true
fail_on_revert = true
48 changes: 48 additions & 0 deletions protocol/test/foundry/silo/Oracle.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,28 @@ contract OracleTest is TestHelper {
assertEq(xEthTimeout, _xEthTimeout);
}

function testExternalOracleImplementation() public {
// setup oracle implementation
address oracle = address(new ExternalOracleTester());
vm.prank(BEANSTALK);
bs.updateOracleImplementationForToken(
WBTC,
IMockFBeanstalk.Implementation(
oracle,
ExternalOracleTester.getPrice.selector,
bytes1(0x00), // 0x00 is external
abi.encode(LibChainlinkOracle.FOUR_HOUR_TIMEOUT)
)
);

uint256 tokenPriceWBTC = OracleFacet(BEANSTALK).getTokenUsdPrice(WBTC); // should be 50000e6
assertEq(tokenPriceWBTC, 50000e6, "getTokenUsdPrice wbtc");

// also exercise getMillionUsdPrice
uint256 tokenPriceWBTCMillion = OracleFacet(BEANSTALK).getMillionUsdPrice(WBTC, 0);
assertEq(tokenPriceWBTCMillion, 50000e12, "getMillionUsdPrice wbtc");
}

function testGetOracleImplementationForToken() public {
vm.prank(BEANSTALK);
bs.updateOracleImplementationForToken(
Expand Down Expand Up @@ -422,3 +444,29 @@ contract OracleTest is TestHelper {
);
}
}

contract ExternalOracleTester {
function getPrice(
uint256 tokenDecimals,
uint256 lookback,
bytes memory data
) external view returns (uint256) {
uint256 timeout;
bool isMillion = false;

if (data.length > 32) {
assembly {
timeout := mload(add(data, 32))
isMillion := byte(0, mload(add(data, 64)))
}
} else if (data.length == 32) {
// only timeout supplied
(timeout) = abi.decode(data, (uint256));
}

if (isMillion) {
return 50000e12;
}
return 50000e6;
}
}
Loading