-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: carefully handle optional calls to external contracts (#297)
- Loading branch information
Showing
7 changed files
with
235 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
// Gearbox Protocol. Generalized leverage for DeFi protocols | ||
// (c) Gearbox Foundation, 2024. | ||
pragma solidity ^0.8.17; | ||
|
||
/// @title Optional call library | ||
/// @notice Implements a function that calls a contract that may not have | ||
/// an expected selector. Handles the case where the contract has a fallback function that may or may | ||
/// not change state. | ||
library OptionalCall { | ||
function staticCallOptionalSafe(address target, bytes memory data, uint256 gasAllowance) | ||
internal | ||
view | ||
returns (bool, bytes memory) | ||
{ | ||
(bool success, bytes memory returnData) = target.staticcall{gas: gasAllowance}(data); | ||
return returnData.length > 0 ? (success, returnData) : (false, returnData); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
// Gearbox Protocol. Generalized leverage for DeFi protocols | ||
// (c) Gearbox Foundation, 2023. | ||
pragma solidity ^0.8.10; | ||
|
||
contract PriceFeedFallbackMock { | ||
uint256 public constant version = 3_10; | ||
bytes32 public constant contractType = "PRICE_FEED_FALLBACK_MOCK"; | ||
|
||
int256 public price; | ||
uint8 public immutable decimals; | ||
|
||
bool public immutable changeStateInFallback; | ||
bool public fallbackStateFlag; | ||
|
||
uint80 internal roundId; | ||
uint256 startedAt; | ||
uint256 updatedAt; | ||
uint80 internal answerInRound; | ||
|
||
bool internal revertOnLatestRound; | ||
|
||
constructor(int256 _price, uint8 _decimals, bool _changeStateInFallback) { | ||
price = _price; | ||
decimals = _decimals; | ||
changeStateInFallback = _changeStateInFallback; | ||
roundId = 80; | ||
answerInRound = 80; | ||
// set to quite far in the future | ||
startedAt = block.timestamp + 36500 days; | ||
updatedAt = block.timestamp + 36500 days; | ||
} | ||
|
||
function setParams(uint80 _roundId, uint256 _startedAt, uint256 _updatedAt, uint80 _answerInRound) external { | ||
roundId = _roundId; | ||
startedAt = _startedAt; | ||
updatedAt = _updatedAt; | ||
answerInRound = _answerInRound; | ||
} | ||
|
||
function description() external pure returns (string memory) { | ||
return "price oracle"; | ||
} | ||
|
||
function setPrice(int256 newPrice) external { | ||
price = newPrice; | ||
} | ||
|
||
function latestRoundData() | ||
external | ||
view | ||
returns ( | ||
uint80, // roundId, | ||
int256, // answer, | ||
uint256, // startedAt, | ||
uint256, // updatedAt, | ||
uint80 //answeredInRound | ||
) | ||
{ | ||
if (revertOnLatestRound) revert(); | ||
|
||
return (roundId, price, startedAt, updatedAt, answerInRound); | ||
} | ||
|
||
fallback() external { | ||
if (changeStateInFallback) { | ||
fallbackStateFlag = true; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.10; | ||
|
||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
|
||
contract WETHFallbackMock is IERC20 { | ||
string public name = "Wrapped Ether"; | ||
string public symbol = "WETH"; | ||
uint8 public decimals = 18; | ||
|
||
bool public depositOnFallback = true; | ||
|
||
// 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; | ||
|
||
function mint(address to, uint256 amount) external { | ||
balanceOf[to] += amount; | ||
} | ||
|
||
function setDepositOnFallback(bool _depositOnFallback) external { | ||
depositOnFallback = _depositOnFallback; | ||
} | ||
|
||
fallback() external payable { | ||
if (depositOnFallback) { | ||
deposit(); // T:[WM-1] | ||
} | ||
} | ||
|
||
function deposit() public payable { | ||
balanceOf[msg.sender] += msg.value; // T:[WM-1] | ||
emit Deposit(msg.sender, msg.value); // T:[WM-1] | ||
} | ||
|
||
function withdraw(uint256 wad) public { | ||
require(balanceOf[msg.sender] >= wad); // T:[WM-2] | ||
balanceOf[msg.sender] -= wad; // T:[WM-2] | ||
payable(msg.sender).transfer(wad); // T:[WM-3] | ||
emit Withdrawal(msg.sender, wad); // T:[WM-4] | ||
} | ||
|
||
function totalSupply() public view returns (uint256) { | ||
return address(this).balance; // T:[WM-1, 2] | ||
} | ||
|
||
function approve(address guy, uint256 wad) public returns (bool) { | ||
allowance[msg.sender][guy] = wad; // T:[WM-3] | ||
emit Approval(msg.sender, guy, wad); // T:[WM-3] | ||
return true; | ||
} | ||
|
||
function transfer(address dst, uint256 wad) public returns (bool) { | ||
return transferFrom(msg.sender, dst, wad); // T:[WM-4,5,6] | ||
} | ||
|
||
function transferFrom(address src, address dst, uint256 wad) public returns (bool) { | ||
require(balanceOf[src] >= wad); // T:[WM-4] | ||
|
||
if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { | ||
require(allowance[src][msg.sender] >= wad); // T:[WM-4] | ||
allowance[src][msg.sender] -= wad; // T:[WM-7] | ||
} | ||
|
||
balanceOf[src] -= wad; // T:[WM-5] | ||
balanceOf[dst] += wad; // T:[WM-5] | ||
|
||
emit Transfer(src, dst, wad); // T:[WM-6] | ||
|
||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
// Gearbox Protocol. Generalized leverage for DeFi protocols | ||
// (c) Gearbox Foundation, 2023. | ||
pragma solidity ^0.8.17; | ||
|
||
import {PriceFeedValidationTrait} from "../../../traits/PriceFeedValidationTrait.sol"; | ||
import {TestHelper} from "../../lib/helper.sol"; | ||
import {PriceFeedFallbackMock} from "../../mocks/oracles/PriceFeedFallbackMock.sol"; | ||
|
||
contract PriceFeedValidationTraitUnitTest is PriceFeedValidationTrait, TestHelper { | ||
function test_U_PFVT_01_validatePriceFeed_works_correctly_for_PF_with_fallback() public { | ||
address priceFeed = address(new PriceFeedFallbackMock(1e8, 8, false)); | ||
|
||
_validatePriceFeed(priceFeed, 1000); | ||
|
||
priceFeed = address(new PriceFeedFallbackMock(1e8, 8, true)); | ||
_validatePriceFeed(priceFeed, 1000); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters