文件 1 的 4:Address.sol
pragma solidity ^0.8.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) private pure returns (bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 4:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 3 的 4:SafeERC20.sol
pragma solidity ^0.8.0;
import "IERC20.sol";
import "Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 4 的 4:owner.sol
pragma solidity ^0.8.0;
import "IERC20.sol";
import "SafeERC20.sol";
interface HistoricalPriceConsumerV3 {
function getLatestPriceX1e6(address) external view returns (int);
}
interface VaultV0 {
function expiry() external returns (uint);
function COLLAT_ADDRESS() external returns (address);
function PRICE_FEED() external returns (address);
function LINK_AGGREGATOR() external returns (address);
function setOwner(address newOwner) external;
function settleStrike_MM(uint priceX1e6) external;
function setExpiry(uint arbitraryExpiry) external;
function setMaxCap(uint newDepositCap) external;
function setMaker(address newMaker) external;
function setPriceFeed(HistoricalPriceConsumerV3 newPriceFeed) external;
function emergencyWithdraw() external;
function depositOnBehalf(address tgt, uint256 amt) external;
function setAllowInteraction(bool _flag) external;
}
contract OwnerProxy {
using SafeERC20 for IERC20;
address public multisigAlpha;
address public multisigBeta;
address public teamKey;
address public multisigAlpha_pending;
address public multisigBeta_pending;
address public teamKey_pending;
mapping(bytes32 => uint) public queuedPriceFeed;
event PriceFeedQueued(address indexed _vault, address pricedFeed);
constructor() {
multisigAlpha = msg.sender;
multisigBeta = msg.sender;
teamKey = msg.sender;
}
function setMultisigAlpha(address _newMultisig) external {
require(msg.sender == multisigAlpha, "!multisigAlpha");
multisigAlpha_pending = _newMultisig;
}
function setMultisigBeta(address _newMultisig) external {
require(msg.sender == multisigAlpha || msg.sender == multisigBeta, "!multisigAlpha/Beta");
multisigBeta_pending = _newMultisig;
}
function setTeamKey(address _newTeamKey) external {
require(msg.sender == multisigAlpha || msg.sender == multisigBeta || msg.sender == teamKey, "!ownerKey");
teamKey_pending = _newTeamKey;
}
function acceptMultisigAlpha() external {
require(msg.sender == multisigAlpha_pending, "!multisigAlpha_pending");
multisigAlpha = multisigAlpha_pending;
}
function acceptMultisigBeta() external {
require(msg.sender == multisigBeta_pending, "!multisigBeta_pending");
multisigBeta = multisigBeta_pending;
}
function acceptTeamKey() external {
require(msg.sender == teamKey_pending, "!teamKey_pending");
teamKey = teamKey_pending;
}
function setOwner(VaultV0 _vault, address _newOwner) external {
require(msg.sender == multisigAlpha, "!multisigAlpha");
_vault.setOwner(_newOwner);
}
function emergencyWithdraw(VaultV0 _vault) external {
require(msg.sender == multisigAlpha, "!multisigAlpha");
_vault.emergencyWithdraw();
IERC20 COLLAT = IERC20(_vault.COLLAT_ADDRESS());
COLLAT.safeTransfer(multisigAlpha, COLLAT.balanceOf( address(this) ));
require(COLLAT.balanceOf(address(this)) == 0, "eWithdraw transfer failed.");
}
function queuePriceFeed(VaultV0 _vault, HistoricalPriceConsumerV3 _priceFeed) external {
if (msg.sender == multisigAlpha) {
_vault.setPriceFeed(_priceFeed);
return;
} else if (msg.sender == multisigBeta) {
bytes32 hashedParams = keccak256(abi.encodePacked(_vault, _priceFeed));
if (queuedPriceFeed[hashedParams] == 0) {
queuedPriceFeed[hashedParams] = block.timestamp + 1 days;
emit PriceFeedQueued(address(_vault), address(_priceFeed));
} else {
require(block.timestamp > queuedPriceFeed[hashedParams], "Timelocked");
_vault.setPriceFeed(_priceFeed);
}
} else if (msg.sender == teamKey) {
bytes32 hashedParams = keccak256(abi.encodePacked(_vault, _priceFeed));
if (queuedPriceFeed[hashedParams] > 0) {
require(block.timestamp > queuedPriceFeed[hashedParams], "Timelocked");
_vault.setPriceFeed(_priceFeed);
}
} else {
revert("Not Privileged Key");
}
}
function settleStrike_MM(VaultV0 _vault, uint _priceX1e6) external {
if (msg.sender == multisigAlpha) {
_vault.settleStrike_MM(_priceX1e6);
} else {
uint curPrice = uint(HistoricalPriceConsumerV3(_vault.PRICE_FEED()).getLatestPriceX1e6(_vault.LINK_AGGREGATOR()));
uint upperBound = curPrice;
uint lowerBound = curPrice;
if (msg.sender == multisigBeta) {
upperBound = curPrice * 1200 / 1000;
lowerBound = curPrice * 800 / 1000;
} else if (msg.sender == teamKey) {
upperBound = curPrice * 1050 / 1000;
lowerBound = curPrice * 950 / 1000;
} else {
revert("Not Owner Keys");
}
if (_priceX1e6 > upperBound) revert("Price too high");
if (_priceX1e6 < lowerBound) revert("Price too low");
_vault.settleStrike_MM(_priceX1e6);
}
}
function setExpiry(VaultV0 _vault, uint _expiry) external {
require(msg.sender == multisigBeta, "Not multisigBeta");
require(_vault.expiry() > 0, "Expired");
require(_expiry < _vault.expiry(), "Can only set expiry nearer");
require(_expiry > block.timestamp + 1 hours, "At least 1 hour buffer");
_vault.setExpiry(_expiry);
}
function depositOnBehalf(VaultV0 _vault, address _onBehalfOf, uint _amt) external {
require(msg.sender == teamKey, "Not teamKey");
IERC20 COLLAT = IERC20(_vault.COLLAT_ADDRESS());
COLLAT.transferFrom(msg.sender, address(this), _amt);
COLLAT.approve(address(_vault), _amt);
_vault.depositOnBehalf(_onBehalfOf, _amt);
require(COLLAT.balanceOf(address(this)) == 0, "Balance Left On OwnerProxy");
}
function setMaxCap(VaultV0 _vault, uint _maxCap) external {
require(msg.sender == teamKey, "Not teamKey");
_vault.setMaxCap(_maxCap);
}
function setAllowInteraction(VaultV0 _vault, bool _flag) external {
require(msg.sender == teamKey, "Not teamKey");
require(_vault.expiry() == 0, "Not Expired");
_vault.setAllowInteraction(_flag);
}
function setMaker(VaultV0 _vault, address _newMaker) external {
if (msg.sender == multisigBeta) {
_vault.setMaker(_newMaker);
} else if (msg.sender == teamKey) {
require(_vault.expiry() == 0, "Not Expired");
_vault.setMaker(_newMaker);
} else {
revert("!teamKey,!musigBeta");
}
}
}
{
"compilationTarget": {
"owner.sol": "OwnerProxy"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_vault","type":"address"},{"indexed":false,"internalType":"address","name":"pricedFeed","type":"address"}],"name":"PriceFeedQueued","type":"event"},{"inputs":[],"name":"acceptMultisigAlpha","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptMultisigBeta","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptTeamKey","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract VaultV0","name":"_vault","type":"address"},{"internalType":"address","name":"_onBehalfOf","type":"address"},{"internalType":"uint256","name":"_amt","type":"uint256"}],"name":"depositOnBehalf","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract VaultV0","name":"_vault","type":"address"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"multisigAlpha","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"multisigAlpha_pending","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"multisigBeta","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"multisigBeta_pending","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract VaultV0","name":"_vault","type":"address"},{"internalType":"contract HistoricalPriceConsumerV3","name":"_priceFeed","type":"address"}],"name":"queuePriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"queuedPriceFeed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract VaultV0","name":"_vault","type":"address"},{"internalType":"bool","name":"_flag","type":"bool"}],"name":"setAllowInteraction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract VaultV0","name":"_vault","type":"address"},{"internalType":"uint256","name":"_expiry","type":"uint256"}],"name":"setExpiry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract VaultV0","name":"_vault","type":"address"},{"internalType":"address","name":"_newMaker","type":"address"}],"name":"setMaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract VaultV0","name":"_vault","type":"address"},{"internalType":"uint256","name":"_maxCap","type":"uint256"}],"name":"setMaxCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newMultisig","type":"address"}],"name":"setMultisigAlpha","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newMultisig","type":"address"}],"name":"setMultisigBeta","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract VaultV0","name":"_vault","type":"address"},{"internalType":"address","name":"_newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newTeamKey","type":"address"}],"name":"setTeamKey","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract VaultV0","name":"_vault","type":"address"},{"internalType":"uint256","name":"_priceX1e6","type":"uint256"}],"name":"settleStrike_MM","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"teamKey","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"teamKey_pending","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]