diff --git a/.env.example b/.env.example index c69316dd5..07910968f 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,10 @@ MAINNET_MNEMONIC= KOVAN_MNEMONIC= +GOERLI_MNEMONIC= AVAX_MNEMONIC= FUJI_MNEMONIC= MAINNET_URI= +GOERLI_URI= KOVAN_URI= TEST_URI= AVAX_URI="https://api.avax.network/ext/bc/C/rpc" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8cee224e0..a05820011 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,6 +37,8 @@ jobs: TEST_URI: ${{ secrets.TEST_URI }} KOVAN_URI: ${{ secrets.TEST_URI }} KOVAN_MNEMONIC: ${{ secrets.KOVAN_MNEMONIC }} + GOERLI_URI: ${{ secrets.GOERLI_URI }} + GOERLI_MNEMONIC: ${{ secrets.GOERLI_MNEMONIC }} MAINNET_URI: ${{ secrets.MAINNET_URI }} MAINNET_MNEMONIC: ${{ secrets.MAINNET_MNEMONIC }} AVAX_URI: "https://api.avax.network/ext/bc/C/rpc" @@ -49,6 +51,8 @@ jobs: TEST_URI: ${{ secrets.TEST_URI }} KOVAN_URI: ${{ secrets.TEST_URI }} KOVAN_MNEMONIC: ${{ secrets.KOVAN_MNEMONIC }} + GOERLI_URI: ${{ secrets.GOERLI_URI }} + GOERLI_MNEMONIC: ${{ secrets.GOERLI_MNEMONIC }} MAINNET_URI: ${{ secrets.MAINNET_URI }} MAINNET_MNEMONIC: ${{ secrets.MAINNET_MNEMONIC }} AVAX_URI: "https://api.avax.network/ext/bc/C/rpc" diff --git a/constants/constants.ts b/constants/constants.ts index d5b478500..a128689d6 100644 --- a/constants/constants.ts +++ b/constants/constants.ts @@ -5,6 +5,7 @@ export enum CHAINID { ETH_MAINNET = 1, // eslint-disable-line no-unused-vars ETH_KOVAN = 42, // eslint-disable-line no-unused-vars + ETH_GOERLI = 5, // eslint-disable-line no-unused-vars AVAX_MAINNET = 43114, // eslint-disable-line no-unused-vars AVAX_FUJI = 43113, // eslint-disable-line no-unused-vars } diff --git a/deployments/goerli/.chainId b/deployments/goerli/.chainId new file mode 100644 index 000000000..7813681f5 --- /dev/null +++ b/deployments/goerli/.chainId @@ -0,0 +1 @@ +5 \ No newline at end of file diff --git a/deployments/goerli/Swap.json b/deployments/goerli/Swap.json new file mode 100644 index 000000000..1655968cf --- /dev/null +++ b/deployments/goerli/Swap.json @@ -0,0 +1,261 @@ +{ + "address": "0x9f4033afd50cFbbe2C3FE172069c63A9B2c3Dcb8", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_logic", + "type": "address" + }, + { + "internalType": "address", + "name": "admin_", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "adminAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "changeAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "implementationAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0xaeae5d9afc8237a98c196d636d811c9a58d9b5ad050dc079d82eba4cb3f60f8e", + "receipt": { + "to": null, + "from": "0xF8368119Bb1073Cf01B841848725d81b542A4c19", + "contractAddress": "0x9f4033afd50cFbbe2C3FE172069c63A9B2c3Dcb8", + "transactionIndex": 14, + "gasUsed": "621182", + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000120000000000000000000000000000000000000010000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000008000000000800000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000002000000000000000000000000000000000000000000010000000", + "blockHash": "0x817151c087e06e18e6dcfe0a99f8788b3c90db400057a6a8002c67abe709b840", + "transactionHash": "0xaeae5d9afc8237a98c196d636d811c9a58d9b5ad050dc079d82eba4cb3f60f8e", + "logs": [ + { + "transactionIndex": 14, + "blockNumber": 7763851, + "transactionHash": "0xaeae5d9afc8237a98c196d636d811c9a58d9b5ad050dc079d82eba4cb3f60f8e", + "address": "0x9f4033afd50cFbbe2C3FE172069c63A9B2c3Dcb8", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000f8368119bb1073cf01b841848725d81b542a4c19" + ], + "data": "0x", + "logIndex": 14, + "blockHash": "0x817151c087e06e18e6dcfe0a99f8788b3c90db400057a6a8002c67abe709b840" + }, + { + "transactionIndex": 14, + "blockNumber": 7763851, + "transactionHash": "0xaeae5d9afc8237a98c196d636d811c9a58d9b5ad050dc079d82eba4cb3f60f8e", + "address": "0x9f4033afd50cFbbe2C3FE172069c63A9B2c3Dcb8", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x000000000000000000000000f8368119bb1073cf01b841848725d81b542a4c19", + "0x000000000000000000000000f8368119bb1073cf01b841848725d81b542a4c19" + ], + "data": "0x", + "logIndex": 15, + "blockHash": "0x817151c087e06e18e6dcfe0a99f8788b3c90db400057a6a8002c67abe709b840" + } + ], + "blockNumber": 7763851, + "cumulativeGasUsed": "5286344", + "status": 1, + "byzantium": true + }, + "args": [ + "0xf6d1C9CE7d7E0BeC2abC10004F5E43147D4d6fA8", + "0x422f7Bb366608723c8fe61Ac6D923023dCCBC3d7", + "0x077f224a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000f8368119bb1073cf01b841848725d81b542a4c19000000000000000000000000000000000000000000000000000000000000000b524942424f4e205357415000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013100000000000000000000000000000000000000000000000000000000000000" + ], + "solcInputHash": "7ecbd1f874c019a019ec6af82a4cbc00", + "metadata": "{\"compiler\":{\"version\":\"0.8.4+commit.c7e474f2\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_logic\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"admin_\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"adminAddress\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"changeAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"implementationAddress\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"upgradeToAndCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"details\":\"This contract combines an upgradeability proxy with an authorization mechanism for administrative tasks. All external functions in this contract must be guarded by the `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity feature proposal that would enable this to be done automatically.\",\"events\":{\"AdminChanged(address,address)\":{\"details\":\"Emitted when the administration has been transferred.\",\"params\":{\"newAdmin\":\"Address of the new admin.\",\"previousAdmin\":\"Address of the previous admin.\"}}},\"kind\":\"dev\",\"methods\":{\"admin()\":{\"returns\":{\"adminAddress\":\"The address of the proxy admin.\"}},\"changeAdmin(address)\":{\"details\":\"Changes the admin of the proxy. Only the current admin can call this function.\",\"params\":{\"newAdmin\":\"Address to transfer proxy administration to.\"}},\"constructor\":{\"params\":{\"_data\":\"Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\",\"_logic\":\"address of the initial implementation.\",\"admin_\":\"Address of the proxy administrator.\"}},\"implementation()\":{\"returns\":{\"implementationAddress\":\"The address of the implementation.\"}},\"upgradeTo(address)\":{\"details\":\"Upgrade the backing implementation of the proxy. Only the admin can call this function.\",\"params\":{\"newImplementation\":\"Address of the new implementation.\"}},\"upgradeToAndCall(address,bytes)\":{\"details\":\"Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.\",\"params\":{\"data\":\"Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\",\"newImplementation\":\"Address of the new implementation.\"}}},\"stateVariables\":{\"ADMIN_SLOT\":{\"details\":\"Storage slot with the admin of the contract. This is the keccak-256 hash of \\\"eip1967.proxy.admin\\\" subtracted by 1, and is validated in the constructor.\"}},\"title\":\"AdminUpgradeabilityProxy\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"constructor\":{\"notice\":\"Contract constructor.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/vendor/upgrades/AdminUpgradeabilityProxy.sol\":\"AdminUpgradeabilityProxy\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x3336baae5cf23e94274d75336e2d412193be508504aee185e61dc7d58cd05c8a\",\"license\":\"MIT\"},\"contracts/vendor/upgrades/AdminUpgradeabilityProxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity >=0.6.8;\\n\\nimport './UpgradeabilityProxy.sol';\\n\\n/**\\n * @title AdminUpgradeabilityProxy\\n * @dev This contract combines an upgradeability proxy with an authorization\\n * mechanism for administrative tasks.\\n * All external functions in this contract must be guarded by the\\n * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity\\n * feature proposal that would enable this to be done automatically.\\n */\\ncontract AdminUpgradeabilityProxy is UpgradeabilityProxy {\\n /**\\n * Contract constructor.\\n * @param _logic address of the initial implementation.\\n * @param admin_ Address of the proxy administrator.\\n * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.\\n * It should include the signature and the parameters of the function to be called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\\n */\\n constructor(address _logic, address admin_, bytes memory _data) UpgradeabilityProxy(_logic, _data) payable {\\n assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));\\n _setAdmin(admin_);\\n }\\n\\n /**\\n * @dev Emitted when the administration has been transferred.\\n * @param previousAdmin Address of the previous admin.\\n * @param newAdmin Address of the new admin.\\n */\\n event AdminChanged(address previousAdmin, address newAdmin);\\n\\n /**\\n * @dev Storage slot with the admin of the contract.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.admin\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n\\n bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\\n\\n /**\\n * @dev Modifier to check whether the `msg.sender` is the admin.\\n * If it is, it will run the function. Otherwise, it will delegate the call\\n * to the implementation.\\n */\\n modifier ifAdmin() {\\n if (msg.sender == _admin()) {\\n _;\\n } else {\\n _fallback();\\n }\\n }\\n\\n /**\\n * @return adminAddress The address of the proxy admin.\\n */\\n function admin() external ifAdmin returns (address adminAddress) {\\n return _admin();\\n }\\n\\n /**\\n * @return implementationAddress The address of the implementation.\\n */\\n function implementation() external ifAdmin returns (address implementationAddress) {\\n return _implementation();\\n }\\n\\n /**\\n * @dev Changes the admin of the proxy.\\n * Only the current admin can call this function.\\n * @param newAdmin Address to transfer proxy administration to.\\n */\\n function changeAdmin(address newAdmin) external ifAdmin {\\n require(newAdmin != address(0), \\\"Cannot change the admin of a proxy to the zero address\\\");\\n emit AdminChanged(_admin(), newAdmin);\\n _setAdmin(newAdmin);\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy.\\n * Only the admin can call this function.\\n * @param newImplementation Address of the new implementation.\\n */\\n function upgradeTo(address newImplementation) external ifAdmin {\\n _upgradeTo(newImplementation);\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy and call a function\\n * on the new implementation.\\n * This is useful to initialize the proxied contract.\\n * @param newImplementation Address of the new implementation.\\n * @param data Data to send as msg.data in the low level call.\\n * It should include the signature and the parameters of the function to be called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n */\\n function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {\\n _upgradeTo(newImplementation);\\n (bool success,) = newImplementation.delegatecall(data);\\n require(success);\\n }\\n\\n /**\\n * @return adm The admin slot.\\n */\\n function _admin() internal view returns (address adm) {\\n bytes32 slot = ADMIN_SLOT;\\n assembly {\\n adm := sload(slot)\\n }\\n }\\n\\n /**\\n * @dev Sets the address of the proxy admin.\\n * @param newAdmin Address of the new proxy admin.\\n */\\n function _setAdmin(address newAdmin) internal {\\n bytes32 slot = ADMIN_SLOT;\\n\\n assembly {\\n sstore(slot, newAdmin)\\n }\\n }\\n\\n /**\\n * @dev Only fall back when the sender is not the admin.\\n */\\n function _willFallback() internal override virtual {\\n require(msg.sender != _admin(), \\\"Cannot call fallback function from the proxy admin\\\");\\n super._willFallback();\\n }\\n}\\n\",\"keccak256\":\"0xb4950866bd640d9348e0b7cc56218d9f24312b7ec3c35fcf7ec26753bb3e48ce\",\"license\":\"MIT\"},\"contracts/vendor/upgrades/Proxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity =0.8.4;\\n\\n/**\\n * @title Proxy\\n * @dev Implements delegation of calls to other contracts, with proper\\n * forwarding of return values and bubbling of failures.\\n * It defines a fallback function that delegates all calls to the address\\n * returned by the abstract _implementation() internal function.\\n */\\nabstract contract Proxy {\\n /**\\n * @dev Fallback function.\\n * Implemented entirely in `_fallback`.\\n */\\n fallback () payable external {\\n _fallback();\\n }\\n\\n /**\\n * @dev Receive function.\\n * Implemented entirely in `_fallback`.\\n */\\n receive () payable external {\\n // _fallback();\\n }\\n\\n /**\\n * @return The Address of the implementation.\\n */\\n function _implementation() internal virtual view returns (address);\\n\\n /**\\n * @dev Delegates execution to an implementation contract.\\n * This is a low level function that doesn't return to its internal call site.\\n * It will return to the external caller whatever the implementation returns.\\n * @param implementation Address to delegate.\\n */\\n function _delegate(address implementation) internal {\\n assembly {\\n // Copy msg.data. We take full control of memory in this inline assembly\\n // block because it will not return to Solidity code. We overwrite the\\n // Solidity scratch pad at memory position 0.\\n calldatacopy(0, 0, calldatasize())\\n\\n // Call the implementation.\\n // out and outsize are 0 because we don't know the size yet.\\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\\n\\n // Copy the returned data.\\n returndatacopy(0, 0, returndatasize())\\n\\n switch result\\n // delegatecall returns 0 on error.\\n case 0 { revert(0, returndatasize()) }\\n default { return(0, returndatasize()) }\\n }\\n }\\n\\n /**\\n * @dev Function that is run as the first thing in the fallback function.\\n * Can be redefined in derived contracts to add functionality.\\n * Redefinitions must call super._willFallback().\\n */\\n function _willFallback() internal virtual {\\n }\\n\\n /**\\n * @dev fallback implementation.\\n * Extracted to enable manual triggering.\\n */\\n function _fallback() internal {\\n _willFallback();\\n _delegate(_implementation());\\n }\\n}\\n\",\"keccak256\":\"0xb7343b1d10f8f15fa7588e6d867d172a8f4aa03ab7390bfe5b6d387f21e85870\",\"license\":\"MIT\"},\"contracts/vendor/upgrades/UpgradeabilityProxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity =0.8.4;\\n\\nimport './Proxy.sol';\\nimport '@openzeppelin/contracts/utils/Address.sol';\\n\\n/**\\n * @title UpgradeabilityProxy\\n * @dev This contract implements a proxy that allows to change the\\n * implementation address to which it will delegate.\\n * Such a change is called an implementation upgrade.\\n */\\ncontract UpgradeabilityProxy is Proxy {\\n /**\\n * @dev Contract constructor.\\n * @param _logic Address of the initial implementation.\\n * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.\\n * It should include the signature and the parameters of the function to be called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\\n */\\n constructor(address _logic, bytes memory _data) payable {\\n assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));\\n _setImplementation(_logic);\\n if(_data.length > 0) {\\n (bool success,) = _logic.delegatecall(_data);\\n require(success);\\n }\\n }\\n\\n /**\\n * @dev Emitted when the implementation is upgraded.\\n * @param implementation Address of the new implementation.\\n */\\n event Upgraded(address indexed implementation);\\n\\n /**\\n * @dev Storage slot with the address of the current implementation.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.implementation\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n\\n /**\\n * @dev Returns the current implementation.\\n * @return impl Address of the current implementation\\n */\\n function _implementation() internal override view returns (address impl) {\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n assembly {\\n impl := sload(slot)\\n }\\n }\\n\\n /**\\n * @dev Upgrades the proxy to a new implementation.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _upgradeTo(address newImplementation) internal {\\n _setImplementation(newImplementation);\\n emit Upgraded(newImplementation);\\n }\\n\\n /**\\n * @dev Sets the implementation address of the proxy.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _setImplementation(address newImplementation) internal {\\n require(Address.isContract(newImplementation), \\\"Cannot set a proxy implementation to a non-contract address\\\");\\n\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n\\n assembly {\\n sstore(slot, newImplementation)\\n }\\n }\\n}\\n\",\"keccak256\":\"0xbc609da5fd7f73c75e06d3239203a36f51f1b0507c08ed0e728887ed6ade8d31\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x608060405260405162000a8038038062000a80833981016040819052620000269162000243565b82816200005560017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6200033d565b60008051602062000a60833981519152146200008157634e487b7160e01b600052600160045260246000fd5b6200008c8262000180565b80511562000103576000826001600160a01b031682604051620000b091906200031f565b600060405180830381855af49150503d8060008114620000ed576040519150601f19603f3d011682016040523d82523d6000602084013e620000f2565b606091505b50509050806200010157600080fd5b505b5062000133905060017fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61046200033d565b60008051602062000a40833981519152146200015f57634e487b7160e01b600052600160045260246000fd5b620001778260008051602062000a4083398151915255565b505050620003aa565b62000196816200022060201b620003d71760201c565b6200020d5760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e747261637420616464726573730000000000606482015260840160405180910390fd5b60008051602062000a6083398151915255565b3b151590565b80516001600160a01b03811681146200023e57600080fd5b919050565b60008060006060848603121562000258578283fd5b620002638462000226565b9250620002736020850162000226565b60408501519092506001600160401b038082111562000290578283fd5b818601915086601f830112620002a4578283fd5b815181811115620002b957620002b962000394565b604051601f8201601f19908116603f01168101908382118183101715620002e457620002e462000394565b81604052828152896020848701011115620002fd578586fd5b6200031083602083016020880162000361565b80955050505050509250925092565b600082516200033381846020870162000361565b9190910192915050565b6000828210156200035c57634e487b7160e01b81526011600452602481fd5b500390565b60005b838110156200037e57818101518382015260200162000364565b838111156200038e576000848401525b50505050565b634e487b7160e01b600052604160045260246000fd5b61068680620003ba6000396000f3fe60806040526004361061004e5760003560e01c80633659cfe61461005f5780634f1ef2861461007f5780635c60da1b146100925780638f283970146100c3578063f851a440146100e357610055565b3661005557005b61005d6100f8565b005b34801561006b57600080fd5b5061005d61007a366004610581565b610132565b61005d61008d3660046105a2565b61016f565b34801561009e57600080fd5b506100a761021e565b6040516001600160a01b03909116815260200160405180910390f35b3480156100cf57600080fd5b5061005d6100de366004610581565b610280565b3480156100ef57600080fd5b506100a7610392565b6101006103dd565b61013061012b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b610469565b565b600080516020610631833981519152546001600160a01b0316336001600160a01b03161415610167576101648161048d565b50565b6101646100f8565b600080516020610631833981519152546001600160a01b0316336001600160a01b03161415610211576101a18361048d565b6000836001600160a01b031683836040516101bd929190610620565b600060405180830381855af49150503d80600081146101f8576040519150601f19603f3d011682016040523d82523d6000602084013e6101fd565b606091505b505090508061020b57600080fd5b50505050565b6102196100f8565b505050565b60006102366000805160206106318339815191525490565b6001600160a01b0316336001600160a01b0316141561027557507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b61027d6100f8565b90565b600080516020610631833981519152546001600160a01b0316336001600160a01b03161415610167576001600160a01b0381166103235760405162461bcd60e51b815260206004820152603660248201527f43616e6e6f74206368616e6765207468652061646d696e206f6620612070726f604482015275787920746f20746865207a65726f206164647265737360501b60648201526084015b60405180910390fd5b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f61035a6000805160206106318339815191525490565b604080516001600160a01b03928316815291841660208301520160405180910390a16101648160008051602061063183398151915255565b60006103aa6000805160206106318339815191525490565b6001600160a01b0316336001600160a01b0316141561027557506000805160206106318339815191525490565b3b151590565b600080516020610631833981519152546001600160a01b0316336001600160a01b031614156101305760405162461bcd60e51b815260206004820152603260248201527f43616e6e6f742063616c6c2066616c6c6261636b2066756e6374696f6e20667260448201527137b6903a343290383937bc3c9030b236b4b760711b606482015260840161031a565b3660008037600080366000845af43d6000803e808015610488573d6000f35b3d6000fd5b610496816104cd565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b803b6105415760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e747261637420616464726573730000000000606482015260840161031a565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55565b80356001600160a01b038116811461057c57600080fd5b919050565b600060208284031215610592578081fd5b61059b82610565565b9392505050565b6000806000604084860312156105b6578182fd5b6105bf84610565565b9250602084013567ffffffffffffffff808211156105db578384fd5b818601915086601f8301126105ee578384fd5b8135818111156105fc578485fd5b87602082850101111561060d578485fd5b6020830194508093505050509250925092565b818382376000910190815291905056feb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103a2646970667358221220ccbb94ef5092a0bf84853cefa22901724a90c6dc70692625ee3f5c86b34819a464736f6c63430008040033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + "deployedBytecode": "0x60806040526004361061004e5760003560e01c80633659cfe61461005f5780634f1ef2861461007f5780635c60da1b146100925780638f283970146100c3578063f851a440146100e357610055565b3661005557005b61005d6100f8565b005b34801561006b57600080fd5b5061005d61007a366004610581565b610132565b61005d61008d3660046105a2565b61016f565b34801561009e57600080fd5b506100a761021e565b6040516001600160a01b03909116815260200160405180910390f35b3480156100cf57600080fd5b5061005d6100de366004610581565b610280565b3480156100ef57600080fd5b506100a7610392565b6101006103dd565b61013061012b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b610469565b565b600080516020610631833981519152546001600160a01b0316336001600160a01b03161415610167576101648161048d565b50565b6101646100f8565b600080516020610631833981519152546001600160a01b0316336001600160a01b03161415610211576101a18361048d565b6000836001600160a01b031683836040516101bd929190610620565b600060405180830381855af49150503d80600081146101f8576040519150601f19603f3d011682016040523d82523d6000602084013e6101fd565b606091505b505090508061020b57600080fd5b50505050565b6102196100f8565b505050565b60006102366000805160206106318339815191525490565b6001600160a01b0316336001600160a01b0316141561027557507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b61027d6100f8565b90565b600080516020610631833981519152546001600160a01b0316336001600160a01b03161415610167576001600160a01b0381166103235760405162461bcd60e51b815260206004820152603660248201527f43616e6e6f74206368616e6765207468652061646d696e206f6620612070726f604482015275787920746f20746865207a65726f206164647265737360501b60648201526084015b60405180910390fd5b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f61035a6000805160206106318339815191525490565b604080516001600160a01b03928316815291841660208301520160405180910390a16101648160008051602061063183398151915255565b60006103aa6000805160206106318339815191525490565b6001600160a01b0316336001600160a01b0316141561027557506000805160206106318339815191525490565b3b151590565b600080516020610631833981519152546001600160a01b0316336001600160a01b031614156101305760405162461bcd60e51b815260206004820152603260248201527f43616e6e6f742063616c6c2066616c6c6261636b2066756e6374696f6e20667260448201527137b6903a343290383937bc3c9030b236b4b760711b606482015260840161031a565b3660008037600080366000845af43d6000803e808015610488573d6000f35b3d6000fd5b610496816104cd565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b803b6105415760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e747261637420616464726573730000000000606482015260840161031a565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55565b80356001600160a01b038116811461057c57600080fd5b919050565b600060208284031215610592578081fd5b61059b82610565565b9392505050565b6000806000604084860312156105b6578182fd5b6105bf84610565565b9250602084013567ffffffffffffffff808211156105db578384fd5b818601915086601f8301126105ee578384fd5b8135818111156105fc578485fd5b87602082850101111561060d578485fd5b6020830194508093505050509250925092565b818382376000910190815291905056feb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103a2646970667358221220ccbb94ef5092a0bf84853cefa22901724a90c6dc70692625ee3f5c86b34819a464736f6c63430008040033", + "devdoc": { + "details": "This contract combines an upgradeability proxy with an authorization mechanism for administrative tasks. All external functions in this contract must be guarded by the `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity feature proposal that would enable this to be done automatically.", + "events": { + "AdminChanged(address,address)": { + "details": "Emitted when the administration has been transferred.", + "params": { + "newAdmin": "Address of the new admin.", + "previousAdmin": "Address of the previous admin." + } + } + }, + "kind": "dev", + "methods": { + "admin()": { + "returns": { + "adminAddress": "The address of the proxy admin." + } + }, + "changeAdmin(address)": { + "details": "Changes the admin of the proxy. Only the current admin can call this function.", + "params": { + "newAdmin": "Address to transfer proxy administration to." + } + }, + "constructor": { + "params": { + "_data": "Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.", + "_logic": "address of the initial implementation.", + "admin_": "Address of the proxy administrator." + } + }, + "implementation()": { + "returns": { + "implementationAddress": "The address of the implementation." + } + }, + "upgradeTo(address)": { + "details": "Upgrade the backing implementation of the proxy. Only the admin can call this function.", + "params": { + "newImplementation": "Address of the new implementation." + } + }, + "upgradeToAndCall(address,bytes)": { + "details": "Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.", + "params": { + "data": "Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.", + "newImplementation": "Address of the new implementation." + } + } + }, + "stateVariables": { + "ADMIN_SLOT": { + "details": "Storage slot with the admin of the contract. This is the keccak-256 hash of \"eip1967.proxy.admin\" subtracted by 1, and is validated in the constructor." + } + }, + "title": "AdminUpgradeabilityProxy", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "constructor": { + "notice": "Contract constructor." + } + }, + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/deployments/goerli/SwapLogic.json b/deployments/goerli/SwapLogic.json new file mode 100644 index 000000000..52e6f0758 --- /dev/null +++ b/deployments/goerli/SwapLogic.json @@ -0,0 +1,1268 @@ +{ + "address": "0xf6d1C9CE7d7E0BeC2abC10004F5E43147D4d6fA8", + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "signerWallet", + "type": "address" + } + ], + "name": "Authorize", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "signerWallet", + "type": "address" + } + ], + "name": "Cancel", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "swapId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "seller", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "oToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "biddingToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "minPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "minBidSize", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalSize", + "type": "uint256" + } + ], + "name": "NewOffer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "signerWallet", + "type": "address" + } + ], + "name": "Revoke", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "referrer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "SetFee", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "SetPriceFeed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "swapId", + "type": "uint256" + } + ], + "name": "SettleOffer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "swapId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "signerWallet", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "signerAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "sellerAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "referrer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "feeAmount", + "type": "uint256" + } + ], + "name": "Swap", + "type": "event" + }, + { + "inputs": [], + "name": "BID_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DOMAIN_CHAIN_ID", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DOMAIN_NAME", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DOMAIN_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DOMAIN_VERSION", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_FEE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_PERCENTAGE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "authorize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "authorized", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "swapId", + "type": "uint256" + } + ], + "name": "averagePriceForOffer", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "otokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "feePercent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "numContracts", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "premium", + "type": "uint256" + } + ], + "name": "calculateReferralFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "nonces", + "type": "uint256[]" + } + ], + "name": "cancelNonce", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "swapId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "address", + "name": "signerWallet", + "type": "address" + }, + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "referrer", + "type": "address" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "internalType": "struct ISwap.Bid", + "name": "bid", + "type": "tuple" + } + ], + "name": "check", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "oToken", + "type": "address" + }, + { + "internalType": "address", + "name": "biddingToken", + "type": "address" + }, + { + "internalType": "uint96", + "name": "minPrice", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "minBidSize", + "type": "uint96" + }, + { + "internalType": "uint128", + "name": "totalSize", + "type": "uint128" + } + ], + "name": "createOffer", + "outputs": [ + { + "internalType": "uint256", + "name": "swapId", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getMarketPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_domainName", + "type": "string" + }, + { + "internalType": "string", + "name": "_domainVersion", + "type": "string" + }, + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + } + ], + "name": "nonceUsed", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "offersCounter", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "priceFeeds", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "referralFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "revoke", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "referrer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "setFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "setPriceFeed", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "swapId", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "swapId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "address", + "name": "signerWallet", + "type": "address" + }, + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "referrer", + "type": "address" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "internalType": "struct ISwap.Bid[]", + "name": "bids", + "type": "tuple[]" + } + ], + "name": "settleOffer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "swapOffers", + "outputs": [ + { + "internalType": "address", + "name": "seller", + "type": "address" + }, + { + "internalType": "address", + "name": "oToken", + "type": "address" + }, + { + "internalType": "uint96", + "name": "minPrice", + "type": "uint96" + }, + { + "internalType": "address", + "name": "biddingToken", + "type": "address" + }, + { + "internalType": "uint96", + "name": "minBidSize", + "type": "uint96" + }, + { + "internalType": "uint128", + "name": "totalSize", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "availableSize", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "totalSales", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "transactionHash": "0xc7d54514b059bfe85953c9cb46bb2445a5ea298e55e4763fde22503f1b4f2ae2", + "receipt": { + "to": null, + "from": "0xF8368119Bb1073Cf01B841848725d81b542A4c19", + "contractAddress": "0xf6d1C9CE7d7E0BeC2abC10004F5E43147D4d6fA8", + "transactionIndex": 40, + "gasUsed": "2923633", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xe7b47a56e044de64cb43f5b835ded14e320a7fceda6dd02bc249a5953c2c18c3", + "transactionHash": "0xc7d54514b059bfe85953c9cb46bb2445a5ea298e55e4763fde22503f1b4f2ae2", + "logs": [], + "blockNumber": 7763838, + "cumulativeGasUsed": "14956213", + "status": 1, + "byzantium": true + }, + "args": [], + "solcInputHash": "7ecbd1f874c019a019ec6af82a4cbc00", + "metadata": "{\"compiler\":{\"version\":\"0.8.4+commit.c7e474f2\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"signerWallet\",\"type\":\"address\"}],\"name\":\"Authorize\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"signerWallet\",\"type\":\"address\"}],\"name\":\"Cancel\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"swapId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"seller\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"biddingToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"minPrice\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"minBidSize\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalSize\",\"type\":\"uint256\"}],\"name\":\"NewOffer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"signerWallet\",\"type\":\"address\"}],\"name\":\"Revoke\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"referrer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"name\":\"SetFee\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"aggregator\",\"type\":\"address\"}],\"name\":\"SetPriceFeed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"swapId\",\"type\":\"uint256\"}],\"name\":\"SettleOffer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"swapId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"signerWallet\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"signerAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"sellerAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"referrer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeAmount\",\"type\":\"uint256\"}],\"name\":\"Swap\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BID_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DOMAIN_CHAIN_ID\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DOMAIN_NAME\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DOMAIN_SEPARATOR\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DOMAIN_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DOMAIN_VERSION\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_FEE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_PERCENTAGE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"authorize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"authorized\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"swapId\",\"type\":\"uint256\"}],\"name\":\"averagePriceForOffer\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"otokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feePercent\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"numContracts\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"premium\",\"type\":\"uint256\"}],\"name\":\"calculateReferralFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nonces\",\"type\":\"uint256[]\"}],\"name\":\"cancelNonce\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"swapId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"signerWallet\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"sellAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"buyAmount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"referrer\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"struct ISwap.Bid\",\"name\":\"bid\",\"type\":\"tuple\"}],\"name\":\"check\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"oToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"biddingToken\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"minPrice\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"minBidSize\",\"type\":\"uint96\"},{\"internalType\":\"uint128\",\"name\":\"totalSize\",\"type\":\"uint128\"}],\"name\":\"createOffer\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"swapId\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"getMarketPrice\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_domainName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_domainVersion\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"nonceUsed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"offersCounter\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"priceFeeds\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"referralFees\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"revoke\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"referrer\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"name\":\"setFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"aggregator\",\"type\":\"address\"}],\"name\":\"setPriceFeed\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"swapId\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"swapId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"signerWallet\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"sellAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"buyAmount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"referrer\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"struct ISwap.Bid[]\",\"name\":\"bids\",\"type\":\"tuple[]\"}],\"name\":\"settleOffer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"swapOffers\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"oToken\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"minPrice\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"biddingToken\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"minBidSize\",\"type\":\"uint96\"},{\"internalType\":\"uint128\",\"name\":\"totalSize\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"availableSize\",\"type\":\"uint128\"},{\"internalType\":\"uint256\",\"name\":\"totalSales\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"authorize(address)\":{\"details\":\"Emits an Authorize event\",\"params\":{\"signer\":\"address Wallet of the signer to authorize\"}},\"averagePriceForOffer(uint256)\":{\"params\":{\"swapId\":\"unique identifier of the swap offer\"}},\"cancelNonce(uint256[])\":{\"details\":\"Cancelled nonces are marked as usedEmits a Cancel eventOut of gas may occur in arrays of length > 400\",\"params\":{\"nonces\":\"uint256[] List of nonces to cancel\"}},\"check((uint256,uint256,address,uint256,uint256,address,uint8,bytes32,bytes32))\":{\"params\":{\"bid\":\"Bid struct containing bid details\"},\"returns\":{\"_0\":\"tuple of error count and bytes32[] memory array of error messages\"}},\"createOffer(address,address,uint96,uint96,uint128)\":{\"params\":{\"biddingToken\":\"token asked by seller\",\"minBidSize\":\"minimum amount of oToken requested in a single bid\",\"minPrice\":\"minimum price of oToken denominated in biddingToken\",\"oToken\":\"token offered by seller\",\"totalSize\":\"amount of oToken offered by seller\"}},\"nonceUsed(address,uint256)\":{\"params\":{\"nonce\":\"uint256 Nonce being checked\",\"signer\":\"address Address of the signer\"}},\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"renounceOwnership()\":{\"details\":\"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner.\"},\"revoke()\":{\"details\":\"Emits a Revoke event\"},\"setFee(address,uint256)\":{\"params\":{\"fee\":\"is the fee in percent in 2 decimals\",\"referrer\":\"is the address of the referrer\"}},\"settleOffer(uint256,(uint256,uint256,address,uint256,uint256,address,uint8,bytes32,bytes32)[])\":{\"params\":{\"bids\":\"bids for swaps\",\"swapId\":\"unique identifier of the swap offer\"}},\"transferOwnership(address)\":{\"details\":\"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"authorize(address)\":{\"notice\":\"Authorize a signer\"},\"averagePriceForOffer(uint256)\":{\"notice\":\"Returns the average settlement price for a swap offer\"},\"calculateReferralFee(address,uint256,uint256,uint256)\":{\"notice\":\"This function assumes that all CALL premiums are denominated in the Offer.biddingToken This could easily change if we enabled Paradigm for Treasury - Calls are sold for USDC. It assumes that all PUT premiums are denominated in USDC.\"},\"cancelNonce(uint256[])\":{\"notice\":\"Cancel one or more nonces\"},\"check((uint256,uint256,address,uint256,uint256,address,uint8,bytes32,bytes32))\":{\"notice\":\"Validates Swap bid for any potential errors\"},\"createOffer(address,address,uint96,uint96,uint128)\":{\"notice\":\"Create a new offer available for swap\"},\"nonceUsed(address,uint256)\":{\"notice\":\"Returns true if the nonce has been used\"},\"revoke()\":{\"notice\":\"Revoke the signer\"},\"setFee(address,uint256)\":{\"notice\":\"Sets the referral fee for a specific referrer\"},\"settleOffer(uint256,(uint256,uint256,address,uint256,uint256,address,uint8,bytes32,bytes32)[])\":{\"notice\":\"Settles the swap offering by iterating through the bids\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/utils/Swap.sol\":\"Swap\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface AggregatorV3Interface {\\n function decimals() external view returns (uint8);\\n\\n function description() external view returns (string memory);\\n\\n function version() external view returns (uint256);\\n\\n // getRoundData and latestRoundData should both raise \\\"No data present\\\"\\n // if they do not have data to report, instead of returning unset values\\n // which could be misinterpreted as actual reported values.\\n function getRoundData(uint80 _roundId)\\n external\\n view\\n returns (\\n uint80 roundId,\\n int256 answer,\\n uint256 startedAt,\\n uint256 updatedAt,\\n uint80 answeredInRound\\n );\\n\\n function latestRoundData()\\n external\\n view\\n returns (\\n uint80 roundId,\\n int256 answer,\\n uint256 startedAt,\\n uint256 updatedAt,\\n uint80 answeredInRound\\n );\\n}\\n\",\"keccak256\":\"0xf2b6c9adb3552254df1445b73563cf014434ff5e78663e9b961b6c059506ceb5\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../utils/ContextUpgradeable.sol\\\";\\nimport \\\"../proxy/utils/Initializable.sol\\\";\\n\\n/**\\n * @dev Contract module which provides a basic access control mechanism, where\\n * there is an account (an owner) that can be granted exclusive access to\\n * specific functions.\\n *\\n * By default, the owner account will be the one that deploys the contract. This\\n * can later be changed with {transferOwnership}.\\n *\\n * This module is used through inheritance. It will make available the modifier\\n * `onlyOwner`, which can be applied to your functions to restrict their use to\\n * the owner.\\n */\\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\\n address private _owner;\\n\\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\\n\\n /**\\n * @dev Initializes the contract setting the deployer as the initial owner.\\n */\\n function __Ownable_init() internal initializer {\\n __Context_init_unchained();\\n __Ownable_init_unchained();\\n }\\n\\n function __Ownable_init_unchained() internal initializer {\\n _setOwner(_msgSender());\\n }\\n\\n /**\\n * @dev Returns the address of the current owner.\\n */\\n function owner() public view virtual returns (address) {\\n return _owner;\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the owner.\\n */\\n modifier onlyOwner() {\\n require(owner() == _msgSender(), \\\"Ownable: caller is not the owner\\\");\\n _;\\n }\\n\\n /**\\n * @dev Leaves the contract without owner. It will not be possible to call\\n * `onlyOwner` functions anymore. Can only be called by the current owner.\\n *\\n * NOTE: Renouncing ownership will leave the contract without an owner,\\n * thereby removing any functionality that is only available to the owner.\\n */\\n function renounceOwnership() public virtual onlyOwner {\\n _setOwner(address(0));\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Can only be called by the current owner.\\n */\\n function transferOwnership(address newOwner) public virtual onlyOwner {\\n require(newOwner != address(0), \\\"Ownable: new owner is the zero address\\\");\\n _setOwner(newOwner);\\n }\\n\\n function _setOwner(address newOwner) private {\\n address oldOwner = _owner;\\n _owner = newOwner;\\n emit OwnershipTransferred(oldOwner, newOwner);\\n }\\n uint256[49] private __gap;\\n}\\n\",\"keccak256\":\"0x35b09b69aca3bc2633da8f47382a81ecf367efe57167a2114f60f9ec81988afa\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\\n * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an\\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\\n *\\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\\n *\\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\\n */\\nabstract contract Initializable {\\n /**\\n * @dev Indicates that the contract has been initialized.\\n */\\n bool private _initialized;\\n\\n /**\\n * @dev Indicates that the contract is in the process of being initialized.\\n */\\n bool private _initializing;\\n\\n /**\\n * @dev Modifier to protect an initializer function from being invoked twice.\\n */\\n modifier initializer() {\\n require(_initializing || !_initialized, \\\"Initializable: contract is already initialized\\\");\\n\\n bool isTopLevelCall = !_initializing;\\n if (isTopLevelCall) {\\n _initializing = true;\\n _initialized = true;\\n }\\n\\n _;\\n\\n if (isTopLevelCall) {\\n _initializing = false;\\n }\\n }\\n}\\n\",\"keccak256\":\"0x8b2abd85d0ece7e866e100e9d47ca9cbec93c87cf71a8d267b2b93eb81f7d5e9\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\nimport \\\"../proxy/utils/Initializable.sol\\\";\\n\\n/**\\n * @dev Contract module that helps prevent reentrant calls to a function.\\n *\\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\\n * available, which can be applied to functions to make sure there are no nested\\n * (reentrant) calls to them.\\n *\\n * Note that because there is a single `nonReentrant` guard, functions marked as\\n * `nonReentrant` may not call one another. This can be worked around by making\\n * those functions `private`, and then adding `external` `nonReentrant` entry\\n * points to them.\\n *\\n * TIP: If you would like to learn more about reentrancy and alternative ways\\n * to protect against it, check out our blog post\\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\\n */\\nabstract contract ReentrancyGuardUpgradeable is Initializable {\\n // Booleans are more expensive than uint256 or any type that takes up a full\\n // word because each write operation emits an extra SLOAD to first read the\\n // slot's contents, replace the bits taken up by the boolean, and then write\\n // back. This is the compiler's defense against contract upgrades and\\n // pointer aliasing, and it cannot be disabled.\\n\\n // The values being non-zero value makes deployment a bit more expensive,\\n // but in exchange the refund on every call to nonReentrant will be lower in\\n // amount. Since refunds are capped to a percentage of the total\\n // transaction's gas, it is best to keep them low in cases like this one, to\\n // increase the likelihood of the full refund coming into effect.\\n uint256 private constant _NOT_ENTERED = 1;\\n uint256 private constant _ENTERED = 2;\\n\\n uint256 private _status;\\n\\n function __ReentrancyGuard_init() internal initializer {\\n __ReentrancyGuard_init_unchained();\\n }\\n\\n function __ReentrancyGuard_init_unchained() internal initializer {\\n _status = _NOT_ENTERED;\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_status != _ENTERED, \\\"ReentrancyGuard: reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n _status = _ENTERED;\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n _status = _NOT_ENTERED;\\n }\\n uint256[49] private __gap;\\n}\\n\",\"keccak256\":\"0x89fa60d14355f7ae06af11e28fce2bb90c5c6186645d681a30e1b36234a4c210\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"./IERC20Upgradeable.sol\\\";\\nimport \\\"./extensions/IERC20MetadataUpgradeable.sol\\\";\\nimport \\\"../../utils/ContextUpgradeable.sol\\\";\\nimport \\\"../../proxy/utils/Initializable.sol\\\";\\n\\n/**\\n * @dev Implementation of the {IERC20} interface.\\n *\\n * This implementation is agnostic to the way tokens are created. This means\\n * that a supply mechanism has to be added in a derived contract using {_mint}.\\n * For a generic mechanism see {ERC20PresetMinterPauser}.\\n *\\n * TIP: For a detailed writeup see our guide\\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\\n * to implement supply mechanisms].\\n *\\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\\n * instead returning `false` on failure. This behavior is nonetheless\\n * conventional and does not conflict with the expectations of ERC20\\n * applications.\\n *\\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\\n * This allows applications to reconstruct the allowance for all accounts just\\n * by listening to said events. Other implementations of the EIP may not emit\\n * these events, as it isn't required by the specification.\\n *\\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\\n * functions have been added to mitigate the well-known issues around setting\\n * allowances. See {IERC20-approve}.\\n */\\ncontract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {\\n mapping(address => uint256) private _balances;\\n\\n mapping(address => mapping(address => uint256)) private _allowances;\\n\\n uint256 private _totalSupply;\\n\\n string private _name;\\n string private _symbol;\\n\\n /**\\n * @dev Sets the values for {name} and {symbol}.\\n *\\n * The default value of {decimals} is 18. To select a different value for\\n * {decimals} you should overload it.\\n *\\n * All two of these values are immutable: they can only be set once during\\n * construction.\\n */\\n function __ERC20_init(string memory name_, string memory symbol_) internal initializer {\\n __Context_init_unchained();\\n __ERC20_init_unchained(name_, symbol_);\\n }\\n\\n function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer {\\n _name = name_;\\n _symbol = symbol_;\\n }\\n\\n /**\\n * @dev Returns the name of the token.\\n */\\n function name() public view virtual override returns (string memory) {\\n return _name;\\n }\\n\\n /**\\n * @dev Returns the symbol of the token, usually a shorter version of the\\n * name.\\n */\\n function symbol() public view virtual override returns (string memory) {\\n return _symbol;\\n }\\n\\n /**\\n * @dev Returns the number of decimals used to get its user representation.\\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\\n *\\n * Tokens usually opt for a value of 18, imitating the relationship between\\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\\n * overridden;\\n *\\n * NOTE: This information is only used for _display_ purposes: it in\\n * no way affects any of the arithmetic of the contract, including\\n * {IERC20-balanceOf} and {IERC20-transfer}.\\n */\\n function decimals() public view virtual override returns (uint8) {\\n return 18;\\n }\\n\\n /**\\n * @dev See {IERC20-totalSupply}.\\n */\\n function totalSupply() public view virtual override returns (uint256) {\\n return _totalSupply;\\n }\\n\\n /**\\n * @dev See {IERC20-balanceOf}.\\n */\\n function balanceOf(address account) public view virtual override returns (uint256) {\\n return _balances[account];\\n }\\n\\n /**\\n * @dev See {IERC20-transfer}.\\n *\\n * Requirements:\\n *\\n * - `recipient` cannot be the zero address.\\n * - the caller must have a balance of at least `amount`.\\n */\\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\\n _transfer(_msgSender(), recipient, amount);\\n return true;\\n }\\n\\n /**\\n * @dev See {IERC20-allowance}.\\n */\\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\\n return _allowances[owner][spender];\\n }\\n\\n /**\\n * @dev See {IERC20-approve}.\\n *\\n * Requirements:\\n *\\n * - `spender` cannot be the zero address.\\n */\\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\\n _approve(_msgSender(), spender, amount);\\n return true;\\n }\\n\\n /**\\n * @dev See {IERC20-transferFrom}.\\n *\\n * Emits an {Approval} event indicating the updated allowance. This is not\\n * required by the EIP. See the note at the beginning of {ERC20}.\\n *\\n * Requirements:\\n *\\n * - `sender` and `recipient` cannot be the zero address.\\n * - `sender` must have a balance of at least `amount`.\\n * - the caller must have allowance for ``sender``'s tokens of at least\\n * `amount`.\\n */\\n function transferFrom(\\n address sender,\\n address recipient,\\n uint256 amount\\n ) public virtual override returns (bool) {\\n _transfer(sender, recipient, amount);\\n\\n uint256 currentAllowance = _allowances[sender][_msgSender()];\\n require(currentAllowance >= amount, \\\"ERC20: transfer amount exceeds allowance\\\");\\n unchecked {\\n _approve(sender, _msgSender(), currentAllowance - amount);\\n }\\n\\n return true;\\n }\\n\\n /**\\n * @dev Atomically increases the allowance granted to `spender` by the caller.\\n *\\n * This is an alternative to {approve} that can be used as a mitigation for\\n * problems described in {IERC20-approve}.\\n *\\n * Emits an {Approval} event indicating the updated allowance.\\n *\\n * Requirements:\\n *\\n * - `spender` cannot be the zero address.\\n */\\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\\n return true;\\n }\\n\\n /**\\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\\n *\\n * This is an alternative to {approve} that can be used as a mitigation for\\n * problems described in {IERC20-approve}.\\n *\\n * Emits an {Approval} event indicating the updated allowance.\\n *\\n * Requirements:\\n *\\n * - `spender` cannot be the zero address.\\n * - `spender` must have allowance for the caller of at least\\n * `subtractedValue`.\\n */\\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\\n uint256 currentAllowance = _allowances[_msgSender()][spender];\\n require(currentAllowance >= subtractedValue, \\\"ERC20: decreased allowance below zero\\\");\\n unchecked {\\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\\n }\\n\\n return true;\\n }\\n\\n /**\\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\\n *\\n * This internal function is equivalent to {transfer}, and can be used to\\n * e.g. implement automatic token fees, slashing mechanisms, etc.\\n *\\n * Emits a {Transfer} event.\\n *\\n * Requirements:\\n *\\n * - `sender` cannot be the zero address.\\n * - `recipient` cannot be the zero address.\\n * - `sender` must have a balance of at least `amount`.\\n */\\n function _transfer(\\n address sender,\\n address recipient,\\n uint256 amount\\n ) internal virtual {\\n require(sender != address(0), \\\"ERC20: transfer from the zero address\\\");\\n require(recipient != address(0), \\\"ERC20: transfer to the zero address\\\");\\n\\n _beforeTokenTransfer(sender, recipient, amount);\\n\\n uint256 senderBalance = _balances[sender];\\n require(senderBalance >= amount, \\\"ERC20: transfer amount exceeds balance\\\");\\n unchecked {\\n _balances[sender] = senderBalance - amount;\\n }\\n _balances[recipient] += amount;\\n\\n emit Transfer(sender, recipient, amount);\\n\\n _afterTokenTransfer(sender, recipient, amount);\\n }\\n\\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\\n * the total supply.\\n *\\n * Emits a {Transfer} event with `from` set to the zero address.\\n *\\n * Requirements:\\n *\\n * - `account` cannot be the zero address.\\n */\\n function _mint(address account, uint256 amount) internal virtual {\\n require(account != address(0), \\\"ERC20: mint to the zero address\\\");\\n\\n _beforeTokenTransfer(address(0), account, amount);\\n\\n _totalSupply += amount;\\n _balances[account] += amount;\\n emit Transfer(address(0), account, amount);\\n\\n _afterTokenTransfer(address(0), account, amount);\\n }\\n\\n /**\\n * @dev Destroys `amount` tokens from `account`, reducing the\\n * total supply.\\n *\\n * Emits a {Transfer} event with `to` set to the zero address.\\n *\\n * Requirements:\\n *\\n * - `account` cannot be the zero address.\\n * - `account` must have at least `amount` tokens.\\n */\\n function _burn(address account, uint256 amount) internal virtual {\\n require(account != address(0), \\\"ERC20: burn from the zero address\\\");\\n\\n _beforeTokenTransfer(account, address(0), amount);\\n\\n uint256 accountBalance = _balances[account];\\n require(accountBalance >= amount, \\\"ERC20: burn amount exceeds balance\\\");\\n unchecked {\\n _balances[account] = accountBalance - amount;\\n }\\n _totalSupply -= amount;\\n\\n emit Transfer(account, address(0), amount);\\n\\n _afterTokenTransfer(account, address(0), amount);\\n }\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\\n *\\n * This internal function is equivalent to `approve`, and can be used to\\n * e.g. set automatic allowances for certain subsystems, etc.\\n *\\n * Emits an {Approval} event.\\n *\\n * Requirements:\\n *\\n * - `owner` cannot be the zero address.\\n * - `spender` cannot be the zero address.\\n */\\n function _approve(\\n address owner,\\n address spender,\\n uint256 amount\\n ) internal virtual {\\n require(owner != address(0), \\\"ERC20: approve from the zero address\\\");\\n require(spender != address(0), \\\"ERC20: approve to the zero address\\\");\\n\\n _allowances[owner][spender] = amount;\\n emit Approval(owner, spender, amount);\\n }\\n\\n /**\\n * @dev Hook that is called before any transfer of tokens. This includes\\n * minting and burning.\\n *\\n * Calling conditions:\\n *\\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\\n * will be transferred to `to`.\\n * - when `from` is zero, `amount` tokens will be minted for `to`.\\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\\n * - `from` and `to` are never both zero.\\n *\\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\\n */\\n function _beforeTokenTransfer(\\n address from,\\n address to,\\n uint256 amount\\n ) internal virtual {}\\n\\n /**\\n * @dev Hook that is called after any transfer of tokens. This includes\\n * minting and burning.\\n *\\n * Calling conditions:\\n *\\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\\n * has been transferred to `to`.\\n * - when `from` is zero, `amount` tokens have been minted for `to`.\\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\\n * - `from` and `to` are never both zero.\\n *\\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\\n */\\n function _afterTokenTransfer(\\n address from,\\n address to,\\n uint256 amount\\n ) internal virtual {}\\n uint256[45] private __gap;\\n}\\n\",\"keccak256\":\"0x891fbf39ac5b9524f417db912d33684f239195a0fc5c11db439659676e51aab6\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20Upgradeable {\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address recipient, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(\\n address sender,\\n address recipient,\\n uint256 amount\\n ) external returns (bool);\\n\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n}\\n\",\"keccak256\":\"0xf449ca0a763d4b1b4462cf8706857074492f324020a2b9d099181b5383055ad3\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../IERC20Upgradeable.sol\\\";\\n\\n/**\\n * @dev Interface for the optional metadata functions from the ERC20 standard.\\n *\\n * _Available since v4.1._\\n */\\ninterface IERC20MetadataUpgradeable is IERC20Upgradeable {\\n /**\\n * @dev Returns the name of the token.\\n */\\n function name() external view returns (string memory);\\n\\n /**\\n * @dev Returns the symbol of the token.\\n */\\n function symbol() external view returns (string memory);\\n\\n /**\\n * @dev Returns the decimals places of the token.\\n */\\n function decimals() external view returns (uint8);\\n}\\n\",\"keccak256\":\"0x6795c369a4eefa78468e38966f7851fbc2dda5e5b9ccd3fa2b45970e2e4d3abd\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\nimport \\\"../proxy/utils/Initializable.sol\\\";\\n\\n/**\\n * @dev Provides information about the current execution context, including the\\n * sender of the transaction and its data. While these are generally available\\n * via msg.sender and msg.data, they should not be accessed in such a direct\\n * manner, since when dealing with meta-transactions the account sending and\\n * paying for execution may not be the actual sender (as far as an application\\n * is concerned).\\n *\\n * This contract is only required for intermediate, library-like contracts.\\n */\\nabstract contract ContextUpgradeable is Initializable {\\n function __Context_init() internal initializer {\\n __Context_init_unchained();\\n }\\n\\n function __Context_init_unchained() internal initializer {\\n }\\n function _msgSender() internal view virtual returns (address) {\\n return msg.sender;\\n }\\n\\n function _msgData() internal view virtual returns (bytes calldata) {\\n return msg.data;\\n }\\n uint256[50] private __gap;\\n}\\n\",\"keccak256\":\"0x062b5a0f7cc6b0528fa350033759f3a15ba42afb57423d7c593753860f2c82e0\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20 {\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address recipient, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(\\n address sender,\\n address recipient,\\n uint256 amount\\n ) external returns (bool);\\n\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n}\\n\",\"keccak256\":\"0x027b891937d20ccf213fdb9c31531574256de774bda99d3a70ecef6e1913ed2a\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../IERC20.sol\\\";\\nimport \\\"../../../utils/Address.sol\\\";\\n\\n/**\\n * @title SafeERC20\\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\\n * contract returns false). Tokens that return no value (and instead revert or\\n * throw on failure) are also supported, non-reverting calls are assumed to be\\n * successful.\\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\\n */\\nlibrary SafeERC20 {\\n using Address for address;\\n\\n function safeTransfer(\\n IERC20 token,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\\n }\\n\\n function safeTransferFrom(\\n IERC20 token,\\n address from,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\\n }\\n\\n /**\\n * @dev Deprecated. This function has issues similar to the ones found in\\n * {IERC20-approve}, and its usage is discouraged.\\n *\\n * Whenever possible, use {safeIncreaseAllowance} and\\n * {safeDecreaseAllowance} instead.\\n */\\n function safeApprove(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n // safeApprove should only be called when setting an initial allowance,\\n // or when resetting it to zero. To increase and decrease it, use\\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\\n require(\\n (value == 0) || (token.allowance(address(this), spender) == 0),\\n \\\"SafeERC20: approve from non-zero to non-zero allowance\\\"\\n );\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\\n }\\n\\n function safeIncreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n uint256 newAllowance = token.allowance(address(this), spender) + value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n\\n function safeDecreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n unchecked {\\n uint256 oldAllowance = token.allowance(address(this), spender);\\n require(oldAllowance >= value, \\\"SafeERC20: decreased allowance below zero\\\");\\n uint256 newAllowance = oldAllowance - value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n }\\n\\n /**\\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\\n * on the return value: the return value is optional (but if data is returned, it must not be false).\\n * @param token The token targeted by the call.\\n * @param data The call data (encoded using abi.encode or one of its variants).\\n */\\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\\n // the target address contains contract code and also asserts for success in the low-level call.\\n\\n bytes memory returndata = address(token).functionCall(data, \\\"SafeERC20: low-level call failed\\\");\\n if (returndata.length > 0) {\\n // Return data is optional\\n require(abi.decode(returndata, (bool)), \\\"SafeERC20: ERC20 operation did not succeed\\\");\\n }\\n }\\n}\\n\",\"keccak256\":\"0x02348b2e4b9f3200c7e3907c5c2661643a6d8520e9f79939fbb9b4005a54894d\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x3336baae5cf23e94274d75336e2d412193be508504aee185e61dc7d58cd05c8a\",\"license\":\"MIT\"},\"contracts/interfaces/GammaInterface.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity =0.8.4;\\n\\nlibrary GammaTypes {\\n // vault is a struct of 6 arrays that describe a position a user has, a user can have multiple vaults.\\n struct Vault {\\n // addresses of oTokens a user has shorted (i.e. written) against this vault\\n address[] shortOtokens;\\n // addresses of oTokens a user has bought and deposited in this vault\\n // user can be long oTokens without opening a vault (e.g. by buying on a DEX)\\n // generally, long oTokens will be 'deposited' in vaults to act as collateral\\n // in order to write oTokens against (i.e. in spreads)\\n address[] longOtokens;\\n // addresses of other ERC-20s a user has deposited as collateral in this vault\\n address[] collateralAssets;\\n // quantity of oTokens minted/written for each oToken address in shortOtokens\\n uint256[] shortAmounts;\\n // quantity of oTokens owned and held in the vault for each oToken address in longOtokens\\n uint256[] longAmounts;\\n // quantity of ERC-20 deposited as collateral in the vault for each ERC-20 address in collateralAssets\\n uint256[] collateralAmounts;\\n }\\n}\\n\\ninterface IOtoken {\\n function underlyingAsset() external view returns (address);\\n\\n function strikeAsset() external view returns (address);\\n\\n function collateralAsset() external view returns (address);\\n\\n function strikePrice() external view returns (uint256);\\n\\n function expiryTimestamp() external view returns (uint256);\\n\\n function isPut() external view returns (bool);\\n}\\n\\ninterface IOtokenFactory {\\n function getOtoken(\\n address _underlyingAsset,\\n address _strikeAsset,\\n address _collateralAsset,\\n uint256 _strikePrice,\\n uint256 _expiry,\\n bool _isPut\\n ) external view returns (address);\\n\\n function createOtoken(\\n address _underlyingAsset,\\n address _strikeAsset,\\n address _collateralAsset,\\n uint256 _strikePrice,\\n uint256 _expiry,\\n bool _isPut\\n ) external returns (address);\\n\\n function getTargetOtokenAddress(\\n address _underlyingAsset,\\n address _strikeAsset,\\n address _collateralAsset,\\n uint256 _strikePrice,\\n uint256 _expiry,\\n bool _isPut\\n ) external view returns (address);\\n\\n event OtokenCreated(\\n address tokenAddress,\\n address creator,\\n address indexed underlying,\\n address indexed strike,\\n address indexed collateral,\\n uint256 strikePrice,\\n uint256 expiry,\\n bool isPut\\n );\\n}\\n\\ninterface IController {\\n // possible actions that can be performed\\n enum ActionType {\\n OpenVault,\\n MintShortOption,\\n BurnShortOption,\\n DepositLongOption,\\n WithdrawLongOption,\\n DepositCollateral,\\n WithdrawCollateral,\\n SettleVault,\\n Redeem,\\n Call,\\n Liquidate\\n }\\n\\n struct ActionArgs {\\n // type of action that is being performed on the system\\n ActionType actionType;\\n // address of the account owner\\n address owner;\\n // address which we move assets from or to (depending on the action type)\\n address secondAddress;\\n // asset that is to be transfered\\n address asset;\\n // index of the vault that is to be modified (if any)\\n uint256 vaultId;\\n // amount of asset that is to be transfered\\n uint256 amount;\\n // each vault can hold multiple short / long / collateral assets\\n // but we are restricting the scope to only 1 of each in this version\\n // in future versions this would be the index of the short / long / collateral asset that needs to be modified\\n uint256 index;\\n // any other data that needs to be passed in for arbitrary function calls\\n bytes data;\\n }\\n\\n struct RedeemArgs {\\n // address to which we pay out the oToken proceeds\\n address receiver;\\n // oToken that is to be redeemed\\n address otoken;\\n // amount of oTokens that is to be redeemed\\n uint256 amount;\\n }\\n\\n function getPayout(address _otoken, uint256 _amount)\\n external\\n view\\n returns (uint256);\\n\\n function operate(ActionArgs[] calldata _actions) external;\\n\\n function getAccountVaultCounter(address owner)\\n external\\n view\\n returns (uint256);\\n\\n function oracle() external view returns (address);\\n\\n function getVault(address _owner, uint256 _vaultId)\\n external\\n view\\n returns (GammaTypes.Vault memory);\\n\\n function getProceed(address _owner, uint256 _vaultId)\\n external\\n view\\n returns (uint256);\\n\\n function isSettlementAllowed(\\n address _underlying,\\n address _strike,\\n address _collateral,\\n uint256 _expiry\\n ) external view returns (bool);\\n}\\n\\ninterface IOracle {\\n function setAssetPricer(address _asset, address _pricer) external;\\n\\n function updateAssetPricer(address _asset, address _pricer) external;\\n\\n function getPrice(address _asset) external view returns (uint256);\\n}\\n\",\"keccak256\":\"0x148930e8fd36c64b8dbe75c2ba71117eaf4a6da5a019e08f3ff8161b274785ba\",\"license\":\"MIT\"},\"contracts/interfaces/IERC20Detailed.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity =0.8.4;\\n\\nimport {IERC20} from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\n\\ninterface IERC20Detailed is IERC20 {\\n function decimals() external view returns (uint8);\\n\\n function symbol() external view returns (string calldata);\\n\\n function name() external view returns (string calldata);\\n}\\n\",\"keccak256\":\"0x90fd29862ecc4bc1f2ee3e4d5c42364c5fbcc0f43f907d2bb82665f93df0d218\",\"license\":\"MIT\"},\"contracts/interfaces/ISwap.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity =0.8.4;\\n\\ninterface ISwap {\\n struct Offer {\\n // 32 byte slot 1, partial fill\\n // Seller wallet address\\n address seller;\\n // 32 byte slot 2\\n // Addess of oToken\\n address oToken;\\n // Price per oToken denominated in biddingToken\\n uint96 minPrice;\\n // 32 byte slot 3\\n // ERC20 Token to bid for oToken\\n address biddingToken;\\n // Minimum oToken amount acceptable for a single bid\\n uint96 minBidSize;\\n // 32 byte slot 4\\n // Total available oToken amount\\n uint128 totalSize;\\n // Remaining available oToken amount\\n // This figure is updated after each successfull swap\\n uint128 availableSize;\\n // 32 byte slot 5\\n // Amount of biddingToken received\\n // This figure is updated after each successfull swap\\n uint256 totalSales;\\n }\\n\\n struct Bid {\\n // ID assigned to offers\\n uint256 swapId;\\n // Number only used once for each wallet\\n uint256 nonce;\\n // Signer wallet address\\n address signerWallet;\\n // Amount of biddingToken offered by signer\\n uint256 sellAmount;\\n // Amount of oToken requested by signer\\n uint256 buyAmount;\\n // Referrer wallet address\\n address referrer;\\n // Signature recovery id\\n uint8 v;\\n // r portion of the ECSDA signature\\n bytes32 r;\\n // s portion of the ECSDA signature\\n bytes32 s;\\n }\\n\\n struct OfferDetails {\\n // Seller wallet address\\n address seller;\\n // Addess of oToken\\n address oToken;\\n // Price per oToken denominated in biddingToken\\n uint256 minPrice;\\n // ERC20 Token to bid for oToken\\n address biddingToken;\\n // Minimum oToken amount acceptable for a single bid\\n uint256 minBidSize;\\n }\\n\\n event Swap(\\n uint256 indexed swapId,\\n uint256 nonce,\\n address indexed signerWallet,\\n uint256 signerAmount,\\n uint256 sellerAmount,\\n address referrer,\\n uint256 feeAmount\\n );\\n\\n event NewOffer(\\n uint256 swapId,\\n address seller,\\n address oToken,\\n address biddingToken,\\n uint256 minPrice,\\n uint256 minBidSize,\\n uint256 totalSize\\n );\\n\\n event SetFee(address referrer, uint256 fee);\\n\\n event SetPriceFeed(address asset, address aggregator);\\n\\n event SettleOffer(uint256 swapId);\\n\\n event Cancel(uint256 indexed nonce, address indexed signerWallet);\\n\\n event Authorize(address indexed signer, address indexed signerWallet);\\n\\n event Revoke(address indexed signer, address indexed signerWallet);\\n\\n function createOffer(\\n address oToken,\\n address biddingToken,\\n uint96 minPrice,\\n uint96 minBidSize,\\n uint128 totalSize\\n ) external returns (uint256 swapId);\\n\\n function settleOffer(uint256 swapId, Bid[] calldata bids) external;\\n\\n function cancelNonce(uint256[] calldata nonces) external;\\n\\n function check(Bid calldata bid)\\n external\\n view\\n returns (uint256, bytes32[] memory);\\n\\n function averagePriceForOffer(uint256 swapId)\\n external\\n view\\n returns (uint256);\\n\\n function authorize(address sender) external;\\n\\n function revoke() external;\\n\\n function nonceUsed(address, uint256) external view returns (bool);\\n}\\n\",\"keccak256\":\"0x63869f607e9d427034a928008e429ee2ffee0469717d05a64dc1fbb28db8e1e1\",\"license\":\"MIT\"},\"contracts/storage/SwapStorage.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity =0.8.4;\\nimport \\\"../interfaces/ISwap.sol\\\";\\n\\nabstract contract SwapStorageV1 {\\n // Keccak256 of domain name for signature verification\\n bytes32 public DOMAIN_NAME;\\n // Keccak256 of domain version for signature verification\\n bytes32 public DOMAIN_VERSION;\\n // Keccak256 of abi-encoded domain parameters for signature verification\\n bytes32 public DOMAIN_SEPARATOR;\\n\\n // Counter to keep track number of offers\\n uint256 public offersCounter;\\n // Mapping of swap offer details for a given swapId\\n mapping(uint256 => ISwap.Offer) public swapOffers;\\n // Mapping of referral fees for a given address, 1000000 = 100%\\n mapping(address => uint256) public referralFees;\\n // Mapping of authorized delegate for a given address\\n mapping(address => address) public authorized;\\n /**\\n * @notice Double mapping of signers to nonce groups to nonce states\\n * @dev The nonce group is computed as nonce / 256, so each group of 256 sequential nonces uses the same key\\n * @dev The nonce states are encoded as 256 bits, for each nonce in the group 0 means available and 1 means used\\n */\\n mapping(address => mapping(uint256 => uint256)) internal _nonceGroups;\\n}\\n\\nabstract contract SwapStorageV2 {\\n // Price feed for looking up value of asset\\n mapping(address => address) public priceFeeds;\\n}\\n\\n// We are following Compound's method of upgrading new contract implementations\\n// When we need to add new storage variables, we create a new version of SwapStorage\\n// e.g. SwapStorage, so finally it would look like\\n// contract SwapStorage is SwapStorageV1, SwapStorageV2\\nabstract contract SwapStorage is SwapStorageV1, SwapStorageV2 {\\n\\n}\\n\",\"keccak256\":\"0xf61276f6ad414dd199f35a3917bdffabb6d7fb3fdb87ace29b83eabc1d8950a2\",\"license\":\"MIT\"},\"contracts/utils/Swap.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// Source: https://github.com/airswap/airswap-protocols/blob/main/source/swap/contracts/Swap.sol\\n\\npragma solidity =0.8.4;\\n\\nimport \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\nimport \\\"../interfaces/ISwap.sol\\\";\\nimport \\\"../storage/SwapStorage.sol\\\";\\nimport {\\n ReentrancyGuardUpgradeable\\n} from \\\"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\\\";\\nimport {\\n OwnableUpgradeable\\n} from \\\"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\\\";\\nimport {\\n ERC20Upgradeable\\n} from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\\\";\\nimport \\\"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\\\";\\nimport {IERC20Detailed} from \\\"../interfaces/IERC20Detailed.sol\\\";\\nimport {IOtoken} from \\\"../interfaces/GammaInterface.sol\\\";\\n\\ncontract Swap is\\n ISwap,\\n ReentrancyGuardUpgradeable,\\n OwnableUpgradeable,\\n SwapStorage\\n{\\n using SafeERC20 for IERC20;\\n\\n uint256 public immutable DOMAIN_CHAIN_ID;\\n\\n bytes32 public constant DOMAIN_TYPEHASH =\\n keccak256(\\n abi.encodePacked(\\n \\\"EIP712Domain(\\\",\\n \\\"string name,\\\",\\n \\\"string version,\\\",\\n \\\"uint256 chainId,\\\",\\n \\\"address verifyingContract\\\",\\n \\\")\\\"\\n )\\n );\\n\\n bytes32 public constant BID_TYPEHASH =\\n keccak256(\\n abi.encodePacked(\\n \\\"Bid(\\\",\\n \\\"uint256 swapId,\\\",\\n \\\"uint256 nonce,\\\",\\n \\\"address signerWallet,\\\",\\n \\\"uint256 sellAmount,\\\",\\n \\\"uint256 buyAmount,\\\",\\n \\\"address referrer\\\",\\n \\\")\\\"\\n )\\n );\\n\\n uint256 public constant MAX_PERCENTAGE = 1000000;\\n uint256 public constant MAX_FEE = 125000; // 12.5%\\n uint256 internal constant MAX_ERROR_COUNT = 10;\\n uint256 internal constant OTOKEN_DECIMALS = 8;\\n\\n /************************************************\\n * CONSTRUCTOR\\n ***********************************************/\\n\\n constructor() {\\n uint256 currentChainId = getChainId();\\n DOMAIN_CHAIN_ID = currentChainId;\\n }\\n\\n /************************************************\\n * INITIALIZATION\\n ***********************************************/\\n\\n function initialize(\\n string memory _domainName,\\n string memory _domainVersion,\\n address _owner\\n ) external initializer {\\n require(bytes(_domainName).length > 0, \\\"!_domainName\\\");\\n require(bytes(_domainVersion).length > 0, \\\"!_domainVersion\\\");\\n require(_owner != address(0), \\\"!_owner\\\");\\n\\n __ReentrancyGuard_init();\\n __Ownable_init();\\n transferOwnership(_owner);\\n\\n DOMAIN_NAME = keccak256(bytes(_domainName));\\n DOMAIN_VERSION = keccak256(bytes(_domainVersion));\\n DOMAIN_SEPARATOR = keccak256(\\n abi.encode(\\n DOMAIN_TYPEHASH,\\n DOMAIN_NAME,\\n DOMAIN_VERSION,\\n DOMAIN_CHAIN_ID,\\n this\\n )\\n );\\n }\\n\\n /************************************************\\n * SETTER\\n ***********************************************/\\n\\n /**\\n * @notice Sets the referral fee for a specific referrer\\n * @param referrer is the address of the referrer\\n * @param fee is the fee in percent in 2 decimals\\n */\\n function setFee(address referrer, uint256 fee) external onlyOwner {\\n require(referrer != address(0), \\\"Referrer cannot be the zero address\\\");\\n require(fee < MAX_FEE, \\\"Fee exceeds maximum\\\");\\n\\n referralFees[referrer] = fee;\\n\\n emit SetFee(referrer, fee);\\n }\\n\\n /************************************************\\n * OFFER CREATION AND SETTLEMENT\\n ***********************************************/\\n\\n /**\\n * @notice Create a new offer available for swap\\n * @param oToken token offered by seller\\n * @param biddingToken token asked by seller\\n * @param minPrice minimum price of oToken denominated in biddingToken\\n * @param minBidSize minimum amount of oToken requested in a single bid\\n * @param totalSize amount of oToken offered by seller\\n */\\n function createOffer(\\n address oToken,\\n address biddingToken,\\n uint96 minPrice,\\n uint96 minBidSize,\\n uint128 totalSize\\n ) external override returns (uint256 swapId) {\\n require(oToken != address(0), \\\"oToken cannot be the zero address\\\");\\n require(\\n biddingToken != address(0),\\n \\\"BiddingToken cannot be the zero address\\\"\\n );\\n require(minPrice > 0, \\\"MinPrice must be larger than zero\\\");\\n require(minBidSize > 0, \\\"MinBidSize must be larger than zero\\\");\\n require(minBidSize <= totalSize, \\\"MinBidSize exceeds total size\\\");\\n\\n if (IOtoken(oToken).isPut()) {\\n require(\\n priceFeeds[IOtoken(oToken).underlyingAsset()] != address(0),\\n \\\"No price feed set\\\"\\n );\\n }\\n\\n // Check seller allowance\\n uint256 sellerAllowance =\\n IERC20(oToken).allowance(msg.sender, address(this));\\n require(sellerAllowance >= totalSize, \\\"Seller allowance low\\\");\\n\\n // Check seller balance\\n uint256 sellerBalance = IERC20(oToken).balanceOf(msg.sender);\\n require(sellerBalance >= totalSize, \\\"Seller balance low\\\");\\n\\n offersCounter += 1;\\n\\n swapId = offersCounter;\\n\\n swapOffers[swapId].seller = msg.sender;\\n swapOffers[swapId].oToken = oToken;\\n swapOffers[swapId].biddingToken = biddingToken;\\n swapOffers[swapId].minBidSize = minBidSize;\\n swapOffers[swapId].minPrice = minPrice;\\n swapOffers[swapId].totalSize = totalSize;\\n swapOffers[swapId].availableSize = totalSize;\\n // We warm the storage slot with 1 wei so we avoid a cold SSTORE\\n swapOffers[swapId].totalSales = 1;\\n\\n emit NewOffer(\\n swapId,\\n msg.sender,\\n oToken,\\n biddingToken,\\n minPrice,\\n minBidSize,\\n totalSize\\n );\\n }\\n\\n /**\\n * @notice Settles the swap offering by iterating through the bids\\n * @param swapId unique identifier of the swap offer\\n * @param bids bids for swaps\\n */\\n function settleOffer(uint256 swapId, Bid[] calldata bids)\\n external\\n override\\n nonReentrant\\n {\\n Offer storage offer = swapOffers[swapId];\\n\\n address seller = offer.seller;\\n require(\\n seller == msg.sender,\\n \\\"Only seller can settle or offer doesn't exist\\\"\\n );\\n require(offer.availableSize > 0, \\\"Offer fully settled\\\");\\n\\n uint256 totalSales;\\n OfferDetails memory offerDetails;\\n offerDetails.seller = seller;\\n offerDetails.oToken = offer.oToken;\\n offerDetails.biddingToken = offer.biddingToken;\\n offerDetails.minPrice = offer.minPrice;\\n offerDetails.minBidSize = offer.minBidSize;\\n\\n for (uint256 i = 0; i < bids.length; i++) {\\n require(\\n swapId == bids[i].swapId,\\n \\\"Offer and bid swapId mismatched\\\"\\n );\\n\\n _swap(offerDetails, offer, bids[i]);\\n totalSales += bids[i].sellAmount;\\n }\\n\\n bool fullySettled = offer.availableSize == 0;\\n\\n // Deduct the initial 1 wei offset if offer is fully settled\\n offer.totalSales += totalSales - (fullySettled ? 1 : 0);\\n\\n if (fullySettled) {\\n offer.seller = address(0);\\n offer.oToken = address(0);\\n offer.biddingToken = address(0);\\n offer.minBidSize = 0;\\n offer.minPrice = 0;\\n\\n emit SettleOffer(swapId);\\n }\\n }\\n\\n /**\\n * @notice Authorize a signer\\n * @param signer address Wallet of the signer to authorize\\n * @dev Emits an Authorize event\\n */\\n function authorize(address signer) external override {\\n require(signer != address(0), \\\"SIGNER_INVALID\\\");\\n authorized[msg.sender] = signer;\\n emit Authorize(signer, msg.sender);\\n }\\n\\n /**\\n * @notice Revoke the signer\\n * @dev Emits a Revoke event\\n */\\n function revoke() external override {\\n address tmp = authorized[msg.sender];\\n delete authorized[msg.sender];\\n emit Revoke(tmp, msg.sender);\\n }\\n\\n /**\\n * @notice Cancel one or more nonces\\n * @dev Cancelled nonces are marked as used\\n * @dev Emits a Cancel event\\n * @dev Out of gas may occur in arrays of length > 400\\n * @param nonces uint256[] List of nonces to cancel\\n */\\n function cancelNonce(uint256[] calldata nonces) external override {\\n for (uint256 i = 0; i < nonces.length; i++) {\\n uint256 nonce = nonces[i];\\n if (_markNonceAsUsed(msg.sender, nonce)) {\\n emit Cancel(nonce, msg.sender);\\n }\\n }\\n }\\n\\n /************************************************\\n * PUBLIC VIEW FUNCTIONS\\n ***********************************************/\\n\\n /**\\n * @notice Validates Swap bid for any potential errors\\n * @param bid Bid struct containing bid details\\n * @return tuple of error count and bytes32[] memory array of error messages\\n */\\n function check(Bid calldata bid)\\n external\\n view\\n override\\n returns (uint256, bytes32[] memory)\\n {\\n Offer memory offer = swapOffers[bid.swapId];\\n require(offer.seller != address(0), \\\"Offer does not exist\\\");\\n\\n bytes32[] memory errors = new bytes32[](MAX_ERROR_COUNT);\\n\\n uint256 errCount;\\n\\n // Check signature\\n address signatory = _getSignatory(bid);\\n\\n if (signatory == address(0)) {\\n errors[errCount] = \\\"SIGNATURE_INVALID\\\";\\n errCount++;\\n }\\n\\n if (\\n bid.signerWallet != signatory &&\\n authorized[bid.signerWallet] != signatory\\n ) {\\n errors[errCount] = \\\"UNAUTHORIZED\\\";\\n errCount++;\\n }\\n\\n // Check nonce\\n if (nonceUsed(signatory, bid.nonce)) {\\n errors[errCount] = \\\"NONCE_ALREADY_USED\\\";\\n errCount++;\\n }\\n\\n // Check bid size\\n if (bid.buyAmount < offer.minBidSize) {\\n errors[errCount] = \\\"BID_TOO_SMALL\\\";\\n errCount++;\\n }\\n if (bid.buyAmount > offer.availableSize) {\\n errors[errCount] = \\\"BID_EXCEED_AVAILABLE_SIZE\\\";\\n errCount++;\\n }\\n\\n // Check bid price\\n uint256 bidPrice =\\n (bid.sellAmount * 10**OTOKEN_DECIMALS) / bid.buyAmount;\\n if (bidPrice < offer.minPrice) {\\n errors[errCount] = \\\"PRICE_TOO_LOW\\\";\\n errCount++;\\n }\\n\\n // Check signer allowance\\n uint256 signerAllowance =\\n IERC20(offer.biddingToken).allowance(\\n bid.signerWallet,\\n address(this)\\n );\\n if (signerAllowance < bid.sellAmount) {\\n errors[errCount] = \\\"SIGNER_ALLOWANCE_LOW\\\";\\n errCount++;\\n }\\n\\n // Check signer balance\\n uint256 signerBalance =\\n IERC20(offer.biddingToken).balanceOf(bid.signerWallet);\\n if (signerBalance < bid.sellAmount) {\\n errors[errCount] = \\\"SIGNER_BALANCE_LOW\\\";\\n errCount++;\\n }\\n\\n return (errCount, errors);\\n }\\n\\n /**\\n * @notice Returns the average settlement price for a swap offer\\n * @param swapId unique identifier of the swap offer\\n */\\n function averagePriceForOffer(uint256 swapId)\\n external\\n view\\n override\\n returns (uint256)\\n {\\n Offer storage offer = swapOffers[swapId];\\n require(offer.totalSize != 0, \\\"Offer does not exist\\\");\\n\\n uint256 availableSize = offer.availableSize;\\n\\n // Deduct the initial 1 wei offset if offer is not fully settled\\n uint256 adjustment = availableSize != 0 ? 1 : 0;\\n\\n return\\n ((offer.totalSales - adjustment) * (10**8)) /\\n (offer.totalSize - availableSize);\\n }\\n\\n /**\\n * @notice Returns true if the nonce has been used\\n * @param signer address Address of the signer\\n * @param nonce uint256 Nonce being checked\\n */\\n function nonceUsed(address signer, uint256 nonce)\\n public\\n view\\n override\\n returns (bool)\\n {\\n uint256 groupKey = nonce / 256;\\n uint256 indexInGroup = nonce % 256;\\n return (_nonceGroups[signer][groupKey] >> indexInGroup) & 1 == 1;\\n }\\n\\n /************************************************\\n * INTERNAL FUNCTIONS\\n ***********************************************/\\n\\n /**\\n * @notice Swap Atomic ERC20 Swap\\n * @param details Details of offering\\n * @param offer Offer struct containing offer details\\n * @param bid Bid struct containing bid details\\n */\\n function _swap(\\n OfferDetails memory details,\\n Offer storage offer,\\n Bid calldata bid\\n ) internal {\\n require(DOMAIN_CHAIN_ID == getChainId(), \\\"CHAIN_ID_CHANGED\\\");\\n\\n address signatory = _getSignatory(bid);\\n\\n require(signatory != address(0), \\\"SIGNATURE_INVALID\\\");\\n\\n if (bid.signerWallet != signatory) {\\n require(authorized[bid.signerWallet] == signatory, \\\"UNAUTHORIZED\\\");\\n }\\n\\n require(_markNonceAsUsed(signatory, bid.nonce), \\\"NONCE_ALREADY_USED\\\");\\n require(\\n bid.buyAmount <= offer.availableSize,\\n \\\"BID_EXCEED_AVAILABLE_SIZE\\\"\\n );\\n require(bid.buyAmount >= details.minBidSize, \\\"BID_TOO_SMALL\\\");\\n\\n // Ensure min. price is met\\n uint256 bidPrice =\\n (bid.sellAmount * 10**OTOKEN_DECIMALS) / bid.buyAmount;\\n require(bidPrice >= details.minPrice, \\\"PRICE_TOO_LOW\\\");\\n\\n // don't have to do a uint128 check because we already check\\n // that bid.buyAmount <= offer.availableSize\\n offer.availableSize -= uint128(bid.buyAmount);\\n\\n // Transfer token from sender to signer\\n IERC20(details.oToken).safeTransferFrom(\\n details.seller,\\n bid.signerWallet,\\n bid.buyAmount\\n );\\n\\n // Transfer to referrer if any\\n uint256 feeAmount;\\n if (bid.referrer != address(0)) {\\n uint256 feePercent = referralFees[bid.referrer];\\n\\n if (feePercent > 0) {\\n feeAmount = calculateReferralFee(\\n details.oToken,\\n feePercent,\\n bid.buyAmount,\\n bid.sellAmount\\n );\\n\\n IERC20(details.biddingToken).safeTransferFrom(\\n bid.signerWallet,\\n bid.referrer,\\n feeAmount\\n );\\n }\\n }\\n\\n // Transfer token from signer to recipient\\n IERC20(details.biddingToken).safeTransferFrom(\\n bid.signerWallet,\\n details.seller,\\n bid.sellAmount - feeAmount\\n );\\n\\n // Emit a Swap event\\n emit Swap(\\n bid.swapId,\\n bid.nonce,\\n bid.signerWallet,\\n bid.sellAmount,\\n bid.buyAmount,\\n bid.referrer,\\n feeAmount\\n );\\n }\\n\\n /**\\n * @notice Marks a nonce as used for the given signer\\n * @param signer address Address of the signer for which to mark the nonce as used\\n * @param nonce uint256 Nonce to be marked as used\\n * @return bool True if the nonce was not marked as used already\\n */\\n function _markNonceAsUsed(address signer, uint256 nonce)\\n internal\\n returns (bool)\\n {\\n uint256 groupKey = nonce / 256;\\n uint256 indexInGroup = nonce % 256;\\n uint256 group = _nonceGroups[signer][groupKey];\\n\\n // If it is already used, return false\\n if ((group >> indexInGroup) & 1 == 1) {\\n return false;\\n }\\n\\n _nonceGroups[signer][groupKey] = group | (uint256(1) << indexInGroup);\\n\\n return true;\\n }\\n\\n /**\\n * @notice Recover the signatory from a signature\\n * @param bid Bid struct containing bid details\\n */\\n function _getSignatory(Bid calldata bid) internal view returns (address) {\\n return\\n ecrecover(\\n keccak256(\\n abi.encodePacked(\\n \\\"\\\\x19\\\\x01\\\",\\n DOMAIN_SEPARATOR,\\n keccak256(\\n abi.encode(\\n BID_TYPEHASH,\\n bid.swapId,\\n bid.nonce,\\n bid.signerWallet,\\n bid.sellAmount,\\n bid.buyAmount,\\n bid.referrer\\n )\\n )\\n )\\n ),\\n bid.v,\\n bid.r,\\n bid.s\\n );\\n }\\n\\n /**\\n * This function assumes that all CALL premiums are denominated in the Offer.biddingToken\\n * This could easily change if we enabled Paradigm for Treasury - Calls are sold for USDC.\\n * It assumes that all PUT premiums are denominated in USDC.\\n */\\n function calculateReferralFee(\\n address otokenAddress,\\n uint256 feePercent,\\n uint256 numContracts,\\n uint256 premium\\n ) public view returns (uint256) {\\n IOtoken otoken = IOtoken(otokenAddress);\\n uint256 maxFee = (premium * MAX_FEE) / MAX_PERCENTAGE;\\n uint256 fee;\\n\\n if (otoken.isPut()) {\\n uint256 marketPrice = getMarketPrice(otoken.underlyingAsset());\\n // both numContracts and marketPrice are 10**8\\n // then you scale it down to 10**6 because of USDC\\n uint256 notional = (numContracts * marketPrice) / 10**10;\\n fee = (notional * feePercent) / MAX_PERCENTAGE;\\n } else {\\n IERC20Detailed underlying =\\n IERC20Detailed(otoken.underlyingAsset());\\n uint256 underlyingDecimals = underlying.decimals();\\n uint256 numContractsInUnderlying;\\n if (underlyingDecimals < 8) {\\n numContractsInUnderlying =\\n numContracts /\\n 10**(underlyingDecimals - 8);\\n } else {\\n numContractsInUnderlying =\\n numContracts *\\n 10**(underlyingDecimals - 8);\\n }\\n fee = (numContractsInUnderlying * feePercent) / MAX_PERCENTAGE;\\n }\\n\\n if (fee > maxFee) {\\n return maxFee;\\n }\\n return fee;\\n }\\n\\n function getMarketPrice(address asset) public view returns (uint256) {\\n address feed = priceFeeds[asset];\\n require(feed != address(0), \\\"NO_PRICE_FEED_SET\\\");\\n (\\n ,\\n /*uint80 roundID*/\\n int256 price,\\n ,\\n ,\\n\\n ) =\\n /*uint startedAt*/\\n /*uint timeStamp*/\\n /*uint80 answeredInRound*/\\n AggregatorV3Interface(feed).latestRoundData();\\n\\n require(price > 0, \\\"INVALID_PRICE_FEED\\\");\\n\\n return uint256(price);\\n }\\n\\n function setPriceFeed(address asset, address aggregator)\\n external\\n onlyOwner\\n {\\n priceFeeds[asset] = aggregator;\\n emit SetPriceFeed(asset, aggregator);\\n }\\n\\n /**\\n * @notice Returns the current chainId using the chainid opcode\\n * @return id uint256 The chain id\\n */\\n function getChainId() internal view returns (uint256 id) {\\n // no-inline-assembly\\n assembly {\\n id := chainid()\\n }\\n }\\n}\\n\",\"keccak256\":\"0xd3ce0b2d7512080fbf1e588c2d332bb5bfa68d844fb9e6fdcd0df72c17564ced\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x60a060405234801561001057600080fd5b504660808190526133ed61003d6000396000818161026e0152818161067f015261214b01526133ed6000f3fe608060405234801561001057600080fd5b50600436106101cf5760003560e01c806376e1128611610104578063b6549f75116100a2578063d5172fcc11610071578063d5172fcc14610492578063e55156b5146104a5578063f2fde38b146104b8578063fbcac9e1146104cb57600080fd5b8063b6549f7514610444578063b6a5d7de1461044c578063b91816111461045f578063bc063e1a1461048857600080fd5b80639c6f3026116100de5780639c6f3026146103275780639dcb511a1461033a578063a13f1c6214610363578063acb8cc491461043b57600080fd5b806376e11286146102e6578063796f077b146102f95780638da5cb5b1461030257600080fd5b8063416f281d1161017157806369fd925f1161014b57806369fd925f146102a25780636c1c56f1146102ab578063715018a6146102be57806375780f42146102c657600080fd5b8063416f281d146102695780634c255c971461029057806351bb9b1e1461029a57600080fd5b80631771855e116101ad5780631771855e1461023257806320606b70146102455780632787b5f81461024d5780633644e5151461026057600080fd5b8063077f224a146101d457806307a975a1146101e95780631647795e1461020f575b600080fd5b6101e76101e2366004612d5d565b6104ec565b005b6101fc6101f7366004612bf1565b6106df565b6040519081526020015b60405180910390f35b61022261021d366004612c68565b610cf2565b6040519015158152602001610206565b6101fc610240366004612c93565b610d4a565b6101fc611019565b6101fc61025b366004612dea565b611041565b6101fc60995481565b6101fc7f000000000000000000000000000000000000000000000000000000000000000081565b6101fc620f424081565b6101fc611118565b6101fc609a5481565b6101e76102b9366004612ccd565b611127565b6101e76111b2565b6101fc6102d4366004612b81565b609c6020526000908152604090205481565b6101e76102f4366004612bb9565b6111e8565b6101fc60975481565b6065546001600160a01b03165b6040516001600160a01b039091168152602001610206565b6101fc610335366004612b81565b61127e565b61030f610348366004612b81565b609f602052600090815260409020546001600160a01b031681565b6103dc610371366004612dea565b609b60205260009081526040902080546001820154600283015460038401546004909401546001600160a01b0393841694838516946001600160601b03600160a01b95869004811695918516949190910416916001600160801b0380831692600160801b9004169088565b604080516001600160a01b03998a16815297891660208901526001600160601b0396871690880152969093166060860152921660808401526001600160801b0391821660a08401521660c082015260e081019190915261010001610206565b6101fc60985481565b6101e76113a1565b6101e761045a366004612b81565b6113f9565b61030f61046d366004612b81565b609d602052600090815260409020546001600160a01b031681565b6101fc6201e84881565b6101e76104a0366004612e1a565b611496565b6101e76104b3366004612c68565b6117c8565b6101e76104c6366004612b81565b6118ed565b6104de6104d9366004612dd2565b611988565b604051610206929190613134565b600054610100900460ff1680610505575060005460ff16155b61052a5760405162461bcd60e51b8152600401610521906130b1565b60405180910390fd5b600054610100900460ff1615801561054c576000805461ffff19166101011790555b600084511161058c5760405162461bcd60e51b815260206004820152600c60248201526b215f646f6d61696e4e616d6560a01b6044820152606401610521565b60008351116105cf5760405162461bcd60e51b815260206004820152600f60248201526e10afb237b6b0b4b72b32b939b4b7b760891b6044820152606401610521565b6001600160a01b03821661060f5760405162461bcd60e51b815260206004820152600760248201526610afb7bbb732b960c91b6044820152606401610521565b610617611f83565b61061f611ff6565b610628826118ed565b835160208086019190912060975583518482012060985560405161064c9101612fef565b60408051601f198184030181528282528051602091820120609754609854928501919091529183019190915260608201527f000000000000000000000000000000000000000000000000000000000000000060808201523060a082015260c00160408051601f19818403018152919052805160209091012060995580156106d9576000805461ff00191690555b50505050565b60006001600160a01b0386166107415760405162461bcd60e51b815260206004820152602160248201527f6f546f6b656e2063616e6e6f7420626520746865207a65726f206164647265736044820152607360f81b6064820152608401610521565b6001600160a01b0385166107a75760405162461bcd60e51b815260206004820152602760248201527f42696464696e67546f6b656e2063616e6e6f7420626520746865207a65726f206044820152666164647265737360c81b6064820152608401610521565b6000846001600160601b03161161080a5760405162461bcd60e51b815260206004820152602160248201527f4d696e5072696365206d757374206265206c6172676572207468616e207a65726044820152606f60f81b6064820152608401610521565b6000836001600160601b03161161086f5760405162461bcd60e51b815260206004820152602360248201527f4d696e42696453697a65206d757374206265206c6172676572207468616e207a60448201526265726f60e81b6064820152608401610521565b816001600160801b0316836001600160601b031611156108d15760405162461bcd60e51b815260206004820152601d60248201527f4d696e42696453697a65206578636565647320746f74616c2073697a650000006044820152606401610521565b856001600160a01b031663f3c274a66040518163ffffffff1660e01b815260040160206040518083038186803b15801561090a57600080fd5b505afa15801561091e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109429190612d3d565b15610a265760006001600160a01b0316609f6000886001600160a01b0316637158da7c6040518163ffffffff1660e01b815260040160206040518083038186803b15801561098f57600080fd5b505afa1580156109a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109c79190612b9d565b6001600160a01b039081168252602082019290925260400160002054161415610a265760405162461bcd60e51b8152602060048201526011602482015270139bc81c1c9a58d94819995959081cd95d607a1b6044820152606401610521565b604051636eb1769f60e11b81523360048201523060248201526000906001600160a01b0388169063dd62ed3e9060440160206040518083038186803b158015610a6e57600080fd5b505afa158015610a82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aa69190612e02565b9050826001600160801b0316811015610af85760405162461bcd60e51b815260206004820152601460248201527353656c6c657220616c6c6f77616e6365206c6f7760601b6044820152606401610521565b6040516370a0823160e01b81523360048201526000906001600160a01b038916906370a082319060240160206040518083038186803b158015610b3a57600080fd5b505afa158015610b4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b729190612e02565b9050836001600160801b0316811015610bc25760405162461bcd60e51b815260206004820152601260248201527153656c6c65722062616c616e6365206c6f7760701b6044820152606401610521565b6001609a6000828254610bd59190613181565b9091555050609a546000818152609b60205260409081902080546001600160a01b0319163390811782556001600160601b03808a16600160a01b9081026001600160a01b038e8116919091176002860155918c1602908d1617600180840191909155600160801b6001600160801b038a1690810217600384015560049092019190915590519194507f61ed224de71f3815c67ff417a9cba223a61831d6223cfd50f3986f55cca8116691610cdf9186918c908c908c908c908c909687526001600160a01b03958616602088015293851660408701529190931660608501526001600160601b03928316608085015290911660a08301526001600160801b031660c082015260e00190565b60405180910390a1505095945050505050565b600080610d0161010084613199565b90506000610d116101008561333d565b6001600160a01b0386166000908152609e6020908152604080832095835294905292909220546001921c82169091149150505b92915050565b60008481620f4240610d5f6201e84886613298565b610d699190613199565b90506000826001600160a01b031663f3c274a66040518163ffffffff1660e01b815260040160206040518083038186803b158015610da657600080fd5b505afa158015610dba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dde9190612d3d565b15610e9a576000610e59846001600160a01b0316637158da7c6040518163ffffffff1660e01b815260040160206040518083038186803b158015610e2157600080fd5b505afa158015610e35573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103359190612b9d565b905060006402540be400610e6d838a613298565b610e779190613199565b9050620f4240610e878a83613298565b610e919190613199565b92505050610ffa565b6000836001600160a01b0316637158da7c6040518163ffffffff1660e01b815260040160206040518083038186803b158015610ed557600080fd5b505afa158015610ee9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f0d9190612b9d565b90506000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610f4a57600080fd5b505afa158015610f5e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f829190612f00565b60ff16905060006008821015610fb957610f9d6008836132df565b610fa890600a6131f0565b610fb2908a613199565b9050610fdc565b610fc46008836132df565b610fcf90600a6131f0565b610fd9908a613298565b90505b620f4240610fea8b83613298565b610ff49190613199565b93505050505b8181111561100c575091506110119050565b925050505b949350505050565b60405160200161102890612fef565b6040516020818303038152906040528051906020012081565b6000818152609b6020526040812060038101546001600160801b03166110a05760405162461bcd60e51b815260206004820152601460248201527313d999995c88191bd95cc81b9bdd08195e1a5cdd60621b6044820152606401610521565b6003810154600160801b90046001600160801b03166000816110c35760006110c6565b60015b600384015460ff9190911691506110e79083906001600160801b03166132df565b8184600401546110f791906132df565b611105906305f5e100613298565b61110f9190613199565b95945050505050565b60405160200161102890612f38565b60005b818110156111ad57600083838381811061115457634e487b7160e01b600052603260045260246000fd5b905060200201359050611167338261205d565b1561119a57604051339082907f8dd3c361eb2366ff27c2db0eb07b9261f1d052570742ab8c9a0c326f37aa576d90600090a35b50806111a581613322565b91505061112a565b505050565b6065546001600160a01b031633146111dc5760405162461bcd60e51b8152600401610521906130ff565b6111e660006120f6565b565b6065546001600160a01b031633146112125760405162461bcd60e51b8152600401610521906130ff565b6001600160a01b038281166000818152609f602090815260409182902080546001600160a01b031916948616948517905581519283528201929092527fc02f01e22efc017c1c952d0eef1a8fc038571f14ea5a285d79d1d7ba87d8b00191015b60405180910390a15050565b6001600160a01b038082166000908152609f6020526040812054909116806112dc5760405162461bcd60e51b81526020600482015260116024820152701393d7d4149250d157d191515117d4d155607a1b6044820152606401610521565b6000816001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a06040518083038186803b15801561131757600080fd5b505afa15801561132b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061134f9190612eb1565b5050509150506000811361139a5760405162461bcd60e51b81526020600482015260126024820152711253959053125117d4149250d157d191515160721b6044820152606401610521565b9392505050565b336000818152609d602052604080822080546001600160a01b0319811690915590516001600160a01b03909116929183917fd7426110292f20fe59e73ccf52124e0f5440a756507c91c7b0a6c50e1eb1a23a9190a350565b6001600160a01b0381166114405760405162461bcd60e51b815260206004820152600e60248201526d14d251d3915497d253959053125160921b6044820152606401610521565b336000818152609d602052604080822080546001600160a01b0319166001600160a01b038616908117909155905190917f30468de898bda644e26bab66e5a2241a3aa6aaf527257f5ca54e0f65204ba14a91a350565b600260015414156114e95760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610521565b60026001556000838152609b6020526040902080546001600160a01b031633811461156c5760405162461bcd60e51b815260206004820152602d60248201527f4f6e6c792073656c6c65722063616e20736574746c65206f72206f666665722060448201526c191bd95cdb89dd08195e1a5cdd609a1b6064820152608401610521565b6003820154600160801b90046001600160801b03166115c35760405162461bcd60e51b815260206004820152601360248201527213d999995c88199d5b1b1e481cd95d1d1b1959606a1b6044820152606401610521565b6040805160a0810182526001600160a01b03838116825260018501548082166020840152600286015491821660608401526001600160601b03600160a01b9182900481169484019490945290049091166080820152600090815b858110156117195786868281811061164557634e487b7160e01b600052603260045260246000fd5b9050610120020160000135881461169e5760405162461bcd60e51b815260206004820152601f60248201527f4f6666657220616e642062696420737761704964206d69736d617463686564006044820152606401610521565b6116cf82868989858181106116c357634e487b7160e01b600052603260045260246000fd5b90506101200201612148565b8686828181106116ef57634e487b7160e01b600052603260045260246000fd5b9050610120020160600135836117059190613181565b92508061171181613322565b91505061161d565b506003840154600160801b90046001600160801b0316158061173c57600061173f565b60015b61174c9060ff16846132df565b85600401600082825461175f9190613181565b909155505080156117ba5784546001600160a01b031916855560006002860181905560018601556040518881527f1b5db04ed24662a962854839dba8e5b26c71e843c99221f770571a4ea3ecfdcd9060200160405180910390a15b505060018055505050505050565b6065546001600160a01b031633146117f25760405162461bcd60e51b8152600401610521906130ff565b6001600160a01b0382166118545760405162461bcd60e51b815260206004820152602360248201527f52656665727265722063616e6e6f7420626520746865207a65726f206164647260448201526265737360e81b6064820152608401610521565b6201e848811061189c5760405162461bcd60e51b81526020600482015260136024820152724665652065786365656473206d6178696d756d60681b6044820152606401610521565b6001600160a01b0382166000818152609c6020908152604091829020849055815192835282018390527f01fe2943baee27f47add82886c2200f910c749c461c9b63c5fe83901a53bdb499101611272565b6065546001600160a01b031633146119175760405162461bcd60e51b8152600401610521906130ff565b6001600160a01b03811661197c5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610521565b611985816120f6565b50565b80356000908152609b6020908152604080832081516101008101835281546001600160a01b039081168083526001840154808316968401969096526001600160601b03600160a01b96879004811695840195909552600284015491821660608481019190915295909104909316608082015260038201546001600160801b0380821660a0840152600160801b9091041660c082015260049091015460e082015290611a6c5760405162461bcd60e51b815260206004820152601460248201527313d999995c88191bd95cc81b9bdd08195e1a5cdd60621b6044820152606401610521565b60408051600a80825261016082019092526000916020820161014080368337019050509050600080611a9d8761260b565b90506001600160a01b038116611afb577014d251d3905515549157d2539590531251607a1b838381518110611ae257634e487b7160e01b600052603260045260246000fd5b602090810291909101015281611af781613322565b9250505b6001600160a01b038116611b156060890160408a01612b81565b6001600160a01b031614158015611b6557506001600160a01b038116609d6000611b4560608b0160408c01612b81565b6001600160a01b0390811682526020820192909252604001600020541614155b15611bb3576b15539055551213d49256915160a21b838381518110611b9a57634e487b7160e01b600052603260045260246000fd5b602090810291909101015281611baf81613322565b9250505b611bc1818860200135610cf2565b15611c1557711393d390d157d053149150511657d554d15160721b838381518110611bfc57634e487b7160e01b600052603260045260246000fd5b602090810291909101015281611c1181613322565b9250505b83608001516001600160601b031687608001351015611c78576c10925117d513d3d7d4d3505313609a1b838381518110611c5f57634e487b7160e01b600052603260045260246000fd5b602090810291909101015281611c7481613322565b9250505b8360c001516001600160801b031687608001351115611ce757784249445f4558434545445f415641494c41424c455f53495a4560381b838381518110611cce57634e487b7160e01b600052603260045260246000fd5b602090810291909101015281611ce381613322565b9250505b60006080880135611cfa6008600a6131f0565b611d089060608b0135613298565b611d129190613199565b905084604001516001600160601b0316811015611d73576c50524943455f544f4f5f4c4f5760981b848481518110611d5a57634e487b7160e01b600052603260045260246000fd5b602090810291909101015282611d6f81613322565b9350505b600085606001516001600160a01b031663dd62ed3e8a6040016020810190611d9b9190612b81565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015230602482015260440160206040518083038186803b158015611de057600080fd5b505afa158015611df4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e189190612e02565b90508860600135811015611e7757735349474e45525f414c4c4f57414e43455f4c4f5760601b858581518110611e5e57634e487b7160e01b600052603260045260246000fd5b602090810291909101015283611e7381613322565b9450505b600086606001516001600160a01b03166370a082318b6040016020810190611e9f9190612b81565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b158015611ede57600080fd5b505afa158015611ef2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f169190612e02565b90508960600135811015611f7357715349474e45525f42414c414e43455f4c4f5760701b868681518110611f5a57634e487b7160e01b600052603260045260246000fd5b602090810291909101015284611f6f81613322565b9550505b5092989397509295505050505050565b600054610100900460ff1680611f9c575060005460ff16155b611fb85760405162461bcd60e51b8152600401610521906130b1565b600054610100900460ff16158015611fda576000805461ffff19166101011790555b611fe261277b565b8015611985576000805461ff001916905550565b600054610100900460ff168061200f575060005460ff16155b61202b5760405162461bcd60e51b8152600401610521906130b1565b600054610100900460ff1615801561204d576000805461ffff19166101011790555b6120556127ea565b611fe2612854565b60008061206c61010084613199565b9050600061207c6101008561333d565b6001600160a01b0386166000908152609e60209081526040808320868452909152902054909150600181831c811614156120bc5760009350505050610d44565b6001600160a01b0386166000908152609e602090815260408083209583529490529290922060019182901b92909217909155905092915050565b606580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b467f0000000000000000000000000000000000000000000000000000000000000000146121aa5760405162461bcd60e51b815260206004820152601060248201526f10d210525397d25117d0d2105391d15160821b6044820152606401610521565b60006121b58261260b565b90506001600160a01b0381166122015760405162461bcd60e51b815260206004820152601160248201527014d251d3905515549157d2539590531251607a1b6044820152606401610521565b6001600160a01b03811661221b6060840160408501612b81565b6001600160a01b0316146122a0576001600160a01b038116609d60006122476060860160408701612b81565b6001600160a01b03908116825260208201929092526040016000205416146122a05760405162461bcd60e51b815260206004820152600c60248201526b15539055551213d49256915160a21b6044820152606401610521565b6122ae81836020013561205d565b6122ef5760405162461bcd60e51b81526020600482015260126024820152711393d390d157d053149150511657d554d15160721b6044820152606401610521565b6003830154600160801b90046001600160801b0316608083013511156123535760405162461bcd60e51b81526020600482015260196024820152784249445f4558434545445f415641494c41424c455f53495a4560381b6044820152606401610521565b83608001518260800135101561239b5760405162461bcd60e51b815260206004820152600d60248201526c10925117d513d3d7d4d3505313609a1b6044820152606401610521565b600060808301356123ae6008600a6131f0565b6123bc906060860135613298565b6123c69190613199565b9050846040015181101561240c5760405162461bcd60e51b815260206004820152600d60248201526c50524943455f544f4f5f4c4f5760981b6044820152606401610521565b82608001358460030160108282829054906101000a90046001600160801b031661243691906132b7565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555061248f85600001518460400160208101906124759190612b81565b60208801516001600160a01b0316919060808701356128b4565b6000806124a260c0860160a08701612b81565b6001600160a01b03161461253c576000609c816124c560c0880160a08901612b81565b6001600160a01b031681526020810191909152604001600020549050801561253a576124ff87602001518287608001358860600135610d4a565b915061253a6125146060870160408801612b81565b61252460c0880160a08901612b81565b60608a01516001600160a01b03169190856128b4565b505b61257561254f6060860160408701612b81565b875161255f8460608901356132df565b60608a01516001600160a01b03169291906128b4565b6125856060850160408601612b81565b6001600160a01b031684357f71abc95ed6b8cb4d0cc5eee25c1212bea3ca594e723da5c9facf161f3f5b6bd56020870135606088013560808901356125d060c08b0160a08c01612b81565b604080519485526020850193909352918301526001600160a01b031660608201526080810185905260a00160405180910390a3505050505050565b6000600160995460405160200161262190612f38565b60405160208183030381529060405280519060200120846000013585602001358660400160208101906126549190612b81565b6060880135608089013561266e60c08b0160a08c01612b81565b60408051602081019890985287019590955260608601939093526001600160a01b03918216608086015260a085015260c08401919091521660e082015261010001604051602081830303815290604052805190602001206040516020016126ec92919061190160f01b81526002810192909252602282015260420190565b60408051601f19818403018152919052805160209091012061271460e0850160c08601612e95565b6040805160008152602081018083529390935260ff9091169082015260e08401356060820152610100840135608082015260a0016020604051602081039080840390855afa15801561276a573d6000803e3d6000fd5b5050604051601f1901519392505050565b600054610100900460ff1680612794575060005460ff16155b6127b05760405162461bcd60e51b8152600401610521906130b1565b600054610100900460ff161580156127d2576000805461ffff19166101011790555b600180558015611985576000805461ff001916905550565b600054610100900460ff1680612803575060005460ff16155b61281f5760405162461bcd60e51b8152600401610521906130b1565b600054610100900460ff16158015611fe2576000805461ffff19166101011790558015611985576000805461ff001916905550565b600054610100900460ff168061286d575060005460ff16155b6128895760405162461bcd60e51b8152600401610521906130b1565b600054610100900460ff161580156128ab576000805461ffff19166101011790555b611fe2336120f6565b604080516001600160a01b038581166024830152848116604483015260648083018590528351808403909101815260849092018352602080830180516001600160e01b03166323b872dd60e01b17905283518085019094528084527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908401526106d99287929160009161294c9185169084906129c9565b8051909150156111ad578080602001905181019061296a9190612d3d565b6111ad5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610521565b6060611011848460008585843b612a225760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610521565b600080866001600160a01b03168587604051612a3e9190612f1c565b60006040518083038185875af1925050503d8060008114612a7b576040519150601f19603f3d011682016040523d82523d6000602084013e612a80565b606091505b509150915061100c82828660608315612a9a57508161139a565b825115612aaa5782518084602001fd5b8160405162461bcd60e51b8152600401610521919061307e565b600082601f830112612ad4578081fd5b813567ffffffffffffffff80821115612aef57612aef61337d565b604051601f8301601f19908116603f01168101908282118183101715612b1757612b1761337d565b81604052838152866020858801011115612b2f578485fd5b8360208701602083013792830160200193909352509392505050565b805169ffffffffffffffffffff81168114612b6557600080fd5b919050565b80356001600160601b0381168114612b6557600080fd5b600060208284031215612b92578081fd5b813561139a81613393565b600060208284031215612bae578081fd5b815161139a81613393565b60008060408385031215612bcb578081fd5b8235612bd681613393565b91506020830135612be681613393565b809150509250929050565b600080600080600060a08688031215612c08578081fd5b8535612c1381613393565b94506020860135612c2381613393565b9350612c3160408701612b6a565b9250612c3f60608701612b6a565b915060808601356001600160801b0381168114612c5a578182fd5b809150509295509295909350565b60008060408385031215612c7a578182fd5b8235612c8581613393565b946020939093013593505050565b60008060008060808587031215612ca8578384fd5b8435612cb381613393565b966020860135965060408601359560600135945092505050565b60008060208385031215612cdf578182fd5b823567ffffffffffffffff80821115612cf6578384fd5b818501915085601f830112612d09578384fd5b813581811115612d17578485fd5b8660208260051b8501011115612d2b578485fd5b60209290920196919550909350505050565b600060208284031215612d4e578081fd5b8151801515811461139a578182fd5b600080600060608486031215612d71578283fd5b833567ffffffffffffffff80821115612d88578485fd5b612d9487838801612ac4565b94506020860135915080821115612da9578384fd5b50612db686828701612ac4565b9250506040840135612dc781613393565b809150509250925092565b60006101208284031215612de4578081fd5b50919050565b600060208284031215612dfb578081fd5b5035919050565b600060208284031215612e13578081fd5b5051919050565b600080600060408486031215612e2e578081fd5b83359250602084013567ffffffffffffffff80821115612e4c578283fd5b818601915086601f830112612e5f578283fd5b813581811115612e6d578384fd5b87602061012083028501011115612e82578384fd5b6020830194508093505050509250925092565b600060208284031215612ea6578081fd5b813561139a816133a8565b600080600080600060a08688031215612ec8578283fd5b612ed186612b4b565b9450602086015193506040860151925060608601519150612ef460808701612b4b565b90509295509295909350565b600060208284031215612f11578081fd5b815161139a816133a8565b60008251612f2e8184602087016132f6565b9190910192915050565b63084d2c8560e31b81526e1d5a5b9d0c8d4d881cddd85c12590b608a1b60048201526d1d5a5b9d0c8d4d881b9bdb98d94b60921b6013820152741859191c995cdcc81cda59db995c95d85b1b195d0b605a1b6021820152721d5a5b9d0c8d4d881cd95b1b105b5bdd5b9d0b606a1b6036820152711d5a5b9d0c8d4d88189d5e505b5bdd5b9d0b60721b60498201526f30b2323932b9b9903932b332b93932b960811b605b820152602960f81b606b820152606c0190565b6c08a92a06e626488dedac2d2dc5609b1b81526b1cdd1c9a5b99c81b985b594b60a21b600d8201526e1cdd1c9a5b99c81d995c9cda5bdb8b608a1b60198201526f1d5a5b9d0c8d4d8818da185a5b92590b60821b60288201527f6164647265737320766572696679696e67436f6e7472616374000000000000006038820152602960f81b605182015260520190565b602081526000825180602084015261309d8160408501602087016132f6565b601f01601f19169190910160400192915050565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60006040820184835260206040818501528185518084526060860191508287019350845b8181101561317457845183529383019391830191600101613158565b5090979650505050505050565b6000821982111561319457613194613351565b500190565b6000826131a8576131a8613367565b500490565b600181815b808511156131e85781600019048211156131ce576131ce613351565b808516156131db57918102915b93841c93908002906131b2565b509250929050565b600061139a838360008261320657506001610d44565b8161321357506000610d44565b816001811461322957600281146132335761324f565b6001915050610d44565b60ff84111561324457613244613351565b50506001821b610d44565b5060208310610133831016604e8410600b8410161715613272575081810a610d44565b61327c83836131ad565b806000190482111561329057613290613351565b029392505050565b60008160001904831182151516156132b2576132b2613351565b500290565b60006001600160801b03838116908316818110156132d7576132d7613351565b039392505050565b6000828210156132f1576132f1613351565b500390565b60005b838110156133115781810151838201526020016132f9565b838111156106d95750506000910152565b600060001982141561333657613336613351565b5060010190565b60008261334c5761334c613367565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461198557600080fd5b60ff8116811461198557600080fdfea26469706673582212209fb53fe1f6357b24e8adc4d5abd2ff27ec5170609c7cb8d3b53beacadcedff8f64736f6c63430008040033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101cf5760003560e01c806376e1128611610104578063b6549f75116100a2578063d5172fcc11610071578063d5172fcc14610492578063e55156b5146104a5578063f2fde38b146104b8578063fbcac9e1146104cb57600080fd5b8063b6549f7514610444578063b6a5d7de1461044c578063b91816111461045f578063bc063e1a1461048857600080fd5b80639c6f3026116100de5780639c6f3026146103275780639dcb511a1461033a578063a13f1c6214610363578063acb8cc491461043b57600080fd5b806376e11286146102e6578063796f077b146102f95780638da5cb5b1461030257600080fd5b8063416f281d1161017157806369fd925f1161014b57806369fd925f146102a25780636c1c56f1146102ab578063715018a6146102be57806375780f42146102c657600080fd5b8063416f281d146102695780634c255c971461029057806351bb9b1e1461029a57600080fd5b80631771855e116101ad5780631771855e1461023257806320606b70146102455780632787b5f81461024d5780633644e5151461026057600080fd5b8063077f224a146101d457806307a975a1146101e95780631647795e1461020f575b600080fd5b6101e76101e2366004612d5d565b6104ec565b005b6101fc6101f7366004612bf1565b6106df565b6040519081526020015b60405180910390f35b61022261021d366004612c68565b610cf2565b6040519015158152602001610206565b6101fc610240366004612c93565b610d4a565b6101fc611019565b6101fc61025b366004612dea565b611041565b6101fc60995481565b6101fc7f000000000000000000000000000000000000000000000000000000000000000081565b6101fc620f424081565b6101fc611118565b6101fc609a5481565b6101e76102b9366004612ccd565b611127565b6101e76111b2565b6101fc6102d4366004612b81565b609c6020526000908152604090205481565b6101e76102f4366004612bb9565b6111e8565b6101fc60975481565b6065546001600160a01b03165b6040516001600160a01b039091168152602001610206565b6101fc610335366004612b81565b61127e565b61030f610348366004612b81565b609f602052600090815260409020546001600160a01b031681565b6103dc610371366004612dea565b609b60205260009081526040902080546001820154600283015460038401546004909401546001600160a01b0393841694838516946001600160601b03600160a01b95869004811695918516949190910416916001600160801b0380831692600160801b9004169088565b604080516001600160a01b03998a16815297891660208901526001600160601b0396871690880152969093166060860152921660808401526001600160801b0391821660a08401521660c082015260e081019190915261010001610206565b6101fc60985481565b6101e76113a1565b6101e761045a366004612b81565b6113f9565b61030f61046d366004612b81565b609d602052600090815260409020546001600160a01b031681565b6101fc6201e84881565b6101e76104a0366004612e1a565b611496565b6101e76104b3366004612c68565b6117c8565b6101e76104c6366004612b81565b6118ed565b6104de6104d9366004612dd2565b611988565b604051610206929190613134565b600054610100900460ff1680610505575060005460ff16155b61052a5760405162461bcd60e51b8152600401610521906130b1565b60405180910390fd5b600054610100900460ff1615801561054c576000805461ffff19166101011790555b600084511161058c5760405162461bcd60e51b815260206004820152600c60248201526b215f646f6d61696e4e616d6560a01b6044820152606401610521565b60008351116105cf5760405162461bcd60e51b815260206004820152600f60248201526e10afb237b6b0b4b72b32b939b4b7b760891b6044820152606401610521565b6001600160a01b03821661060f5760405162461bcd60e51b815260206004820152600760248201526610afb7bbb732b960c91b6044820152606401610521565b610617611f83565b61061f611ff6565b610628826118ed565b835160208086019190912060975583518482012060985560405161064c9101612fef565b60408051601f198184030181528282528051602091820120609754609854928501919091529183019190915260608201527f000000000000000000000000000000000000000000000000000000000000000060808201523060a082015260c00160408051601f19818403018152919052805160209091012060995580156106d9576000805461ff00191690555b50505050565b60006001600160a01b0386166107415760405162461bcd60e51b815260206004820152602160248201527f6f546f6b656e2063616e6e6f7420626520746865207a65726f206164647265736044820152607360f81b6064820152608401610521565b6001600160a01b0385166107a75760405162461bcd60e51b815260206004820152602760248201527f42696464696e67546f6b656e2063616e6e6f7420626520746865207a65726f206044820152666164647265737360c81b6064820152608401610521565b6000846001600160601b03161161080a5760405162461bcd60e51b815260206004820152602160248201527f4d696e5072696365206d757374206265206c6172676572207468616e207a65726044820152606f60f81b6064820152608401610521565b6000836001600160601b03161161086f5760405162461bcd60e51b815260206004820152602360248201527f4d696e42696453697a65206d757374206265206c6172676572207468616e207a60448201526265726f60e81b6064820152608401610521565b816001600160801b0316836001600160601b031611156108d15760405162461bcd60e51b815260206004820152601d60248201527f4d696e42696453697a65206578636565647320746f74616c2073697a650000006044820152606401610521565b856001600160a01b031663f3c274a66040518163ffffffff1660e01b815260040160206040518083038186803b15801561090a57600080fd5b505afa15801561091e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109429190612d3d565b15610a265760006001600160a01b0316609f6000886001600160a01b0316637158da7c6040518163ffffffff1660e01b815260040160206040518083038186803b15801561098f57600080fd5b505afa1580156109a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109c79190612b9d565b6001600160a01b039081168252602082019290925260400160002054161415610a265760405162461bcd60e51b8152602060048201526011602482015270139bc81c1c9a58d94819995959081cd95d607a1b6044820152606401610521565b604051636eb1769f60e11b81523360048201523060248201526000906001600160a01b0388169063dd62ed3e9060440160206040518083038186803b158015610a6e57600080fd5b505afa158015610a82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aa69190612e02565b9050826001600160801b0316811015610af85760405162461bcd60e51b815260206004820152601460248201527353656c6c657220616c6c6f77616e6365206c6f7760601b6044820152606401610521565b6040516370a0823160e01b81523360048201526000906001600160a01b038916906370a082319060240160206040518083038186803b158015610b3a57600080fd5b505afa158015610b4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b729190612e02565b9050836001600160801b0316811015610bc25760405162461bcd60e51b815260206004820152601260248201527153656c6c65722062616c616e6365206c6f7760701b6044820152606401610521565b6001609a6000828254610bd59190613181565b9091555050609a546000818152609b60205260409081902080546001600160a01b0319163390811782556001600160601b03808a16600160a01b9081026001600160a01b038e8116919091176002860155918c1602908d1617600180840191909155600160801b6001600160801b038a1690810217600384015560049092019190915590519194507f61ed224de71f3815c67ff417a9cba223a61831d6223cfd50f3986f55cca8116691610cdf9186918c908c908c908c908c909687526001600160a01b03958616602088015293851660408701529190931660608501526001600160601b03928316608085015290911660a08301526001600160801b031660c082015260e00190565b60405180910390a1505095945050505050565b600080610d0161010084613199565b90506000610d116101008561333d565b6001600160a01b0386166000908152609e6020908152604080832095835294905292909220546001921c82169091149150505b92915050565b60008481620f4240610d5f6201e84886613298565b610d699190613199565b90506000826001600160a01b031663f3c274a66040518163ffffffff1660e01b815260040160206040518083038186803b158015610da657600080fd5b505afa158015610dba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dde9190612d3d565b15610e9a576000610e59846001600160a01b0316637158da7c6040518163ffffffff1660e01b815260040160206040518083038186803b158015610e2157600080fd5b505afa158015610e35573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103359190612b9d565b905060006402540be400610e6d838a613298565b610e779190613199565b9050620f4240610e878a83613298565b610e919190613199565b92505050610ffa565b6000836001600160a01b0316637158da7c6040518163ffffffff1660e01b815260040160206040518083038186803b158015610ed557600080fd5b505afa158015610ee9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f0d9190612b9d565b90506000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610f4a57600080fd5b505afa158015610f5e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f829190612f00565b60ff16905060006008821015610fb957610f9d6008836132df565b610fa890600a6131f0565b610fb2908a613199565b9050610fdc565b610fc46008836132df565b610fcf90600a6131f0565b610fd9908a613298565b90505b620f4240610fea8b83613298565b610ff49190613199565b93505050505b8181111561100c575091506110119050565b925050505b949350505050565b60405160200161102890612fef565b6040516020818303038152906040528051906020012081565b6000818152609b6020526040812060038101546001600160801b03166110a05760405162461bcd60e51b815260206004820152601460248201527313d999995c88191bd95cc81b9bdd08195e1a5cdd60621b6044820152606401610521565b6003810154600160801b90046001600160801b03166000816110c35760006110c6565b60015b600384015460ff9190911691506110e79083906001600160801b03166132df565b8184600401546110f791906132df565b611105906305f5e100613298565b61110f9190613199565b95945050505050565b60405160200161102890612f38565b60005b818110156111ad57600083838381811061115457634e487b7160e01b600052603260045260246000fd5b905060200201359050611167338261205d565b1561119a57604051339082907f8dd3c361eb2366ff27c2db0eb07b9261f1d052570742ab8c9a0c326f37aa576d90600090a35b50806111a581613322565b91505061112a565b505050565b6065546001600160a01b031633146111dc5760405162461bcd60e51b8152600401610521906130ff565b6111e660006120f6565b565b6065546001600160a01b031633146112125760405162461bcd60e51b8152600401610521906130ff565b6001600160a01b038281166000818152609f602090815260409182902080546001600160a01b031916948616948517905581519283528201929092527fc02f01e22efc017c1c952d0eef1a8fc038571f14ea5a285d79d1d7ba87d8b00191015b60405180910390a15050565b6001600160a01b038082166000908152609f6020526040812054909116806112dc5760405162461bcd60e51b81526020600482015260116024820152701393d7d4149250d157d191515117d4d155607a1b6044820152606401610521565b6000816001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a06040518083038186803b15801561131757600080fd5b505afa15801561132b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061134f9190612eb1565b5050509150506000811361139a5760405162461bcd60e51b81526020600482015260126024820152711253959053125117d4149250d157d191515160721b6044820152606401610521565b9392505050565b336000818152609d602052604080822080546001600160a01b0319811690915590516001600160a01b03909116929183917fd7426110292f20fe59e73ccf52124e0f5440a756507c91c7b0a6c50e1eb1a23a9190a350565b6001600160a01b0381166114405760405162461bcd60e51b815260206004820152600e60248201526d14d251d3915497d253959053125160921b6044820152606401610521565b336000818152609d602052604080822080546001600160a01b0319166001600160a01b038616908117909155905190917f30468de898bda644e26bab66e5a2241a3aa6aaf527257f5ca54e0f65204ba14a91a350565b600260015414156114e95760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610521565b60026001556000838152609b6020526040902080546001600160a01b031633811461156c5760405162461bcd60e51b815260206004820152602d60248201527f4f6e6c792073656c6c65722063616e20736574746c65206f72206f666665722060448201526c191bd95cdb89dd08195e1a5cdd609a1b6064820152608401610521565b6003820154600160801b90046001600160801b03166115c35760405162461bcd60e51b815260206004820152601360248201527213d999995c88199d5b1b1e481cd95d1d1b1959606a1b6044820152606401610521565b6040805160a0810182526001600160a01b03838116825260018501548082166020840152600286015491821660608401526001600160601b03600160a01b9182900481169484019490945290049091166080820152600090815b858110156117195786868281811061164557634e487b7160e01b600052603260045260246000fd5b9050610120020160000135881461169e5760405162461bcd60e51b815260206004820152601f60248201527f4f6666657220616e642062696420737761704964206d69736d617463686564006044820152606401610521565b6116cf82868989858181106116c357634e487b7160e01b600052603260045260246000fd5b90506101200201612148565b8686828181106116ef57634e487b7160e01b600052603260045260246000fd5b9050610120020160600135836117059190613181565b92508061171181613322565b91505061161d565b506003840154600160801b90046001600160801b0316158061173c57600061173f565b60015b61174c9060ff16846132df565b85600401600082825461175f9190613181565b909155505080156117ba5784546001600160a01b031916855560006002860181905560018601556040518881527f1b5db04ed24662a962854839dba8e5b26c71e843c99221f770571a4ea3ecfdcd9060200160405180910390a15b505060018055505050505050565b6065546001600160a01b031633146117f25760405162461bcd60e51b8152600401610521906130ff565b6001600160a01b0382166118545760405162461bcd60e51b815260206004820152602360248201527f52656665727265722063616e6e6f7420626520746865207a65726f206164647260448201526265737360e81b6064820152608401610521565b6201e848811061189c5760405162461bcd60e51b81526020600482015260136024820152724665652065786365656473206d6178696d756d60681b6044820152606401610521565b6001600160a01b0382166000818152609c6020908152604091829020849055815192835282018390527f01fe2943baee27f47add82886c2200f910c749c461c9b63c5fe83901a53bdb499101611272565b6065546001600160a01b031633146119175760405162461bcd60e51b8152600401610521906130ff565b6001600160a01b03811661197c5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610521565b611985816120f6565b50565b80356000908152609b6020908152604080832081516101008101835281546001600160a01b039081168083526001840154808316968401969096526001600160601b03600160a01b96879004811695840195909552600284015491821660608481019190915295909104909316608082015260038201546001600160801b0380821660a0840152600160801b9091041660c082015260049091015460e082015290611a6c5760405162461bcd60e51b815260206004820152601460248201527313d999995c88191bd95cc81b9bdd08195e1a5cdd60621b6044820152606401610521565b60408051600a80825261016082019092526000916020820161014080368337019050509050600080611a9d8761260b565b90506001600160a01b038116611afb577014d251d3905515549157d2539590531251607a1b838381518110611ae257634e487b7160e01b600052603260045260246000fd5b602090810291909101015281611af781613322565b9250505b6001600160a01b038116611b156060890160408a01612b81565b6001600160a01b031614158015611b6557506001600160a01b038116609d6000611b4560608b0160408c01612b81565b6001600160a01b0390811682526020820192909252604001600020541614155b15611bb3576b15539055551213d49256915160a21b838381518110611b9a57634e487b7160e01b600052603260045260246000fd5b602090810291909101015281611baf81613322565b9250505b611bc1818860200135610cf2565b15611c1557711393d390d157d053149150511657d554d15160721b838381518110611bfc57634e487b7160e01b600052603260045260246000fd5b602090810291909101015281611c1181613322565b9250505b83608001516001600160601b031687608001351015611c78576c10925117d513d3d7d4d3505313609a1b838381518110611c5f57634e487b7160e01b600052603260045260246000fd5b602090810291909101015281611c7481613322565b9250505b8360c001516001600160801b031687608001351115611ce757784249445f4558434545445f415641494c41424c455f53495a4560381b838381518110611cce57634e487b7160e01b600052603260045260246000fd5b602090810291909101015281611ce381613322565b9250505b60006080880135611cfa6008600a6131f0565b611d089060608b0135613298565b611d129190613199565b905084604001516001600160601b0316811015611d73576c50524943455f544f4f5f4c4f5760981b848481518110611d5a57634e487b7160e01b600052603260045260246000fd5b602090810291909101015282611d6f81613322565b9350505b600085606001516001600160a01b031663dd62ed3e8a6040016020810190611d9b9190612b81565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015230602482015260440160206040518083038186803b158015611de057600080fd5b505afa158015611df4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e189190612e02565b90508860600135811015611e7757735349474e45525f414c4c4f57414e43455f4c4f5760601b858581518110611e5e57634e487b7160e01b600052603260045260246000fd5b602090810291909101015283611e7381613322565b9450505b600086606001516001600160a01b03166370a082318b6040016020810190611e9f9190612b81565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b158015611ede57600080fd5b505afa158015611ef2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f169190612e02565b90508960600135811015611f7357715349474e45525f42414c414e43455f4c4f5760701b868681518110611f5a57634e487b7160e01b600052603260045260246000fd5b602090810291909101015284611f6f81613322565b9550505b5092989397509295505050505050565b600054610100900460ff1680611f9c575060005460ff16155b611fb85760405162461bcd60e51b8152600401610521906130b1565b600054610100900460ff16158015611fda576000805461ffff19166101011790555b611fe261277b565b8015611985576000805461ff001916905550565b600054610100900460ff168061200f575060005460ff16155b61202b5760405162461bcd60e51b8152600401610521906130b1565b600054610100900460ff1615801561204d576000805461ffff19166101011790555b6120556127ea565b611fe2612854565b60008061206c61010084613199565b9050600061207c6101008561333d565b6001600160a01b0386166000908152609e60209081526040808320868452909152902054909150600181831c811614156120bc5760009350505050610d44565b6001600160a01b0386166000908152609e602090815260408083209583529490529290922060019182901b92909217909155905092915050565b606580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b467f0000000000000000000000000000000000000000000000000000000000000000146121aa5760405162461bcd60e51b815260206004820152601060248201526f10d210525397d25117d0d2105391d15160821b6044820152606401610521565b60006121b58261260b565b90506001600160a01b0381166122015760405162461bcd60e51b815260206004820152601160248201527014d251d3905515549157d2539590531251607a1b6044820152606401610521565b6001600160a01b03811661221b6060840160408501612b81565b6001600160a01b0316146122a0576001600160a01b038116609d60006122476060860160408701612b81565b6001600160a01b03908116825260208201929092526040016000205416146122a05760405162461bcd60e51b815260206004820152600c60248201526b15539055551213d49256915160a21b6044820152606401610521565b6122ae81836020013561205d565b6122ef5760405162461bcd60e51b81526020600482015260126024820152711393d390d157d053149150511657d554d15160721b6044820152606401610521565b6003830154600160801b90046001600160801b0316608083013511156123535760405162461bcd60e51b81526020600482015260196024820152784249445f4558434545445f415641494c41424c455f53495a4560381b6044820152606401610521565b83608001518260800135101561239b5760405162461bcd60e51b815260206004820152600d60248201526c10925117d513d3d7d4d3505313609a1b6044820152606401610521565b600060808301356123ae6008600a6131f0565b6123bc906060860135613298565b6123c69190613199565b9050846040015181101561240c5760405162461bcd60e51b815260206004820152600d60248201526c50524943455f544f4f5f4c4f5760981b6044820152606401610521565b82608001358460030160108282829054906101000a90046001600160801b031661243691906132b7565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555061248f85600001518460400160208101906124759190612b81565b60208801516001600160a01b0316919060808701356128b4565b6000806124a260c0860160a08701612b81565b6001600160a01b03161461253c576000609c816124c560c0880160a08901612b81565b6001600160a01b031681526020810191909152604001600020549050801561253a576124ff87602001518287608001358860600135610d4a565b915061253a6125146060870160408801612b81565b61252460c0880160a08901612b81565b60608a01516001600160a01b03169190856128b4565b505b61257561254f6060860160408701612b81565b875161255f8460608901356132df565b60608a01516001600160a01b03169291906128b4565b6125856060850160408601612b81565b6001600160a01b031684357f71abc95ed6b8cb4d0cc5eee25c1212bea3ca594e723da5c9facf161f3f5b6bd56020870135606088013560808901356125d060c08b0160a08c01612b81565b604080519485526020850193909352918301526001600160a01b031660608201526080810185905260a00160405180910390a3505050505050565b6000600160995460405160200161262190612f38565b60405160208183030381529060405280519060200120846000013585602001358660400160208101906126549190612b81565b6060880135608089013561266e60c08b0160a08c01612b81565b60408051602081019890985287019590955260608601939093526001600160a01b03918216608086015260a085015260c08401919091521660e082015261010001604051602081830303815290604052805190602001206040516020016126ec92919061190160f01b81526002810192909252602282015260420190565b60408051601f19818403018152919052805160209091012061271460e0850160c08601612e95565b6040805160008152602081018083529390935260ff9091169082015260e08401356060820152610100840135608082015260a0016020604051602081039080840390855afa15801561276a573d6000803e3d6000fd5b5050604051601f1901519392505050565b600054610100900460ff1680612794575060005460ff16155b6127b05760405162461bcd60e51b8152600401610521906130b1565b600054610100900460ff161580156127d2576000805461ffff19166101011790555b600180558015611985576000805461ff001916905550565b600054610100900460ff1680612803575060005460ff16155b61281f5760405162461bcd60e51b8152600401610521906130b1565b600054610100900460ff16158015611fe2576000805461ffff19166101011790558015611985576000805461ff001916905550565b600054610100900460ff168061286d575060005460ff16155b6128895760405162461bcd60e51b8152600401610521906130b1565b600054610100900460ff161580156128ab576000805461ffff19166101011790555b611fe2336120f6565b604080516001600160a01b038581166024830152848116604483015260648083018590528351808403909101815260849092018352602080830180516001600160e01b03166323b872dd60e01b17905283518085019094528084527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908401526106d99287929160009161294c9185169084906129c9565b8051909150156111ad578080602001905181019061296a9190612d3d565b6111ad5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610521565b6060611011848460008585843b612a225760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610521565b600080866001600160a01b03168587604051612a3e9190612f1c565b60006040518083038185875af1925050503d8060008114612a7b576040519150601f19603f3d011682016040523d82523d6000602084013e612a80565b606091505b509150915061100c82828660608315612a9a57508161139a565b825115612aaa5782518084602001fd5b8160405162461bcd60e51b8152600401610521919061307e565b600082601f830112612ad4578081fd5b813567ffffffffffffffff80821115612aef57612aef61337d565b604051601f8301601f19908116603f01168101908282118183101715612b1757612b1761337d565b81604052838152866020858801011115612b2f578485fd5b8360208701602083013792830160200193909352509392505050565b805169ffffffffffffffffffff81168114612b6557600080fd5b919050565b80356001600160601b0381168114612b6557600080fd5b600060208284031215612b92578081fd5b813561139a81613393565b600060208284031215612bae578081fd5b815161139a81613393565b60008060408385031215612bcb578081fd5b8235612bd681613393565b91506020830135612be681613393565b809150509250929050565b600080600080600060a08688031215612c08578081fd5b8535612c1381613393565b94506020860135612c2381613393565b9350612c3160408701612b6a565b9250612c3f60608701612b6a565b915060808601356001600160801b0381168114612c5a578182fd5b809150509295509295909350565b60008060408385031215612c7a578182fd5b8235612c8581613393565b946020939093013593505050565b60008060008060808587031215612ca8578384fd5b8435612cb381613393565b966020860135965060408601359560600135945092505050565b60008060208385031215612cdf578182fd5b823567ffffffffffffffff80821115612cf6578384fd5b818501915085601f830112612d09578384fd5b813581811115612d17578485fd5b8660208260051b8501011115612d2b578485fd5b60209290920196919550909350505050565b600060208284031215612d4e578081fd5b8151801515811461139a578182fd5b600080600060608486031215612d71578283fd5b833567ffffffffffffffff80821115612d88578485fd5b612d9487838801612ac4565b94506020860135915080821115612da9578384fd5b50612db686828701612ac4565b9250506040840135612dc781613393565b809150509250925092565b60006101208284031215612de4578081fd5b50919050565b600060208284031215612dfb578081fd5b5035919050565b600060208284031215612e13578081fd5b5051919050565b600080600060408486031215612e2e578081fd5b83359250602084013567ffffffffffffffff80821115612e4c578283fd5b818601915086601f830112612e5f578283fd5b813581811115612e6d578384fd5b87602061012083028501011115612e82578384fd5b6020830194508093505050509250925092565b600060208284031215612ea6578081fd5b813561139a816133a8565b600080600080600060a08688031215612ec8578283fd5b612ed186612b4b565b9450602086015193506040860151925060608601519150612ef460808701612b4b565b90509295509295909350565b600060208284031215612f11578081fd5b815161139a816133a8565b60008251612f2e8184602087016132f6565b9190910192915050565b63084d2c8560e31b81526e1d5a5b9d0c8d4d881cddd85c12590b608a1b60048201526d1d5a5b9d0c8d4d881b9bdb98d94b60921b6013820152741859191c995cdcc81cda59db995c95d85b1b195d0b605a1b6021820152721d5a5b9d0c8d4d881cd95b1b105b5bdd5b9d0b606a1b6036820152711d5a5b9d0c8d4d88189d5e505b5bdd5b9d0b60721b60498201526f30b2323932b9b9903932b332b93932b960811b605b820152602960f81b606b820152606c0190565b6c08a92a06e626488dedac2d2dc5609b1b81526b1cdd1c9a5b99c81b985b594b60a21b600d8201526e1cdd1c9a5b99c81d995c9cda5bdb8b608a1b60198201526f1d5a5b9d0c8d4d8818da185a5b92590b60821b60288201527f6164647265737320766572696679696e67436f6e7472616374000000000000006038820152602960f81b605182015260520190565b602081526000825180602084015261309d8160408501602087016132f6565b601f01601f19169190910160400192915050565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60006040820184835260206040818501528185518084526060860191508287019350845b8181101561317457845183529383019391830191600101613158565b5090979650505050505050565b6000821982111561319457613194613351565b500190565b6000826131a8576131a8613367565b500490565b600181815b808511156131e85781600019048211156131ce576131ce613351565b808516156131db57918102915b93841c93908002906131b2565b509250929050565b600061139a838360008261320657506001610d44565b8161321357506000610d44565b816001811461322957600281146132335761324f565b6001915050610d44565b60ff84111561324457613244613351565b50506001821b610d44565b5060208310610133831016604e8410600b8410161715613272575081810a610d44565b61327c83836131ad565b806000190482111561329057613290613351565b029392505050565b60008160001904831182151516156132b2576132b2613351565b500290565b60006001600160801b03838116908316818110156132d7576132d7613351565b039392505050565b6000828210156132f1576132f1613351565b500390565b60005b838110156133115781810151838201526020016132f9565b838111156106d95750506000910152565b600060001982141561333657613336613351565b5060010190565b60008261334c5761334c613367565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461198557600080fd5b60ff8116811461198557600080fdfea26469706673582212209fb53fe1f6357b24e8adc4d5abd2ff27ec5170609c7cb8d3b53beacadcedff8f64736f6c63430008040033", + "devdoc": { + "kind": "dev", + "methods": { + "authorize(address)": { + "details": "Emits an Authorize event", + "params": { + "signer": "address Wallet of the signer to authorize" + } + }, + "averagePriceForOffer(uint256)": { + "params": { + "swapId": "unique identifier of the swap offer" + } + }, + "cancelNonce(uint256[])": { + "details": "Cancelled nonces are marked as usedEmits a Cancel eventOut of gas may occur in arrays of length > 400", + "params": { + "nonces": "uint256[] List of nonces to cancel" + } + }, + "check((uint256,uint256,address,uint256,uint256,address,uint8,bytes32,bytes32))": { + "params": { + "bid": "Bid struct containing bid details" + }, + "returns": { + "_0": "tuple of error count and bytes32[] memory array of error messages" + } + }, + "createOffer(address,address,uint96,uint96,uint128)": { + "params": { + "biddingToken": "token asked by seller", + "minBidSize": "minimum amount of oToken requested in a single bid", + "minPrice": "minimum price of oToken denominated in biddingToken", + "oToken": "token offered by seller", + "totalSize": "amount of oToken offered by seller" + } + }, + "nonceUsed(address,uint256)": { + "params": { + "nonce": "uint256 Nonce being checked", + "signer": "address Address of the signer" + } + }, + "owner()": { + "details": "Returns the address of the current owner." + }, + "renounceOwnership()": { + "details": "Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner." + }, + "revoke()": { + "details": "Emits a Revoke event" + }, + "setFee(address,uint256)": { + "params": { + "fee": "is the fee in percent in 2 decimals", + "referrer": "is the address of the referrer" + } + }, + "settleOffer(uint256,(uint256,uint256,address,uint256,uint256,address,uint8,bytes32,bytes32)[])": { + "params": { + "bids": "bids for swaps", + "swapId": "unique identifier of the swap offer" + } + }, + "transferOwnership(address)": { + "details": "Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner." + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "authorize(address)": { + "notice": "Authorize a signer" + }, + "averagePriceForOffer(uint256)": { + "notice": "Returns the average settlement price for a swap offer" + }, + "calculateReferralFee(address,uint256,uint256,uint256)": { + "notice": "This function assumes that all CALL premiums are denominated in the Offer.biddingToken This could easily change if we enabled Paradigm for Treasury - Calls are sold for USDC. It assumes that all PUT premiums are denominated in USDC." + }, + "cancelNonce(uint256[])": { + "notice": "Cancel one or more nonces" + }, + "check((uint256,uint256,address,uint256,uint256,address,uint8,bytes32,bytes32))": { + "notice": "Validates Swap bid for any potential errors" + }, + "createOffer(address,address,uint96,uint96,uint128)": { + "notice": "Create a new offer available for swap" + }, + "nonceUsed(address,uint256)": { + "notice": "Returns true if the nonce has been used" + }, + "revoke()": { + "notice": "Revoke the signer" + }, + "setFee(address,uint256)": { + "notice": "Sets the referral fee for a specific referrer" + }, + "settleOffer(uint256,(uint256,uint256,address,uint256,uint256,address,uint8,bytes32,bytes32)[])": { + "notice": "Settles the swap offering by iterating through the bids" + } + }, + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 176, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 179, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 230, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "_status", + "offset": 0, + "slot": "1", + "type": "t_uint256" + }, + { + "astId": 273, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "__gap", + "offset": 0, + "slot": "2", + "type": "t_array(t_uint256)49_storage" + }, + { + "astId": 993, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "__gap", + "offset": 0, + "slot": "51", + "type": "t_array(t_uint256)50_storage" + }, + { + "astId": 56, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "_owner", + "offset": 0, + "slot": "101", + "type": "t_address" + }, + { + "astId": 169, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "__gap", + "offset": 0, + "slot": "102", + "type": "t_array(t_uint256)49_storage" + }, + { + "astId": 14493, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "DOMAIN_NAME", + "offset": 0, + "slot": "151", + "type": "t_bytes32" + }, + { + "astId": 14495, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "DOMAIN_VERSION", + "offset": 0, + "slot": "152", + "type": "t_bytes32" + }, + { + "astId": 14497, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "DOMAIN_SEPARATOR", + "offset": 0, + "slot": "153", + "type": "t_bytes32" + }, + { + "astId": 14499, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "offersCounter", + "offset": 0, + "slot": "154", + "type": "t_uint256" + }, + { + "astId": 14504, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "swapOffers", + "offset": 0, + "slot": "155", + "type": "t_mapping(t_uint256,t_struct(Offer)4120_storage)" + }, + { + "astId": 14508, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "referralFees", + "offset": 0, + "slot": "156", + "type": "t_mapping(t_address,t_uint256)" + }, + { + "astId": 14512, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "authorized", + "offset": 0, + "slot": "157", + "type": "t_mapping(t_address,t_address)" + }, + { + "astId": 14519, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "_nonceGroups", + "offset": 0, + "slot": "158", + "type": "t_mapping(t_address,t_mapping(t_uint256,t_uint256))" + }, + { + "astId": 14524, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "priceFeeds", + "offset": 0, + "slot": "159", + "type": "t_mapping(t_address,t_address)" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_uint256)49_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "encoding": "inplace", + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_address)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => address)", + "numberOfBytes": "32", + "value": "t_address" + }, + "t_mapping(t_address,t_mapping(t_uint256,t_uint256))": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => mapping(uint256 => uint256))", + "numberOfBytes": "32", + "value": "t_mapping(t_uint256,t_uint256)" + }, + "t_mapping(t_address,t_uint256)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": "t_uint256" + }, + "t_mapping(t_uint256,t_struct(Offer)4120_storage)": { + "encoding": "mapping", + "key": "t_uint256", + "label": "mapping(uint256 => struct ISwap.Offer)", + "numberOfBytes": "32", + "value": "t_struct(Offer)4120_storage" + }, + "t_mapping(t_uint256,t_uint256)": { + "encoding": "mapping", + "key": "t_uint256", + "label": "mapping(uint256 => uint256)", + "numberOfBytes": "32", + "value": "t_uint256" + }, + "t_struct(Offer)4120_storage": { + "encoding": "inplace", + "label": "struct ISwap.Offer", + "members": [ + { + "astId": 4105, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "seller", + "offset": 0, + "slot": "0", + "type": "t_address" + }, + { + "astId": 4107, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "oToken", + "offset": 0, + "slot": "1", + "type": "t_address" + }, + { + "astId": 4109, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "minPrice", + "offset": 20, + "slot": "1", + "type": "t_uint96" + }, + { + "astId": 4111, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "biddingToken", + "offset": 0, + "slot": "2", + "type": "t_address" + }, + { + "astId": 4113, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "minBidSize", + "offset": 20, + "slot": "2", + "type": "t_uint96" + }, + { + "astId": 4115, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "totalSize", + "offset": 0, + "slot": "3", + "type": "t_uint128" + }, + { + "astId": 4117, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "availableSize", + "offset": 16, + "slot": "3", + "type": "t_uint128" + }, + { + "astId": 4119, + "contract": "contracts/utils/Swap.sol:Swap", + "label": "totalSales", + "offset": 0, + "slot": "4", + "type": "t_uint256" + } + ], + "numberOfBytes": "160" + }, + "t_uint128": { + "encoding": "inplace", + "label": "uint128", + "numberOfBytes": "16" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint96": { + "encoding": "inplace", + "label": "uint96", + "numberOfBytes": "12" + } + } + } +} \ No newline at end of file diff --git a/deployments/goerli/solcInputs/7ecbd1f874c019a019ec6af82a4cbc00.json b/deployments/goerli/solcInputs/7ecbd1f874c019a019ec6af82a4cbc00.json new file mode 100644 index 000000000..fe9e89ea7 --- /dev/null +++ b/deployments/goerli/solcInputs/7ecbd1f874c019a019ec6af82a4cbc00.json @@ -0,0 +1,308 @@ +{ + "language": "Solidity", + "sources": { + "contracts/interfaces/GammaInterface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nlibrary GammaTypes {\n // vault is a struct of 6 arrays that describe a position a user has, a user can have multiple vaults.\n struct Vault {\n // addresses of oTokens a user has shorted (i.e. written) against this vault\n address[] shortOtokens;\n // addresses of oTokens a user has bought and deposited in this vault\n // user can be long oTokens without opening a vault (e.g. by buying on a DEX)\n // generally, long oTokens will be 'deposited' in vaults to act as collateral\n // in order to write oTokens against (i.e. in spreads)\n address[] longOtokens;\n // addresses of other ERC-20s a user has deposited as collateral in this vault\n address[] collateralAssets;\n // quantity of oTokens minted/written for each oToken address in shortOtokens\n uint256[] shortAmounts;\n // quantity of oTokens owned and held in the vault for each oToken address in longOtokens\n uint256[] longAmounts;\n // quantity of ERC-20 deposited as collateral in the vault for each ERC-20 address in collateralAssets\n uint256[] collateralAmounts;\n }\n}\n\ninterface IOtoken {\n function underlyingAsset() external view returns (address);\n\n function strikeAsset() external view returns (address);\n\n function collateralAsset() external view returns (address);\n\n function strikePrice() external view returns (uint256);\n\n function expiryTimestamp() external view returns (uint256);\n\n function isPut() external view returns (bool);\n}\n\ninterface IOtokenFactory {\n function getOtoken(\n address _underlyingAsset,\n address _strikeAsset,\n address _collateralAsset,\n uint256 _strikePrice,\n uint256 _expiry,\n bool _isPut\n ) external view returns (address);\n\n function createOtoken(\n address _underlyingAsset,\n address _strikeAsset,\n address _collateralAsset,\n uint256 _strikePrice,\n uint256 _expiry,\n bool _isPut\n ) external returns (address);\n\n function getTargetOtokenAddress(\n address _underlyingAsset,\n address _strikeAsset,\n address _collateralAsset,\n uint256 _strikePrice,\n uint256 _expiry,\n bool _isPut\n ) external view returns (address);\n\n event OtokenCreated(\n address tokenAddress,\n address creator,\n address indexed underlying,\n address indexed strike,\n address indexed collateral,\n uint256 strikePrice,\n uint256 expiry,\n bool isPut\n );\n}\n\ninterface IController {\n // possible actions that can be performed\n enum ActionType {\n OpenVault,\n MintShortOption,\n BurnShortOption,\n DepositLongOption,\n WithdrawLongOption,\n DepositCollateral,\n WithdrawCollateral,\n SettleVault,\n Redeem,\n Call,\n Liquidate\n }\n\n struct ActionArgs {\n // type of action that is being performed on the system\n ActionType actionType;\n // address of the account owner\n address owner;\n // address which we move assets from or to (depending on the action type)\n address secondAddress;\n // asset that is to be transfered\n address asset;\n // index of the vault that is to be modified (if any)\n uint256 vaultId;\n // amount of asset that is to be transfered\n uint256 amount;\n // each vault can hold multiple short / long / collateral assets\n // but we are restricting the scope to only 1 of each in this version\n // in future versions this would be the index of the short / long / collateral asset that needs to be modified\n uint256 index;\n // any other data that needs to be passed in for arbitrary function calls\n bytes data;\n }\n\n struct RedeemArgs {\n // address to which we pay out the oToken proceeds\n address receiver;\n // oToken that is to be redeemed\n address otoken;\n // amount of oTokens that is to be redeemed\n uint256 amount;\n }\n\n function getPayout(address _otoken, uint256 _amount)\n external\n view\n returns (uint256);\n\n function operate(ActionArgs[] calldata _actions) external;\n\n function getAccountVaultCounter(address owner)\n external\n view\n returns (uint256);\n\n function oracle() external view returns (address);\n\n function getVault(address _owner, uint256 _vaultId)\n external\n view\n returns (GammaTypes.Vault memory);\n\n function getProceed(address _owner, uint256 _vaultId)\n external\n view\n returns (uint256);\n\n function isSettlementAllowed(\n address _underlying,\n address _strike,\n address _collateral,\n uint256 _expiry\n ) external view returns (bool);\n}\n\ninterface IOracle {\n function setAssetPricer(address _asset, address _pricer) external;\n\n function updateAssetPricer(address _asset, address _pricer) external;\n\n function getPrice(address _asset) external view returns (uint256);\n}\n" + }, + "contracts/utils/Swap.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Source: https://github.com/airswap/airswap-protocols/blob/main/source/swap/contracts/Swap.sol\n\npragma solidity =0.8.4;\n\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"../interfaces/ISwap.sol\";\nimport \"../storage/SwapStorage.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\nimport \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\nimport {IOtoken} from \"../interfaces/GammaInterface.sol\";\n\ncontract Swap is\n ISwap,\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n SwapStorage\n{\n using SafeERC20 for IERC20;\n\n uint256 public immutable DOMAIN_CHAIN_ID;\n\n bytes32 public constant DOMAIN_TYPEHASH =\n keccak256(\n abi.encodePacked(\n \"EIP712Domain(\",\n \"string name,\",\n \"string version,\",\n \"uint256 chainId,\",\n \"address verifyingContract\",\n \")\"\n )\n );\n\n bytes32 public constant BID_TYPEHASH =\n keccak256(\n abi.encodePacked(\n \"Bid(\",\n \"uint256 swapId,\",\n \"uint256 nonce,\",\n \"address signerWallet,\",\n \"uint256 sellAmount,\",\n \"uint256 buyAmount,\",\n \"address referrer\",\n \")\"\n )\n );\n\n uint256 public constant MAX_PERCENTAGE = 1000000;\n uint256 public constant MAX_FEE = 125000; // 12.5%\n uint256 internal constant MAX_ERROR_COUNT = 10;\n uint256 internal constant OTOKEN_DECIMALS = 8;\n\n /************************************************\n * CONSTRUCTOR\n ***********************************************/\n\n constructor() {\n uint256 currentChainId = getChainId();\n DOMAIN_CHAIN_ID = currentChainId;\n }\n\n /************************************************\n * INITIALIZATION\n ***********************************************/\n\n function initialize(\n string memory _domainName,\n string memory _domainVersion,\n address _owner\n ) external initializer {\n require(bytes(_domainName).length > 0, \"!_domainName\");\n require(bytes(_domainVersion).length > 0, \"!_domainVersion\");\n require(_owner != address(0), \"!_owner\");\n\n __ReentrancyGuard_init();\n __Ownable_init();\n transferOwnership(_owner);\n\n DOMAIN_NAME = keccak256(bytes(_domainName));\n DOMAIN_VERSION = keccak256(bytes(_domainVersion));\n DOMAIN_SEPARATOR = keccak256(\n abi.encode(\n DOMAIN_TYPEHASH,\n DOMAIN_NAME,\n DOMAIN_VERSION,\n DOMAIN_CHAIN_ID,\n this\n )\n );\n }\n\n /************************************************\n * SETTER\n ***********************************************/\n\n /**\n * @notice Sets the referral fee for a specific referrer\n * @param referrer is the address of the referrer\n * @param fee is the fee in percent in 2 decimals\n */\n function setFee(address referrer, uint256 fee) external onlyOwner {\n require(referrer != address(0), \"Referrer cannot be the zero address\");\n require(fee < MAX_FEE, \"Fee exceeds maximum\");\n\n referralFees[referrer] = fee;\n\n emit SetFee(referrer, fee);\n }\n\n /************************************************\n * OFFER CREATION AND SETTLEMENT\n ***********************************************/\n\n /**\n * @notice Create a new offer available for swap\n * @param oToken token offered by seller\n * @param biddingToken token asked by seller\n * @param minPrice minimum price of oToken denominated in biddingToken\n * @param minBidSize minimum amount of oToken requested in a single bid\n * @param totalSize amount of oToken offered by seller\n */\n function createOffer(\n address oToken,\n address biddingToken,\n uint96 minPrice,\n uint96 minBidSize,\n uint128 totalSize\n ) external override returns (uint256 swapId) {\n require(oToken != address(0), \"oToken cannot be the zero address\");\n require(\n biddingToken != address(0),\n \"BiddingToken cannot be the zero address\"\n );\n require(minPrice > 0, \"MinPrice must be larger than zero\");\n require(minBidSize > 0, \"MinBidSize must be larger than zero\");\n require(minBidSize <= totalSize, \"MinBidSize exceeds total size\");\n\n if (IOtoken(oToken).isPut()) {\n require(\n priceFeeds[IOtoken(oToken).underlyingAsset()] != address(0),\n \"No price feed set\"\n );\n }\n\n // Check seller allowance\n uint256 sellerAllowance =\n IERC20(oToken).allowance(msg.sender, address(this));\n require(sellerAllowance >= totalSize, \"Seller allowance low\");\n\n // Check seller balance\n uint256 sellerBalance = IERC20(oToken).balanceOf(msg.sender);\n require(sellerBalance >= totalSize, \"Seller balance low\");\n\n offersCounter += 1;\n\n swapId = offersCounter;\n\n swapOffers[swapId].seller = msg.sender;\n swapOffers[swapId].oToken = oToken;\n swapOffers[swapId].biddingToken = biddingToken;\n swapOffers[swapId].minBidSize = minBidSize;\n swapOffers[swapId].minPrice = minPrice;\n swapOffers[swapId].totalSize = totalSize;\n swapOffers[swapId].availableSize = totalSize;\n // We warm the storage slot with 1 wei so we avoid a cold SSTORE\n swapOffers[swapId].totalSales = 1;\n\n emit NewOffer(\n swapId,\n msg.sender,\n oToken,\n biddingToken,\n minPrice,\n minBidSize,\n totalSize\n );\n }\n\n /**\n * @notice Settles the swap offering by iterating through the bids\n * @param swapId unique identifier of the swap offer\n * @param bids bids for swaps\n */\n function settleOffer(uint256 swapId, Bid[] calldata bids)\n external\n override\n nonReentrant\n {\n Offer storage offer = swapOffers[swapId];\n\n address seller = offer.seller;\n require(\n seller == msg.sender,\n \"Only seller can settle or offer doesn't exist\"\n );\n require(offer.availableSize > 0, \"Offer fully settled\");\n\n uint256 totalSales;\n OfferDetails memory offerDetails;\n offerDetails.seller = seller;\n offerDetails.oToken = offer.oToken;\n offerDetails.biddingToken = offer.biddingToken;\n offerDetails.minPrice = offer.minPrice;\n offerDetails.minBidSize = offer.minBidSize;\n\n for (uint256 i = 0; i < bids.length; i++) {\n require(\n swapId == bids[i].swapId,\n \"Offer and bid swapId mismatched\"\n );\n\n _swap(offerDetails, offer, bids[i]);\n totalSales += bids[i].sellAmount;\n }\n\n bool fullySettled = offer.availableSize == 0;\n\n // Deduct the initial 1 wei offset if offer is fully settled\n offer.totalSales += totalSales - (fullySettled ? 1 : 0);\n\n if (fullySettled) {\n offer.seller = address(0);\n offer.oToken = address(0);\n offer.biddingToken = address(0);\n offer.minBidSize = 0;\n offer.minPrice = 0;\n\n emit SettleOffer(swapId);\n }\n }\n\n /**\n * @notice Authorize a signer\n * @param signer address Wallet of the signer to authorize\n * @dev Emits an Authorize event\n */\n function authorize(address signer) external override {\n require(signer != address(0), \"SIGNER_INVALID\");\n authorized[msg.sender] = signer;\n emit Authorize(signer, msg.sender);\n }\n\n /**\n * @notice Revoke the signer\n * @dev Emits a Revoke event\n */\n function revoke() external override {\n address tmp = authorized[msg.sender];\n delete authorized[msg.sender];\n emit Revoke(tmp, msg.sender);\n }\n\n /**\n * @notice Cancel one or more nonces\n * @dev Cancelled nonces are marked as used\n * @dev Emits a Cancel event\n * @dev Out of gas may occur in arrays of length > 400\n * @param nonces uint256[] List of nonces to cancel\n */\n function cancelNonce(uint256[] calldata nonces) external override {\n for (uint256 i = 0; i < nonces.length; i++) {\n uint256 nonce = nonces[i];\n if (_markNonceAsUsed(msg.sender, nonce)) {\n emit Cancel(nonce, msg.sender);\n }\n }\n }\n\n /************************************************\n * PUBLIC VIEW FUNCTIONS\n ***********************************************/\n\n /**\n * @notice Validates Swap bid for any potential errors\n * @param bid Bid struct containing bid details\n * @return tuple of error count and bytes32[] memory array of error messages\n */\n function check(Bid calldata bid)\n external\n view\n override\n returns (uint256, bytes32[] memory)\n {\n Offer memory offer = swapOffers[bid.swapId];\n require(offer.seller != address(0), \"Offer does not exist\");\n\n bytes32[] memory errors = new bytes32[](MAX_ERROR_COUNT);\n\n uint256 errCount;\n\n // Check signature\n address signatory = _getSignatory(bid);\n\n if (signatory == address(0)) {\n errors[errCount] = \"SIGNATURE_INVALID\";\n errCount++;\n }\n\n if (\n bid.signerWallet != signatory &&\n authorized[bid.signerWallet] != signatory\n ) {\n errors[errCount] = \"UNAUTHORIZED\";\n errCount++;\n }\n\n // Check nonce\n if (nonceUsed(signatory, bid.nonce)) {\n errors[errCount] = \"NONCE_ALREADY_USED\";\n errCount++;\n }\n\n // Check bid size\n if (bid.buyAmount < offer.minBidSize) {\n errors[errCount] = \"BID_TOO_SMALL\";\n errCount++;\n }\n if (bid.buyAmount > offer.availableSize) {\n errors[errCount] = \"BID_EXCEED_AVAILABLE_SIZE\";\n errCount++;\n }\n\n // Check bid price\n uint256 bidPrice =\n (bid.sellAmount * 10**OTOKEN_DECIMALS) / bid.buyAmount;\n if (bidPrice < offer.minPrice) {\n errors[errCount] = \"PRICE_TOO_LOW\";\n errCount++;\n }\n\n // Check signer allowance\n uint256 signerAllowance =\n IERC20(offer.biddingToken).allowance(\n bid.signerWallet,\n address(this)\n );\n if (signerAllowance < bid.sellAmount) {\n errors[errCount] = \"SIGNER_ALLOWANCE_LOW\";\n errCount++;\n }\n\n // Check signer balance\n uint256 signerBalance =\n IERC20(offer.biddingToken).balanceOf(bid.signerWallet);\n if (signerBalance < bid.sellAmount) {\n errors[errCount] = \"SIGNER_BALANCE_LOW\";\n errCount++;\n }\n\n return (errCount, errors);\n }\n\n /**\n * @notice Returns the average settlement price for a swap offer\n * @param swapId unique identifier of the swap offer\n */\n function averagePriceForOffer(uint256 swapId)\n external\n view\n override\n returns (uint256)\n {\n Offer storage offer = swapOffers[swapId];\n require(offer.totalSize != 0, \"Offer does not exist\");\n\n uint256 availableSize = offer.availableSize;\n\n // Deduct the initial 1 wei offset if offer is not fully settled\n uint256 adjustment = availableSize != 0 ? 1 : 0;\n\n return\n ((offer.totalSales - adjustment) * (10**8)) /\n (offer.totalSize - availableSize);\n }\n\n /**\n * @notice Returns true if the nonce has been used\n * @param signer address Address of the signer\n * @param nonce uint256 Nonce being checked\n */\n function nonceUsed(address signer, uint256 nonce)\n public\n view\n override\n returns (bool)\n {\n uint256 groupKey = nonce / 256;\n uint256 indexInGroup = nonce % 256;\n return (_nonceGroups[signer][groupKey] >> indexInGroup) & 1 == 1;\n }\n\n /************************************************\n * INTERNAL FUNCTIONS\n ***********************************************/\n\n /**\n * @notice Swap Atomic ERC20 Swap\n * @param details Details of offering\n * @param offer Offer struct containing offer details\n * @param bid Bid struct containing bid details\n */\n function _swap(\n OfferDetails memory details,\n Offer storage offer,\n Bid calldata bid\n ) internal {\n require(DOMAIN_CHAIN_ID == getChainId(), \"CHAIN_ID_CHANGED\");\n\n address signatory = _getSignatory(bid);\n\n require(signatory != address(0), \"SIGNATURE_INVALID\");\n\n if (bid.signerWallet != signatory) {\n require(authorized[bid.signerWallet] == signatory, \"UNAUTHORIZED\");\n }\n\n require(_markNonceAsUsed(signatory, bid.nonce), \"NONCE_ALREADY_USED\");\n require(\n bid.buyAmount <= offer.availableSize,\n \"BID_EXCEED_AVAILABLE_SIZE\"\n );\n require(bid.buyAmount >= details.minBidSize, \"BID_TOO_SMALL\");\n\n // Ensure min. price is met\n uint256 bidPrice =\n (bid.sellAmount * 10**OTOKEN_DECIMALS) / bid.buyAmount;\n require(bidPrice >= details.minPrice, \"PRICE_TOO_LOW\");\n\n // don't have to do a uint128 check because we already check\n // that bid.buyAmount <= offer.availableSize\n offer.availableSize -= uint128(bid.buyAmount);\n\n // Transfer token from sender to signer\n IERC20(details.oToken).safeTransferFrom(\n details.seller,\n bid.signerWallet,\n bid.buyAmount\n );\n\n // Transfer to referrer if any\n uint256 feeAmount;\n if (bid.referrer != address(0)) {\n uint256 feePercent = referralFees[bid.referrer];\n\n if (feePercent > 0) {\n feeAmount = calculateReferralFee(\n details.oToken,\n feePercent,\n bid.buyAmount,\n bid.sellAmount\n );\n\n IERC20(details.biddingToken).safeTransferFrom(\n bid.signerWallet,\n bid.referrer,\n feeAmount\n );\n }\n }\n\n // Transfer token from signer to recipient\n IERC20(details.biddingToken).safeTransferFrom(\n bid.signerWallet,\n details.seller,\n bid.sellAmount - feeAmount\n );\n\n // Emit a Swap event\n emit Swap(\n bid.swapId,\n bid.nonce,\n bid.signerWallet,\n bid.sellAmount,\n bid.buyAmount,\n bid.referrer,\n feeAmount\n );\n }\n\n /**\n * @notice Marks a nonce as used for the given signer\n * @param signer address Address of the signer for which to mark the nonce as used\n * @param nonce uint256 Nonce to be marked as used\n * @return bool True if the nonce was not marked as used already\n */\n function _markNonceAsUsed(address signer, uint256 nonce)\n internal\n returns (bool)\n {\n uint256 groupKey = nonce / 256;\n uint256 indexInGroup = nonce % 256;\n uint256 group = _nonceGroups[signer][groupKey];\n\n // If it is already used, return false\n if ((group >> indexInGroup) & 1 == 1) {\n return false;\n }\n\n _nonceGroups[signer][groupKey] = group | (uint256(1) << indexInGroup);\n\n return true;\n }\n\n /**\n * @notice Recover the signatory from a signature\n * @param bid Bid struct containing bid details\n */\n function _getSignatory(Bid calldata bid) internal view returns (address) {\n return\n ecrecover(\n keccak256(\n abi.encodePacked(\n \"\\x19\\x01\",\n DOMAIN_SEPARATOR,\n keccak256(\n abi.encode(\n BID_TYPEHASH,\n bid.swapId,\n bid.nonce,\n bid.signerWallet,\n bid.sellAmount,\n bid.buyAmount,\n bid.referrer\n )\n )\n )\n ),\n bid.v,\n bid.r,\n bid.s\n );\n }\n\n /**\n * This function assumes that all CALL premiums are denominated in the Offer.biddingToken\n * This could easily change if we enabled Paradigm for Treasury - Calls are sold for USDC.\n * It assumes that all PUT premiums are denominated in USDC.\n */\n function calculateReferralFee(\n address otokenAddress,\n uint256 feePercent,\n uint256 numContracts,\n uint256 premium\n ) public view returns (uint256) {\n IOtoken otoken = IOtoken(otokenAddress);\n uint256 maxFee = (premium * MAX_FEE) / MAX_PERCENTAGE;\n uint256 fee;\n\n if (otoken.isPut()) {\n uint256 marketPrice = getMarketPrice(otoken.underlyingAsset());\n // both numContracts and marketPrice are 10**8\n // then you scale it down to 10**6 because of USDC\n uint256 notional = (numContracts * marketPrice) / 10**10;\n fee = (notional * feePercent) / MAX_PERCENTAGE;\n } else {\n IERC20Detailed underlying =\n IERC20Detailed(otoken.underlyingAsset());\n uint256 underlyingDecimals = underlying.decimals();\n uint256 numContractsInUnderlying;\n if (underlyingDecimals < 8) {\n numContractsInUnderlying =\n numContracts /\n 10**(underlyingDecimals - 8);\n } else {\n numContractsInUnderlying =\n numContracts *\n 10**(underlyingDecimals - 8);\n }\n fee = (numContractsInUnderlying * feePercent) / MAX_PERCENTAGE;\n }\n\n if (fee > maxFee) {\n return maxFee;\n }\n return fee;\n }\n\n function getMarketPrice(address asset) public view returns (uint256) {\n address feed = priceFeeds[asset];\n require(feed != address(0), \"NO_PRICE_FEED_SET\");\n (\n ,\n /*uint80 roundID*/\n int256 price,\n ,\n ,\n\n ) =\n /*uint startedAt*/\n /*uint timeStamp*/\n /*uint80 answeredInRound*/\n AggregatorV3Interface(feed).latestRoundData();\n\n require(price > 0, \"INVALID_PRICE_FEED\");\n\n return uint256(price);\n }\n\n function setPriceFeed(address asset, address aggregator)\n external\n onlyOwner\n {\n priceFeeds[asset] = aggregator;\n emit SetPriceFeed(asset, aggregator);\n }\n\n /**\n * @notice Returns the current chainId using the chainid opcode\n * @return id uint256 The chain id\n */\n function getChainId() internal view returns (uint256 id) {\n // no-inline-assembly\n assembly {\n id := chainid()\n }\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "contracts/interfaces/ISwap.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity =0.8.4;\n\ninterface ISwap {\n struct Offer {\n // 32 byte slot 1, partial fill\n // Seller wallet address\n address seller;\n // 32 byte slot 2\n // Addess of oToken\n address oToken;\n // Price per oToken denominated in biddingToken\n uint96 minPrice;\n // 32 byte slot 3\n // ERC20 Token to bid for oToken\n address biddingToken;\n // Minimum oToken amount acceptable for a single bid\n uint96 minBidSize;\n // 32 byte slot 4\n // Total available oToken amount\n uint128 totalSize;\n // Remaining available oToken amount\n // This figure is updated after each successfull swap\n uint128 availableSize;\n // 32 byte slot 5\n // Amount of biddingToken received\n // This figure is updated after each successfull swap\n uint256 totalSales;\n }\n\n struct Bid {\n // ID assigned to offers\n uint256 swapId;\n // Number only used once for each wallet\n uint256 nonce;\n // Signer wallet address\n address signerWallet;\n // Amount of biddingToken offered by signer\n uint256 sellAmount;\n // Amount of oToken requested by signer\n uint256 buyAmount;\n // Referrer wallet address\n address referrer;\n // Signature recovery id\n uint8 v;\n // r portion of the ECSDA signature\n bytes32 r;\n // s portion of the ECSDA signature\n bytes32 s;\n }\n\n struct OfferDetails {\n // Seller wallet address\n address seller;\n // Addess of oToken\n address oToken;\n // Price per oToken denominated in biddingToken\n uint256 minPrice;\n // ERC20 Token to bid for oToken\n address biddingToken;\n // Minimum oToken amount acceptable for a single bid\n uint256 minBidSize;\n }\n\n event Swap(\n uint256 indexed swapId,\n uint256 nonce,\n address indexed signerWallet,\n uint256 signerAmount,\n uint256 sellerAmount,\n address referrer,\n uint256 feeAmount\n );\n\n event NewOffer(\n uint256 swapId,\n address seller,\n address oToken,\n address biddingToken,\n uint256 minPrice,\n uint256 minBidSize,\n uint256 totalSize\n );\n\n event SetFee(address referrer, uint256 fee);\n\n event SetPriceFeed(address asset, address aggregator);\n\n event SettleOffer(uint256 swapId);\n\n event Cancel(uint256 indexed nonce, address indexed signerWallet);\n\n event Authorize(address indexed signer, address indexed signerWallet);\n\n event Revoke(address indexed signer, address indexed signerWallet);\n\n function createOffer(\n address oToken,\n address biddingToken,\n uint96 minPrice,\n uint96 minBidSize,\n uint128 totalSize\n ) external returns (uint256 swapId);\n\n function settleOffer(uint256 swapId, Bid[] calldata bids) external;\n\n function cancelNonce(uint256[] calldata nonces) external;\n\n function check(Bid calldata bid)\n external\n view\n returns (uint256, bytes32[] memory);\n\n function averagePriceForOffer(uint256 swapId)\n external\n view\n returns (uint256);\n\n function authorize(address sender) external;\n\n function revoke() external;\n\n function nonceUsed(address, uint256) external view returns (bool);\n}\n" + }, + "contracts/storage/SwapStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport \"../interfaces/ISwap.sol\";\n\nabstract contract SwapStorageV1 {\n // Keccak256 of domain name for signature verification\n bytes32 public DOMAIN_NAME;\n // Keccak256 of domain version for signature verification\n bytes32 public DOMAIN_VERSION;\n // Keccak256 of abi-encoded domain parameters for signature verification\n bytes32 public DOMAIN_SEPARATOR;\n\n // Counter to keep track number of offers\n uint256 public offersCounter;\n // Mapping of swap offer details for a given swapId\n mapping(uint256 => ISwap.Offer) public swapOffers;\n // Mapping of referral fees for a given address, 1000000 = 100%\n mapping(address => uint256) public referralFees;\n // Mapping of authorized delegate for a given address\n mapping(address => address) public authorized;\n /**\n * @notice Double mapping of signers to nonce groups to nonce states\n * @dev The nonce group is computed as nonce / 256, so each group of 256 sequential nonces uses the same key\n * @dev The nonce states are encoded as 256 bits, for each nonce in the group 0 means available and 1 means used\n */\n mapping(address => mapping(uint256 => uint256)) internal _nonceGroups;\n}\n\nabstract contract SwapStorageV2 {\n // Price feed for looking up value of asset\n mapping(address => address) public priceFeeds;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of SwapStorage\n// e.g. SwapStorage, so finally it would look like\n// contract SwapStorage is SwapStorageV1, SwapStorageV2\nabstract contract SwapStorage is SwapStorageV1, SwapStorageV2 {\n\n}\n" + }, + "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuardUpgradeable is Initializable {\n // Booleans are more expensive than uint256 or any type that takes up a full\n // word because each write operation emits an extra SLOAD to first read the\n // slot's contents, replace the bits taken up by the boolean, and then write\n // back. This is the compiler's defense against contract upgrades and\n // pointer aliasing, and it cannot be disabled.\n\n // The values being non-zero value makes deployment a bit more expensive,\n // but in exchange the refund on every call to nonReentrant will be lower in\n // amount. Since refunds are capped to a percentage of the total\n // transaction's gas, it is best to keep them low in cases like this one, to\n // increase the likelihood of the full refund coming into effect.\n uint256 private constant _NOT_ENTERED = 1;\n uint256 private constant _ENTERED = 2;\n\n uint256 private _status;\n\n function __ReentrancyGuard_init() internal initializer {\n __ReentrancyGuard_init_unchained();\n }\n\n function __ReentrancyGuard_init_unchained() internal initializer {\n _status = _NOT_ENTERED;\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n // On the first call to nonReentrant, _notEntered will be true\n require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n _status = _ENTERED;\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = _NOT_ENTERED;\n }\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../utils/ContextUpgradeable.sol\";\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n function __Ownable_init() internal initializer {\n __Context_init_unchained();\n __Ownable_init_unchained();\n }\n\n function __Ownable_init_unchained() internal initializer {\n _setOwner(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _setOwner(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _setOwner(newOwner);\n }\n\n function _setOwner(address newOwner) private {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20Upgradeable.sol\";\nimport \"./extensions/IERC20MetadataUpgradeable.sol\";\nimport \"../../utils/ContextUpgradeable.sol\";\nimport \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n function __ERC20_init(string memory name_, string memory symbol_) internal initializer {\n __Context_init_unchained();\n __ERC20_init_unchained(name_, symbol_);\n }\n\n function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n\n uint256 currentAllowance = _allowances[sender][_msgSender()];\n require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n unchecked {\n _approve(sender, _msgSender(), currentAllowance - amount);\n }\n\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n uint256 currentAllowance = _allowances[_msgSender()][spender];\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n uint256 senderBalance = _balances[sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[sender] = senderBalance - amount;\n }\n _balances[recipient] += amount;\n\n emit Transfer(sender, recipient, amount);\n\n _afterTokenTransfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n _balances[account] += amount;\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n }\n _totalSupply -= amount;\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n uint256[45] private __gap;\n}\n" + }, + "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n}\n" + }, + "contracts/interfaces/IERC20Detailed.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IERC20Detailed is IERC20 {\n function decimals() external view returns (uint8);\n\n function symbol() external view returns (string calldata);\n\n function name() external view returns (string calldata);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private _initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private _initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(_initializing || !_initialized, \"Initializable: contract is already initialized\");\n\n bool isTopLevelCall = !_initializing;\n if (isTopLevelCall) {\n _initializing = true;\n _initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n _initializing = false;\n }\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n function __Context_init() internal initializer {\n __Context_init_unchained();\n }\n\n function __Context_init_unchained() internal initializer {\n }\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n uint256[50] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20Upgradeable {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20Upgradeable.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20MetadataUpgradeable is IERC20Upgradeable {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/vaults/TreasuryVault/RibbonTreasuryVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\n\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {\n VaultLifecycleTreasury\n} from \"../../libraries/VaultLifecycleTreasury.sol\";\nimport {\n RibbonTreasuryVaultStorage\n} from \"../../storage/RibbonTreasuryVaultStorage.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {IWETH} from \"../../interfaces/IWETH.sol\";\nimport {GnosisAuction} from \"../../libraries/GnosisAuction.sol\";\nimport {IERC20Detailed} from \"../../interfaces/IERC20Detailed.sol\";\n\ncontract RibbonTreasuryVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable,\n RibbonTreasuryVaultStorage\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // GNOSIS_EASY_AUCTION is Gnosis protocol's contract for initiating auctions and placing bids\n // https://github.com/gnosis/ido-contracts/blob/main/contracts/EasyAuction.sol\n address public immutable GNOSIS_EASY_AUCTION;\n\n /// OTOKEN_FACTORY is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n // The minimum amount above which premium distribution will occur during commitAndClose\n uint256 private constant MIN_DUST_AMOUNT = 10000000;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectManagementFee(\n uint256 managementFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n event CollectPerformanceFee(\n uint256 performanceFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n event DistributePremium(\n uint256 amount,\n uint256[] amounts,\n address[] recipients,\n uint256 round\n );\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event PremiumDiscountSet(\n uint256 premiumDiscount,\n uint256 newPremiumDiscount\n );\n\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event InitiateGnosisAuction(\n address indexed auctioningToken,\n address indexed biddingToken,\n uint256 auctionCounter,\n address indexed manager\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions\n */\n constructor(\n address _weth,\n address _usdc,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _gnosisEasyAuction\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n require(_gnosisEasyAuction != address(0), \"!_gnosisEasyAuction\");\n\n WETH = _weth;\n USDC = _usdc;\n OTOKEN_FACTORY = _oTokenFactory;\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n GNOSIS_EASY_AUCTION = _gnosisEasyAuction;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function initialize(\n VaultLifecycleTreasury.InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n VaultLifecycleTreasury.verifyInitializerParams(\n _initParams,\n _vaultParams,\n MIN_AUCTION_DURATION\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_initParams._tokenName, _initParams._tokenSymbol);\n __Ownable_init();\n transferOwnership(_initParams._owner);\n\n keeper = _initParams._keeper;\n period = _initParams._period;\n optionsPremiumPricer = _initParams._optionsPremiumPricer;\n strikeSelection = _initParams._strikeSelection;\n premiumDiscount = _initParams._premiumDiscount;\n auctionDuration = _initParams._auctionDuration;\n feeRecipient = _initParams._feeRecipient;\n performanceFee = _initParams._performanceFee;\n managementFee = _perRoundManagementFee(_initParams._managementFee);\n maxDepositors = _initParams._maxDepositors;\n minDeposit = _initParams._minDeposit;\n\n vaultParams = _vaultParams;\n vaultState.round = 1;\n\n uint256 assetBalance =\n IERC20(vaultParams.asset).balanceOf(address(this));\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n managementFee = _perRoundManagementFee(newManagementFee);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n }\n\n /**\n * @notice Internal function to set the management fee for the vault\n * @param managementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2\n * @return perRoundManagementFee is the management divided by the number of rounds per year\n */\n function _perRoundManagementFee(uint256 managementFee)\n internal\n view\n returns (uint256)\n {\n uint256 _period = period;\n uint256 feeDivider =\n _period % 30 == 0\n ? Vault.FEE_MULTIPLIER * (12 / (_period / 30))\n : WEEKS_PER_YEAR / (_period / 7);\n\n // We are dividing annualized management fee by num weeks in a year\n return managementFee.mul(Vault.FEE_MULTIPLIER).div(feeDivider);\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /**\n * @notice Sets the new discount on premiums for options we are selling\n * @param newPremiumDiscount is the premium discount\n */\n function setPremiumDiscount(uint256 newPremiumDiscount) external onlyOwner {\n require(\n newPremiumDiscount > 0 &&\n newPremiumDiscount < 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"Invalid discount\"\n );\n\n emit PremiumDiscountSet(premiumDiscount, newPremiumDiscount);\n\n premiumDiscount = newPremiumDiscount;\n }\n\n /**\n * @notice Sets the new auction duration\n * @param newAuctionDuration is the auction duration\n */\n function setAuctionDuration(uint256 newAuctionDuration) external onlyOwner {\n require(\n newAuctionDuration >= MIN_AUCTION_DURATION,\n \"Invalid auction duration\"\n );\n\n emit AuctionDurationSet(auctionDuration, newAuctionDuration);\n\n auctionDuration = newAuctionDuration;\n }\n\n /**\n * @notice Sets the new strike selection contract\n * @param newStrikeSelection is the address of the new strike selection contract\n */\n function setStrikeSelection(address newStrikeSelection) external onlyOwner {\n require(newStrikeSelection != address(0), \"!newStrikeSelection\");\n strikeSelection = newStrikeSelection;\n }\n\n /**\n * @notice Sets the new options premium pricer contract\n * @param newOptionsPremiumPricer is the address of the new strike selection contract\n */\n function setOptionsPremiumPricer(address newOptionsPremiumPricer)\n external\n onlyOwner\n {\n require(\n newOptionsPremiumPricer != address(0),\n \"!newOptionsPremiumPricer\"\n );\n optionsPremiumPricer = newOptionsPremiumPricer;\n }\n\n /**\n * @notice Optionality to set strike price manually\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice)\n external\n onlyOwner\n nonReentrant\n {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Set the maximum number of depositors\n * @param newMaxDepositors is the new cap for number of depositors\n */\n function setMaxDepositors(uint256 newMaxDepositors)\n external\n onlyOwner\n nonReentrant\n {\n require(newMaxDepositors > 0, \"!newMaxDepositors\");\n maxDepositors = newMaxDepositors;\n }\n\n /**\n * @notice Set the minimum deposit amount\n * @param newMinDeposit is the new minimum amount for deposit\n */\n function setMinDeposit(uint256 newMinDeposit)\n external\n onlyOwner\n nonReentrant\n {\n require(newMinDeposit > 0, \"!newMinDeposit\");\n minDeposit = newMinDeposit;\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Internal function to add new depositor address\n * @param newDepositor is the address to include in the depositors list\n */\n function _addDepositor(address newDepositor) internal {\n if (!depositorsMap[newDepositor]) {\n require(newDepositor != address(0), \"Depositor address null\");\n require(\n (depositorsArray.length + 1) <= maxDepositors,\n \"Number of depositors exceeds limit\"\n );\n\n depositorsMap[newDepositor] = true;\n depositorsArray.push(newDepositor);\n }\n }\n\n /**\n * @notice Remove addresses from depositors list\n * @param excludeDepositor is the address to exclude from the depositors list\n */\n function _removeDepositor(address excludeDepositor) internal {\n address[] storage array = depositorsArray;\n uint256 arrayLength = array.length;\n\n require(depositorsMap[excludeDepositor], \"Depositor does not exist\");\n\n depositorsMap[excludeDepositor] = false;\n\n for (uint256 i = 0; i < arrayLength - 1; i++) {\n if (excludeDepositor == array[i]) {\n (array[i], array[arrayLength - 1]) = (\n array[arrayLength - 1],\n array[i]\n );\n }\n }\n array.pop();\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender.\n * @param amount is the amount of `asset` to deposit\n */\n function deposit(uint256 amount) external nonReentrant {\n require(amount > 0, \"!amount\");\n\n _addDepositor(msg.sender);\n\n _depositFor(amount, msg.sender);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n */\n function _depositFor(uint256 amount, address creditor) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount = totalBalance().add(amount);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n uint256 totalUserDeposit =\n accountVaultBalance(msg.sender).add(depositReceipt.amount).add(\n amount\n );\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n require(totalUserDeposit >= minDeposit, \"Minimum deposit not reached\");\n\n emit Deposit(creditor, amount, currentRound);\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n if (depositReceipt.amount > 0 || depositReceipt.unredeemedShares > 0) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n uint256 withdrawalRound = withdrawal.round;\n\n bool withdrawalIsSameRound = withdrawalRound == currentRound;\n\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n\n uint256 existingShares = uint256(withdrawal.shares);\n\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n\n // Ensure withdrawal does not reduce user deposit below the minimum amount\n uint256 vaultDecimals = vaultParams.decimals;\n uint256 userBalance = accountVaultBalance(msg.sender);\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n numShares,\n currentRound != 1\n ? roundPricePerShare[currentRound - 1]\n : 10**vaultDecimals,\n vaultDecimals\n );\n\n if (userBalance > withdrawAmount) {\n uint256 totalDeposit = userBalance.sub(withdrawAmount);\n require(totalDeposit >= minDeposit, \"Minimum deposit not reached\");\n }\n\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(numShares);\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n if (depositReceipt.amount == 0 && balanceOf(msg.sender) == numShares) {\n _removeDepositor(msg.sender);\n }\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return withdrawAmount the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n require(withdrawAmount > 0, \"!withdrawAmount\");\n transferAsset(msg.sender, withdrawAmount);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n uint256 userBalance =\n accountVaultBalance(msg.sender).add(receiptAmount);\n\n if (userBalance > amount) {\n uint256 totalUserDeposit = userBalance.sub(amount);\n require(\n totalUserDeposit >= minDeposit,\n \"Minimum deposit not reached\"\n );\n }\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n if (depositReceipt.amount == 0 && shares(msg.sender) == 0) {\n _removeDepositor(msg.sender);\n }\n\n transferAsset(msg.sender, amount);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /*\n * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map.\n * Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k.\n * Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0.\n * @param numRounds is the number of rounds to initialize in the map\n */\n function initRounds(uint256 numRounds) external nonReentrant {\n require(numRounds > 0, \"!numRounds\");\n\n uint256 _round = vaultState.round;\n for (uint256 i = 0; i < numRounds; i++) {\n uint256 index = _round + i;\n require(roundPricePerShare[index] == 0, \"Initialized\"); // AVOID OVERWRITING ACTUAL VALUES\n roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT;\n }\n }\n\n /*\n * @notice Helper function that performs most administrative tasks\n * such as setting next option, minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @return newOption is the new option address\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _rollToNextOption(uint256 lastQueuedWithdrawAmount)\n internal\n returns (\n address newOption,\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount\n )\n {\n require(block.timestamp >= optionState.nextOptionReadyAt, \"!ready\");\n\n newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n uint256 currentRound = vaultState.round;\n address recipient = feeRecipient;\n uint256 mintShares;\n uint256 managementFeeInAsset;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n managementFeeInAsset\n ) = VaultLifecycleTreasury.rollover(\n vaultState,\n VaultLifecycleTreasury.RolloverParams(\n vaultParams.decimals,\n IERC20(vaultParams.asset).balanceOf(address(this)),\n totalSupply(),\n lastQueuedWithdrawAmount,\n currentRound != 1 ? managementFee : 0\n )\n );\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n\n // Finalize the pricePerShare at the end of the round\n\n roundPricePerShare[currentRound] = newPricePerShare;\n\n emit CollectManagementFee(\n managementFeeInAsset,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (managementFeeInAsset > 0) {\n transferAsset(payable(recipient), managementFeeInAsset);\n }\n\n return (newOption, lockedBalance, queuedWithdrawAmount);\n }\n\n /**\n * @notice Helper function to make an ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) internal {\n address asset = vaultParams.asset;\n IERC20(asset).safeTransfer(recipient, amount);\n }\n\n /**\n * @notice Sets the next option the vault will be shorting, and closes the existing short.\n * This allows all the users to withdraw if the next option is malicious.\n */\n function commitAndClose() external nonReentrant {\n address oldOption = optionState.currentOption;\n\n VaultLifecycleTreasury.CloseParams memory closeParams =\n VaultLifecycleTreasury.CloseParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n currentOption: oldOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n period: period\n });\n\n (\n address otokenAddress,\n uint256 premium,\n uint256 strikePrice,\n uint256 delta\n ) =\n VaultLifecycleTreasury.commitAndClose(\n strikeSelection,\n optionsPremiumPricer,\n premiumDiscount,\n closeParams,\n vaultParams,\n vaultState\n );\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n ShareMath.assertUint104(premium);\n currentOtokenPremium = uint104(premium);\n optionState.nextOption = otokenAddress;\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n\n _closeShort(oldOption);\n\n // In case chargeAndDistribute was not called last round, call\n // the function to conclude last round's performance fee and distribution\n if (IERC20(USDC).balanceOf(address(this)) > MIN_DUST_AMOUNT) {\n _chargeAndDistribute();\n }\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycleTreasury.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n (\n address newOption,\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount\n ) = _rollToNextOption(uint256(lastQueuedWithdrawAmount));\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n VaultLifecycleTreasury.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n _startAuction();\n }\n\n /**\n * @notice Initiate the gnosis auction.\n */\n function startAuction() external onlyKeeper nonReentrant {\n _startAuction();\n }\n\n function _startAuction() private {\n GnosisAuction.AuctionDetails memory auctionDetails;\n\n uint256 currOtokenPremium = currentOtokenPremium;\n\n require(currOtokenPremium > 0, \"!currentOtokenPremium\");\n\n uint256 stableDecimals = IERC20Detailed(USDC).decimals();\n\n auctionDetails.oTokenAddress = optionState.currentOption;\n auctionDetails.gnosisEasyAuction = GNOSIS_EASY_AUCTION;\n auctionDetails.asset = USDC;\n auctionDetails.assetDecimals = stableDecimals;\n auctionDetails.oTokenPremium = currOtokenPremium;\n auctionDetails.duration = auctionDuration;\n\n optionAuctionID = VaultLifecycleTreasury.startAuction(auctionDetails);\n }\n\n /**\n * @notice Burn the remaining oTokens left over from gnosis auction.\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n uint256 unlockedAssetAmount =\n VaultLifecycleTreasury.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n\n vaultState.lockedAmount = uint104(\n uint256(vaultState.lockedAmount).sub(unlockedAssetAmount)\n );\n }\n\n /**\n * @notice Settles the round's Gnosis auction and distribute the premiums earned\n */\n function concludeOptionsSale() external onlyKeeper nonReentrant {\n VaultLifecycleTreasury.settleAuction(\n GNOSIS_EASY_AUCTION,\n optionAuctionID\n );\n\n if (IERC20(USDC).balanceOf(address(this)) > MIN_DUST_AMOUNT) {\n _chargeAndDistribute();\n }\n }\n\n /**\n * @notice Charge performance fee and distribute remaining to depositors addresses\n */\n function chargeAndDistribute() external onlyKeeper nonReentrant {\n _chargeAndDistribute();\n }\n\n /**\n * @notice Calculate performance fee and transfer to fee recipient\n */\n function _chargeAndDistribute() internal {\n IERC20 stableAsset = IERC20(USDC);\n uint256 stableBalance = stableAsset.balanceOf(address(this));\n\n require(stableBalance > 0, \"no premium to distribute\");\n\n _chargePerformanceFee(stableAsset, stableBalance);\n\n _distributePremium(\n stableAsset,\n stableAsset.balanceOf(address(this)) // Get the new balance\n );\n }\n\n /**\n * @notice Charge performance fee\n */\n function _chargePerformanceFee(IERC20 token, uint256 amount) internal {\n address recipient = feeRecipient;\n uint256 transferAmount =\n amount.mul(performanceFee).div(100 * Vault.FEE_MULTIPLIER);\n\n token.safeTransfer(recipient, transferAmount);\n\n // Performance fee for the round is charged after rollover\n // hence we need to adjust the round to the previous\n emit CollectPerformanceFee(\n transferAmount,\n vaultState.round - 1,\n recipient\n );\n }\n\n /**\n * @notice Distribute the premium to depositor addresses\n */\n function _distributePremium(IERC20 token, uint256 amount) internal {\n // Distribute to depositor address\n address[] storage _depositors = depositorsArray;\n uint256[] memory _amounts = new uint256[](_depositors.length);\n uint256 totalSupply = totalSupply() - lastQueuedWithdrawAmount;\n\n for (uint256 i = 0; i < _depositors.length; i++) {\n // Distribute to depositors proportional to the amount of\n // shares they own\n address depositorAddress = _depositors[i];\n _amounts[i] = shares(depositorAddress).mul(amount).div(totalSupply);\n\n token.safeTransfer(depositorAddress, _amounts[i]);\n }\n\n emit DistributePremium(\n amount,\n _amounts,\n _depositors,\n vaultState.round - 1\n );\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n public\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n return\n uint256(vaultState.lockedAmount).add(\n IERC20(vaultParams.asset).balanceOf(address(this))\n );\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n /**\n * @notice Returns the maximum capacity of the vault in terms of the vault's asset\n */\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n /**\n * @notice Returns the date and time for the next options sale\n */\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n /**\n * @notice Returns the options specification for the current round\n */\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n /**\n * @notice Returns the options specification for the next round\n */\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n /**\n * @notice Returns total pending deposit for the current round\n */\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n\n /**\n * @notice ERC20 _transfer override function\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal override {\n require(\n recipient == address(this) || sender == address(this),\n \"Treasury rToken is not transferrable\"\n );\n return ERC20Upgradeable._transfer(sender, recipient, amount);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n// CAUTION\n// This version of SafeMath should only be used with Solidity 0.8 or later,\n// because it relies on the compiler's built in overflow checks.\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations.\n *\n * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler\n * now has built in overflow checking.\n */\nlibrary SafeMath {\n /**\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n uint256 c = a + b;\n if (c < a) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b > a) return (false, 0);\n return (true, a - b);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) return (true, 0);\n uint256 c = a * b;\n if (c / a != b) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a / b);\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a % b);\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n return a + b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n return a - b;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n return a * b;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator.\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n return a % b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n * overflow (when the result is negative).\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {trySub}.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b <= a, errorMessage);\n return a - b;\n }\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a / b;\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting with custom message when dividing by zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryMod}.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a % b;\n }\n }\n}\n" + }, + "contracts/libraries/Vault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nlibrary Vault {\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n // Fees are 6-decimal places. For example: 20 * 10**6 = 20%\n uint256 internal constant FEE_MULTIPLIER = 10**6;\n\n // Premium discount has 1-decimal place. For example: 80 * 10**1 = 80%. Which represents a 20% discount.\n uint256 internal constant PREMIUM_DISCOUNT_MULTIPLIER = 10;\n\n // Otokens have 8 decimal places.\n uint256 internal constant OTOKEN_DECIMALS = 8;\n\n // Percentage of funds allocated to options is 2 decimal places. 10 * 10**2 = 10%\n uint256 internal constant OPTION_ALLOCATION_MULTIPLIER = 10**2;\n\n // Placeholder uint value to prevent cold writes\n uint256 internal constant PLACEHOLDER_UINT = 1;\n\n struct VaultParams {\n // Option type the vault is selling\n bool isPut;\n // Token decimals for vault shares\n uint8 decimals;\n // Asset used in Theta / Delta Vault\n address asset;\n // Underlying asset of the options sold by vault\n address underlying;\n // Minimum supply of the vault shares issued, for ETH it's 10**10\n uint56 minimumSupply;\n // Vault cap\n uint104 cap;\n }\n\n struct OptionState {\n // Option that the vault is shorting / longing in the next cycle\n address nextOption;\n // Option that the vault is currently shorting / longing\n address currentOption;\n // The timestamp when the `nextOption` can be used by the vault\n uint32 nextOptionReadyAt;\n }\n\n struct VaultState {\n // 32 byte slot 1\n // Current round number. `round` represents the number of `period`s elapsed.\n uint16 round;\n // Amount that is currently locked for selling options\n uint104 lockedAmount;\n // Amount that was locked for selling options in the previous round\n // used for calculating performance fee deduction\n uint104 lastLockedAmount;\n // 32 byte slot 2\n // Stores the total tally of how much of `asset` there is\n // to be used to mint rTHETA tokens\n uint128 totalPending;\n // Total amount of queued withdrawal shares from previous rounds (doesn't include the current round)\n uint128 queuedWithdrawShares;\n }\n\n struct DepositReceipt {\n // Maximum of 65535 rounds. Assuming 1 round is 7 days, maximum is 1256 years.\n uint16 round;\n // Deposit amount, max 20,282,409,603,651 or 20 trillion ETH deposit\n uint104 amount;\n // Unredeemed shares balance\n uint128 unredeemedShares;\n }\n\n struct Withdrawal {\n // Maximum of 65535 rounds. Assuming 1 round is 7 days, maximum is 1256 years.\n uint16 round;\n // Number of shares withdrawn\n uint128 shares;\n }\n\n struct AuctionSellOrder {\n // Amount of `asset` token offered in auction\n uint96 sellAmount;\n // Amount of oToken requested in auction\n uint96 buyAmount;\n // User Id of delta vault in latest gnosis auction\n uint64 userId;\n }\n}\n" + }, + "contracts/libraries/VaultLifecycleTreasury.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {ShareMath} from \"./ShareMath.sol\";\nimport {IStrikeSelection} from \"../interfaces/IRibbon.sol\";\nimport {GnosisAuction} from \"./GnosisAuction.sol\";\nimport {DateTime} from \"./DateTime.sol\";\nimport {\n IOtokenFactory,\n IOtoken,\n IController,\n GammaTypes\n} from \"../interfaces/GammaInterface.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\nimport {IGnosisAuction} from \"../interfaces/IGnosisAuction.sol\";\nimport {SupportsNonCompliantERC20} from \"./SupportsNonCompliantERC20.sol\";\n\nlibrary VaultLifecycleTreasury {\n using SafeMath for uint256;\n using SupportsNonCompliantERC20 for IERC20;\n\n struct CloseParams {\n address OTOKEN_FACTORY;\n address USDC;\n address currentOption;\n uint256 delay;\n uint16 lastStrikeOverrideRound;\n uint256 overriddenStrikePrice;\n uint256 period;\n }\n\n /**\n * @notice Initialization parameters for the vault.\n * @param _owner is the owner of the vault with critical permissions\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n * @param _premiumDiscount is the vault's discount applied to the premium\n * @param _auctionDuration is the duration of the gnosis auction\n * @param _period is the period between each option sales\n */\n struct InitParams {\n address _owner;\n address _keeper;\n address _feeRecipient;\n uint256 _managementFee;\n uint256 _performanceFee;\n string _tokenName;\n string _tokenSymbol;\n address _optionsPremiumPricer;\n address _strikeSelection;\n uint32 _premiumDiscount;\n uint256 _auctionDuration;\n uint256 _period;\n uint256 _maxDepositors;\n uint256 _minDeposit;\n }\n\n /**\n * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction\n * @param strikeSelection is the address of the contract with strike selection logic\n * @param optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param premiumDiscount is the vault's discount applied to the premium\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @return otokenAddress is the address of the new option\n * @return premium is the premium of the new option\n * @return strikePrice is the strike price of the new option\n * @return delta is the delta of the new option\n */\n function commitAndClose(\n address strikeSelection,\n address optionsPremiumPricer,\n uint256 premiumDiscount,\n CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n Vault.VaultState storage vaultState\n )\n external\n returns (\n address otokenAddress,\n uint256 premium,\n uint256 strikePrice,\n uint256 delta\n )\n {\n uint256 expiry;\n\n // uninitialized state\n if (closeParams.currentOption == address(0)) {\n expiry = getNextExpiry(block.timestamp, closeParams.period);\n } else {\n expiry = getNextExpiry(\n IOtoken(closeParams.currentOption).expiryTimestamp(),\n closeParams.period\n );\n }\n\n IStrikeSelection selection = IStrikeSelection(strikeSelection);\n\n bool isPut = vaultParams.isPut;\n address underlying = vaultParams.underlying;\n address asset = vaultParams.asset;\n\n (strikePrice, delta) = closeParams.lastStrikeOverrideRound ==\n vaultState.round\n ? (closeParams.overriddenStrikePrice, 0)\n : selection.getStrikePrice(expiry, isPut);\n\n require(strikePrice != 0, \"!strikePrice\");\n\n // retrieve address if option already exists, or deploy it\n otokenAddress = getOrDeployOtoken(\n closeParams,\n vaultParams,\n underlying,\n asset,\n strikePrice,\n expiry,\n isPut\n );\n\n // get the black scholes premium of the option\n premium = GnosisAuction.getOTokenPremiumInStables(\n otokenAddress,\n optionsPremiumPricer,\n premiumDiscount\n );\n\n require(premium > 0, \"!premium\");\n\n return (otokenAddress, premium, strikePrice, delta);\n }\n\n /**\n * @notice Verify the otoken has the correct parameters to prevent vulnerability to opyn contract changes\n * @param otokenAddress is the address of the otoken\n * @param vaultParams is the struct with vault general data\n * @param collateralAsset is the address of the collateral asset\n * @param USDC is the address of usdc\n * @param delay is the delay between commitAndClose and rollToNextOption\n */\n function verifyOtoken(\n address otokenAddress,\n Vault.VaultParams storage vaultParams,\n address collateralAsset,\n address USDC,\n uint256 delay\n ) private view {\n require(otokenAddress != address(0), \"!otokenAddress\");\n\n IOtoken otoken = IOtoken(otokenAddress);\n require(otoken.isPut() == vaultParams.isPut, \"Type mismatch\");\n require(\n otoken.underlyingAsset() == vaultParams.underlying,\n \"Wrong underlyingAsset\"\n );\n require(\n otoken.collateralAsset() == collateralAsset,\n \"Wrong collateralAsset\"\n );\n\n // we just assume all options use USDC as the strike\n require(otoken.strikeAsset() == USDC, \"strikeAsset != USDC\");\n\n uint256 readyAt = block.timestamp.add(delay);\n require(otoken.expiryTimestamp() >= readyAt, \"Expiry before delay\");\n }\n\n /**\n * @param currentShareSupply is the supply of the shares invoked with totalSupply()\n * @param asset is the address of the vault's asset\n * @param decimals is the decimals of the asset\n * @param lastQueuedWithdrawAmount is the amount queued for withdrawals from last round\n * @param managementFee is the management fee percent to charge on the AUM\n */\n struct RolloverParams {\n uint256 decimals;\n uint256 totalBalance;\n uint256 currentShareSupply;\n uint256 lastQueuedWithdrawAmount;\n uint256 managementFee;\n }\n\n /**\n * @notice Calculate the shares to mint, new price per share, and\n amount of funds to re-allocate as collateral for the new round\n * @param vaultState is the storage variable vaultState passed from RibbonVault\n * @param params is the rollover parameters passed to compute the next state\n * @return newLockedAmount is the amount of funds to allocate for the new round\n * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal\n * @return newPricePerShare is the price per share of the new round\n * @return mintShares is the amount of shares to mint from deposits\n * @return managementFeeInAsset is the amount of management fee charged by vault\n */\n function rollover(\n Vault.VaultState storage vaultState,\n RolloverParams calldata params\n )\n external\n view\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares,\n uint256 managementFeeInAsset\n )\n {\n uint256 currentBalance = params.totalBalance;\n uint256 pendingAmount = vaultState.totalPending;\n uint256 queuedWithdrawShares = vaultState.queuedWithdrawShares;\n\n uint256 balanceForVaultFees;\n {\n uint256 pricePerShareBeforeFee =\n ShareMath.pricePerShare(\n params.currentShareSupply,\n currentBalance,\n pendingAmount,\n params.decimals\n );\n\n uint256 queuedWithdrawBeforeFee =\n params.currentShareSupply > 0\n ? ShareMath.sharesToAsset(\n queuedWithdrawShares,\n pricePerShareBeforeFee,\n params.decimals\n )\n : 0;\n\n // Deduct the difference between the newly scheduled withdrawals\n // and the older withdrawals\n // so we can charge them fees before they leave\n uint256 withdrawAmountDiff =\n queuedWithdrawBeforeFee > params.lastQueuedWithdrawAmount\n ? queuedWithdrawBeforeFee.sub(\n params.lastQueuedWithdrawAmount\n )\n : 0;\n\n balanceForVaultFees = currentBalance\n .sub(queuedWithdrawBeforeFee)\n .add(withdrawAmountDiff);\n }\n\n managementFeeInAsset = getManagementFee(\n balanceForVaultFees,\n vaultState.totalPending,\n params.managementFee\n );\n\n // Take into account the fee\n // so we can calculate the newPricePerShare\n currentBalance = currentBalance.sub(managementFeeInAsset);\n\n {\n newPricePerShare = ShareMath.pricePerShare(\n params.currentShareSupply,\n currentBalance,\n pendingAmount,\n params.decimals\n );\n\n // After closing the short, if the options expire in-the-money\n // vault pricePerShare would go down because vault's asset balance decreased.\n // This ensures that the newly-minted shares do not take on the loss.\n mintShares = ShareMath.assetToShares(\n pendingAmount,\n newPricePerShare,\n params.decimals\n );\n\n uint256 newSupply = params.currentShareSupply.add(mintShares);\n\n queuedWithdrawAmount = newSupply > 0\n ? ShareMath.sharesToAsset(\n queuedWithdrawShares,\n newPricePerShare,\n params.decimals\n )\n : 0;\n }\n\n return (\n currentBalance.sub(queuedWithdrawAmount), // new locked balance subtracts the queued withdrawals\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n managementFeeInAsset\n );\n }\n\n /**\n * @notice Creates the actual Opyn short position by depositing collateral and minting otokens\n * @param gammaController is the address of the opyn controller contract\n * @param marginPool is the address of the opyn margin contract which holds the collateral\n * @param oTokenAddress is the address of the otoken to mint\n * @param depositAmount is the amount of collateral to deposit\n * @return the otoken mint amount\n */\n function createShort(\n address gammaController,\n address marginPool,\n address oTokenAddress,\n uint256 depositAmount\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n uint256 newVaultID =\n (controller.getAccountVaultCounter(address(this))).add(1);\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IOtoken oToken = IOtoken(oTokenAddress);\n address collateralAsset = oToken.collateralAsset();\n\n uint256 collateralDecimals =\n uint256(IERC20Detailed(collateralAsset).decimals());\n uint256 mintAmount;\n\n if (oToken.isPut()) {\n // For minting puts, there will be instances where the full depositAmount will not be used for minting.\n // This is because of an issue with precision.\n //\n // For ETH put options, we are calculating the mintAmount (10**8 decimals) using\n // the depositAmount (10**18 decimals), which will result in truncation of decimals when scaling down.\n // As a result, there will be tiny amounts of dust left behind in the Opyn vault when minting put otokens.\n //\n // For simplicity's sake, we do not refund the dust back to the address(this) on minting otokens.\n // We retain the dust in the vault so the calling contract can withdraw the\n // actual locked amount + dust at settlement.\n //\n // To test this behavior, we can console.log\n // MarginCalculatorInterface(0x7A48d10f372b3D7c60f6c9770B91398e4ccfd3C7).getExcessCollateral(vault)\n // to see how much dust (or excess collateral) is left behind.\n mintAmount = depositAmount\n .mul(10**Vault.OTOKEN_DECIMALS)\n .mul(10**18) // we use 10**18 to give extra precision\n .div(oToken.strikePrice().mul(10**(10 + collateralDecimals)));\n } else {\n mintAmount = depositAmount;\n\n if (collateralDecimals > 8) {\n uint256 scaleBy = 10**(collateralDecimals.sub(8)); // oTokens have 8 decimals\n if (mintAmount > scaleBy) {\n mintAmount = depositAmount.div(scaleBy); // scale down from 10**18 to 10**8\n }\n }\n }\n\n // double approve to fix non-compliant ERC20s\n IERC20 collateralToken = IERC20(collateralAsset);\n collateralToken.safeApproveNonCompliant(marginPool, depositAmount);\n\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](3);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.OpenVault,\n address(this), // owner\n address(this), // receiver\n address(0), // asset, otoken\n newVaultID, // vaultId\n 0, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.DepositCollateral,\n address(this), // owner\n address(this), // address to transfer from\n collateralAsset, // deposited asset\n newVaultID, // vaultId\n depositAmount, // amount\n 0, //index\n \"\" //data\n );\n\n actions[2] = IController.ActionArgs(\n IController.ActionType.MintShortOption,\n address(this), // owner\n address(this), // address to transfer to\n oTokenAddress, // option address\n newVaultID, // vaultId\n mintAmount, // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n return mintAmount;\n }\n\n /**\n * @notice Close the existing short otoken position. Currently this implementation is simple.\n * It closes the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time. Since calling `_closeShort` deletes vaults by\n calling SettleVault action, this assumption should hold.\n * @param gammaController is the address of the opyn controller contract\n * @return amount of collateral redeemed from the vault\n */\n function settleShort(address gammaController) external returns (uint256) {\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n // The short position has been previously closed, or all the otokens have been burned.\n // So we return early.\n if (address(collateralToken) == address(0)) {\n return 0;\n }\n\n // This is equivalent to doing IERC20(vault.asset).balanceOf(address(this))\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // If it is after expiry, we need to settle the short position using the normal way\n // Delete the vault and withdraw all remaining collateral from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.SettleVault,\n address(this), // owner\n address(this), // address to transfer to\n address(0), // not used\n vaultID, // vaultId\n 0, // not used\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Exercises the ITM option using existing long otoken position. Currently this implementation is simple.\n * It calls the `Redeem` action to claim the payout.\n * @param gammaController is the address of the opyn controller contract\n * @param oldOption is the address of the old option\n * @param asset is the address of the vault's asset\n * @return amount of asset received by exercising the option\n */\n function settleLong(\n address gammaController,\n address oldOption,\n address asset\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n\n uint256 oldOptionBalance = IERC20(oldOption).balanceOf(address(this));\n\n if (controller.getPayout(oldOption, oldOptionBalance) == 0) {\n return 0;\n }\n\n uint256 startAssetBalance = IERC20(asset).balanceOf(address(this));\n\n // If it is after expiry, we need to redeem the profits\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.Redeem,\n address(0), // not used\n address(this), // address to send profits to\n oldOption, // address of otoken\n 0, // not used\n oldOptionBalance, // otoken balance\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endAssetBalance = IERC20(asset).balanceOf(address(this));\n\n return endAssetBalance.sub(startAssetBalance);\n }\n\n /**\n * @notice Burn the remaining oTokens left over from auction. Currently this implementation is simple.\n * It burns oTokens from the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time.\n * @param gammaController is the address of the opyn controller contract\n * @param currentOption is the address of the current option\n * @return amount of collateral redeemed by burning otokens\n */\n function burnOtokens(address gammaController, address currentOption)\n external\n returns (uint256)\n {\n uint256 numOTokensToBurn =\n IERC20(currentOption).balanceOf(address(this));\n\n require(numOTokensToBurn > 0, \"No oTokens to burn\");\n\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // Burning `amount` of oTokens from the ribbon vault,\n // then withdrawing the corresponding collateral amount from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](2);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.BurnShortOption,\n address(this), // owner\n address(this), // address to transfer from\n address(vault.shortOtokens[0]), // otoken address\n vaultID, // vaultId\n numOTokensToBurn, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.WithdrawCollateral,\n address(this), // owner\n address(this), // address to transfer to\n address(collateralToken), // withdrawn asset\n vaultID, // vaultId\n vault.collateralAmounts[0].mul(numOTokensToBurn).div(\n vault.shortAmounts[0]\n ), // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Calculates the management fee for this week's round\n * @param currentBalance is the balance of funds held on the vault after closing short\n * @param pendingAmount is the pending deposit amount\n * @param managementFeePercent is the management fee pct.\n * @return managementFeeInAsset is the management fee\n */\n function getManagementFee(\n uint256 currentBalance,\n uint256 pendingAmount,\n uint256 managementFeePercent\n ) internal pure returns (uint256 managementFeeInAsset) {\n // At the first round, currentBalance=0, pendingAmount>0\n // so we just do not charge anything on the first round\n uint256 lockedBalanceSansPending =\n currentBalance > pendingAmount\n ? currentBalance.sub(pendingAmount)\n : 0;\n\n uint256 _managementFeeInAsset;\n\n // Always charge management fee regardless of whether the vault is\n // making a profit from the previous options sale\n _managementFeeInAsset = managementFeePercent > 0\n ? lockedBalanceSansPending.mul(managementFeePercent).div(\n 100 * Vault.FEE_MULTIPLIER\n )\n : 0;\n\n return _managementFeeInAsset;\n }\n\n /**\n * @notice Either retrieves the option token if it already exists, or deploy it\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param underlying is the address of the underlying asset of the option\n * @param collateralAsset is the address of the collateral asset of the option\n * @param strikePrice is the strike price of the option\n * @param expiry is the expiry timestamp of the option\n * @param isPut is whether the option is a put\n * @return the address of the option\n */\n function getOrDeployOtoken(\n CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n address underlying,\n address collateralAsset,\n uint256 strikePrice,\n uint256 expiry,\n bool isPut\n ) internal returns (address) {\n IOtokenFactory factory = IOtokenFactory(closeParams.OTOKEN_FACTORY);\n\n address otokenFromFactory =\n factory.getOtoken(\n underlying,\n closeParams.USDC,\n collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n if (otokenFromFactory != address(0)) {\n return otokenFromFactory;\n }\n\n address otoken =\n factory.createOtoken(\n underlying,\n closeParams.USDC,\n collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n verifyOtoken(\n otoken,\n vaultParams,\n collateralAsset,\n closeParams.USDC,\n closeParams.delay\n );\n\n return otoken;\n }\n\n /**\n * @notice Starts the gnosis auction\n * @param auctionDetails is the struct with all the custom parameters of the auction\n * @return the auction id of the newly created auction\n */\n function startAuction(GnosisAuction.AuctionDetails calldata auctionDetails)\n external\n returns (uint256)\n {\n return GnosisAuction.startAuction(auctionDetails);\n }\n\n /**\n * @notice Settles the gnosis auction\n * @param gnosisEasyAuction is the contract address of Gnosis easy auction protocol\n * @param auctionID is the auction ID of the gnosis easy auction\n */\n function settleAuction(address gnosisEasyAuction, uint256 auctionID)\n internal\n {\n IGnosisAuction(gnosisEasyAuction).settleAuction(auctionID);\n }\n\n /**\n * @notice Places a bid in an auction\n * @param bidDetails is the struct with all the details of the\n bid including the auction's id and how much to bid\n */\n function placeBid(GnosisAuction.BidDetails calldata bidDetails)\n external\n returns (\n uint256 sellAmount,\n uint256 buyAmount,\n uint64 userId\n )\n {\n return GnosisAuction.placeBid(bidDetails);\n }\n\n /**\n * @notice Claims the oTokens belonging to the vault\n * @param auctionSellOrder is the sell order of the bid\n * @param gnosisEasyAuction is the address of the gnosis auction contract\n holding custody to the funds\n * @param counterpartyThetaVault is the address of the counterparty theta\n vault of this delta vault\n */\n function claimAuctionOtokens(\n Vault.AuctionSellOrder calldata auctionSellOrder,\n address gnosisEasyAuction,\n address counterpartyThetaVault\n ) external {\n GnosisAuction.claimAuctionOtokens(\n auctionSellOrder,\n gnosisEasyAuction,\n counterpartyThetaVault\n );\n }\n\n /**\n * @notice Verify the constructor params satisfy requirements\n * @param _initParams is the initialization parameter including owner, keeper, etc.\n * @param _vaultParams is the struct with vault general data\n */\n function verifyInitializerParams(\n InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams,\n uint256 _min_auction_duration\n ) external pure {\n require(_initParams._owner != address(0), \"!_owner\");\n require(_initParams._keeper != address(0), \"!_keeper\");\n require(_initParams._feeRecipient != address(0), \"!_feeRecipient\");\n require(\n _initParams._performanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"performanceFee >= 100%\"\n );\n require(\n _initParams._managementFee < 100 * Vault.FEE_MULTIPLIER,\n \"managementFee >= 100%\"\n );\n require(bytes(_initParams._tokenName).length > 0, \"!_tokenName\");\n require(bytes(_initParams._tokenSymbol).length > 0, \"!_tokenSymbol\");\n require(\n (_initParams._period == 7) ||\n (_initParams._period == 14) ||\n (_initParams._period == 30) ||\n (_initParams._period == 90) ||\n (_initParams._period == 180),\n \"!_period\"\n );\n require(\n _initParams._optionsPremiumPricer != address(0),\n \"!_optionsPremiumPricer\"\n );\n require(\n _initParams._strikeSelection != address(0),\n \"!_strikeSelection\"\n );\n require(\n _initParams._premiumDiscount > 0 &&\n _initParams._premiumDiscount <\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"!_premiumDiscount\"\n );\n require(\n _initParams._auctionDuration >= _min_auction_duration,\n \"!_auctionDuration\"\n );\n require(_initParams._maxDepositors > 0, \"!_maxDepositors\");\n require(_initParams._minDeposit > 0, \"!_minDeposit\");\n\n require(_vaultParams.asset != address(0), \"!asset\");\n require(_vaultParams.underlying != address(0), \"!underlying\");\n require(_vaultParams.minimumSupply > 0, \"!minimumSupply\");\n require(_vaultParams.cap > 0, \"!cap\");\n require(\n _vaultParams.cap > _vaultParams.minimumSupply,\n \"cap has to be higher than minimumSupply\"\n );\n }\n\n /**\n * @notice Gets the next options expiry timestamp, this function should be called\n when there is sufficient guard to ensure valid period\n * @param timestamp is the expiry timestamp of the current option\n * @param period is no. of days in between option sales. Available periods are: \n * 7(1w), 14(2w), 30(1m), 90(3m), 180(6m)\n */\n function getNextExpiry(uint256 timestamp, uint256 period)\n internal\n pure\n returns (uint256 nextExpiry)\n {\n if (period == 7) {\n nextExpiry = DateTime.getNextFriday(timestamp);\n nextExpiry = nextExpiry <= timestamp\n ? nextExpiry + 1 weeks\n : nextExpiry;\n } else if (period == 14) {\n nextExpiry = DateTime.getNextFriday(timestamp);\n nextExpiry = nextExpiry <= timestamp\n ? nextExpiry + 2 weeks\n : nextExpiry;\n } else if (period == 30) {\n nextExpiry = DateTime.getMonthLastFriday(timestamp);\n nextExpiry = nextExpiry <= timestamp\n ? DateTime.getMonthLastFriday(nextExpiry + 1 weeks)\n : nextExpiry;\n } else if (period == 90) {\n nextExpiry = DateTime.getQuarterLastFriday(timestamp);\n nextExpiry = nextExpiry <= timestamp\n ? DateTime.getQuarterLastFriday(nextExpiry + 1 weeks)\n : nextExpiry;\n } else if (period == 180) {\n nextExpiry = DateTime.getBiannualLastFriday(timestamp);\n nextExpiry = nextExpiry <= timestamp\n ? DateTime.getBiannualLastFriday(nextExpiry + 1 weeks)\n : nextExpiry;\n }\n\n nextExpiry = nextExpiry - (nextExpiry % (24 hours)) + (8 hours);\n }\n}\n" + }, + "contracts/storage/RibbonTreasuryVaultStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {Vault} from \"../libraries/Vault.sol\";\n\nabstract contract RibbonTreasuryVaultStorageV1 {\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Mapping of depositors in the vault\n mapping(address => bool) public depositorsMap;\n\n /// @notice Array of depositors in the vault\n address[] public depositorsArray;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Logic contract used to price options\n address public optionsPremiumPricer;\n\n /// @notice Logic contract used to select strike prices\n address public strikeSelection;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n /// @notice Premium discount on options we are selling (thousandths place: 000 - 999)\n uint256 public premiumDiscount;\n\n /// @notice Current oToken premium\n uint256 public currentOtokenPremium;\n\n /// @notice Price last overridden strike set to\n uint256 public overriddenStrikePrice;\n\n /// @notice Auction duration\n uint256 public auctionDuration;\n\n /// @notice Auction id of current option\n uint256 public optionAuctionID;\n\n /// @notice Amount locked for scheduled withdrawals last week;\n uint256 public lastQueuedWithdrawAmount;\n\n /// @notice Period between each options sale.\n /// Available options 7 (weekly), 14 (biweekly), 30 (monthly), 90 (quarterly), 180 (biannually)\n uint256 public period;\n\n /// @notice Maximum number of depositors\n uint256 public maxDepositors;\n\n /// @notice Minimum amount to deposit\n uint256 public minDeposit;\n\n /// @notice Last round id at which the strike was manually overridden\n uint16 public lastStrikeOverrideRound;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of RibbonTreasuryVaultStorage\n// e.g. RibbonTreasuryVaultStorage, so finally it would look like\n// contract RibbonTreasuryVaultStorage is RibbonTreasuryVaultStorageV1, RibbonTreasuryVaultStorageV2\nabstract contract RibbonTreasuryVaultStorage is RibbonTreasuryVaultStorageV1 {\n\n}\n" + }, + "contracts/libraries/ShareMath.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {Vault} from \"./Vault.sol\";\n\nlibrary ShareMath {\n using SafeMath for uint256;\n\n uint256 internal constant PLACEHOLDER_UINT = 1;\n\n function assetToShares(\n uint256 assetAmount,\n uint256 assetPerShare,\n uint256 decimals\n ) internal pure returns (uint256) {\n // If this throws, it means that vault's roundPricePerShare[currentRound] has not been set yet\n // which should never happen.\n // Has to be larger than 1 because `1` is used in `initRoundPricePerShares` to prevent cold writes.\n require(assetPerShare > PLACEHOLDER_UINT, \"Invalid assetPerShare\");\n\n return assetAmount.mul(10**decimals).div(assetPerShare);\n }\n\n function sharesToAsset(\n uint256 shares,\n uint256 assetPerShare,\n uint256 decimals\n ) internal pure returns (uint256) {\n // If this throws, it means that vault's roundPricePerShare[currentRound] has not been set yet\n // which should never happen.\n // Has to be larger than 1 because `1` is used in `initRoundPricePerShares` to prevent cold writes.\n require(assetPerShare > PLACEHOLDER_UINT, \"Invalid assetPerShare\");\n\n return shares.mul(assetPerShare).div(10**decimals);\n }\n\n /**\n * @notice Returns the shares unredeemed by the user given their DepositReceipt\n * @param depositReceipt is the user's deposit receipt\n * @param currentRound is the `round` stored on the vault\n * @param assetPerShare is the price in asset per share\n * @param decimals is the number of decimals the asset/shares use\n * @return unredeemedShares is the user's virtual balance of shares that are owed\n */\n function getSharesFromReceipt(\n Vault.DepositReceipt memory depositReceipt,\n uint256 currentRound,\n uint256 assetPerShare,\n uint256 decimals\n ) internal pure returns (uint256 unredeemedShares) {\n if (depositReceipt.round > 0 && depositReceipt.round < currentRound) {\n uint256 sharesFromRound =\n assetToShares(depositReceipt.amount, assetPerShare, decimals);\n\n return\n uint256(depositReceipt.unredeemedShares).add(sharesFromRound);\n }\n return depositReceipt.unredeemedShares;\n }\n\n function pricePerShare(\n uint256 totalSupply,\n uint256 totalBalance,\n uint256 pendingAmount,\n uint256 decimals\n ) internal pure returns (uint256) {\n uint256 singleShare = 10**decimals;\n return\n totalSupply > 0\n ? singleShare.mul(totalBalance.sub(pendingAmount)).div(\n totalSupply\n )\n : singleShare;\n }\n\n /************************************************\n * HELPERS\n ***********************************************/\n\n function assertUint104(uint256 num) internal pure {\n require(num <= type(uint104).max, \"Overflow uint104\");\n }\n\n function assertUint128(uint256 num) internal pure {\n require(num <= type(uint128).max, \"Overflow uint128\");\n }\n}\n" + }, + "contracts/interfaces/IWETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface IWETH {\n function deposit() external payable;\n\n function withdraw(uint256) external;\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function decimals() external view returns (uint256);\n}\n" + }, + "contracts/libraries/GnosisAuction.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {DSMath} from \"../vendor/DSMath.sol\";\nimport {IGnosisAuction} from \"../interfaces/IGnosisAuction.sol\";\nimport {IOtoken} from \"../interfaces/GammaInterface.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {IRibbonThetaVault} from \"../interfaces/IRibbonThetaVault.sol\";\n\nlibrary GnosisAuction {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n event InitiateGnosisAuction(\n address indexed auctioningToken,\n address indexed biddingToken,\n uint256 auctionCounter,\n address indexed manager\n );\n\n event PlaceAuctionBid(\n uint256 auctionId,\n address indexed auctioningToken,\n uint256 sellAmount,\n uint256 buyAmount,\n address indexed bidder\n );\n\n struct AuctionDetails {\n address oTokenAddress;\n address gnosisEasyAuction;\n address asset;\n uint256 assetDecimals;\n uint256 oTokenPremium;\n uint256 duration;\n }\n\n struct BidDetails {\n address oTokenAddress;\n address gnosisEasyAuction;\n address asset;\n uint256 assetDecimals;\n uint256 auctionId;\n uint256 lockedBalance;\n uint256 optionAllocation;\n uint256 optionPremium;\n address bidder;\n }\n\n function startAuction(AuctionDetails calldata auctionDetails)\n internal\n returns (uint256 auctionID)\n {\n uint256 oTokenSellAmount =\n getOTokenSellAmount(auctionDetails.oTokenAddress);\n require(oTokenSellAmount > 0, \"No otokens to sell\");\n\n IERC20(auctionDetails.oTokenAddress).safeApprove(\n auctionDetails.gnosisEasyAuction,\n IERC20(auctionDetails.oTokenAddress).balanceOf(address(this))\n );\n\n // minBidAmount is total oTokens to sell * premium per oToken\n // shift decimals to correspond to decimals of USDC for puts\n // and underlying for calls\n uint256 minBidAmount =\n DSMath.wmul(\n oTokenSellAmount.mul(10**10),\n auctionDetails.oTokenPremium\n );\n\n minBidAmount = auctionDetails.assetDecimals > 18\n ? minBidAmount.mul(10**(auctionDetails.assetDecimals.sub(18)))\n : minBidAmount.div(\n 10**(uint256(18).sub(auctionDetails.assetDecimals))\n );\n\n require(\n minBidAmount <= type(uint96).max,\n \"optionPremium * oTokenSellAmount > type(uint96) max value!\"\n );\n\n uint256 auctionEnd = block.timestamp.add(auctionDetails.duration);\n\n auctionID = IGnosisAuction(auctionDetails.gnosisEasyAuction)\n .initiateAuction(\n // address of oToken we minted and are selling\n auctionDetails.oTokenAddress,\n // address of asset we want in exchange for oTokens. Should match vault `asset`\n auctionDetails.asset,\n // orders can be cancelled at any time during the auction\n auctionEnd,\n // order will last for `duration`\n auctionEnd,\n // we are selling all of the otokens minus a fee taken by gnosis\n uint96(oTokenSellAmount),\n // the minimum we are willing to sell all the oTokens for. A discount is applied on black-scholes price\n uint96(minBidAmount),\n // the minimum bidding amount must be 1 * 10 ** -assetDecimals\n 1,\n // the min funding threshold\n 0,\n // no atomic closure\n false,\n // access manager contract\n address(0),\n // bytes for storing info like a whitelist for who can bid\n bytes(\"\")\n );\n\n emit InitiateGnosisAuction(\n auctionDetails.oTokenAddress,\n auctionDetails.asset,\n auctionID,\n msg.sender\n );\n }\n\n function placeBid(BidDetails calldata bidDetails)\n internal\n returns (\n uint256 sellAmount,\n uint256 buyAmount,\n uint64 userId\n )\n {\n // calculate how much to allocate\n sellAmount = bidDetails\n .lockedBalance\n .mul(bidDetails.optionAllocation)\n .div(100 * Vault.OPTION_ALLOCATION_MULTIPLIER);\n\n // divide the `asset` sellAmount by the target premium per oToken to\n // get the number of oTokens to buy (8 decimals)\n buyAmount = sellAmount\n .mul(10**(bidDetails.assetDecimals.add(Vault.OTOKEN_DECIMALS)))\n .div(bidDetails.optionPremium)\n .div(10**bidDetails.assetDecimals);\n\n require(\n sellAmount <= type(uint96).max,\n \"sellAmount > type(uint96) max value!\"\n );\n require(\n buyAmount <= type(uint96).max,\n \"buyAmount > type(uint96) max value!\"\n );\n\n // approve that amount\n IERC20(bidDetails.asset).safeApprove(\n bidDetails.gnosisEasyAuction,\n sellAmount\n );\n\n uint96[] memory _minBuyAmounts = new uint96[](1);\n uint96[] memory _sellAmounts = new uint96[](1);\n bytes32[] memory _prevSellOrders = new bytes32[](1);\n _minBuyAmounts[0] = uint96(buyAmount);\n _sellAmounts[0] = uint96(sellAmount);\n _prevSellOrders[\n 0\n ] = 0x0000000000000000000000000000000000000000000000000000000000000001;\n\n // place sell order with that amount\n userId = IGnosisAuction(bidDetails.gnosisEasyAuction).placeSellOrders(\n bidDetails.auctionId,\n _minBuyAmounts,\n _sellAmounts,\n _prevSellOrders,\n \"0x\"\n );\n\n emit PlaceAuctionBid(\n bidDetails.auctionId,\n bidDetails.oTokenAddress,\n sellAmount,\n buyAmount,\n bidDetails.bidder\n );\n\n return (sellAmount, buyAmount, userId);\n }\n\n function claimAuctionOtokens(\n Vault.AuctionSellOrder calldata auctionSellOrder,\n address gnosisEasyAuction,\n address counterpartyThetaVault\n ) internal {\n bytes32 order =\n encodeOrder(\n auctionSellOrder.userId,\n auctionSellOrder.buyAmount,\n auctionSellOrder.sellAmount\n );\n bytes32[] memory orders = new bytes32[](1);\n orders[0] = order;\n IGnosisAuction(gnosisEasyAuction).claimFromParticipantOrder(\n IRibbonThetaVault(counterpartyThetaVault).optionAuctionID(),\n orders\n );\n }\n\n function getOTokenSellAmount(address oTokenAddress)\n internal\n view\n returns (uint256)\n {\n // We take our current oToken balance. That will be our sell amount\n // but otokens will be transferred to gnosis.\n uint256 oTokenSellAmount =\n IERC20(oTokenAddress).balanceOf(address(this));\n\n require(\n oTokenSellAmount <= type(uint96).max,\n \"oTokenSellAmount > type(uint96) max value!\"\n );\n\n return oTokenSellAmount;\n }\n\n function getOTokenPremiumInStables(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount\n ) internal view returns (uint256) {\n IOtoken newOToken = IOtoken(oTokenAddress);\n IOptionsPremiumPricer premiumPricer =\n IOptionsPremiumPricer(optionsPremiumPricer);\n\n // Apply black-scholes formula (from rvol library) to option given its features\n // and get price for 100 contracts denominated USDC for both call and put options\n uint256 optionPremium =\n premiumPricer.getPremiumInStables(\n newOToken.strikePrice(),\n newOToken.expiryTimestamp(),\n newOToken.isPut()\n );\n\n // Apply a discount to incentivize arbitraguers\n optionPremium = optionPremium.mul(premiumDiscount).div(\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER\n );\n\n require(\n optionPremium <= type(uint96).max,\n \"optionPremium > type(uint96) max value!\"\n );\n\n return optionPremium;\n }\n\n function encodeOrder(\n uint64 userId,\n uint96 buyAmount,\n uint96 sellAmount\n ) internal pure returns (bytes32) {\n return\n bytes32(\n (uint256(userId) << 192) +\n (uint256(buyAmount) << 96) +\n uint256(sellAmount)\n );\n }\n}\n" + }, + "contracts/interfaces/IRibbon.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport {Vault} from \"../libraries/Vault.sol\";\n\ninterface IRibbonVault {\n function deposit(uint256 amount) external;\n\n function depositETH() external payable;\n\n function cap() external view returns (uint256);\n\n function depositFor(uint256 amount, address creditor) external;\n\n function vaultParams() external view returns (Vault.VaultParams memory);\n}\n\ninterface IStrikeSelection {\n function getStrikePrice(uint256 expiryTimestamp, bool isPut)\n external\n view\n returns (uint256, uint256);\n\n function delta() external view returns (uint256);\n}\n\ninterface IOptionsPremiumPricer {\n function getPremium(\n uint256 strikePrice,\n uint256 timeToExpiry,\n bool isPut\n ) external view returns (uint256);\n\n function getPremiumInStables(\n uint256 strikePrice,\n uint256 timeToExpiry,\n bool isPut\n ) external view returns (uint256);\n\n function getOptionDelta(\n uint256 spotPrice,\n uint256 strikePrice,\n uint256 volatility,\n uint256 expiryTimestamp\n ) external view returns (uint256 delta);\n\n function getUnderlyingPrice() external view returns (uint256);\n\n function priceOracle() external view returns (address);\n\n function volatilityOracle() external view returns (address);\n\n function optionId() external view returns (bytes32);\n}\n" + }, + "contracts/libraries/DateTime.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Source: https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary\n// ----------------------------------------------------------------------------\n// BokkyPooBah's DateTime Library v1.01\n// ----------------------------------------------------------------------------\n\npragma solidity =0.8.4;\n\nlibrary DateTime {\n uint256 constant SECONDS_PER_DAY = 24 * 60 * 60;\n uint256 constant SECONDS_PER_HOUR = 60 * 60;\n uint256 constant SECONDS_PER_MINUTE = 60;\n int256 constant OFFSET19700101 = 2440588;\n\n uint256 constant DOW_MON = 1;\n uint256 constant DOW_TUE = 2;\n uint256 constant DOW_WED = 3;\n uint256 constant DOW_THU = 4;\n uint256 constant DOW_FRI = 5;\n uint256 constant DOW_SAT = 6;\n uint256 constant DOW_SUN = 7;\n\n // ------------------------------------------------------------------------\n // Calculate the number of days from 1970/01/01 to year/month/day using\n // the date conversion algorithm from\n // http://aa.usno.navy.mil/faq/docs/JD_Formula.php\n // and subtracting the offset 2440588 so that 1970/01/01 is day 0\n //\n // days = day\n // - 32075\n // + 1461 * (year + 4800 + (month - 14) / 12) / 4\n // + 367 * (month - 2 - (month - 14) / 12 * 12) / 12\n // - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4\n // - offset\n // ------------------------------------------------------------------------\n function _daysFromDate(\n uint256 year,\n uint256 month,\n uint256 day\n ) internal pure returns (uint256 _days) {\n require(year >= 1970);\n int256 _year = int256(year);\n int256 _month = int256(month);\n int256 _day = int256(day);\n\n int256 __days =\n _day -\n 32075 +\n (1461 * (_year + 4800 + (_month - 14) / 12)) /\n 4 +\n (367 * (_month - 2 - ((_month - 14) / 12) * 12)) /\n 12 -\n (3 * ((_year + 4900 + (_month - 14) / 12) / 100)) /\n 4 -\n OFFSET19700101;\n\n _days = uint256(__days);\n }\n\n // ------------------------------------------------------------------------\n // Calculate year/month/day from the number of days since 1970/01/01 using\n // the date conversion algorithm from\n // http://aa.usno.navy.mil/faq/docs/JD_Formula.php\n // and adding the offset 2440588 so that 1970/01/01 is day 0\n //\n // int L = days + 68569 + offset\n // int N = 4 * L / 146097\n // L = L - (146097 * N + 3) / 4\n // year = 4000 * (L + 1) / 1461001\n // L = L - 1461 * year / 4 + 31\n // month = 80 * L / 2447\n // dd = L - 2447 * month / 80\n // L = month / 11\n // month = month + 2 - 12 * L\n // year = 100 * (N - 49) + year + L\n // ------------------------------------------------------------------------\n function _daysToDate(uint256 _days)\n internal\n pure\n returns (\n uint256 year,\n uint256 month,\n uint256 day\n )\n {\n int256 __days = int256(_days);\n\n int256 L = __days + 68569 + OFFSET19700101;\n int256 N = (4 * L) / 146097;\n L = L - (146097 * N + 3) / 4;\n int256 _year = (4000 * (L + 1)) / 1461001;\n L = L - (1461 * _year) / 4 + 31;\n int256 _month = (80 * L) / 2447;\n int256 _day = L - (2447 * _month) / 80;\n L = _month / 11;\n _month = _month + 2 - 12 * L;\n _year = 100 * (N - 49) + _year + L;\n\n year = uint256(_year);\n month = uint256(_month);\n day = uint256(_day);\n }\n\n function isLeapYear(uint256 timestamp)\n internal\n pure\n returns (bool leapYear)\n {\n (uint256 year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);\n leapYear = _isLeapYear(year);\n }\n\n function _isLeapYear(uint256 year) internal pure returns (bool leapYear) {\n leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);\n }\n\n function getDaysInMonth(uint256 timestamp)\n internal\n pure\n returns (uint256 daysInMonth)\n {\n (uint256 year, uint256 month, ) =\n _daysToDate(timestamp / SECONDS_PER_DAY);\n daysInMonth = _getDaysInMonth(year, month);\n }\n\n function _getDaysInMonth(uint256 year, uint256 month)\n internal\n pure\n returns (uint256 daysInMonth)\n {\n if (\n month == 1 ||\n month == 3 ||\n month == 5 ||\n month == 7 ||\n month == 8 ||\n month == 10 ||\n month == 12\n ) {\n daysInMonth = 31;\n } else if (month != 2) {\n daysInMonth = 30;\n } else {\n daysInMonth = _isLeapYear(year) ? 29 : 28;\n }\n }\n\n // 1 = Monday, 7 = Sunday\n function getDayOfWeek(uint256 timestamp)\n internal\n pure\n returns (uint256 dayOfWeek)\n {\n uint256 _days = timestamp / SECONDS_PER_DAY;\n dayOfWeek = ((_days + 3) % 7) + 1;\n }\n\n function getYear(uint256 timestamp) internal pure returns (uint256 year) {\n (year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);\n }\n\n function getMonth(uint256 timestamp) internal pure returns (uint256 month) {\n (, month, ) = _daysToDate(timestamp / SECONDS_PER_DAY);\n }\n\n function getDay(uint256 timestamp) internal pure returns (uint256 day) {\n (, , day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n }\n\n function timestampFromDate(\n uint256 year,\n uint256 month,\n uint256 day\n ) internal pure returns (uint256 timestamp) {\n timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY;\n }\n\n /**\n * @notice Gets the Friday of the same week\n * @param timestamp is the given date and time\n * @return the Friday of the same week in unix time\n */\n function getThisWeekFriday(uint256 timestamp)\n internal\n pure\n returns (uint256)\n {\n return timestamp + 5 days - getDayOfWeek(timestamp) * 1 days;\n }\n\n /**\n * @notice Gets the next friday after the given date and time\n * @param timestamp is the given date and time\n * @return the next friday after the given date and time\n */\n function getNextFriday(uint256 timestamp) internal pure returns (uint256) {\n uint256 friday = getThisWeekFriday(timestamp);\n return friday >= timestamp ? friday : friday + 1 weeks;\n }\n\n /**\n * @notice Gets the last day of the month\n * @param timestamp is the given date and time\n * @return the last day of the same month in unix time\n */\n function getLastDayOfMonth(uint256 timestamp)\n internal\n pure\n returns (uint256)\n {\n return\n timestampFromDate(getYear(timestamp), getMonth(timestamp) + 1, 1) -\n 1 days;\n }\n\n /**\n * @notice Gets the last Friday of the month\n * @param timestamp is the given date and time\n * @return the last Friday of the same month in unix time\n */\n function getMonthLastFriday(uint256 timestamp)\n internal\n pure\n returns (uint256)\n {\n uint256 lastDay = getLastDayOfMonth(timestamp);\n uint256 friday = getThisWeekFriday(lastDay);\n\n return friday > lastDay ? friday - 1 weeks : friday;\n }\n\n /**\n * @notice Gets the last Friday of the quarter\n * @param timestamp is the given date and time\n * @return the last Friday of the quarter in unix time\n */\n function getQuarterLastFriday(uint256 timestamp)\n internal\n pure\n returns (uint256)\n {\n uint256 month = getMonth(timestamp);\n uint256 quarterMonth =\n (month <= 3) ? 3 : (month <= 6) ? 6 : (month <= 9) ? 9 : 12;\n\n uint256 quarterDate =\n timestampFromDate(getYear(timestamp), quarterMonth, 1);\n\n return getMonthLastFriday(quarterDate);\n }\n\n /**\n * @notice Gets the last Friday of the half-year\n * @param timestamp is the given date and time\n * @return the last friday of the half-year\n */\n function getBiannualLastFriday(uint256 timestamp)\n internal\n pure\n returns (uint256)\n {\n uint256 month = getMonth(timestamp);\n uint256 biannualMonth = (month <= 6) ? 6 : 12;\n\n uint256 biannualDate =\n timestampFromDate(getYear(timestamp), biannualMonth, 1);\n\n return getMonthLastFriday(biannualDate);\n }\n}\n" + }, + "contracts/interfaces/IGnosisAuction.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nlibrary AuctionType {\n struct AuctionData {\n IERC20 auctioningToken;\n IERC20 biddingToken;\n uint256 orderCancellationEndDate;\n uint256 auctionEndDate;\n bytes32 initialAuctionOrder;\n uint256 minimumBiddingAmountPerOrder;\n uint256 interimSumBidAmount;\n bytes32 interimOrder;\n bytes32 clearingPriceOrder;\n uint96 volumeClearingPriceOrder;\n bool minFundingThresholdNotReached;\n bool isAtomicClosureAllowed;\n uint256 feeNumerator;\n uint256 minFundingThreshold;\n }\n}\n\ninterface IGnosisAuction {\n function initiateAuction(\n address _auctioningToken,\n address _biddingToken,\n uint256 orderCancellationEndDate,\n uint256 auctionEndDate,\n uint96 _auctionedSellAmount,\n uint96 _minBuyAmount,\n uint256 minimumBiddingAmountPerOrder,\n uint256 minFundingThreshold,\n bool isAtomicClosureAllowed,\n address accessManagerContract,\n bytes memory accessManagerContractData\n ) external returns (uint256);\n\n function auctionCounter() external view returns (uint256);\n\n function auctionData(uint256 auctionId)\n external\n view\n returns (AuctionType.AuctionData memory);\n\n function auctionAccessManager(uint256 auctionId)\n external\n view\n returns (address);\n\n function auctionAccessData(uint256 auctionId)\n external\n view\n returns (bytes memory);\n\n function FEE_DENOMINATOR() external view returns (uint256);\n\n function feeNumerator() external view returns (uint256);\n\n function settleAuction(uint256 auctionId) external returns (bytes32);\n\n function placeSellOrders(\n uint256 auctionId,\n uint96[] memory _minBuyAmounts,\n uint96[] memory _sellAmounts,\n bytes32[] memory _prevSellOrders,\n bytes calldata allowListCallData\n ) external returns (uint64);\n\n function claimFromParticipantOrder(\n uint256 auctionId,\n bytes32[] memory orders\n ) external returns (uint256, uint256);\n}\n" + }, + "contracts/libraries/SupportsNonCompliantERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\n/**\n * This library supports ERC20s that have quirks in their behavior.\n * One such ERC20 is USDT, which requires allowance to be 0 before calling approve.\n * We plan to update this library with ERC20s that display such idiosyncratic behavior.\n */\nlibrary SupportsNonCompliantERC20 {\n address private constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;\n\n function safeApproveNonCompliant(\n IERC20 token,\n address spender,\n uint256 amount\n ) internal {\n if (address(token) == USDT) {\n SafeERC20.safeApprove(token, spender, 0);\n }\n SafeERC20.safeApprove(token, spender, amount);\n }\n}\n" + }, + "contracts/vendor/DSMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n\n/// math.sol -- mixin for inline numerical wizardry\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >0.4.13;\n\nlibrary DSMath {\n function add(uint256 x, uint256 y) internal pure returns (uint256 z) {\n require((z = x + y) >= x, \"ds-math-add-overflow\");\n }\n\n function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {\n require((z = x - y) <= x, \"ds-math-sub-underflow\");\n }\n\n function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {\n require(y == 0 || (z = x * y) / y == x, \"ds-math-mul-overflow\");\n }\n\n function min(uint256 x, uint256 y) internal pure returns (uint256 z) {\n return x <= y ? x : y;\n }\n\n function max(uint256 x, uint256 y) internal pure returns (uint256 z) {\n return x >= y ? x : y;\n }\n\n function imin(int256 x, int256 y) internal pure returns (int256 z) {\n return x <= y ? x : y;\n }\n\n function imax(int256 x, int256 y) internal pure returns (int256 z) {\n return x >= y ? x : y;\n }\n\n uint256 constant WAD = 10**18;\n uint256 constant RAY = 10**27;\n\n //rounds to zero if x*y < WAD / 2\n function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {\n z = add(mul(x, y), WAD / 2) / WAD;\n }\n\n //rounds to zero if x*y < WAD / 2\n function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {\n z = add(mul(x, y), RAY / 2) / RAY;\n }\n\n //rounds to zero if x*y < WAD / 2\n function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {\n z = add(mul(x, WAD), y / 2) / y;\n }\n\n //rounds to zero if x*y < RAY / 2\n function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {\n z = add(mul(x, RAY), y / 2) / y;\n }\n\n // This famous algorithm is called \"exponentiation by squaring\"\n // and calculates x^n with x as fixed-point and n as regular unsigned.\n //\n // It's O(log n), instead of O(n) for naive repeated multiplication.\n //\n // These facts are why it works:\n //\n // If n is even, then x^n = (x^2)^(n/2).\n // If n is odd, then x^n = x * x^(n-1),\n // and applying the equation for even x gives\n // x^n = x * (x^2)^((n-1) / 2).\n //\n // Also, EVM division is flooring and\n // floor[(n-1) / 2] = floor[n / 2].\n //\n function rpow(uint256 x, uint256 n) internal pure returns (uint256 z) {\n z = n % 2 != 0 ? x : RAY;\n\n for (n /= 2; n != 0; n /= 2) {\n x = rmul(x, x);\n\n if (n % 2 != 0) {\n z = rmul(z, x);\n }\n }\n }\n}\n" + }, + "contracts/interfaces/IRibbonThetaVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {Vault} from \"../libraries/Vault.sol\";\n\ninterface IRibbonThetaVault {\n function currentOption() external view returns (address);\n\n function nextOption() external view returns (address);\n\n function vaultParams() external view returns (Vault.VaultParams memory);\n\n function vaultState() external view returns (Vault.VaultState memory);\n\n function optionState() external view returns (Vault.OptionState memory);\n\n function optionAuctionID() external view returns (uint256);\n\n function pricePerShare() external view returns (uint256);\n\n function roundPricePerShare(uint256) external view returns (uint256);\n\n function depositFor(uint256 amount, address creditor) external;\n\n function initiateWithdraw(uint256 numShares) external;\n\n function completeWithdraw() external;\n\n function maxRedeem() external;\n\n function depositYieldTokenFor(uint256 amount, address creditor) external;\n\n function symbol() external view returns (string calldata);\n}\n" + }, + "contracts/vaults/BaseVaults/RibbonThetaVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport {GnosisAuction} from \"../../libraries/GnosisAuction.sol\";\nimport {\n RibbonThetaVaultStorage\n} from \"../../storage/RibbonThetaVaultStorage.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {VaultLifecycle} from \"../../libraries/VaultLifecycle.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {ILiquidityGauge} from \"../../interfaces/ILiquidityGauge.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\nimport {RibbonVault} from \"./base/RibbonVault.sol\";\n\n/**\n * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe\n * the inheritance chain closely.\n * Any changes/appends in storage variable needs to happen in RibbonThetaVaultStorage.\n * RibbonThetaVault should not inherit from any other contract aside from RibbonVault, RibbonThetaVaultStorage\n */\ncontract RibbonThetaVault is RibbonVault, RibbonThetaVaultStorage {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice oTokenFactory is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event PremiumDiscountSet(\n uint256 premiumDiscount,\n uint256 newPremiumDiscount\n );\n\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event InitiateGnosisAuction(\n address indexed auctioningToken,\n address indexed biddingToken,\n uint256 auctionCounter,\n address indexed manager\n );\n\n /************************************************\n * STRUCTS\n ***********************************************/\n\n /**\n * @notice Initialization parameters for the vault.\n * @param _owner is the owner of the vault with critical permissions\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n * @param _premiumDiscount is the vault's discount applied to the premium\n * @param _auctionDuration is the duration of the gnosis auction\n */\n struct InitParams {\n address _owner;\n address _keeper;\n address _feeRecipient;\n uint256 _managementFee;\n uint256 _performanceFee;\n string _tokenName;\n string _tokenSymbol;\n address _optionsPremiumPricer;\n address _strikeSelection;\n uint32 _premiumDiscount;\n uint256 _auctionDuration;\n }\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions\n */\n constructor(\n address _weth,\n address _usdc,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _gnosisEasyAuction\n )\n RibbonVault(\n _weth,\n _usdc,\n _gammaController,\n _marginPool,\n _gnosisEasyAuction\n )\n {\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n OTOKEN_FACTORY = _oTokenFactory;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n * @param _initParams is the struct with vault initialization parameters\n * @param _vaultParams is the struct with vault general data\n */\n function initialize(\n InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n baseInitialize(\n _initParams._owner,\n _initParams._keeper,\n _initParams._feeRecipient,\n _initParams._managementFee,\n _initParams._performanceFee,\n _initParams._tokenName,\n _initParams._tokenSymbol,\n _vaultParams\n );\n require(\n _initParams._optionsPremiumPricer != address(0),\n \"!_optionsPremiumPricer\"\n );\n require(\n _initParams._strikeSelection != address(0),\n \"!_strikeSelection\"\n );\n require(\n _initParams._premiumDiscount > 0 &&\n _initParams._premiumDiscount <\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"!_premiumDiscount\"\n );\n require(\n _initParams._auctionDuration >= MIN_AUCTION_DURATION,\n \"!_auctionDuration\"\n );\n optionsPremiumPricer = _initParams._optionsPremiumPricer;\n strikeSelection = _initParams._strikeSelection;\n premiumDiscount = _initParams._premiumDiscount;\n auctionDuration = _initParams._auctionDuration;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new discount on premiums for options we are selling\n * @param newPremiumDiscount is the premium discount\n */\n function setPremiumDiscount(uint256 newPremiumDiscount)\n external\n onlyKeeper\n {\n require(\n newPremiumDiscount > 0 &&\n newPremiumDiscount <= 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"Invalid discount\"\n );\n\n emit PremiumDiscountSet(premiumDiscount, newPremiumDiscount);\n\n premiumDiscount = newPremiumDiscount;\n }\n\n /**\n * @notice Sets the new auction duration\n * @param newAuctionDuration is the auction duration\n */\n function setAuctionDuration(uint256 newAuctionDuration) external onlyOwner {\n require(\n newAuctionDuration >= MIN_AUCTION_DURATION,\n \"Invalid auction duration\"\n );\n\n emit AuctionDurationSet(auctionDuration, newAuctionDuration);\n\n auctionDuration = newAuctionDuration;\n }\n\n /**\n * @notice Sets the new strike selection contract\n * @param newStrikeSelection is the address of the new strike selection contract\n */\n function setStrikeSelection(address newStrikeSelection) external onlyOwner {\n require(newStrikeSelection != address(0), \"!newStrikeSelection\");\n strikeSelection = newStrikeSelection;\n }\n\n /**\n * @notice Sets the new options premium pricer contract\n * @param newOptionsPremiumPricer is the address of the new strike selection contract\n */\n function setOptionsPremiumPricer(address newOptionsPremiumPricer)\n external\n onlyOwner\n {\n require(\n newOptionsPremiumPricer != address(0),\n \"!newOptionsPremiumPricer\"\n );\n optionsPremiumPricer = newOptionsPremiumPricer;\n }\n\n /**\n * @notice Optionality to set strike price manually\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice) external onlyOwner {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Sets the new liquidityGauge contract for this vault\n * @param newLiquidityGauge is the address of the new liquidityGauge contract\n */\n function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {\n liquidityGauge = newLiquidityGauge;\n }\n\n /**\n * @notice Sets the new optionsPurchaseQueue contract for this vault\n * @param newOptionsPurchaseQueue is the address of the new optionsPurchaseQueue contract\n */\n function setOptionsPurchaseQueue(address newOptionsPurchaseQueue)\n external\n onlyOwner\n {\n optionsPurchaseQueue = newOptionsPurchaseQueue;\n }\n\n /**\n * @notice Sets oToken Premium\n * @param minPrice is the new oToken Premium in the units of 10**18\n */\n function setMinPrice(uint256 minPrice) external onlyKeeper {\n require(minPrice > 0, \"!minPrice\");\n currentOtokenPremium = minPrice;\n }\n\n /**\n * @notice Sets the new Vault Pauser contract for this vault\n * @param newVaultPauser is the address of the new vaultPauser contract\n */\n function setVaultPauser(address newVaultPauser) external onlyOwner {\n vaultPauser = newVaultPauser;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n transferAsset(msg.sender, amount);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n _initiateWithdraw(numShares);\n currentQueuedWithdrawShares = currentQueuedWithdrawShares.add(\n numShares\n );\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /**\n * @notice Stakes a users vault shares\n * @param numShares is the number of shares to stake\n */\n function stake(uint256 numShares) external nonReentrant {\n address _liquidityGauge = liquidityGauge;\n require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit\n require(numShares > 0);\n uint256 heldByAccount = balanceOf(msg.sender);\n if (heldByAccount < numShares) {\n _redeem(numShares.sub(heldByAccount), false);\n }\n _transfer(msg.sender, address(this), numShares);\n _approve(address(this), _liquidityGauge, numShares);\n ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);\n }\n\n /**\n * @notice Sets the next option the vault will be shorting, and closes the existing short.\n * This allows all the users to withdraw if the next option is malicious.\n */\n function commitAndClose() external nonReentrant {\n address oldOption = optionState.currentOption;\n\n VaultLifecycle.CloseParams memory closeParams =\n VaultLifecycle.CloseParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n currentOption: oldOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n strikeSelection: strikeSelection,\n optionsPremiumPricer: optionsPremiumPricer,\n premiumDiscount: premiumDiscount\n });\n\n (address otokenAddress, uint256 strikePrice, uint256 delta) =\n VaultLifecycle.commitAndClose(closeParams, vaultParams, vaultState);\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n optionState.nextOption = otokenAddress;\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n\n _closeShort(oldOption);\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycle.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares;\n\n (\n address newOption,\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount\n ) =\n _rollToNextOption(\n lastQueuedWithdrawAmount,\n currQueuedWithdrawShares\n );\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(\n currQueuedWithdrawShares\n );\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n currentQueuedWithdrawShares = 0;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n uint256 optionsMintAmount =\n VaultLifecycle.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n VaultLifecycle.allocateOptions(\n optionsPurchaseQueue,\n newOption,\n optionsMintAmount,\n VaultLifecycle.QUEUE_OPTION_ALLOCATION\n );\n\n _startAuction();\n }\n\n /**\n * @notice Initiate the gnosis auction.\n */\n function startAuction() external onlyKeeper nonReentrant {\n _startAuction();\n }\n\n function _startAuction() private {\n GnosisAuction.AuctionDetails memory auctionDetails;\n\n address currentOtoken = optionState.currentOption;\n\n auctionDetails.oTokenAddress = currentOtoken;\n auctionDetails.gnosisEasyAuction = GNOSIS_EASY_AUCTION;\n auctionDetails.asset = vaultParams.asset;\n auctionDetails.assetDecimals = vaultParams.decimals;\n auctionDetails.oTokenPremium = currentOtokenPremium;\n auctionDetails.duration = auctionDuration;\n\n optionAuctionID = VaultLifecycle.startAuction(auctionDetails);\n }\n\n /**\n * @notice Sell the allocated options to the purchase queue post auction settlement\n */\n function sellOptionsToQueue() external onlyKeeper nonReentrant {\n VaultLifecycle.sellOptionsToQueue(\n optionsPurchaseQueue,\n GNOSIS_EASY_AUCTION,\n optionAuctionID\n );\n }\n\n /**\n * @notice Burn the remaining oTokens left over from gnosis auction.\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n uint256 unlockedAssetAmount =\n VaultLifecycle.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n\n vaultState.lockedAmount = uint104(\n uint256(vaultState.lockedAmount).sub(unlockedAssetAmount)\n );\n }\n\n /**\n * @notice Recovery function that returns an ERC20 token to the recipient\n * @param token is the ERC20 token to recover from the vault\n * @param recipient is the recipient of the recovered tokens\n */\n function recoverTokens(address token, address recipient)\n external\n onlyOwner\n {\n require(token != vaultParams.asset, \"Vault asset not recoverable\");\n require(token != address(this), \"Vault share not recoverable\");\n require(recipient != address(this), \"Recipient cannot be vault\");\n\n IERC20(token).safeTransfer(\n recipient,\n IERC20(token).balanceOf(address(this))\n );\n }\n\n /**\n * @notice pause a user's vault position\n */\n function pausePosition() external {\n address _vaultPauserAddress = vaultPauser;\n require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit\n _redeem(0, true);\n uint256 heldByAccount = balanceOf(msg.sender);\n _approve(msg.sender, _vaultPauserAddress, heldByAccount);\n IVaultPauser(_vaultPauserAddress).pausePosition(\n msg.sender,\n heldByAccount\n );\n }\n}\n" + }, + "contracts/storage/RibbonThetaVaultStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nabstract contract RibbonThetaVaultStorageV1 {\n // Logic contract used to price options\n address public optionsPremiumPricer;\n // Logic contract used to select strike prices\n address public strikeSelection;\n // Premium discount on options we are selling (thousandths place: 000 - 999)\n uint256 public premiumDiscount;\n // Current oToken premium\n uint256 public currentOtokenPremium;\n // Last round id at which the strike was manually overridden\n uint16 public lastStrikeOverrideRound;\n // Price last overridden strike set to\n uint256 public overriddenStrikePrice;\n // Auction duration\n uint256 public auctionDuration;\n // Auction id of current option\n uint256 public optionAuctionID;\n}\n\nabstract contract RibbonThetaVaultStorageV2 {\n // Amount locked for scheduled withdrawals last week;\n uint256 public lastQueuedWithdrawAmount;\n}\n\nabstract contract RibbonThetaVaultStorageV3 {\n // DEPRECATED: Auction will be denominated in USDC if true\n bool private _isUsdcAuction;\n // DEPRECATED: Path for swaps\n bytes private _swapPath;\n}\n\nabstract contract RibbonThetaVaultStorageV4 {\n // LiquidityGauge contract for the vault\n address public liquidityGauge;\n}\n\nabstract contract RibbonThetaVaultStorageV5 {\n // OptionsPurchaseQueue contract for selling options\n address public optionsPurchaseQueue;\n}\n\nabstract contract RibbonThetaVaultStorageV6 {\n // Queued withdraw shares for the current round\n uint256 public currentQueuedWithdrawShares;\n}\n\nabstract contract RibbonThetaVaultStorageV7 {\n // Vault Pauser Contract for the vault\n address public vaultPauser;\n}\n\nabstract contract RibbonThetaVaultStorageV8 {\n // DEPRECATED: Executor role for Swap offers\n address private _offerExecutor;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of RibbonThetaVaultStorage\n// e.g. RibbonThetaVaultStorage, so finally it would look like\n// contract RibbonThetaVaultStorage is RibbonThetaVaultStorageV1, RibbonThetaVaultStorageV2\nabstract contract RibbonThetaVaultStorage is\n RibbonThetaVaultStorageV1,\n RibbonThetaVaultStorageV2,\n RibbonThetaVaultStorageV3,\n RibbonThetaVaultStorageV4,\n RibbonThetaVaultStorageV5,\n RibbonThetaVaultStorageV6,\n RibbonThetaVaultStorageV7,\n RibbonThetaVaultStorageV8\n{\n\n}\n" + }, + "contracts/libraries/VaultLifecycle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {ShareMath} from \"./ShareMath.sol\";\nimport {IStrikeSelection} from \"../interfaces/IRibbon.sol\";\nimport {GnosisAuction} from \"./GnosisAuction.sol\";\nimport {\n IOtokenFactory,\n IOtoken,\n IController,\n GammaTypes\n} from \"../interfaces/GammaInterface.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\nimport {IGnosisAuction} from \"../interfaces/IGnosisAuction.sol\";\nimport {IOptionsPurchaseQueue} from \"../interfaces/IOptionsPurchaseQueue.sol\";\nimport {SupportsNonCompliantERC20} from \"./SupportsNonCompliantERC20.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\n\nlibrary VaultLifecycle {\n using SafeMath for uint256;\n using SupportsNonCompliantERC20 for IERC20;\n\n struct CloseParams {\n address OTOKEN_FACTORY;\n address USDC;\n address currentOption;\n uint256 delay;\n uint16 lastStrikeOverrideRound;\n uint256 overriddenStrikePrice;\n address strikeSelection;\n address optionsPremiumPricer;\n uint256 premiumDiscount;\n }\n\n /// @notice Default maximum option allocation for the queue (50%)\n uint256 internal constant QUEUE_OPTION_ALLOCATION = 5000;\n\n /**\n * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @return otokenAddress is the address of the new option\n * @return strikePrice is the strike price of the new option\n * @return delta is the delta of the new option\n */\n function commitAndClose(\n CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n Vault.VaultState storage vaultState\n )\n external\n returns (\n address otokenAddress,\n uint256 strikePrice,\n uint256 delta\n )\n {\n uint256 expiry = getNextExpiry(closeParams.currentOption);\n\n IStrikeSelection selection =\n IStrikeSelection(closeParams.strikeSelection);\n\n bool isPut = vaultParams.isPut;\n address underlying = vaultParams.underlying;\n address asset = vaultParams.asset;\n\n (strikePrice, delta) = closeParams.lastStrikeOverrideRound ==\n vaultState.round\n ? (closeParams.overriddenStrikePrice, selection.delta())\n : selection.getStrikePrice(expiry, isPut);\n\n require(strikePrice != 0, \"!strikePrice\");\n\n // retrieve address if option already exists, or deploy it\n otokenAddress = getOrDeployOtoken(\n closeParams,\n vaultParams,\n underlying,\n asset,\n strikePrice,\n expiry,\n isPut\n );\n\n return (otokenAddress, strikePrice, delta);\n }\n\n /**\n * @notice Verify the otoken has the correct parameters to prevent vulnerability to opyn contract changes\n * @param otokenAddress is the address of the otoken\n * @param vaultParams is the struct with vault general data\n * @param collateralAsset is the address of the collateral asset\n * @param USDC is the address of usdc\n * @param delay is the delay between commitAndClose and rollToNextOption\n */\n function verifyOtoken(\n address otokenAddress,\n Vault.VaultParams storage vaultParams,\n address collateralAsset,\n address USDC,\n uint256 delay\n ) private view {\n require(otokenAddress != address(0), \"!otokenAddress\");\n\n IOtoken otoken = IOtoken(otokenAddress);\n require(otoken.isPut() == vaultParams.isPut, \"Type mismatch\");\n require(\n otoken.underlyingAsset() == vaultParams.underlying,\n \"Wrong underlyingAsset\"\n );\n require(\n otoken.collateralAsset() == collateralAsset,\n \"Wrong collateralAsset\"\n );\n\n // we just assume all options use USDC as the strike\n require(otoken.strikeAsset() == USDC, \"strikeAsset != USDC\");\n\n uint256 readyAt = block.timestamp.add(delay);\n require(otoken.expiryTimestamp() >= readyAt, \"Expiry before delay\");\n }\n\n /**\n * @param decimals is the decimals of the asset\n * @param totalBalance is the vaults total balance of the asset\n * @param currentShareSupply is the supply of the shares invoked with totalSupply()\n * @param lastQueuedWithdrawAmount is the total amount queued for withdrawals\n * @param performanceFee is the perf fee percent to charge on premiums\n * @param managementFee is the management fee percent to charge on the AUM\n * @param currentQueuedWithdrawShares is amount of queued withdrawals from the current round\n */\n struct RolloverParams {\n uint256 decimals;\n uint256 totalBalance;\n uint256 currentShareSupply;\n uint256 lastQueuedWithdrawAmount;\n uint256 performanceFee;\n uint256 managementFee;\n uint256 currentQueuedWithdrawShares;\n }\n\n /**\n * @notice Calculate the shares to mint, new price per share, and\n amount of funds to re-allocate as collateral for the new round\n * @param vaultState is the storage variable vaultState passed from RibbonVault\n * @param params is the rollover parameters passed to compute the next state\n * @return newLockedAmount is the amount of funds to allocate for the new round\n * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal\n * @return newPricePerShare is the price per share of the new round\n * @return mintShares is the amount of shares to mint from deposits\n * @return performanceFeeInAsset is the performance fee charged by vault\n * @return totalVaultFee is the total amount of fee charged by vault\n */\n function rollover(\n Vault.VaultState storage vaultState,\n RolloverParams calldata params\n )\n external\n view\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares,\n uint256 performanceFeeInAsset,\n uint256 totalVaultFee\n )\n {\n uint256 currentBalance = params.totalBalance;\n uint256 pendingAmount = vaultState.totalPending;\n // Total amount of queued withdrawal shares from previous rounds (doesn't include the current round)\n uint256 lastQueuedWithdrawShares = vaultState.queuedWithdrawShares;\n\n // Deduct older queued withdraws so we don't charge fees on them\n uint256 balanceForVaultFees =\n currentBalance.sub(params.lastQueuedWithdrawAmount);\n\n {\n (performanceFeeInAsset, , totalVaultFee) = VaultLifecycle\n .getVaultFees(\n balanceForVaultFees,\n vaultState.lastLockedAmount,\n vaultState.totalPending,\n params.performanceFee,\n params.managementFee\n );\n }\n\n // Take into account the fee\n // so we can calculate the newPricePerShare\n currentBalance = currentBalance.sub(totalVaultFee);\n\n {\n newPricePerShare = ShareMath.pricePerShare(\n params.currentShareSupply.sub(lastQueuedWithdrawShares),\n currentBalance.sub(params.lastQueuedWithdrawAmount),\n pendingAmount,\n params.decimals\n );\n\n queuedWithdrawAmount = params.lastQueuedWithdrawAmount.add(\n ShareMath.sharesToAsset(\n params.currentQueuedWithdrawShares,\n newPricePerShare,\n params.decimals\n )\n );\n\n // After closing the short, if the options expire in-the-money\n // vault pricePerShare would go down because vault's asset balance decreased.\n // This ensures that the newly-minted shares do not take on the loss.\n mintShares = ShareMath.assetToShares(\n pendingAmount,\n newPricePerShare,\n params.decimals\n );\n }\n\n return (\n currentBalance.sub(queuedWithdrawAmount), // new locked balance subtracts the queued withdrawals\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n );\n }\n\n /**\n * @notice Creates the actual Opyn short position by depositing collateral and minting otokens\n * @param gammaController is the address of the opyn controller contract\n * @param marginPool is the address of the opyn margin contract which holds the collateral\n * @param oTokenAddress is the address of the otoken to mint\n * @param depositAmount is the amount of collateral to deposit\n * @return the otoken mint amount\n */\n function createShort(\n address gammaController,\n address marginPool,\n address oTokenAddress,\n uint256 depositAmount\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n uint256 newVaultID =\n (controller.getAccountVaultCounter(address(this))).add(1);\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IOtoken oToken = IOtoken(oTokenAddress);\n address collateralAsset = oToken.collateralAsset();\n\n uint256 collateralDecimals =\n uint256(IERC20Detailed(collateralAsset).decimals());\n uint256 mintAmount;\n\n if (oToken.isPut()) {\n // For minting puts, there will be instances where the full depositAmount will not be used for minting.\n // This is because of an issue with precision.\n //\n // For ETH put options, we are calculating the mintAmount (10**8 decimals) using\n // the depositAmount (10**18 decimals), which will result in truncation of decimals when scaling down.\n // As a result, there will be tiny amounts of dust left behind in the Opyn vault when minting put otokens.\n //\n // For simplicity's sake, we do not refund the dust back to the address(this) on minting otokens.\n // We retain the dust in the vault so the calling contract can withdraw the\n // actual locked amount + dust at settlement.\n //\n // To test this behavior, we can console.log\n // MarginCalculatorInterface(0x7A48d10f372b3D7c60f6c9770B91398e4ccfd3C7).getExcessCollateral(vault)\n // to see how much dust (or excess collateral) is left behind.\n mintAmount = depositAmount\n .mul(10**Vault.OTOKEN_DECIMALS)\n .mul(10**18) // we use 10**18 to give extra precision\n .div(oToken.strikePrice().mul(10**(10 + collateralDecimals)));\n } else {\n mintAmount = depositAmount;\n\n if (collateralDecimals > 8) {\n uint256 scaleBy = 10**(collateralDecimals.sub(8)); // oTokens have 8 decimals\n if (mintAmount > scaleBy) {\n mintAmount = depositAmount.div(scaleBy); // scale down from 10**18 to 10**8\n }\n }\n }\n\n // double approve to fix non-compliant ERC20s\n IERC20 collateralToken = IERC20(collateralAsset);\n collateralToken.safeApproveNonCompliant(marginPool, depositAmount);\n\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](3);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.OpenVault,\n address(this), // owner\n address(this), // receiver\n address(0), // asset, otoken\n newVaultID, // vaultId\n 0, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.DepositCollateral,\n address(this), // owner\n address(this), // address to transfer from\n collateralAsset, // deposited asset\n newVaultID, // vaultId\n depositAmount, // amount\n 0, //index\n \"\" //data\n );\n\n actions[2] = IController.ActionArgs(\n IController.ActionType.MintShortOption,\n address(this), // owner\n address(this), // address to transfer to\n oTokenAddress, // option address\n newVaultID, // vaultId\n mintAmount, // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n return mintAmount;\n }\n\n /**\n * @notice Close the existing short otoken position. Currently this implementation is simple.\n * It closes the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time. Since calling `_closeShort` deletes vaults by\n calling SettleVault action, this assumption should hold.\n * @param gammaController is the address of the opyn controller contract\n * @return amount of collateral redeemed from the vault\n */\n function settleShort(address gammaController) external returns (uint256) {\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n // The short position has been previously closed, or all the otokens have been burned.\n // So we return early.\n if (address(collateralToken) == address(0)) {\n return 0;\n }\n\n // This is equivalent to doing IERC20(vault.asset).balanceOf(address(this))\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // If it is after expiry, we need to settle the short position using the normal way\n // Delete the vault and withdraw all remaining collateral from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.SettleVault,\n address(this), // owner\n address(this), // address to transfer to\n address(0), // not used\n vaultID, // vaultId\n 0, // not used\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Exercises the ITM option using existing long otoken position. Currently this implementation is simple.\n * It calls the `Redeem` action to claim the payout.\n * @param gammaController is the address of the opyn controller contract\n * @param oldOption is the address of the old option\n * @param asset is the address of the vault's asset\n * @return amount of asset received by exercising the option\n */\n function settleLong(\n address gammaController,\n address oldOption,\n address asset\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n\n uint256 oldOptionBalance = IERC20(oldOption).balanceOf(address(this));\n\n if (controller.getPayout(oldOption, oldOptionBalance) == 0) {\n return 0;\n }\n\n uint256 startAssetBalance = IERC20(asset).balanceOf(address(this));\n\n // If it is after expiry, we need to redeem the profits\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.Redeem,\n address(0), // not used\n address(this), // address to send profits to\n oldOption, // address of otoken\n 0, // not used\n oldOptionBalance, // otoken balance\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endAssetBalance = IERC20(asset).balanceOf(address(this));\n\n return endAssetBalance.sub(startAssetBalance);\n }\n\n /**\n * @notice Burn the remaining oTokens left over from auction. Currently this implementation is simple.\n * It burns oTokens from the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time.\n * @param gammaController is the address of the opyn controller contract\n * @param currentOption is the address of the current option\n * @return amount of collateral redeemed by burning otokens\n */\n function burnOtokens(address gammaController, address currentOption)\n external\n returns (uint256)\n {\n uint256 numOTokensToBurn =\n IERC20(currentOption).balanceOf(address(this));\n\n require(numOTokensToBurn > 0, \"No oTokens to burn\");\n\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // Burning `amount` of oTokens from the ribbon vault,\n // then withdrawing the corresponding collateral amount from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](2);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.BurnShortOption,\n address(this), // owner\n address(this), // address to transfer from\n address(vault.shortOtokens[0]), // otoken address\n vaultID, // vaultId\n numOTokensToBurn, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.WithdrawCollateral,\n address(this), // owner\n address(this), // address to transfer to\n address(collateralToken), // withdrawn asset\n vaultID, // vaultId\n vault.collateralAmounts[0].mul(numOTokensToBurn).div(\n vault.shortAmounts[0]\n ), // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Calculates the performance and management fee for this week's round\n * @param currentBalance is the balance of funds held on the vault after closing short\n * @param lastLockedAmount is the amount of funds locked from the previous round\n * @param pendingAmount is the pending deposit amount\n * @param performanceFeePercent is the performance fee pct.\n * @param managementFeePercent is the management fee pct.\n * @return performanceFeeInAsset is the performance fee\n * @return managementFeeInAsset is the management fee\n * @return vaultFee is the total fees\n */\n function getVaultFees(\n uint256 currentBalance,\n uint256 lastLockedAmount,\n uint256 pendingAmount,\n uint256 performanceFeePercent,\n uint256 managementFeePercent\n )\n internal\n pure\n returns (\n uint256 performanceFeeInAsset,\n uint256 managementFeeInAsset,\n uint256 vaultFee\n )\n {\n // At the first round, currentBalance=0, pendingAmount>0\n // so we just do not charge anything on the first round\n uint256 lockedBalanceSansPending =\n currentBalance > pendingAmount\n ? currentBalance.sub(pendingAmount)\n : 0;\n\n uint256 _performanceFeeInAsset;\n uint256 _managementFeeInAsset;\n uint256 _vaultFee;\n\n // Take performance fee and management fee ONLY if difference between\n // last week and this week's vault deposits, taking into account pending\n // deposits and withdrawals, is positive. If it is negative, last week's\n // option expired ITM past breakeven, and the vault took a loss so we\n // do not collect performance fee for last week\n if (lockedBalanceSansPending > lastLockedAmount) {\n _performanceFeeInAsset = performanceFeePercent > 0\n ? lockedBalanceSansPending\n .sub(lastLockedAmount)\n .mul(performanceFeePercent)\n .div(100 * Vault.FEE_MULTIPLIER)\n : 0;\n _managementFeeInAsset = managementFeePercent > 0\n ? lockedBalanceSansPending.mul(managementFeePercent).div(\n 100 * Vault.FEE_MULTIPLIER\n )\n : 0;\n\n _vaultFee = _performanceFeeInAsset.add(_managementFeeInAsset);\n }\n\n return (_performanceFeeInAsset, _managementFeeInAsset, _vaultFee);\n }\n\n /**\n * @notice Either retrieves the option token if it already exists, or deploy it\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param underlying is the address of the underlying asset of the option\n * @param collateralAsset is the address of the collateral asset of the option\n * @param strikePrice is the strike price of the option\n * @param expiry is the expiry timestamp of the option\n * @param isPut is whether the option is a put\n * @return the address of the option\n */\n function getOrDeployOtoken(\n CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n address underlying,\n address collateralAsset,\n uint256 strikePrice,\n uint256 expiry,\n bool isPut\n ) internal returns (address) {\n IOtokenFactory factory = IOtokenFactory(closeParams.OTOKEN_FACTORY);\n\n address otokenFromFactory =\n factory.getOtoken(\n underlying,\n closeParams.USDC,\n collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n if (otokenFromFactory != address(0)) {\n return otokenFromFactory;\n }\n\n address otoken =\n factory.createOtoken(\n underlying,\n closeParams.USDC,\n collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n verifyOtoken(\n otoken,\n vaultParams,\n collateralAsset,\n closeParams.USDC,\n closeParams.delay\n );\n\n return otoken;\n }\n\n function getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount\n ) external view returns (uint256) {\n return\n _getOTokenPremium(\n oTokenAddress,\n optionsPremiumPricer,\n premiumDiscount\n );\n }\n\n function _getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount\n ) internal view returns (uint256) {\n IOtoken newOToken = IOtoken(oTokenAddress);\n IOptionsPremiumPricer premiumPricer =\n IOptionsPremiumPricer(optionsPremiumPricer);\n\n // Apply black-scholes formula (from rvol library) to option given its features\n // and get price for 100 contracts denominated in the underlying asset for call option\n // and USDC for put option\n uint256 optionPremium =\n premiumPricer.getPremium(\n newOToken.strikePrice(),\n newOToken.expiryTimestamp(),\n newOToken.isPut()\n );\n\n // Apply a discount to incentivize arbitraguers\n optionPremium = optionPremium.mul(premiumDiscount).div(\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER\n );\n\n require(\n optionPremium <= type(uint96).max,\n \"optionPremium > type(uint96) max value!\"\n );\n require(optionPremium > 0, \"!optionPremium\");\n\n return optionPremium;\n }\n\n /**\n * @notice Starts the gnosis auction\n * @param auctionDetails is the struct with all the custom parameters of the auction\n * @return the auction id of the newly created auction\n */\n function startAuction(GnosisAuction.AuctionDetails calldata auctionDetails)\n external\n returns (uint256)\n {\n return GnosisAuction.startAuction(auctionDetails);\n }\n\n /**\n * @notice Settles the gnosis auction\n * @param gnosisEasyAuction is the contract address of Gnosis easy auction protocol\n * @param auctionID is the auction ID of the gnosis easy auction\n */\n function settleAuction(address gnosisEasyAuction, uint256 auctionID)\n internal\n {\n IGnosisAuction(gnosisEasyAuction).settleAuction(auctionID);\n }\n\n /**\n * @notice Places a bid in an auction\n * @param bidDetails is the struct with all the details of the\n bid including the auction's id and how much to bid\n */\n function placeBid(GnosisAuction.BidDetails calldata bidDetails)\n external\n returns (\n uint256 sellAmount,\n uint256 buyAmount,\n uint64 userId\n )\n {\n return GnosisAuction.placeBid(bidDetails);\n }\n\n /**\n * @notice Claims the oTokens belonging to the vault\n * @param auctionSellOrder is the sell order of the bid\n * @param gnosisEasyAuction is the address of the gnosis auction contract\n holding custody to the funds\n * @param counterpartyThetaVault is the address of the counterparty theta\n vault of this delta vault\n */\n function claimAuctionOtokens(\n Vault.AuctionSellOrder calldata auctionSellOrder,\n address gnosisEasyAuction,\n address counterpartyThetaVault\n ) external {\n GnosisAuction.claimAuctionOtokens(\n auctionSellOrder,\n gnosisEasyAuction,\n counterpartyThetaVault\n );\n }\n\n /**\n * @notice Allocates the vault's minted options to the OptionsPurchaseQueue contract\n * @dev Skipped if the optionsPurchaseQueue doesn't exist\n * @param optionsPurchaseQueue is the OptionsPurchaseQueue contract\n * @param option is the minted option\n * @param optionsAmount is the amount of options minted\n * @param optionAllocation is the maximum % of options to allocate towards the purchase queue (will only allocate\n * up to the amount that is on the queue)\n * @return allocatedOptions is the amount of options that ended up getting allocated to the OptionsPurchaseQueue\n */\n function allocateOptions(\n address optionsPurchaseQueue,\n address option,\n uint256 optionsAmount,\n uint256 optionAllocation\n ) external returns (uint256 allocatedOptions) {\n // Skip if optionsPurchaseQueue is address(0)\n if (optionsPurchaseQueue != address(0)) {\n allocatedOptions = optionsAmount.mul(optionAllocation).div(\n 100 * Vault.OPTION_ALLOCATION_MULTIPLIER\n );\n allocatedOptions = IOptionsPurchaseQueue(optionsPurchaseQueue)\n .getOptionsAllocation(address(this), allocatedOptions);\n\n if (allocatedOptions != 0) {\n IERC20(option).approve(optionsPurchaseQueue, allocatedOptions);\n IOptionsPurchaseQueue(optionsPurchaseQueue).allocateOptions(\n allocatedOptions\n );\n }\n }\n\n return allocatedOptions;\n }\n\n /**\n * @notice Sell the allocated options to the purchase queue post auction settlement\n * @dev Reverts if the auction hasn't settled yet\n * @param optionsPurchaseQueue is the OptionsPurchaseQueue contract\n * @param gnosisEasyAuction The address of the Gnosis Easy Auction contract\n * @return totalPremiums Total premiums earnt by the vault\n */\n function sellOptionsToQueue(\n address optionsPurchaseQueue,\n address gnosisEasyAuction,\n uint256 optionAuctionID\n ) external returns (uint256) {\n uint256 settlementPrice =\n getAuctionSettlementPrice(gnosisEasyAuction, optionAuctionID);\n require(settlementPrice != 0, \"!settlementPrice\");\n\n return\n IOptionsPurchaseQueue(optionsPurchaseQueue).sellToBuyers(\n settlementPrice\n );\n }\n\n /**\n * @notice Gets the settlement price of a settled auction\n * @param gnosisEasyAuction The address of the Gnosis Easy Auction contract\n * @return settlementPrice Auction settlement price\n */\n function getAuctionSettlementPrice(\n address gnosisEasyAuction,\n uint256 optionAuctionID\n ) public view returns (uint256) {\n bytes32 clearingPriceOrder =\n IGnosisAuction(gnosisEasyAuction)\n .auctionData(optionAuctionID)\n .clearingPriceOrder;\n\n if (clearingPriceOrder == bytes32(0)) {\n // Current auction hasn't settled yet\n return 0;\n } else {\n // We decode the clearingPriceOrder to find the auction settlement price\n // settlementPrice = clearingPriceOrder.sellAmount / clearingPriceOrder.buyAmount\n return\n (10**Vault.OTOKEN_DECIMALS)\n .mul(\n uint96(uint256(clearingPriceOrder)) // sellAmount\n )\n .div(\n uint96(uint256(clearingPriceOrder) >> 96) // buyAmount\n );\n }\n }\n\n /**\n * @notice Verify the constructor params satisfy requirements\n * @param owner is the owner of the vault with critical permissions\n * @param feeRecipient is the address to recieve vault performance and management fees\n * @param performanceFee is the perfomance fee pct.\n * @param tokenName is the name of the token\n * @param tokenSymbol is the symbol of the token\n * @param _vaultParams is the struct with vault general data\n */\n function verifyInitializerParams(\n address owner,\n address keeper,\n address feeRecipient,\n uint256 performanceFee,\n uint256 managementFee,\n string calldata tokenName,\n string calldata tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) external pure {\n require(owner != address(0), \"!owner\");\n require(keeper != address(0), \"!keeper\");\n require(feeRecipient != address(0), \"!feeRecipient\");\n require(\n performanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"performanceFee >= 100%\"\n );\n require(\n managementFee < 100 * Vault.FEE_MULTIPLIER,\n \"managementFee >= 100%\"\n );\n require(bytes(tokenName).length > 0, \"!tokenName\");\n require(bytes(tokenSymbol).length > 0, \"!tokenSymbol\");\n\n require(_vaultParams.asset != address(0), \"!asset\");\n require(_vaultParams.underlying != address(0), \"!underlying\");\n require(_vaultParams.minimumSupply > 0, \"!minimumSupply\");\n require(_vaultParams.cap > 0, \"!cap\");\n require(\n _vaultParams.cap > _vaultParams.minimumSupply,\n \"cap has to be higher than minimumSupply\"\n );\n }\n\n /**\n * @notice Gets the next option expiry timestamp\n * @param currentOption is the otoken address that the vault is currently writing\n */\n function getNextExpiry(address currentOption)\n internal\n view\n returns (uint256)\n {\n // uninitialized state\n if (currentOption == address(0)) {\n return getNextFriday(block.timestamp);\n }\n uint256 currentExpiry = IOtoken(currentOption).expiryTimestamp();\n\n // After options expiry if no options are written for >1 week\n // We need to give the ability continue writing options\n if (block.timestamp > currentExpiry + 7 days) {\n return getNextFriday(block.timestamp);\n }\n return getNextFriday(currentExpiry);\n }\n\n /**\n * @notice Gets the next options expiry timestamp\n * @param timestamp is the expiry timestamp of the current option\n * Reference: https://codereview.stackexchange.com/a/33532\n * Examples:\n * getNextFriday(week 1 thursday) -> week 1 friday\n * getNextFriday(week 1 friday) -> week 2 friday\n * getNextFriday(week 1 saturday) -> week 2 friday\n */\n function getNextFriday(uint256 timestamp) internal pure returns (uint256) {\n // dayOfWeek = 0 (sunday) - 6 (saturday)\n uint256 dayOfWeek = ((timestamp / 1 days) + 4) % 7;\n uint256 nextFriday = timestamp + ((7 + 5 - dayOfWeek) % 7) * 1 days;\n uint256 friday8am = nextFriday - (nextFriday % (24 hours)) + (8 hours);\n\n // If the passed timestamp is day=Friday hour>8am, we simply increment it by a week to next Friday\n if (timestamp >= friday8am) {\n friday8am += 7 days;\n }\n return friday8am;\n }\n}\n" + }, + "contracts/interfaces/ILiquidityGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface ILiquidityGauge {\n function balanceOf(address) external view returns (uint256);\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function withdraw(uint256 _value) external;\n}\n" + }, + "contracts/interfaces/IVaultPauser.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface IVaultPauser {\n /// @notice pause vault position of an account with max amount\n /// @param _account the address of user\n /// @param _amount amount of shares\n function pausePosition(address _account, uint256 _amount) external;\n\n /// @notice resume vault position of an account with max amount\n /// @param _vaultAddress the address of vault\n function resumePosition(address _vaultAddress) external;\n}\n" + }, + "contracts/vaults/BaseVaults/base/RibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\n\nimport {Vault} from \"../../../libraries/Vault.sol\";\nimport {VaultLifecycle} from \"../../../libraries/VaultLifecycle.sol\";\nimport {ShareMath} from \"../../../libraries/ShareMath.sol\";\nimport {IWETH} from \"../../../interfaces/IWETH.sol\";\n\ncontract RibbonVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure.\n uint256[30] private ____gap;\n\n // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE\n // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaVaultStorage\n // or RibbonDeltaVaultStorage instead. Read this documentation to learn more:\n // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice Deprecated: 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n /// @notice 7 day period between each options sale.\n uint256 public constant PERIOD = 7 days;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // GNOSIS_EASY_AUCTION is Gnosis protocol's contract for initiating auctions and placing bids\n // https://github.com/gnosis/ido-contracts/blob/main/contracts/EasyAuction.sol\n address public immutable GNOSIS_EASY_AUCTION;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectVaultFees(\n uint256 performanceFee,\n uint256 vaultFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions\n */\n constructor(\n address _weth,\n address _usdc,\n address _gammaController,\n address _marginPool,\n address _gnosisEasyAuction\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_gnosisEasyAuction != address(0), \"!_gnosisEasyAuction\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n\n WETH = _weth;\n USDC = _usdc;\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n GNOSIS_EASY_AUCTION = _gnosisEasyAuction;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function baseInitialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) internal initializer {\n VaultLifecycle.verifyInitializerParams(\n _owner,\n _keeper,\n _feeRecipient,\n _performanceFee,\n _managementFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_tokenName, _tokenSymbol);\n __Ownable_init();\n transferOwnership(_owner);\n\n keeper = _keeper;\n\n feeRecipient = _feeRecipient;\n performanceFee = _performanceFee;\n managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div(\n WEEKS_PER_YEAR\n );\n vaultParams = _vaultParams;\n\n uint256 assetBalance =\n IERC20(vaultParams.asset).balanceOf(address(this));\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n\n vaultState.round = 1;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n // We are dividing annualized management fee by num weeks in a year\n uint256 tmpManagementFee =\n newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n\n managementFee = tmpManagementFee;\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Deposits ETH into the contract and mint vault shares. Reverts if the asset is not WETH.\n */\n function depositETH() external payable nonReentrant {\n require(vaultParams.asset == WETH, \"!WETH\");\n require(msg.value > 0, \"!value\");\n\n _depositFor(msg.value, msg.sender);\n\n IWETH(WETH).deposit{value: msg.value}();\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender.\n * @param amount is the amount of `asset` to deposit\n */\n function deposit(uint256 amount) external nonReentrant {\n require(amount > 0, \"!amount\");\n\n _depositFor(amount, msg.sender);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.\n * @notice Used for vault -> vault deposits on the user's behalf\n * @param amount is the amount of `asset` to deposit\n * @param creditor is the address that can claim/withdraw deposited amount\n */\n function depositFor(uint256 amount, address creditor)\n external\n nonReentrant\n {\n require(amount > 0, \"!amount\");\n require(creditor != address(0));\n\n _depositFor(amount, creditor);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n */\n function _depositFor(uint256 amount, address creditor) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount = totalBalance().add(amount);\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n\n emit Deposit(creditor, amount, currentRound);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function _initiateWithdraw(uint256 numShares) internal {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n if (\n depositReceipts[msg.sender].amount > 0 ||\n depositReceipts[msg.sender].unredeemedShares > 0\n ) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n bool withdrawalIsSameRound = withdrawal.round == currentRound;\n\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n\n uint256 existingShares = uint256(withdrawal.shares);\n\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return withdrawAmount the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n require(withdrawAmount > 0, \"!withdrawAmount\");\n transferAsset(msg.sender, withdrawAmount);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map.\n * Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k.\n * Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0.\n * @param numRounds is the number of rounds to initialize in the map\n */\n function initRounds(uint256 numRounds) external nonReentrant {\n require(numRounds > 0, \"!numRounds\");\n\n uint256 _round = vaultState.round;\n for (uint256 i = 0; i < numRounds; i++) {\n uint256 index = _round + i;\n require(roundPricePerShare[index] == 0, \"Initialized\"); // AVOID OVERWRITING ACTUAL VALUES\n roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT;\n }\n }\n\n /**\n * @notice Helper function that performs most administrative tasks\n * such as setting next option, minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @param currentQueuedWithdrawShares is the queued withdraw shares for the current round\n * @return newOption is the new option address\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _rollToNextOption(\n uint256 lastQueuedWithdrawAmount,\n uint256 currentQueuedWithdrawShares\n )\n internal\n returns (\n address newOption,\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount\n )\n {\n require(block.timestamp >= optionState.nextOptionReadyAt, \"!ready\");\n\n newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n address recipient = feeRecipient;\n uint256 mintShares;\n uint256 performanceFeeInAsset;\n uint256 totalVaultFee;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n ) = VaultLifecycle.rollover(\n vaultState,\n VaultLifecycle.RolloverParams(\n vaultParams.decimals,\n IERC20(vaultParams.asset).balanceOf(address(this)),\n totalSupply(),\n lastQueuedWithdrawAmount,\n performanceFee,\n managementFee,\n currentQueuedWithdrawShares\n )\n );\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n\n // Finalize the pricePerShare at the end of the round\n uint256 currentRound = vaultState.round;\n roundPricePerShare[currentRound] = newPricePerShare;\n\n emit CollectVaultFees(\n performanceFeeInAsset,\n totalVaultFee,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (totalVaultFee > 0) {\n transferAsset(payable(recipient), totalVaultFee);\n }\n\n return (newOption, lockedBalance, queuedWithdrawAmount);\n }\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) internal {\n address asset = vaultParams.asset;\n if (asset == WETH) {\n IWETH(WETH).withdraw(amount);\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Transfer failed\");\n return;\n }\n IERC20(asset).safeTransfer(recipient, amount);\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n external\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n return\n uint256(vaultState.lockedAmount).add(\n IERC20(vaultParams.asset).balanceOf(address(this))\n );\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n}\n" + }, + "contracts/interfaces/IOptionsPurchaseQueue.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\ninterface IOptionsPurchaseQueue {\n /**\n * @dev Contains purchase request info\n * @param optionsAmount Amount of options to purchase\n * @param premiums Total premiums the buyer is spending to purchase the options (optionsAmount * ceilingPrice)\n * We need to track the premiums here since the ceilingPrice could change between the time the purchase was\n * requested and when the options are sold\n * @param buyer The buyer requesting this purchase\n */\n struct Purchase {\n uint128 optionsAmount; // Slot 0\n uint128 premiums;\n address buyer; // Slot 1\n }\n\n function purchases(address, uint256)\n external\n view\n returns (\n uint128,\n uint128,\n address\n );\n\n function totalOptionsAmount(address) external view returns (uint256);\n\n function vaultAllocatedOptions(address) external view returns (uint256);\n\n function whitelistedBuyer(address) external view returns (bool);\n\n function minPurchaseAmount(address) external view returns (uint256);\n\n function ceilingPrice(address) external view returns (uint256);\n\n function getPurchases(address vault)\n external\n view\n returns (Purchase[] memory);\n\n function getPremiums(address vault, uint256 optionsAmount)\n external\n view\n returns (uint256);\n\n function getOptionsAllocation(address vault, uint256 allocatedOptions)\n external\n view\n returns (uint256);\n\n function requestPurchase(address vault, uint256 optionsAmount)\n external\n returns (uint256);\n\n function allocateOptions(uint256 allocatedOptions)\n external\n returns (uint256);\n\n function sellToBuyers(uint256 settlementPrice) external returns (uint256);\n\n function cancelAllPurchases(address vault) external;\n\n function addWhitelist(address buyer) external;\n\n function removeWhitelist(address buyer) external;\n\n function setCeilingPrice(address vault, uint256 price) external;\n\n function setMinPurchaseAmount(address vault, uint256 amount) external;\n}\n" + }, + "contracts/vaults/YearnVaults/base/RibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\nimport {IYearnVault} from \"../../../interfaces/IYearn.sol\";\nimport {Vault} from \"../../../libraries/Vault.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../../libraries/VaultLifecycleWithSwap.sol\";\nimport {ShareMath} from \"../../../libraries/ShareMath.sol\";\nimport {IWETH} from \"../../../interfaces/IWETH.sol\";\n\ncontract RibbonVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n /// @notice Yearn vault contract\n IYearnVault public collateralToken;\n\n // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure.\n uint256[30] private ____gap;\n\n // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE\n // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaYearnVaultStorage\n // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n /// @notice 7 day period between each options sale.\n uint256 public constant PERIOD = 7 days;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // SWAP_CONTRACT is a contract for settling bids via signed messages\n // https://github.com/ribbon-finance/ribbon-v2/blob/master/contracts/utils/Swap.sol\n address public immutable SWAP_CONTRACT;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectVaultFees(\n uint256 performanceFee,\n uint256 vaultFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _gammaController,\n address _marginPool,\n address _swapContract\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_swapContract != address(0), \"!_swapContract\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n\n WETH = _weth;\n USDC = _usdc;\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n SWAP_CONTRACT = _swapContract;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function baseInitialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) internal initializer {\n VaultLifecycleWithSwap.verifyInitializerParams(\n _owner,\n _keeper,\n _feeRecipient,\n _performanceFee,\n _managementFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_tokenName, _tokenSymbol);\n __Ownable_init();\n transferOwnership(_owner);\n\n keeper = _keeper;\n\n feeRecipient = _feeRecipient;\n performanceFee = _performanceFee;\n managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div(\n WEEKS_PER_YEAR\n );\n vaultParams = _vaultParams;\n\n uint256 assetBalance = totalBalance();\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n\n vaultState.round = 1;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n // We are dividing annualized management fee by num weeks in a year\n uint256 tmpManagementFee =\n newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n\n managementFee = tmpManagementFee;\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Deposits the `asset` from msg.sender.\n * @param amount is the amount of `asset` to deposit\n */\n function deposit(uint256 amount) external nonReentrant {\n require(amount > 0, \"!amount\");\n\n _depositFor(amount, msg.sender);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.\n * @notice Used for vault -> vault deposits on the user's behalf\n * @param amount is the amount of `asset` to deposit\n * @param creditor is the address that can claim/withdraw deposited amount\n */\n function depositFor(uint256 amount, address creditor)\n external\n nonReentrant\n {\n require(amount > 0, \"!amount\");\n require(creditor != address(0), \"!creditor\");\n\n _depositFor(amount, creditor);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n */\n function _depositFor(uint256 amount, address creditor) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount = totalBalance().add(amount);\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n\n emit Deposit(creditor, amount, currentRound);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function _initiateWithdraw(uint256 numShares) internal {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n if (\n depositReceipts[msg.sender].amount > 0 ||\n depositReceipts[msg.sender].unredeemedShares > 0\n ) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n bool withdrawalIsSameRound = withdrawal.round == currentRound;\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n uint256 existingShares = uint256(withdrawal.shares);\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return withdrawAmount the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n require(withdrawAmount > 0, \"!withdrawAmount\");\n\n transferAsset(msg.sender, withdrawAmount);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Helper function that performs most administrative tasks\n * such as minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @param currentQueuedWithdrawShares is the queued withdraw shares for the current round\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _closeRound(\n uint256 lastQueuedWithdrawAmount,\n uint256 currentQueuedWithdrawShares\n ) internal returns (uint256 lockedBalance, uint256 queuedWithdrawAmount) {\n address recipient = feeRecipient;\n uint256 mintShares;\n uint256 performanceFeeInAsset;\n uint256 totalVaultFee;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n ) = VaultLifecycleWithSwap.closeRound(\n vaultState,\n VaultLifecycleWithSwap.CloseParams(\n vaultParams.decimals,\n IERC20(vaultParams.asset).balanceOf(address(this)),\n totalSupply(),\n lastQueuedWithdrawAmount,\n performanceFee,\n managementFee,\n currentQueuedWithdrawShares\n )\n );\n\n // Finalize the pricePerShare at the end of the round\n uint256 currentRound = vaultState.round;\n roundPricePerShare[currentRound] = newPricePerShare;\n\n emit CollectVaultFees(\n performanceFeeInAsset,\n totalVaultFee,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (totalVaultFee > 0) {\n IERC20(vaultParams.asset).safeTransfer(\n payable(recipient),\n totalVaultFee\n );\n }\n\n return (lockedBalance, queuedWithdrawAmount);\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n external\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n // After calling closeRound, current option is set to none\n // We also commit the lockedAmount but do not deposit into Opyn\n // which results in double counting of asset balance and lockedAmount\n\n return\n optionState.currentOption != address(0)\n ? uint256(vaultState.lockedAmount).add(\n IERC20(vaultParams.asset).balanceOf(address(this))\n )\n : IERC20(vaultParams.asset).balanceOf(address(this));\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n\n /************************************************\n * HELPERS\n ***********************************************/\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) internal {\n address asset = vaultParams.asset;\n if (asset == WETH) {\n IWETH(WETH).withdraw(amount);\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Transfer failed\");\n return;\n }\n IERC20(asset).safeTransfer(recipient, amount);\n }\n}\n" + }, + "contracts/interfaces/IYearn.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface IYearnVault {\n function pricePerShare() external view returns (uint256);\n\n function deposit(uint256 _amount, address _recipient)\n external\n returns (uint256);\n\n function withdraw(\n uint256 _maxShares,\n address _recipient,\n uint256 _maxLoss\n ) external returns (uint256);\n\n function approve(address _recipient, uint256 _amount)\n external\n returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n}\n\ninterface IYearnRegistry {\n function latestVault(address token) external returns (address);\n}\n\ninterface IYearnPricer {\n function setExpiryPriceInOracle(uint256 _expiryTimestamp) external;\n\n function getPrice() external view returns (uint256);\n}\n" + }, + "contracts/libraries/VaultLifecycleWithSwap.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {ShareMath} from \"./ShareMath.sol\";\nimport {IStrikeSelection} from \"../interfaces/IRibbon.sol\";\nimport {GnosisAuction} from \"./GnosisAuction.sol\";\nimport {\n IOtokenFactory,\n IOtoken,\n IController,\n GammaTypes\n} from \"../interfaces/GammaInterface.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\nimport {ISwap} from \"../interfaces/ISwap.sol\";\nimport {IOptionsPurchaseQueue} from \"../interfaces/IOptionsPurchaseQueue.sol\";\nimport {SupportsNonCompliantERC20} from \"./SupportsNonCompliantERC20.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\n\nlibrary VaultLifecycleWithSwap {\n using SafeMath for uint256;\n using SupportsNonCompliantERC20 for IERC20;\n using SafeERC20 for IERC20;\n\n struct CommitParams {\n address OTOKEN_FACTORY;\n address USDC;\n address collateralAsset;\n address currentOption;\n uint256 delay;\n uint16 lastStrikeOverrideRound;\n uint256 overriddenStrikePrice;\n address strikeSelection;\n address optionsPremiumPricer;\n }\n\n /**\n * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction\n * @param commitParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @return otokenAddress is the address of the new option\n * @return strikePrice is the strike price of the new option\n * @return delta is the delta of the new option\n */\n function commitNextOption(\n CommitParams calldata commitParams,\n Vault.VaultParams storage vaultParams,\n Vault.VaultState storage vaultState\n )\n external\n returns (\n address otokenAddress,\n uint256 strikePrice,\n uint256 delta\n )\n {\n uint256 expiry = getNextExpiry(commitParams.currentOption);\n\n IStrikeSelection selection =\n IStrikeSelection(commitParams.strikeSelection);\n\n bool isPut = vaultParams.isPut;\n address underlying = vaultParams.underlying;\n\n (strikePrice, delta) = commitParams.lastStrikeOverrideRound ==\n vaultState.round\n ? (commitParams.overriddenStrikePrice, selection.delta())\n : selection.getStrikePrice(expiry, isPut);\n\n require(strikePrice != 0, \"!strikePrice\");\n\n // retrieve address if option already exists, or deploy it\n otokenAddress = getOrDeployOtoken(\n commitParams,\n vaultParams,\n underlying,\n strikePrice,\n expiry,\n isPut\n );\n\n return (otokenAddress, strikePrice, delta);\n }\n\n /**\n * @notice Verify the otoken has the correct parameters to prevent vulnerability to opyn contract changes\n * @param otokenAddress is the address of the otoken\n * @param vaultParams is the struct with vault general data\n * @param collateralAsset is the address of the collateral asset\n * @param USDC is the address of usdc\n * @param delay is the delay between commitAndClose and rollToNextOption\n */\n function verifyOtoken(\n address otokenAddress,\n Vault.VaultParams storage vaultParams,\n address collateralAsset,\n address USDC,\n uint256 delay\n ) private view {\n require(otokenAddress != address(0), \"!otokenAddress\");\n\n IOtoken otoken = IOtoken(otokenAddress);\n require(otoken.isPut() == vaultParams.isPut, \"Type mismatch\");\n require(\n otoken.underlyingAsset() == vaultParams.underlying,\n \"Wrong underlyingAsset\"\n );\n require(\n otoken.collateralAsset() == collateralAsset,\n \"Wrong collateralAsset\"\n );\n\n // we just assume all options use USDC as the strike\n require(otoken.strikeAsset() == USDC, \"strikeAsset != USDC\");\n\n uint256 readyAt = block.timestamp.add(delay);\n require(otoken.expiryTimestamp() >= readyAt, \"Expiry before delay\");\n }\n\n /**\n * @param decimals is the decimals of the asset\n * @param totalBalance is the vault's total asset balance\n * @param currentShareSupply is the supply of the shares invoked with totalSupply()\n * @param lastQueuedWithdrawAmount is the amount queued for withdrawals from last round\n * @param performanceFee is the perf fee percent to charge on premiums\n * @param managementFee is the management fee percent to charge on the AUM\n */\n struct CloseParams {\n uint256 decimals;\n uint256 totalBalance;\n uint256 currentShareSupply;\n uint256 lastQueuedWithdrawAmount;\n uint256 performanceFee;\n uint256 managementFee;\n uint256 currentQueuedWithdrawShares;\n }\n\n /**\n * @notice Calculate the shares to mint, new price per share, and\n amount of funds to re-allocate as collateral for the new round\n * @param vaultState is the storage variable vaultState passed from RibbonVault\n * @param params is the rollover parameters passed to compute the next state\n * @return newLockedAmount is the amount of funds to allocate for the new round\n * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal\n * @return newPricePerShare is the price per share of the new round\n * @return mintShares is the amount of shares to mint from deposits\n * @return performanceFeeInAsset is the performance fee charged by vault\n * @return totalVaultFee is the total amount of fee charged by vault\n */\n function closeRound(\n Vault.VaultState storage vaultState,\n CloseParams calldata params\n )\n external\n view\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares,\n uint256 performanceFeeInAsset,\n uint256 totalVaultFee\n )\n {\n uint256 currentBalance = params.totalBalance;\n uint256 pendingAmount = vaultState.totalPending;\n // Total amount of queued withdrawal shares from previous rounds (doesn't include the current round)\n uint256 lastQueuedWithdrawShares = vaultState.queuedWithdrawShares;\n\n // Deduct older queued withdraws so we don't charge fees on them\n uint256 balanceForVaultFees =\n currentBalance.sub(params.lastQueuedWithdrawAmount);\n\n {\n (performanceFeeInAsset, , totalVaultFee) = getVaultFees(\n balanceForVaultFees,\n vaultState.lastLockedAmount,\n vaultState.totalPending,\n params.performanceFee,\n params.managementFee\n );\n }\n\n // Take into account the fee\n // so we can calculate the newPricePerShare\n currentBalance = currentBalance.sub(totalVaultFee);\n\n {\n newPricePerShare = ShareMath.pricePerShare(\n params.currentShareSupply.sub(lastQueuedWithdrawShares),\n currentBalance.sub(params.lastQueuedWithdrawAmount),\n pendingAmount,\n params.decimals\n );\n\n queuedWithdrawAmount = params.lastQueuedWithdrawAmount.add(\n ShareMath.sharesToAsset(\n params.currentQueuedWithdrawShares,\n newPricePerShare,\n params.decimals\n )\n );\n\n // After closing the short, if the options expire in-the-money\n // vault pricePerShare would go down because vault's asset balance decreased.\n // This ensures that the newly-minted shares do not take on the loss.\n mintShares = ShareMath.assetToShares(\n pendingAmount,\n newPricePerShare,\n params.decimals\n );\n }\n\n return (\n currentBalance.sub(queuedWithdrawAmount), // new locked balance subtracts the queued withdrawals\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n );\n }\n\n /**\n * @notice Creates the actual Opyn short position by depositing collateral and minting otokens\n * @param gammaController is the address of the opyn controller contract\n * @param marginPool is the address of the opyn margin contract which holds the collateral\n * @param oTokenAddress is the address of the otoken to mint\n * @param depositAmount is the amount of collateral to deposit\n * @return the otoken mint amount\n */\n function createShort(\n address gammaController,\n address marginPool,\n address oTokenAddress,\n uint256 depositAmount\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n uint256 newVaultID =\n (controller.getAccountVaultCounter(address(this))).add(1);\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IOtoken oToken = IOtoken(oTokenAddress);\n address collateralAsset = oToken.collateralAsset();\n\n uint256 collateralDecimals =\n uint256(IERC20Detailed(collateralAsset).decimals());\n uint256 mintAmount;\n\n if (oToken.isPut()) {\n // For minting puts, there will be instances where the full depositAmount will not be used for minting.\n // This is because of an issue with precision.\n //\n // For ETH put options, we are calculating the mintAmount (10**8 decimals) using\n // the depositAmount (10**18 decimals), which will result in truncation of decimals when scaling down.\n // As a result, there will be tiny amounts of dust left behind in the Opyn vault when minting put otokens.\n //\n // For simplicity's sake, we do not refund the dust back to the address(this) on minting otokens.\n // We retain the dust in the vault so the calling contract can withdraw the\n // actual locked amount + dust at settlement.\n //\n // To test this behavior, we can console.log\n // MarginCalculatorInterface(0x7A48d10f372b3D7c60f6c9770B91398e4ccfd3C7).getExcessCollateral(vault)\n // to see how much dust (or excess collateral) is left behind.\n mintAmount = depositAmount\n .mul(10**Vault.OTOKEN_DECIMALS)\n .mul(10**18) // we use 10**18 to give extra precision\n .div(oToken.strikePrice().mul(10**(10 + collateralDecimals)));\n } else {\n mintAmount = depositAmount;\n\n if (collateralDecimals > 8) {\n uint256 scaleBy = 10**(collateralDecimals.sub(8)); // oTokens have 8 decimals\n if (mintAmount > scaleBy) {\n mintAmount = depositAmount.div(scaleBy); // scale down from 10**18 to 10**8\n }\n }\n }\n\n // double approve to fix non-compliant ERC20s\n IERC20 collateralToken = IERC20(collateralAsset);\n collateralToken.safeApproveNonCompliant(marginPool, depositAmount);\n\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](3);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.OpenVault,\n address(this), // owner\n address(this), // receiver\n address(0), // asset, otoken\n newVaultID, // vaultId\n 0, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.DepositCollateral,\n address(this), // owner\n address(this), // address to transfer from\n collateralAsset, // deposited asset\n newVaultID, // vaultId\n depositAmount, // amount\n 0, //index\n \"\" //data\n );\n\n actions[2] = IController.ActionArgs(\n IController.ActionType.MintShortOption,\n address(this), // owner\n address(this), // address to transfer to\n oTokenAddress, // option address\n newVaultID, // vaultId\n mintAmount, // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n return mintAmount;\n }\n\n /**\n * @notice Close the existing short otoken position. Currently this implementation is simple.\n * It closes the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time. Since calling `_closeShort` deletes vaults by\n calling SettleVault action, this assumption should hold.\n * @param gammaController is the address of the opyn controller contract\n * @return amount of collateral redeemed from the vault\n */\n function settleShort(address gammaController) external returns (uint256) {\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n // The short position has been previously closed, or all the otokens have been burned.\n // So we return early.\n if (address(collateralToken) == address(0)) {\n return 0;\n }\n\n // This is equivalent to doing IERC20(vault.asset).balanceOf(address(this))\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // If it is after expiry, we need to settle the short position using the normal way\n // Delete the vault and withdraw all remaining collateral from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.SettleVault,\n address(this), // owner\n address(this), // address to transfer to\n address(0), // not used\n vaultID, // vaultId\n 0, // not used\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Exercises the ITM option using existing long otoken position. Currently this implementation is simple.\n * It calls the `Redeem` action to claim the payout.\n * @param gammaController is the address of the opyn controller contract\n * @param oldOption is the address of the old option\n * @param asset is the address of the vault's asset\n * @return amount of asset received by exercising the option\n */\n function settleLong(\n address gammaController,\n address oldOption,\n address asset\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n\n uint256 oldOptionBalance = IERC20(oldOption).balanceOf(address(this));\n\n if (controller.getPayout(oldOption, oldOptionBalance) == 0) {\n return 0;\n }\n\n uint256 startAssetBalance = IERC20(asset).balanceOf(address(this));\n\n // If it is after expiry, we need to redeem the profits\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.Redeem,\n address(0), // not used\n address(this), // address to send profits to\n oldOption, // address of otoken\n 0, // not used\n oldOptionBalance, // otoken balance\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endAssetBalance = IERC20(asset).balanceOf(address(this));\n\n return endAssetBalance.sub(startAssetBalance);\n }\n\n /**\n * @notice Burn the remaining oTokens left over from auction. Currently this implementation is simple.\n * It burns oTokens from the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time.\n * @param gammaController is the address of the opyn controller contract\n * @param currentOption is the address of the current option\n * @return amount of collateral redeemed by burning otokens\n */\n function burnOtokens(address gammaController, address currentOption)\n external\n returns (uint256)\n {\n uint256 numOTokensToBurn =\n IERC20(currentOption).balanceOf(address(this));\n\n require(numOTokensToBurn > 0, \"No oTokens to burn\");\n\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // Burning `amount` of oTokens from the ribbon vault,\n // then withdrawing the corresponding collateral amount from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](2);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.BurnShortOption,\n address(this), // owner\n address(this), // address to transfer from\n address(vault.shortOtokens[0]), // otoken address\n vaultID, // vaultId\n numOTokensToBurn, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.WithdrawCollateral,\n address(this), // owner\n address(this), // address to transfer to\n address(collateralToken), // withdrawn asset\n vaultID, // vaultId\n vault.collateralAmounts[0].mul(numOTokensToBurn).div(\n vault.shortAmounts[0]\n ), // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Calculates the performance and management fee for this week's round\n * @param currentBalance is the balance of funds held on the vault after closing short\n * @param lastLockedAmount is the amount of funds locked from the previous round\n * @param pendingAmount is the pending deposit amount\n * @param performanceFeePercent is the performance fee pct.\n * @param managementFeePercent is the management fee pct.\n * @return performanceFeeInAsset is the performance fee\n * @return managementFeeInAsset is the management fee\n * @return vaultFee is the total fees\n */\n function getVaultFees(\n uint256 currentBalance,\n uint256 lastLockedAmount,\n uint256 pendingAmount,\n uint256 performanceFeePercent,\n uint256 managementFeePercent\n )\n internal\n pure\n returns (\n uint256 performanceFeeInAsset,\n uint256 managementFeeInAsset,\n uint256 vaultFee\n )\n {\n // At the first round, currentBalance=0, pendingAmount>0\n // so we just do not charge anything on the first round\n uint256 lockedBalanceSansPending =\n currentBalance > pendingAmount\n ? currentBalance.sub(pendingAmount)\n : 0;\n\n uint256 _performanceFeeInAsset;\n uint256 _managementFeeInAsset;\n uint256 _vaultFee;\n\n // Take performance fee and management fee ONLY if difference between\n // last week and this week's vault deposits, taking into account pending\n // deposits and withdrawals, is positive. If it is negative, last week's\n // option expired ITM past breakeven, and the vault took a loss so we\n // do not collect performance fee for last week\n if (lockedBalanceSansPending > lastLockedAmount) {\n _performanceFeeInAsset = performanceFeePercent > 0\n ? lockedBalanceSansPending\n .sub(lastLockedAmount)\n .mul(performanceFeePercent)\n .div(100 * Vault.FEE_MULTIPLIER)\n : 0;\n _managementFeeInAsset = managementFeePercent > 0\n ? lockedBalanceSansPending.mul(managementFeePercent).div(\n 100 * Vault.FEE_MULTIPLIER\n )\n : 0;\n\n _vaultFee = _performanceFeeInAsset.add(_managementFeeInAsset);\n }\n\n return (_performanceFeeInAsset, _managementFeeInAsset, _vaultFee);\n }\n\n /**\n * @notice Either retrieves the option token if it already exists, or deploy it\n * @param commitParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param underlying is the address of the underlying asset of the option\n * @param strikePrice is the strike price of the option\n * @param expiry is the expiry timestamp of the option\n * @param isPut is whether the option is a put\n * @return the address of the option\n */\n function getOrDeployOtoken(\n CommitParams calldata commitParams,\n Vault.VaultParams storage vaultParams,\n address underlying,\n uint256 strikePrice,\n uint256 expiry,\n bool isPut\n ) internal returns (address) {\n IOtokenFactory factory = IOtokenFactory(commitParams.OTOKEN_FACTORY);\n\n address otokenFromFactory =\n factory.getOtoken(\n underlying,\n commitParams.USDC,\n commitParams.collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n if (otokenFromFactory != address(0)) {\n return otokenFromFactory;\n }\n\n address otoken =\n factory.createOtoken(\n underlying,\n commitParams.USDC,\n commitParams.collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n verifyOtoken(\n otoken,\n vaultParams,\n commitParams.collateralAsset,\n commitParams.USDC,\n commitParams.delay\n );\n\n return otoken;\n }\n\n /**\n * @notice Creates an offer in the Swap Contract\n * @param currentOtoken is the current otoken address\n * @param currOtokenPremium is premium for each otoken\n * @param swapContract the address of the swap contract\n * @param vaultParams is the struct with vault general data\n * @return optionAuctionID auction id of the newly created offer\n */\n function createOffer(\n address currentOtoken,\n uint256 currOtokenPremium,\n address swapContract,\n Vault.VaultParams storage vaultParams\n ) external returns (uint256 optionAuctionID) {\n require(\n currOtokenPremium <= type(uint96).max,\n \"currentOtokenPremium > type(uint96) max value!\"\n );\n require(currOtokenPremium > 0, \"!currentOtokenPremium\");\n\n uint256 oTokenBalance = IERC20(currentOtoken).balanceOf(address(this));\n require(\n oTokenBalance <= type(uint128).max,\n \"oTokenBalance > type(uint128) max value!\"\n );\n\n // Use safeIncrease instead of safeApproval because safeApproval is only used for initial\n // approval and cannot be called again. Using safeIncrease allow us to call _createOffer\n // even when we are approving the same oTokens we have used before. This might happen if\n // we accidentally burn the oTokens before settlement.\n uint256 allowance =\n IERC20(currentOtoken).allowance(address(this), swapContract);\n\n if (allowance < oTokenBalance) {\n IERC20(currentOtoken).safeIncreaseAllowance(\n swapContract,\n oTokenBalance.sub(allowance)\n );\n }\n\n uint256 decimals = vaultParams.decimals;\n\n // If total size is larger than 1, set minimum bid as 1\n // Otherwise, set minimum bid to one tenth the total size\n uint256 minBidSize =\n oTokenBalance > 10**decimals ? 10**decimals : oTokenBalance.div(10);\n\n require(\n minBidSize <= type(uint96).max,\n \"minBidSize > type(uint96) max value!\"\n );\n\n currOtokenPremium = decimals > 18\n ? currOtokenPremium.mul(10**(decimals.sub(18)))\n : currOtokenPremium.div(10**(uint256(18).sub(decimals)));\n\n optionAuctionID = ISwap(swapContract).createOffer(\n currentOtoken,\n vaultParams.asset,\n uint96(currOtokenPremium),\n uint96(minBidSize),\n uint128(oTokenBalance)\n );\n }\n\n /**\n * @notice Allocates the vault's minted options to the OptionsPurchaseQueue contract\n * @dev Skipped if the optionsPurchaseQueue doesn't exist\n * @param optionsPurchaseQueue is the OptionsPurchaseQueue contract\n * @param option is the minted option\n * @param optionsAmount is the amount of options minted\n * @param optionAllocation is the maximum % of options to allocate towards the purchase queue (will only allocate\n * up to the amount that is on the queue)\n * @return allocatedOptions is the amount of options that ended up getting allocated to the OptionsPurchaseQueue\n */\n function allocateOptions(\n address optionsPurchaseQueue,\n address option,\n uint256 optionsAmount,\n uint256 optionAllocation\n ) external returns (uint256 allocatedOptions) {\n // Skip if optionsPurchaseQueue is address(0)\n if (optionsPurchaseQueue != address(0)) {\n allocatedOptions = optionsAmount.mul(optionAllocation).div(\n 100 * Vault.OPTION_ALLOCATION_MULTIPLIER\n );\n allocatedOptions = IOptionsPurchaseQueue(optionsPurchaseQueue)\n .getOptionsAllocation(address(this), allocatedOptions);\n\n if (allocatedOptions != 0) {\n IERC20(option).approve(optionsPurchaseQueue, allocatedOptions);\n IOptionsPurchaseQueue(optionsPurchaseQueue).allocateOptions(\n allocatedOptions\n );\n }\n }\n\n return allocatedOptions;\n }\n\n /**\n * @notice Sell the allocated options to the purchase queue post auction settlement\n * @dev Reverts if the auction hasn't settled yet\n * @param optionsPurchaseQueue is the OptionsPurchaseQueue contract\n * @param swapContract The address of the swap settlement contract\n * @return totalPremiums Total premiums earnt by the vault\n */\n function sellOptionsToQueue(\n address optionsPurchaseQueue,\n address swapContract,\n uint256 optionAuctionID\n ) external returns (uint256) {\n uint256 settlementPrice =\n getAuctionSettlementPrice(swapContract, optionAuctionID);\n require(settlementPrice != 0, \"!settlementPrice\");\n\n return\n IOptionsPurchaseQueue(optionsPurchaseQueue).sellToBuyers(\n settlementPrice\n );\n }\n\n /**\n * @notice Gets the settlement price of a settled auction\n * @param swapContract The address of the swap settlement contract\n * @param optionAuctionID is the offer ID\n * @return settlementPrice Auction settlement price\n */\n function getAuctionSettlementPrice(\n address swapContract,\n uint256 optionAuctionID\n ) public view returns (uint256) {\n return ISwap(swapContract).averagePriceForOffer(optionAuctionID);\n }\n\n /**\n * @notice Verify the constructor params satisfy requirements\n * @param owner is the owner of the vault with critical permissions\n * @param feeRecipient is the address to recieve vault performance and management fees\n * @param performanceFee is the perfomance fee pct.\n * @param tokenName is the name of the token\n * @param tokenSymbol is the symbol of the token\n * @param _vaultParams is the struct with vault general data\n */\n function verifyInitializerParams(\n address owner,\n address keeper,\n address feeRecipient,\n uint256 performanceFee,\n uint256 managementFee,\n string calldata tokenName,\n string calldata tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) external pure {\n require(owner != address(0), \"!owner\");\n require(keeper != address(0), \"!keeper\");\n require(feeRecipient != address(0), \"!feeRecipient\");\n require(\n performanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"performanceFee >= 100%\"\n );\n require(\n managementFee < 100 * Vault.FEE_MULTIPLIER,\n \"managementFee >= 100%\"\n );\n require(bytes(tokenName).length > 0, \"!tokenName\");\n require(bytes(tokenSymbol).length > 0, \"!tokenSymbol\");\n\n require(_vaultParams.asset != address(0), \"!asset\");\n require(_vaultParams.underlying != address(0), \"!underlying\");\n require(_vaultParams.minimumSupply > 0, \"!minimumSupply\");\n require(_vaultParams.cap > 0, \"!cap\");\n require(\n _vaultParams.cap > _vaultParams.minimumSupply,\n \"cap has to be higher than minimumSupply\"\n );\n }\n\n /**\n * @notice Gets the next option expiry timestamp\n * @param currentOption is the otoken address that the vault is currently writing\n */\n function getNextExpiry(address currentOption)\n internal\n view\n returns (uint256)\n {\n // uninitialized state\n if (currentOption == address(0)) {\n return getNextFriday(block.timestamp);\n }\n uint256 currentExpiry = IOtoken(currentOption).expiryTimestamp();\n\n // After options expiry if no options are written for >1 week\n // We need to give the ability continue writing options\n if (block.timestamp > currentExpiry + 7 days) {\n return getNextFriday(block.timestamp);\n }\n return getNextFriday(currentExpiry);\n }\n\n /**\n * @notice Gets the next options expiry timestamp\n * @param timestamp is the expiry timestamp of the current option\n * Reference: https://codereview.stackexchange.com/a/33532\n * Examples:\n * getNextFriday(week 1 thursday) -> week 1 friday\n * getNextFriday(week 1 friday) -> week 2 friday\n * getNextFriday(week 1 saturday) -> week 2 friday\n */\n function getNextFriday(uint256 timestamp) internal pure returns (uint256) {\n // dayOfWeek = 0 (sunday) - 6 (saturday)\n uint256 dayOfWeek = ((timestamp / 1 days) + 4) % 7;\n uint256 nextFriday = timestamp + ((7 + 5 - dayOfWeek) % 7) * 1 days;\n uint256 friday8am = nextFriday - (nextFriday % (24 hours)) + (8 hours);\n\n // If the passed timestamp is day=Friday hour>8am, we simply increment it by a week to next Friday\n if (timestamp >= friday8am) {\n friday8am += 7 days;\n }\n return friday8am;\n }\n}\n" + }, + "contracts/vaults/YearnVaults/RibbonThetaYearnVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {ISwap} from \"../../interfaces/ISwap.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../libraries/VaultLifecycleWithSwap.sol\";\nimport {ILiquidityGauge} from \"../../interfaces/ILiquidityGauge.sol\";\nimport {RibbonVault} from \"./base/RibbonVault.sol\";\nimport {\n RibbonThetaYearnVaultStorage\n} from \"../../storage/RibbonThetaYearnVaultStorage.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\n\n/**\n * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe\n * the inheritance chain closely.\n * Any changes/appends in storage variable needs to happen in RibbonThetaYearnVaultStorage.\n * RibbonThetaYearnVault should not inherit from any other contract aside from RibbonVault, RibbonThetaYearnVaultStorage\n */\ncontract RibbonThetaYearnVault is RibbonVault, RibbonThetaYearnVaultStorage {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice oTokenFactory is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event PremiumDiscountSet(\n uint256 premiumDiscount,\n uint256 newPremiumDiscount\n );\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event NewOffer(\n uint256 swapId,\n address seller,\n address oToken,\n address biddingToken,\n uint256 minPrice,\n uint256 minBidSize,\n uint256 totalSize\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _swapContract\n ) RibbonVault(_weth, _usdc, _gammaController, _marginPool, _swapContract) {\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n OTOKEN_FACTORY = _oTokenFactory;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n * @param _owner is the owner of the vault with critical permissions\n * @param _keeper is the keeper of the vault with medium permissions (weekly actions)\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n * @param _premiumDiscount is the vault's discount applied to the premium\n * @param _auctionDuration is the duration of the auction\n * @param _vaultParams is the struct with vault general data\n */\n function initialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n address _optionsPremiumPricer,\n address _strikeSelection,\n uint32 _premiumDiscount,\n uint256 _auctionDuration,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n baseInitialize(\n _owner,\n _keeper,\n _feeRecipient,\n _managementFee,\n _performanceFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n require(_optionsPremiumPricer != address(0), \"!_optionsPremiumPricer\");\n require(_strikeSelection != address(0), \"!_strikeSelection\");\n require(\n _premiumDiscount > 0 &&\n _premiumDiscount < 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"!_premiumDiscount\"\n );\n require(_auctionDuration >= MIN_AUCTION_DURATION, \"!_auctionDuration\");\n optionsPremiumPricer = _optionsPremiumPricer;\n strikeSelection = _strikeSelection;\n premiumDiscount = _premiumDiscount;\n auctionDuration = _auctionDuration;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new strike selection contract\n * @param newStrikeSelection is the address of the new strike selection contract\n */\n function setStrikeSelection(address newStrikeSelection) external onlyOwner {\n require(newStrikeSelection != address(0), \"!newStrikeSelection\");\n strikeSelection = newStrikeSelection;\n }\n\n /**\n * @notice Sets the new options premium pricer contract\n * @param newOptionsPremiumPricer is the address of the new strike selection contract\n */\n function setOptionsPremiumPricer(address newOptionsPremiumPricer)\n external\n onlyOwner\n {\n require(\n newOptionsPremiumPricer != address(0),\n \"!newOptionsPremiumPricer\"\n );\n optionsPremiumPricer = newOptionsPremiumPricer;\n }\n\n /**\n * @notice Optionality to set strike price manually\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice) external onlyOwner {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Sets the new liquidityGauge contract for this vault\n * @param newLiquidityGauge is the address of the new liquidityGauge contract\n */\n function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {\n liquidityGauge = newLiquidityGauge;\n }\n\n /**\n * @notice Sets oToken Premium\n * @param minPrice is the new oToken Premium in the units of 10**18\n */\n function setMinPrice(uint256 minPrice) external onlyKeeper {\n require(minPrice > 0, \"!minPrice\");\n currentOtokenPremium = minPrice;\n }\n\n /**\n * @notice Sets the new Vault Pauser contract for this vault\n * @param newVaultPauser is the address of the new vaultPauser contract\n */\n function setVaultPauser(address newVaultPauser) external onlyOwner {\n vaultPauser = newVaultPauser;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n transferAsset(msg.sender, amount);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n _initiateWithdraw(numShares);\n currentQueuedWithdrawShares = currentQueuedWithdrawShares.add(\n numShares\n );\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /**\n * @notice Stakes a users vault shares\n * @param numShares is the number of shares to stake\n */\n function stake(uint256 numShares) external nonReentrant {\n address _liquidityGauge = liquidityGauge;\n require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit\n require(numShares > 0);\n uint256 heldByAccount = balanceOf(msg.sender);\n if (heldByAccount < numShares) {\n _redeem(numShares.sub(heldByAccount), false);\n }\n _transfer(msg.sender, address(this), numShares);\n _approve(address(this), _liquidityGauge, numShares);\n ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);\n }\n\n /**\n * @notice Closes the existing short and calculate the shares to mint, new price per share &\n amount of funds to re-allocate as collateral for the new round\n * Since we are incrementing the round here, the options are sold in the beginning of a round\n * instead of at the end of the round. For example, at round 1, we don't sell any options. We\n * start selling options at the beginning of round 2.\n */\n function closeRound() external nonReentrant {\n address oldOption = optionState.currentOption;\n require(\n oldOption != address(0) || vaultState.round == 1,\n \"Round closed\"\n );\n _closeShort(oldOption);\n\n uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares;\n (uint256 lockedBalance, uint256 queuedWithdrawAmount) =\n _closeRound(\n uint256(lastQueuedWithdrawAmount),\n currQueuedWithdrawShares\n );\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(\n currQueuedWithdrawShares\n );\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n currentQueuedWithdrawShares = 0;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycleWithSwap.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Sets the next option the vault will be shorting\n */\n function commitNextOption() external onlyKeeper nonReentrant {\n address currentOption = optionState.currentOption;\n require(\n currentOption == address(0) && vaultState.round != 1,\n \"Round not closed\"\n );\n\n VaultLifecycleWithSwap.CommitParams memory commitParams =\n VaultLifecycleWithSwap.CommitParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n collateralAsset: vaultParams.asset,\n currentOption: currentOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n strikeSelection: strikeSelection,\n optionsPremiumPricer: optionsPremiumPricer\n });\n\n (address otokenAddress, uint256 strikePrice, uint256 delta) =\n VaultLifecycleWithSwap.commitNextOption(\n commitParams,\n vaultParams,\n vaultState\n );\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n optionState.nextOption = otokenAddress;\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position and create a new offer.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n address newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n uint256 lockedBalance = vaultState.lockedAmount;\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n VaultLifecycleWithSwap.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n _createOffer();\n }\n\n function _createOffer() private {\n address currentOtoken = optionState.currentOption;\n uint256 currOtokenPremium = currentOtokenPremium;\n\n optionAuctionID = VaultLifecycleWithSwap.createOffer(\n currentOtoken,\n currOtokenPremium,\n SWAP_CONTRACT,\n vaultParams\n );\n }\n\n /**\n * @notice Settle current offer\n */\n function settleOffer(ISwap.Bid[] calldata bids)\n external\n onlyKeeper\n nonReentrant\n {\n ISwap(SWAP_CONTRACT).settleOffer(optionAuctionID, bids);\n }\n\n /**\n * @notice Burn the remaining oTokens left over\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n uint256 unlockedAssetAmount =\n VaultLifecycleWithSwap.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n\n vaultState.lockedAmount = uint104(\n uint256(vaultState.lockedAmount).sub(unlockedAssetAmount)\n );\n }\n\n /**\n * @notice pause a user's vault position\n */\n function pausePosition() external {\n address _vaultPauserAddress = vaultPauser;\n require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit\n _redeem(0, true);\n uint256 heldByAccount = balanceOf(msg.sender);\n _approve(msg.sender, _vaultPauserAddress, heldByAccount);\n IVaultPauser(_vaultPauserAddress).pausePosition(\n msg.sender,\n heldByAccount\n );\n }\n}\n" + }, + "contracts/storage/RibbonThetaYearnVaultStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nabstract contract RibbonThetaYearnVaultStorageV1 {\n // Logic contract used to price options\n address public optionsPremiumPricer;\n // Logic contract used to select strike prices\n address public strikeSelection;\n // Premium discount on options we are selling (thousandths place: 000 - 999)\n uint256 public premiumDiscount;\n // Current oToken premium\n uint256 public currentOtokenPremium;\n // Last round id at which the strike was manually overridden\n uint16 public lastStrikeOverrideRound;\n // Price last overridden strike set to\n uint256 public overriddenStrikePrice;\n // Auction duration\n uint256 public auctionDuration;\n // Auction id of current option\n uint256 public optionAuctionID;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV2 {\n // Amount locked for scheduled withdrawals last week;\n uint256 public lastQueuedWithdrawAmount;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV3 {\n // LiquidityGauge contract for the vault\n address public liquidityGauge;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV4 {\n // OptionsPurchaseQueue contract for selling options\n address public optionsPurchaseQueue;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV5 {\n // Queued withdraw shares for the current round\n uint256 public currentQueuedWithdrawShares;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV6 {\n // Vault Pauser Contract for the vault\n address public vaultPauser;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV7 {\n // Check if yearn is used in vault\n bool public isYearnPaused;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of RibbonThetaVaultStorage\n// e.g. RibbonThetaVaultStorage, so finally it would look like\n// contract RibbonThetaVaultStorage is RibbonThetaVaultStorageV1, RibbonThetaVaultStorageV2\nabstract contract RibbonThetaYearnVaultStorage is\n RibbonThetaYearnVaultStorageV1,\n RibbonThetaYearnVaultStorageV2,\n RibbonThetaYearnVaultStorageV3,\n RibbonThetaYearnVaultStorageV4,\n RibbonThetaYearnVaultStorageV5,\n RibbonThetaYearnVaultStorageV6,\n RibbonThetaYearnVaultStorageV7\n{\n\n}\n" + }, + "contracts/vaults/VaultPauser/RibbonVaultPauser.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {IRibbonThetaVault} from \"../../interfaces/IRibbonThetaVault.sol\";\nimport {IWETH} from \"../../interfaces/IWETH.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\n\ncontract RibbonVaultPauser is Ownable, IVaultPauser {\n using SafeERC20 for IERC20;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores all the vault's paused positions\n struct PauseReceipt {\n uint16 round;\n uint128 shares;\n }\n\n mapping(address => mapping(address => PauseReceipt)) public pausedPositions;\n mapping(address => bool) private registeredVaults;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n address public immutable STETH;\n address public immutable STETH_VAULT;\n\n address public keeper;\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Pause(\n address indexed account,\n address indexed vaultAddress,\n uint256 share,\n uint256 round\n );\n\n event Resume(\n address indexed account,\n address indexed vaultAddress,\n uint256 withdrawAmount\n );\n\n event ProcessWithdrawal(address indexed vaultAddress, uint256 round);\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n */\n constructor(\n address _keeper,\n address _weth,\n address _steth,\n address _steth_vault\n ) {\n require(_keeper != address(0), \"!_keeper\");\n require(_weth != address(0), \"!_weth\");\n require(_steth != address(0), \"!_steth\");\n require(_steth_vault != address(0), \"!_steth_vault\");\n\n keeper = _keeper;\n WETH = _weth;\n STETH = _steth;\n STETH_VAULT = _steth_vault;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n function getPausePosition(address _vaultAddress, address _userAddress)\n external\n view\n returns (PauseReceipt memory)\n {\n return pausedPositions[_vaultAddress][_userAddress];\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param _newKeeper is the address of the new keeper\n */\n function setNewKeeper(address _newKeeper) external onlyOwner {\n require(_newKeeper != address(0), \"!newKeeper\");\n keeper = _newKeeper;\n }\n\n /**\n * @notice add vault into registered vaults\n * @param _vaultAddress is the address of the new vault to be registered\n */\n function addVault(address _vaultAddress) external onlyOwner {\n registeredVaults[_vaultAddress] = true;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice pause position from vault by redeem all the shares from vault to Pauser\n * @param _account user's address\n * @param _amount the amount of shares\n */\n function pausePosition(address _account, uint256 _amount)\n external\n override\n {\n address currentVaultAddress = msg.sender;\n IRibbonThetaVault currentVault = IRibbonThetaVault(currentVaultAddress);\n\n // check if vault is registered\n require(\n registeredVaults[currentVaultAddress],\n \"Vault is not registered\"\n );\n\n PauseReceipt storage pausedPosition =\n pausedPositions[currentVaultAddress][_account];\n\n // check if position is paused\n require(\n pausedPosition.shares == 0 && pausedPosition.round == 0,\n \"Position is paused\"\n );\n\n uint16 round = currentVault.vaultState().round;\n\n require(_amount < type(uint128).max, \"_amount overflow\");\n\n pausedPositions[currentVaultAddress][_account] = PauseReceipt({\n round: round,\n shares: uint128(_amount)\n });\n\n emit Pause(_account, currentVaultAddress, _amount, round);\n\n // transfer from user to pauser\n IERC20(currentVaultAddress).safeTransferFrom(\n _account,\n address(this),\n _amount\n );\n\n currentVault.initiateWithdraw(_amount);\n }\n\n /**\n * @notice resume user's position into vault by making a deposit\n * @param _vaultAddress vault's address\n */\n function resumePosition(address _vaultAddress) external override {\n IRibbonThetaVault currentVault = IRibbonThetaVault(_vaultAddress);\n\n // check if vault is registered\n require(registeredVaults[_vaultAddress], \"Vault is not registered\");\n\n // get params and round\n Vault.VaultParams memory currentParams = currentVault.vaultParams();\n uint256 round = currentVault.vaultState().round;\n\n PauseReceipt storage pauseReceipt =\n pausedPositions[_vaultAddress][msg.sender];\n uint256 pauseReceiptRound = pauseReceipt.round;\n\n // check if roun is closed before resuming position\n require(pauseReceiptRound < round, \"Round not closed yet\");\n uint256 totalWithdrawAmount =\n ShareMath.sharesToAsset(\n pauseReceipt.shares,\n currentVault.roundPricePerShare(pauseReceiptRound),\n currentParams.decimals\n );\n\n // delete position once transfer (revert to zero)\n delete pausedPositions[_vaultAddress][msg.sender];\n\n // stETH transfers suffer from an off-by-1 error\n // since we received STETH , we shall deposit using STETH instead of ETH\n if (_vaultAddress == STETH_VAULT) {\n totalWithdrawAmount = totalWithdrawAmount - 3;\n\n emit Resume(msg.sender, _vaultAddress, totalWithdrawAmount - 1);\n IERC20(STETH).safeApprove(_vaultAddress, totalWithdrawAmount);\n currentVault.depositYieldTokenFor(totalWithdrawAmount, msg.sender);\n } else {\n emit Resume(msg.sender, _vaultAddress, totalWithdrawAmount);\n\n // if asset is ETH, we will convert it into WETH before depositing\n if (currentParams.asset == WETH) {\n IWETH(WETH).deposit{value: totalWithdrawAmount}();\n }\n IERC20(currentParams.asset).safeApprove(\n _vaultAddress,\n totalWithdrawAmount\n );\n\n currentVault.depositFor(totalWithdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice process withdrawals by completing in a batch\n * @param _vaultAddress vault's address to be processed\n */\n function processWithdrawal(address _vaultAddress) external onlyKeeper {\n IRibbonThetaVault currentVault = IRibbonThetaVault(_vaultAddress);\n // we can only process withdrawal after closing the previous round\n // hence round should be - 1\n emit ProcessWithdrawal(\n _vaultAddress,\n currentVault.vaultState().round - 1\n );\n currentVault.completeWithdraw();\n }\n\n fallback() external payable {}\n\n receive() external payable {}\n}\n" + }, + "@openzeppelin/contracts/access/Ownable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _setOwner(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _setOwner(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _setOwner(newOwner);\n }\n\n function _setOwner(address newOwner) private {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "contracts/vaults/STETHVault/RibbonThetaSTETHVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport {ISwap} from \"../../interfaces/ISwap.sol\";\nimport {\n RibbonThetaSTETHVaultStorage\n} from \"../../storage/RibbonThetaSTETHVaultStorage.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../libraries/VaultLifecycleWithSwap.sol\";\nimport {VaultLifecycleSTETH} from \"../../libraries/VaultLifecycleSTETH.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {ILiquidityGauge} from \"../../interfaces/ILiquidityGauge.sol\";\nimport {RibbonVault} from \"./base/RibbonVault.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\nimport {IWSTETH} from \"../../interfaces/ISTETH.sol\";\n\n/**\n * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe\n * the inheritance chain closely.\n * Any changes/appends in storage variable needs to happen in RibbonThetaSTETHVaultStorage.\n * RibbonThetaSTETHVault should not inherit from any other contract aside from RibbonVault, RibbonThetaSTETHVaultStorage\n */\ncontract RibbonThetaSTETHVault is RibbonVault, RibbonThetaSTETHVaultStorage {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice oTokenFactory is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event NewOffer(\n uint256 swapId,\n address seller,\n address oToken,\n address biddingToken,\n uint256 minPrice,\n uint256 minBidSize,\n uint256 totalSize\n );\n\n /************************************************\n * STRUCTS\n ***********************************************/\n\n /**\n * @notice Initialization parameters for the vault.\n * @param _owner is the owner of the vault with critical permissions\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n */\n struct InitParams {\n address _owner;\n address _keeper;\n address _feeRecipient;\n uint256 _managementFee;\n uint256 _performanceFee;\n string _tokenName;\n string _tokenSymbol;\n address _optionsPremiumPricer;\n address _strikeSelection;\n }\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _wsteth is the LDO contract\n * @param _ldo is the LDO contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _wsteth,\n address _ldo,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _swapContract\n )\n RibbonVault(\n _weth,\n _usdc,\n _wsteth,\n _ldo,\n _gammaController,\n _marginPool,\n _swapContract\n )\n {\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n OTOKEN_FACTORY = _oTokenFactory;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n * @param _initParams is the struct with vault initialization parameters\n * @param _vaultParams is the struct with vault general data\n */\n function initialize(\n InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n baseInitialize(\n _initParams._owner,\n _initParams._keeper,\n _initParams._feeRecipient,\n _initParams._managementFee,\n _initParams._performanceFee,\n _initParams._tokenName,\n _initParams._tokenSymbol,\n _vaultParams\n );\n require(\n _initParams._optionsPremiumPricer != address(0),\n \"!_optionsPremiumPricer\"\n );\n require(\n _initParams._strikeSelection != address(0),\n \"!_strikeSelection\"\n );\n\n optionsPremiumPricer = _initParams._optionsPremiumPricer;\n strikeSelection = _initParams._strikeSelection;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new strike selection or options premium pricer contract\n * @param newContract is the address of the new strike selection or options premium pricer contract\n * @param isStrikeSelection is whether we are setting the strike selection contract\n */\n function setStrikeSelectionOrPricer(\n address newContract,\n bool isStrikeSelection\n ) external onlyOwner {\n require(newContract != address(0), \"!newContract\");\n if (isStrikeSelection) {\n strikeSelection = newContract;\n } else {\n optionsPremiumPricer = newContract;\n }\n }\n\n /**\n * @notice Optionality to set strike price manually\n * Should be called after closeRound if we are setting current week's strike\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice) external onlyOwner {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Sets the new liquidityGauge contract for this vault\n * @param newLiquidityGauge is the address of the new liquidityGauge contract\n */\n function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {\n liquidityGauge = newLiquidityGauge;\n }\n\n /**\n * @notice Sets oToken Premium\n * @param minPrice is the new oToken Premium in the units of 10**18\n */\n function setMinPrice(uint256 minPrice) external onlyKeeper {\n require(minPrice > 0, \"!minPrice\");\n currentOtokenPremium = minPrice;\n }\n\n /**\n * @notice Sets the new Vault Pauser contract for this vault\n * @param newVaultPauser is the address of the new vaultPauser contract\n */\n function setVaultPauser(address newVaultPauser) external onlyOwner {\n vaultPauser = newVaultPauser;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount, uint256) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n IERC20(STETH).safeTransfer(\n msg.sender,\n VaultLifecycleSTETH.withdrawStEth(\n STETH,\n address(collateralToken),\n amount\n )\n );\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n _initiateWithdraw(numShares);\n currentQueuedWithdrawShares = currentQueuedWithdrawShares.add(\n numShares\n );\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /**\n * @notice Stakes a users vault shares\n * @param numShares is the number of shares to stake\n */\n function stake(uint256 numShares) external nonReentrant {\n address _liquidityGauge = liquidityGauge;\n require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit\n require(numShares > 0);\n uint256 heldByAccount = balanceOf(msg.sender);\n if (heldByAccount < numShares) {\n _redeem(numShares.sub(heldByAccount), false);\n }\n _transfer(msg.sender, address(this), numShares);\n _approve(address(this), _liquidityGauge, numShares);\n ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);\n }\n\n /**\n * @notice Closes the existing short and calculate the shares to mint, new price per share &\n amount of funds to re-allocate as collateral for the new round\n * Since we are incrementing the round here, the options are sold in the beginning of a round\n * instead of at the end of the round. For example, at round 1, we don't sell any options. We\n * start selling options at the beginning of round 2.\n */\n function closeRound() external nonReentrant {\n address oldOption = optionState.currentOption;\n require(\n oldOption != address(0) || vaultState.round == 1,\n \"Round closed\"\n );\n _closeShort(oldOption);\n\n uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares;\n (uint256 lockedBalance, uint256 queuedWithdrawAmount) =\n _closeRound(\n uint256(lastQueuedWithdrawAmount),\n currQueuedWithdrawShares\n );\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(\n currQueuedWithdrawShares\n );\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n currentQueuedWithdrawShares = 0;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycleWithSwap.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Sets the next option the vault will be shorting\n */\n function commitNextOption() external onlyKeeper nonReentrant {\n address currentOption = optionState.currentOption;\n require(\n currentOption == address(0) && vaultState.round != 1,\n \"Round not closed\"\n );\n\n VaultLifecycleWithSwap.CommitParams memory commitParams =\n VaultLifecycleWithSwap.CommitParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n collateralAsset: address(collateralToken),\n currentOption: currentOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n strikeSelection: strikeSelection,\n optionsPremiumPricer: optionsPremiumPricer\n });\n\n (address otokenAddress, uint256 strikePrice, uint256 delta) =\n VaultLifecycleWithSwap.commitNextOption(\n commitParams,\n vaultParams,\n vaultState\n );\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n optionState.nextOption = otokenAddress;\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position and create a new offer.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n address newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n // Locked balance denominated in `collateralToken`\n uint256 lockedBalance =\n collateralToken.balanceOf(address(this)).sub(\n collateralToken.getWstETHByStETH(lastQueuedWithdrawAmount)\n );\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n VaultLifecycleWithSwap.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n _createOffer();\n }\n\n function _createOffer() private {\n address currentOtoken = optionState.currentOption;\n uint256 currOtokenPremium = currentOtokenPremium;\n\n optionAuctionID = VaultLifecycleWithSwap.createOffer(\n currentOtoken,\n currOtokenPremium,\n SWAP_CONTRACT,\n vaultParams\n );\n }\n\n /**\n * @notice Settle current offer\n */\n function settleOffer(ISwap.Bid[] calldata bids)\n external\n onlyKeeper\n nonReentrant\n {\n ISwap(SWAP_CONTRACT).settleOffer(optionAuctionID, bids);\n }\n\n /**\n * @notice Burn the remaining oTokens left over\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n uint256 unlockedAssetAmount =\n VaultLifecycleWithSwap.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n\n vaultState.lockedAmount = uint104(\n uint256(vaultState.lockedAmount).sub(\n collateralToken.getStETHByWstETH(unlockedAssetAmount)\n )\n );\n\n // Wrap entire `asset` balance to `collateralToken` balance\n VaultLifecycleSTETH.wrapToYieldToken(\n WETH,\n address(collateralToken),\n STETH\n );\n }\n\n /**\n * @notice pause a user's vault position\n */\n function pausePosition() external {\n address _vaultPauserAddress = vaultPauser;\n require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit\n _redeem(0, true);\n uint256 heldByAccount = balanceOf(msg.sender);\n _approve(msg.sender, _vaultPauserAddress, heldByAccount);\n IVaultPauser(_vaultPauserAddress).pausePosition(\n msg.sender,\n heldByAccount\n );\n }\n}\n" + }, + "contracts/storage/RibbonThetaSTETHVaultStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nabstract contract RibbonThetaSTETHVaultStorageV1 {\n // Logic contract used to price options\n address public optionsPremiumPricer;\n // Logic contract used to select strike prices\n address public strikeSelection;\n // Premium discount on options we are selling (thousandths place: 000 - 999)\n uint256 public premiumDiscount;\n // Current oToken premium\n uint256 public currentOtokenPremium;\n // Last round id at which the strike was manually overridden\n uint16 public lastStrikeOverrideRound;\n // Price last overridden strike set to\n uint256 public overriddenStrikePrice;\n // Auction duration\n uint256 public auctionDuration;\n // Auction id of current option\n uint256 public optionAuctionID;\n}\n\nabstract contract RibbonThetaSTETHVaultStorageV2 {\n // Amount locked for scheduled withdrawals last week;\n uint256 public lastQueuedWithdrawAmount;\n}\n\nabstract contract RibbonThetaSTETHVaultStorageV3 {\n // LiquidityGauge contract for the vault\n address public liquidityGauge;\n}\n\nabstract contract RibbonThetaSTETHVaultStorageV4 {\n // Queued withdraw shares for the current round\n uint256 public currentQueuedWithdrawShares;\n}\n\nabstract contract RibbonThetaSTETHVaultStorageV5 {\n // Vault Pauser Contract for the vault\n address public vaultPauser;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of RibbonThetaVaultStorage\n// e.g. RibbonThetaVaultStorage, so finally it would look like\n// contract RibbonThetaVaultStorage is RibbonThetaVaultStorageV1, RibbonThetaVaultStorageV2\nabstract contract RibbonThetaSTETHVaultStorage is\n RibbonThetaSTETHVaultStorageV1,\n RibbonThetaSTETHVaultStorageV2,\n RibbonThetaSTETHVaultStorageV3,\n RibbonThetaSTETHVaultStorageV4,\n RibbonThetaSTETHVaultStorageV5\n{\n\n}\n" + }, + "contracts/libraries/VaultLifecycleSTETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {DSMath} from \"../vendor/DSMath.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {VaultLifecycle} from \"./VaultLifecycle.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {ShareMath} from \"./ShareMath.sol\";\nimport {ISTETH, IWSTETH} from \"../interfaces/ISTETH.sol\";\nimport {IWETH} from \"../interfaces/IWETH.sol\";\nimport {ICRV} from \"../interfaces/ICRV.sol\";\nimport {IStrikeSelection} from \"../interfaces/IRibbon.sol\";\nimport {\n IOtokenFactory,\n IOtoken,\n IController,\n GammaTypes\n} from \"../interfaces/GammaInterface.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\n\nlibrary VaultLifecycleSTETH {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n /**\n * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @param collateralAsset is the address of the collateral asset\n * @return otokenAddress is the address of the new option\n * @return strikePrice is the strike price of the new option\n * @return delta is the delta of the new option\n */\n function commitAndClose(\n VaultLifecycle.CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n Vault.VaultState storage vaultState,\n address collateralAsset\n )\n external\n returns (\n address otokenAddress,\n uint256 strikePrice,\n uint256 delta\n )\n {\n uint256 expiry =\n VaultLifecycle.getNextExpiry(closeParams.currentOption);\n\n IStrikeSelection selection =\n IStrikeSelection(closeParams.strikeSelection);\n\n // calculate strike and delta\n (strikePrice, delta) = closeParams.lastStrikeOverrideRound ==\n vaultState.round\n ? (closeParams.overriddenStrikePrice, selection.delta())\n : selection.getStrikePrice(expiry, false);\n\n require(strikePrice != 0, \"!strikePrice\");\n\n // retrieve address if option already exists, or deploy it\n otokenAddress = VaultLifecycle.getOrDeployOtoken(\n closeParams,\n vaultParams,\n vaultParams.underlying,\n collateralAsset,\n strikePrice,\n expiry,\n false\n );\n\n return (otokenAddress, strikePrice, delta);\n }\n\n /**\n * @notice Calculate the shares to mint, new price per share, and\n amount of funds to re-allocate as collateral for the new round\n * @param currentShareSupply is the total supply of shares\n * @param currentBalance is the total balance of the vault\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @return newLockedAmount is the amount of funds to allocate for the new round\n * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal\n * @return newPricePerShare is the price per share of the new round\n * @return mintShares is the amount of shares to mint from deposits\n */\n function rollover(\n uint256 currentShareSupply,\n uint256 currentBalance,\n Vault.VaultParams calldata vaultParams,\n Vault.VaultState calldata vaultState\n )\n external\n pure\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares\n )\n {\n uint256 pendingAmount = uint256(vaultState.totalPending);\n uint256 _decimals = vaultParams.decimals;\n\n newPricePerShare = ShareMath.pricePerShare(\n currentShareSupply,\n currentBalance,\n pendingAmount,\n _decimals\n );\n\n // After closing the short, if the options expire in-the-money\n // vault pricePerShare would go down because vault's asset balance decreased.\n // This ensures that the newly-minted shares do not take on the loss.\n uint256 _mintShares =\n ShareMath.assetToShares(pendingAmount, newPricePerShare, _decimals);\n\n uint256 newSupply = currentShareSupply.add(_mintShares);\n uint256 queuedAmount =\n newSupply > 0\n ? ShareMath.sharesToAsset(\n vaultState.queuedWithdrawShares,\n newPricePerShare,\n _decimals\n )\n : 0;\n\n return (\n currentBalance.sub(queuedAmount),\n queuedAmount,\n newPricePerShare,\n _mintShares\n );\n }\n\n /**\n * @notice Creates the actual Opyn short position by depositing collateral and minting otokens\n * @param gammaController is the address of the opyn controller contract\n * @param marginPool is the address of the opyn margin contract which holds the collateral\n * @param oTokenAddress is the address of the otoken to mint\n * @param depositAmount is the amount of collateral to deposit\n * @return the otoken mint amount\n */\n function createShort(\n address gammaController,\n address marginPool,\n address oTokenAddress,\n uint256 depositAmount\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n uint256 newVaultID =\n (controller.getAccountVaultCounter(address(this))).add(1);\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IOtoken oToken = IOtoken(oTokenAddress);\n address collateralAsset = oToken.collateralAsset();\n\n uint256 collateralDecimals =\n uint256(IERC20Detailed(collateralAsset).decimals());\n uint256 mintAmount;\n\n mintAmount = depositAmount;\n if (collateralDecimals > 8) {\n uint256 scaleBy = 10**(collateralDecimals.sub(8)); // oTokens have 8 decimals\n if (mintAmount > scaleBy) {\n mintAmount = depositAmount.div(scaleBy); // scale down from 10**18 to 10**8\n }\n }\n\n IERC20 collateralToken = IERC20(collateralAsset);\n collateralToken.safeApprove(marginPool, depositAmount);\n\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](3);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.OpenVault,\n address(this), // owner\n address(this), // receiver\n address(0), // asset, otoken\n newVaultID, // vaultId\n 0, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.DepositCollateral,\n address(this), // owner\n address(this), // address to transfer from\n collateralAsset, // deposited asset\n newVaultID, // vaultId\n depositAmount, // amount\n 0, //index\n \"\" //data\n );\n\n actions[2] = IController.ActionArgs(\n IController.ActionType.MintShortOption,\n address(this), // owner\n address(this), // address to transfer to\n oTokenAddress, // option address\n newVaultID, // vaultId\n mintAmount, // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n return mintAmount;\n }\n\n /**\n * @notice Withdraws stETH + WETH (if necessary) from vault using vault shares\n * @param collateralToken is the address of the collateral token\n * @param weth is the WETH address\n * @param recipient is the recipient\n * @param amount is the withdraw amount in `asset`\n * @return withdrawAmount is the withdraw amount in `collateralToken`\n */\n function withdrawYieldAndBaseToken(\n address collateralToken,\n address weth,\n address recipient,\n uint256 amount\n ) external returns (uint256) {\n IWSTETH collateral = IWSTETH(collateralToken);\n\n uint256 withdrawAmount = collateral.getWstETHByStETH(amount);\n\n uint256 yieldTokenBalance =\n withdrawYieldToken(collateralToken, recipient, withdrawAmount);\n\n // If there is not enough wstETH in the vault, it withdraws as much as possible and\n // transfers the rest in `asset`\n if (withdrawAmount > yieldTokenBalance) {\n withdrawBaseToken(\n collateralToken,\n weth,\n recipient,\n withdrawAmount,\n yieldTokenBalance\n );\n }\n\n return withdrawAmount;\n }\n\n /**\n * @notice Withdraws stETH from vault\n * @param collateralToken is the address of the collateral token\n * @param recipient is the recipient\n * @param withdrawAmount is the withdraw amount in terms of yearn tokens\n * @return yieldTokenBalance is the balance of the yield token\n */\n function withdrawYieldToken(\n address collateralToken,\n address recipient,\n uint256 withdrawAmount\n ) internal returns (uint256) {\n IERC20 collateral = IERC20(collateralToken);\n\n uint256 yieldTokenBalance = collateral.balanceOf(address(this));\n uint256 yieldTokensToWithdraw =\n DSMath.min(yieldTokenBalance, withdrawAmount);\n if (yieldTokensToWithdraw > 0) {\n collateral.safeTransfer(recipient, yieldTokensToWithdraw);\n }\n\n return yieldTokenBalance;\n }\n\n /**\n * @notice Withdraws `asset` from vault\n * @param collateralToken is the address of the collateral token\n * @param weth is the WETH address\n * @param recipient is the recipient\n * @param withdrawAmount is the withdraw amount in terms of yearn tokens\n * @param yieldTokenBalance is the collateral token (stETH) balance of the vault\n */\n function withdrawBaseToken(\n address collateralToken,\n address weth,\n address recipient,\n uint256 withdrawAmount,\n uint256 yieldTokenBalance\n ) internal {\n uint256 underlyingTokensToWithdraw =\n IWSTETH(collateralToken).getStETHByWstETH(\n withdrawAmount.sub(yieldTokenBalance)\n );\n\n IWETH(weth).deposit{value: underlyingTokensToWithdraw}();\n IERC20(weth).safeTransfer(recipient, underlyingTokensToWithdraw);\n }\n\n /**\n * @notice Unwraps the necessary amount of the wstETH token\n * and transfers ETH amount to vault\n * @param amount is the amount of ETH to withdraw\n * @param wstEth is the address of wstETH\n * @param stethToken is the address of stETH\n * @param crvPool is the address of the steth <-> eth pool on curve\n * @param minETHOut is the minimum eth amount to receive from the swap\n * @return amountETHOut is the amount of eth unwrapped\n available for the withdrawal (may incur curve slippage)\n */\n function unwrapYieldToken(\n uint256 amount,\n address wstEth,\n address stethToken,\n address crvPool,\n uint256 minETHOut\n ) external returns (uint256) {\n require(\n amount >= minETHOut,\n \"Amount withdrawn smaller than minETHOut from swap\"\n );\n require(\n minETHOut.mul(10**18).div(amount) >= 0.95 ether,\n \"Slippage on minETHOut too high\"\n );\n\n uint256 ethBalance = address(this).balance;\n IERC20 steth = IERC20(stethToken);\n uint256 stethBalance = steth.balanceOf(address(this));\n\n // 3 different success scenarios\n // Scenario 1. We hold enough ETH to satisfy withdrawal. Send it out directly\n // Scenario 2. We hold enough wstETH to satisy withdrawal. Unwrap then swap\n // Scenario 3. We hold enough ETH + stETH to satisfy withdrawal. Do a swap\n\n // Scenario 1\n if (ethBalance >= amount) {\n return amount;\n }\n\n // Scenario 2\n stethBalance = unwrapWstethForWithdrawal(\n wstEth,\n steth,\n ethBalance,\n stethBalance,\n amount,\n minETHOut\n );\n\n // Scenario 3\n // Now that we satisfied the ETH + stETH sum, we swap the stETH amounts necessary\n // to facilitate a withdrawal\n\n // This won't underflow since we already asserted that ethBalance < amount before this\n uint256 stEthAmountToSwap =\n DSMath.min(amount.sub(ethBalance), stethBalance);\n\n uint256 ethAmountOutFromSwap =\n swapStEthToEth(steth, crvPool, stEthAmountToSwap);\n\n uint256 totalETHOut = ethBalance.add(ethAmountOutFromSwap);\n\n // Since minETHOut is derived from calling the Curve pool's getter,\n // it reverts in the worst case where the user needs to unwrap and sell\n // 100% of their ETH withdrawal amount\n require(\n totalETHOut >= minETHOut,\n \"Output ETH amount smaller than minETHOut\"\n );\n\n return totalETHOut;\n }\n\n /**\n * @notice Unwraps the required amount of wstETH to a target ETH amount\n * @param wstEthAddress is the address for wstETH\n * @param steth is the ERC20 of stETH\n * @param startStEthBalance is the starting stETH balance used to determine how much more to unwrap\n * @param ethAmount is the ETH amount needed for the contract\n * @param minETHOut is the ETH amount but adjusted for slippage\n * @return the new stETH balance\n */\n function unwrapWstethForWithdrawal(\n address wstEthAddress,\n IERC20 steth,\n uint256 ethBalance,\n uint256 startStEthBalance,\n uint256 ethAmount,\n uint256 minETHOut\n ) internal returns (uint256) {\n uint256 ethstEthSum = ethBalance.add(startStEthBalance);\n\n if (ethstEthSum < minETHOut) {\n uint256 stethNeededFromUnwrap = ethAmount.sub(ethstEthSum);\n IWSTETH wstEth = IWSTETH(wstEthAddress);\n uint256 wstAmountToUnwrap =\n wstEth.getWstETHByStETH(stethNeededFromUnwrap);\n\n wstEth.unwrap(wstAmountToUnwrap);\n\n uint256 newStEthBalance = steth.balanceOf(address(this));\n require(\n ethBalance.add(newStEthBalance) >= minETHOut,\n \"Unwrapping wstETH did not return sufficient stETH\"\n );\n return newStEthBalance;\n }\n return startStEthBalance;\n }\n\n /**\n * @notice Swaps from stEth to ETH on the Lido Curve pool\n * @param steth is the address for the Lido staked ether\n * @param crvPool is the Curve pool address to do the swap\n * @param stEthAmount is the stEth amount to be swapped to Ether\n * @return ethAmountOutFromSwap is the returned ETH amount from swap\n */\n function swapStEthToEth(\n IERC20 steth,\n address crvPool,\n uint256 stEthAmount\n ) internal returns (uint256) {\n steth.safeApprove(crvPool, stEthAmount);\n\n // CRV SWAP HERE from steth -> eth\n // 0 = ETH, 1 = STETH\n // We are setting 1, which is the smallest possible value for the _minAmountOut parameter\n // However it is fine because we check that the totalETHOut >= minETHOut at the end\n // which makes sandwich attacks not possible\n uint256 ethAmountOutFromSwap =\n ICRV(crvPool).exchange(1, 0, stEthAmount, 1);\n\n return ethAmountOutFromSwap;\n }\n\n /**\n * @notice Wraps the necessary amount of the base token to the yield-bearing yearn token\n * @param weth is the address of weth\n * @param collateralToken is the address of the collateral token\n */\n function wrapToYieldToken(\n address weth,\n address collateralToken,\n address steth\n ) external {\n // Unwrap all weth premiums transferred to contract\n IWETH wethToken = IWETH(weth);\n uint256 wethBalance = wethToken.balanceOf(address(this));\n\n if (wethBalance > 0) {\n wethToken.withdraw(wethBalance);\n }\n\n uint256 ethBalance = address(this).balance;\n\n IWSTETH collateral = IWSTETH(collateralToken);\n IERC20 stethToken = IERC20(steth);\n\n if (ethBalance > 0) {\n // Send eth to Lido, recieve steth\n ISTETH(steth).submit{value: ethBalance}(address(this));\n }\n\n // Get all steth in contract\n uint256 stethBalance = stethToken.balanceOf(address(this));\n\n if (stethBalance > 0) {\n // approve wrap\n stethToken.safeApprove(collateralToken, stethBalance.add(1));\n // Wrap to wstETH - need to add 1 to steth balance as it is innacurate\n collateral.wrap(stethBalance.add(1));\n }\n }\n\n /**\n * @notice Gets stETH for direct stETH withdrawals, converts wstETH/ETH to stETH if not enough stETH\n * @param steth is the address of steth\n * @param wstEth is the address of wsteth\n * @param amount is the amount to withdraw\n * @return amount of stETH to transfer to the user, this is to account for rounding errors when unwrapping wstETH\n */\n function withdrawStEth(\n address steth,\n address wstEth,\n uint256 amount\n ) external returns (uint256) {\n // 3 different scenarios for withdrawing stETH directly\n // Scenario 1. We hold enough stETH to satisfy withdrawal. Send it out directly\n // Scenario 2. We hold enough stETH + wstETH to satisy withdrawal. Unwrap wstETH then send it\n // Scenario 3. We hold enough stETH + wstETH + ETH satisfy withdrawal. Unwrap wstETH, wrap ETH then send it\n uint256 _amount = amount;\n uint256 stethBalance = IERC20(steth).balanceOf(address(this));\n if (stethBalance >= amount) {\n // Can send out the stETH directly\n return amount; // We return here if we have enough stETH to satisfy the withdrawal\n } else {\n // If amount > stethBalance, send out the entire stethBalance and check wstETH and ETH\n amount = amount.sub(stethBalance);\n }\n uint256 wstethBalance = IWSTETH(wstEth).balanceOf(address(this));\n uint256 totalShares = ISTETH(steth).getTotalShares();\n uint256 totalPooledEther = ISTETH(steth).getTotalPooledEther();\n stethBalance = wstethBalance.mul(totalPooledEther).div(totalShares);\n if (stethBalance >= amount) {\n wstethBalance = amount.mul(totalShares).div(totalPooledEther);\n // Avoids reverting if unwrap amount is 0\n if (wstethBalance > 0) {\n // Unwraps wstETH and sends out the received stETH directly\n IWSTETH(wstEth).unwrap(wstethBalance);\n // Accounts for rounding errors when unwrapping wstETH, this is safe because this function would've\n // returned already if the stETH balance was greater than our withdrawal amount\n return IERC20(steth).balanceOf(address(this)); // We return here if we have enough stETH + wstETH\n }\n } else if (stethBalance > 0) {\n stethBalance = IERC20(steth).balanceOf(address(this));\n IWSTETH(wstEth).unwrap(wstethBalance);\n // Accounts for rounding errors when unwrapping wstETH\n amount = amount.sub(\n IERC20(steth).balanceOf(address(this)).sub(stethBalance)\n );\n }\n // Wrap ETH to stETH if we don't have enough stETH + wstETH\n uint256 ethBalance = address(this).balance;\n if (amount > 0 && ethBalance >= amount) {\n ISTETH(steth).submit{value: amount}(address(this));\n } else if (ethBalance > 0) {\n ISTETH(steth).submit{value: ethBalance}(address(this));\n }\n stethBalance = IERC20(steth).balanceOf(address(this));\n // Accounts for rounding errors by a margin of 3 wei\n require(_amount.add(3) >= stethBalance, \"Unwrapped too much stETH\");\n require(_amount <= stethBalance.add(3), \"Unwrapped insufficient stETH\");\n return stethBalance; // We return here if we have enough stETH + wstETH + ETH\n }\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) public {\n (bool success, ) = payable(recipient).call{value: amount}(\"\");\n require(success, \"!success\");\n }\n\n function getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount,\n address collateralToken\n ) external view returns (uint256) {\n return\n _getOTokenPremium(\n oTokenAddress,\n optionsPremiumPricer,\n premiumDiscount,\n collateralToken\n );\n }\n\n function _getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount,\n address collateralToken\n ) internal view returns (uint256) {\n IOtoken newOToken = IOtoken(oTokenAddress);\n IOptionsPremiumPricer premiumPricer =\n IOptionsPremiumPricer(optionsPremiumPricer);\n\n // Apply black-scholes formula (from rvol library) to option given its features\n // and get price for 100 contracts denominated in the underlying asset for call option\n // and USDC for put option\n uint256 optionPremium =\n premiumPricer.getPremium(\n newOToken.strikePrice(),\n newOToken.expiryTimestamp(),\n newOToken.isPut()\n );\n\n // Apply a discount to incentivize arbitraguers\n optionPremium = optionPremium.mul(premiumDiscount).div(\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER\n );\n\n // get the black scholes premium of the option and adjust premium based on\n // steth <-> eth exchange rate\n uint256 adjustedPremium =\n DSMath.wmul(\n optionPremium,\n IWSTETH(collateralToken).stEthPerToken()\n );\n\n require(\n adjustedPremium <= type(uint96).max,\n \"adjustedPremium > type(uint96) max value!\"\n );\n require(adjustedPremium > 0, \"!adjustedPremium\");\n\n return adjustedPremium;\n }\n}\n" + }, + "contracts/vaults/STETHVault/base/RibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\n\nimport {Vault} from \"../../../libraries/Vault.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../../libraries/VaultLifecycleWithSwap.sol\";\nimport {VaultLifecycleSTETH} from \"../../../libraries/VaultLifecycleSTETH.sol\";\nimport {ShareMath} from \"../../../libraries/ShareMath.sol\";\nimport {IWSTETH} from \"../../../interfaces/ISTETH.sol\";\n\ncontract RibbonVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n /// @notice wstETH vault contract\n IWSTETH public immutable collateralToken;\n\n // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure.\n uint256[30] private ____gap;\n\n // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE\n // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaSTETHVaultStorage\n // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice Deprecated: 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n /// @notice 7 day period between each options sale.\n uint256 public constant PERIOD = 7 days;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // SWAP_CONTRACT is a contract for settling bids via signed messages\n // https://github.com/ribbon-finance/ribbon-v2/blob/master/contracts/utils/Swap.sol\n address public immutable SWAP_CONTRACT;\n\n /// @notice STETH contract address\n address public immutable STETH;\n\n /// @notice Lido DAO token 0x5a98fcbea516cf06857215779fd812ca3bef1b32\n address public immutable LDO;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectVaultFees(\n uint256 performanceFee,\n uint256 vaultFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _wsteth is the wstETH contract\n * @param _ldo is the LDO contract\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _wsteth,\n address _ldo,\n address _gammaController,\n address _marginPool,\n address _swapContract\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_swapContract != address(0), \"!_swapContract\");\n require(_wsteth != address(0), \"!_wsteth\");\n require(_ldo != address(0), \"!_ldo\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n\n WETH = _weth;\n USDC = _usdc;\n LDO = _ldo;\n STETH = IWSTETH(_wsteth).stETH();\n\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n SWAP_CONTRACT = _swapContract;\n collateralToken = IWSTETH(_wsteth);\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function baseInitialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) internal initializer {\n VaultLifecycleWithSwap.verifyInitializerParams(\n _owner,\n _keeper,\n _feeRecipient,\n _performanceFee,\n _managementFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_tokenName, _tokenSymbol);\n __Ownable_init();\n transferOwnership(_owner);\n\n keeper = _keeper;\n\n feeRecipient = _feeRecipient;\n performanceFee = _performanceFee;\n managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div(\n WEEKS_PER_YEAR\n );\n vaultParams = _vaultParams;\n\n uint256 assetBalance = totalBalance();\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n\n vaultState.round = 1;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n // We are dividing annualized management fee by num weeks in a year\n uint256 tmpManagementFee =\n newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n\n managementFee = tmpManagementFee;\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Deposits ETH into the contract and mint vault shares.\n */\n function depositETH() external payable nonReentrant {\n require(msg.value > 0, \"!value\");\n\n _depositFor(msg.value, msg.sender, true);\n }\n\n function depositYieldToken(uint256 amount) external {\n _depositYieldTokenFor(amount, msg.sender);\n }\n\n /**\n * @notice Deposits the `collateralAsset` into the contract and mint vault shares.\n * @param amount is the amount of `collateralAsset` to deposit\n * @param creditor is the amount of `collateralAsset` to deposit\n */\n function depositYieldTokenFor(uint256 amount, address creditor) external {\n require(creditor != address(0), \"!creditor\");\n _depositYieldTokenFor(amount, creditor);\n }\n\n function _depositYieldTokenFor(uint256 amount, address creditor)\n internal\n nonReentrant\n {\n require(amount > 0, \"!amount\");\n\n // stETH transfers suffer from an off-by-1 error\n _depositFor(amount.sub(1), creditor, false);\n\n IERC20(STETH).safeTransferFrom(msg.sender, address(this), amount);\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.\n * @notice Used for vault -> vault deposits on the user's behalf\n * @param creditor is the address that can claim/withdraw deposited amount\n */\n function depositFor(address creditor) external payable nonReentrant {\n require(msg.value > 0, \"!value\");\n require(creditor != address(0), \"!creditor\");\n\n _depositFor(msg.value, creditor, true);\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n * @param isETH is whether this is a depositETH call\n */\n function _depositFor(\n uint256 amount,\n address creditor,\n bool isETH\n ) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount =\n isETH ? totalBalance() : totalBalance().add(amount);\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n\n emit Deposit(creditor, amount, currentRound);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function _initiateWithdraw(uint256 numShares) internal {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n if (\n depositReceipts[msg.sender].amount > 0 ||\n depositReceipts[msg.sender].unredeemedShares > 0\n ) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n bool withdrawalIsSameRound = withdrawal.round == currentRound;\n\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n\n uint256 existingShares = uint256(withdrawal.shares);\n\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return amountETHOut the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n IERC20(STETH).safeTransfer(\n msg.sender,\n VaultLifecycleSTETH.withdrawStEth(\n STETH,\n address(collateralToken),\n withdrawAmount\n )\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map.\n * Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k.\n * Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0.\n * @param numRounds is the number of rounds to initialize in the map\n */\n function initRounds(uint256 numRounds) external nonReentrant {\n require(numRounds > 0, \"!numRounds\");\n\n uint256 _round = vaultState.round;\n for (uint256 i = 0; i < numRounds; i++) {\n uint256 index = _round + i;\n require(roundPricePerShare[index] == 0, \"Initialized\"); // AVOID OVERWRITING ACTUAL VALUES\n roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT;\n }\n }\n\n /**\n * @notice Helper function that performs most administrative tasks\n * such as minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @param currentQueuedWithdrawShares is the queued withdraw shares for the current round\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _closeRound(\n uint256 lastQueuedWithdrawAmount,\n uint256 currentQueuedWithdrawShares\n ) internal returns (uint256 lockedBalance, uint256 queuedWithdrawAmount) {\n address recipient = feeRecipient;\n address collateral = address(collateralToken);\n uint256 mintShares;\n uint256 performanceFeeInAsset;\n uint256 totalVaultFee;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n ) = VaultLifecycleWithSwap.closeRound(\n vaultState,\n VaultLifecycleWithSwap.CloseParams(\n vaultParams.decimals,\n totalBalance(),\n totalSupply(),\n lastQueuedWithdrawAmount,\n performanceFee,\n managementFee,\n currentQueuedWithdrawShares\n )\n );\n\n // Finalize the pricePerShare at the end of the round\n uint256 currentRound = vaultState.round;\n roundPricePerShare[currentRound] = newPricePerShare;\n\n // Wrap entire `asset` balance to `collateralToken` balance\n VaultLifecycleSTETH.wrapToYieldToken(WETH, collateral, STETH);\n\n emit CollectVaultFees(\n performanceFeeInAsset,\n totalVaultFee,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (totalVaultFee > 0) {\n VaultLifecycleSTETH.withdrawYieldAndBaseToken(\n collateral,\n WETH,\n recipient,\n totalVaultFee\n );\n }\n\n return (lockedBalance, queuedWithdrawAmount);\n }\n\n /*\n * @notice Transfers LDO rewards to feeRecipient\n */\n function sendLDORewards() external {\n IERC20 ldo = IERC20(LDO);\n ldo.safeTransfer(feeRecipient, ldo.balanceOf(address(this)));\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n external\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n // After calling closeRound, current option is set to none\n // We also commit the lockedAmount but do not deposit into Opyn\n // which results in double counting of asset balance and lockedAmount\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 ethBalance = address(this).balance;\n uint256 stethFromWsteth =\n collateralToken.getStETHByWstETH(\n collateralToken.balanceOf(address(this))\n );\n\n uint256 stEthBalance = IERC20(STETH).balanceOf(address(this));\n uint256 lockedAmount =\n optionState.currentOption != address(0)\n ? uint256(vaultState.lockedAmount)\n : 0;\n\n return\n wethBalance\n .add(lockedAmount)\n .add(ethBalance)\n .add(stethFromWsteth)\n .add(stEthBalance);\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n}\n" + }, + "contracts/interfaces/ISTETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface IWSTETH {\n function getStETHByWstETH(uint256 _amount) external view returns (uint256);\n\n function getWstETHByStETH(uint256 _amount) external view returns (uint256);\n\n function stEthPerToken() external view returns (uint256);\n\n function tokensPerStEth() external view returns (uint256);\n\n function stETH() external view returns (address);\n\n function wrap(uint256 _amount) external returns (uint256);\n\n function unwrap(uint256 _amount) external returns (uint256);\n\n function approve(address _recipient, uint256 _amount)\n external\n returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function decimals() external view returns (uint256);\n}\n\ninterface ISTETH {\n function getBufferedEther(uint256 _amount) external view returns (uint256);\n\n function getPooledEthByShares(uint256 _amount)\n external\n view\n returns (uint256);\n\n function getSharesByPooledEth(uint256 _amount)\n external\n view\n returns (uint256);\n\n function submit(address _referralAddress)\n external\n payable\n returns (uint256);\n\n function withdraw(uint256 _amount, bytes32 _pubkeyHash)\n external\n returns (uint256);\n\n function approve(address _recipient, uint256 _amount)\n external\n returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function decimals() external view returns (uint256);\n\n function getTotalShares() external view returns (uint256);\n\n function getTotalPooledEther() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICRV.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface ICRV {\n function get_dy(\n int128 _indexIn,\n int128 _indexOut,\n uint256 _amountIn\n ) external view returns (uint256);\n\n // https://github.com/curvefi/curve-contract/blob/\n // b0bbf77f8f93c9c5f4e415bce9cd71f0cdee960e/contracts/pools/steth/StableSwapSTETH.vy#L431\n function exchange(\n int128 _indexIn,\n int128 _indexOut,\n uint256 _amountIn,\n uint256 _minAmountOut\n ) external returns (uint256);\n}\n" + }, + "contracts/tests/TestVaultLifecycleSTETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {ISTETH, IWSTETH} from \"../interfaces/ISTETH.sol\";\nimport {VaultLifecycleSTETH} from \"../libraries/VaultLifecycleSTETH.sol\";\n\ncontract TestVaultLifecycleSTETH {\n address constant wstETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;\n address constant crvPool = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022;\n\n // To avoid using events to get the output, we just set it so we can read\n // it off the contract\n uint256 public output;\n\n function unwrapYieldToken(uint256 amount, uint256 minETHOut) external {\n uint256 amountETHOut =\n VaultLifecycleSTETH.unwrapYieldToken(\n amount,\n wstETH,\n IWSTETH(wstETH).stETH(),\n crvPool,\n minETHOut\n );\n output = amountETHOut;\n }\n\n function withdrawStEth(uint256 amount) external {\n address steth = IWSTETH(wstETH).stETH();\n uint256 amountETHOut =\n VaultLifecycleSTETH.withdrawStEth(steth, wstETH, amount);\n output = amountETHOut;\n ISTETH(steth).transfer(msg.sender, amountETHOut);\n }\n\n // Enables test to send ETH for testing\n receive() external payable {}\n}\n" + }, + "contracts/utils/SAVAXDepositHelper.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {ISAVAX} from \"../interfaces/ISAVAX.sol\";\nimport {IRibbonVault} from \"../interfaces/IRibbon.sol\";\n\ncontract SAVAXDepositHelper {\n ISAVAX public immutable sAVAX;\n IRibbonVault public immutable sAVAXVault;\n\n constructor(address _sAVAX, address _sAVAXVault) {\n require(_sAVAX != address(0), \"!sAVAX\");\n require(_sAVAXVault != address(0), \"!sAVAXVault\");\n\n sAVAX = ISAVAX(_sAVAX);\n sAVAXVault = IRibbonVault(_sAVAXVault);\n }\n\n function deposit() external payable {\n uint256 sAVAXAmount = sAVAX.submit{value: msg.value}();\n sAVAX.approve(address(sAVAXVault), sAVAXAmount);\n sAVAXVault.depositFor(sAVAXAmount, msg.sender);\n }\n}\n" + }, + "contracts/interfaces/ISAVAX.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface ISAVAX is IERC20 {\n function getSharesByPooledAvax(uint256 avaxAmount)\n external\n view\n returns (uint256);\n\n function getPooledAvaxByShares(uint256 shareAmount)\n external\n view\n returns (uint256);\n\n function submit() external payable returns (uint256);\n}\n" + }, + "contracts/oracles/SAvaxOracle.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\nimport \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IPriceOracle} from \"../interfaces/IPriceOracle.sol\";\nimport {ISAVAX} from \"../interfaces/ISAVAX.sol\";\nimport {DSMath} from \"../vendor/DSMath.sol\";\n\ncontract SAvaxOracle is IPriceOracle {\n using SafeMath for uint256;\n\n AggregatorV3Interface public immutable WAVAXOracle;\n ISAVAX public immutable sAVAX;\n\n constructor(address _sAVAX, address _WAVAXOracle) {\n require(_sAVAX != address(0), \"!sAVAX\");\n require(_WAVAXOracle != address(0), \"!WAVAXOracle\");\n\n sAVAX = ISAVAX(_sAVAX);\n WAVAXOracle = AggregatorV3Interface(_WAVAXOracle);\n }\n\n function _underlyingPriceToSAvaxPrice(uint256 underlyingPrice)\n private\n view\n returns (uint256)\n {\n // Passing 1e18 to getPooledAvaxByShares() gives us the number of AVAX per sAVAX.\n uint256 sAvaxPerAvax = sAVAX.getPooledAvaxByShares(1e18);\n return sAvaxPerAvax.mul(underlyingPrice).div(1e18);\n }\n\n function latestAnswer() external view override returns (uint256) {\n (\n uint80 roundID,\n int256 price,\n ,\n uint256 timeStamp,\n uint80 answeredInRound\n ) = WAVAXOracle.latestRoundData();\n\n require(answeredInRound >= roundID, \"Stale oracle price\");\n require(timeStamp != 0, \"!timeStamp\");\n uint256 underlyingPrice = uint256(DSMath.imax(price, 0));\n return _underlyingPriceToSAvaxPrice(underlyingPrice);\n }\n\n function decimals() external view override returns (uint256) {\n return WAVAXOracle.decimals();\n }\n}\n" + }, + "contracts/interfaces/IPriceOracle.sol": { + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.4;\n\ninterface IPriceOracle {\n function decimals() external view returns (uint256 _decimals);\n\n function latestAnswer() external view returns (uint256 price);\n}\n" + }, + "contracts/libraries/VaultLifecycleYearn.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {DSMath} from \"../vendor/DSMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {VaultLifecycle} from \"./VaultLifecycle.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {ShareMath} from \"./ShareMath.sol\";\nimport {IYearnVault} from \"../interfaces/IYearn.sol\";\nimport {IWETH} from \"../interfaces/IWETH.sol\";\nimport {IStrikeSelection} from \"../interfaces/IRibbon.sol\";\nimport {\n IOtokenFactory,\n IOtoken,\n IController,\n GammaTypes\n} from \"../interfaces/GammaInterface.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\nimport {SupportsNonCompliantERC20} from \"./SupportsNonCompliantERC20.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\n\nlibrary VaultLifecycleYearn {\n using SafeMath for uint256;\n using SupportsNonCompliantERC20 for IERC20;\n using SafeERC20 for IERC20;\n\n /**\n * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @param collateralAsset is the address of the collateral asset\n * @param isYearnPaused is whether yearn is enabled in contract\n * @return otokenAddress is the address of the new option\n * @return strikePrice is the strike price of the new option\n * @return delta is the delta of the new option\n */\n function commitAndClose(\n VaultLifecycle.CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n Vault.VaultState storage vaultState,\n address collateralAsset,\n bool isYearnPaused\n )\n external\n returns (\n address otokenAddress,\n uint256 strikePrice,\n uint256 delta\n )\n {\n uint256 expiry =\n VaultLifecycle.getNextExpiry(closeParams.currentOption);\n\n bool isPut = vaultParams.isPut;\n\n IStrikeSelection selection =\n IStrikeSelection(closeParams.strikeSelection);\n\n // calculate strike and delta\n (strikePrice, delta) = closeParams.lastStrikeOverrideRound ==\n vaultState.round\n ? (closeParams.overriddenStrikePrice, selection.delta())\n : selection.getStrikePrice(expiry, isPut);\n\n require(strikePrice != 0, \"!strikePrice\");\n\n // retrieve address if option already exists, or deploy it\n otokenAddress = VaultLifecycle.getOrDeployOtoken(\n closeParams,\n vaultParams,\n vaultParams.underlying,\n isYearnPaused ? vaultParams.asset : collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n return (otokenAddress, strikePrice, delta);\n }\n\n /**\n * @notice Calculate the shares to mint, new price per share, and\n amount of funds to re-allocate as collateral for the new round\n * @param currentShareSupply is the total supply of shares\n * @param currentBalance is the total balance of the vault\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @return newLockedAmount is the amount of funds to allocate for the new round\n * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal\n * @return newPricePerShare is the price per share of the new round\n * @return mintShares is the amount of shares to mint from deposits\n */\n function rollover(\n uint256 currentShareSupply,\n uint256 currentBalance,\n Vault.VaultParams calldata vaultParams,\n Vault.VaultState calldata vaultState\n )\n external\n pure\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares\n )\n {\n uint256 pendingAmount = uint256(vaultState.totalPending);\n uint256 _decimals = vaultParams.decimals;\n\n newPricePerShare = ShareMath.pricePerShare(\n currentShareSupply,\n currentBalance,\n pendingAmount,\n _decimals\n );\n\n // After closing the short, if the options expire in-the-money\n // vault pricePerShare would go down because vault's asset balance decreased.\n // This ensures that the newly-minted shares do not take on the loss.\n uint256 _mintShares =\n ShareMath.assetToShares(pendingAmount, newPricePerShare, _decimals);\n\n uint256 newSupply = currentShareSupply.add(_mintShares);\n\n uint256 queuedAmount =\n newSupply > 0\n ? ShareMath.sharesToAsset(\n vaultState.queuedWithdrawShares,\n newPricePerShare,\n _decimals\n )\n : 0;\n\n return (\n currentBalance.sub(queuedAmount),\n queuedAmount,\n newPricePerShare,\n _mintShares\n );\n }\n\n /**\n * @notice Withdraws yvWETH + WETH (if necessary) from vault using vault shares\n * @param weth is the weth address\n * @param asset is the vault asset address\n * @param collateralToken is the address of the collateral token\n * @param recipient is the recipient\n * @param amount is the withdraw amount in `asset`\n * @return withdrawAmount is the withdraw amount in `collateralToken`\n */\n function withdrawYieldAndBaseToken(\n address weth,\n address asset,\n address collateralToken,\n address recipient,\n uint256 amount\n ) external returns (uint256) {\n uint256 pricePerYearnShare =\n IYearnVault(collateralToken).pricePerShare();\n uint256 withdrawAmount =\n DSMath.wdiv(\n amount,\n pricePerYearnShare.mul(decimalShift(collateralToken))\n );\n uint256 yieldTokenBalance =\n withdrawYieldToken(collateralToken, recipient, withdrawAmount);\n\n // If there is not enough yvWETH in the vault, it withdraws as much as possible and\n // transfers the rest in `asset`\n if (withdrawAmount > yieldTokenBalance) {\n withdrawBaseToken(\n weth,\n asset,\n collateralToken,\n recipient,\n withdrawAmount,\n yieldTokenBalance,\n pricePerYearnShare\n );\n }\n\n return withdrawAmount;\n }\n\n /**\n * @notice Withdraws yvWETH from vault\n * @param collateralToken is the address of the collateral token\n * @param recipient is the recipient\n * @param withdrawAmount is the withdraw amount in terms of yearn tokens\n * @return yieldTokenBalance is the balance of the yield token\n */\n function withdrawYieldToken(\n address collateralToken,\n address recipient,\n uint256 withdrawAmount\n ) internal returns (uint256) {\n IERC20 collateral = IERC20(collateralToken);\n\n uint256 yieldTokenBalance = collateral.balanceOf(address(this));\n uint256 yieldTokensToWithdraw =\n DSMath.min(yieldTokenBalance, withdrawAmount);\n if (yieldTokensToWithdraw > 0) {\n collateral.safeTransfer(recipient, yieldTokensToWithdraw);\n }\n\n return yieldTokenBalance;\n }\n\n /**\n * @notice Withdraws `asset` from vault\n * @param weth is the weth address\n * @param asset is the vault asset address\n * @param collateralToken is the address of the collateral token\n * @param recipient is the recipient\n * @param withdrawAmount is the withdraw amount in terms of yearn tokens\n * @param yieldTokenBalance is the collateral token (yvWETH) balance of the vault\n * @param pricePerYearnShare is the yvWETH<->WETH price ratio\n */\n function withdrawBaseToken(\n address weth,\n address asset,\n address collateralToken,\n address recipient,\n uint256 withdrawAmount,\n uint256 yieldTokenBalance,\n uint256 pricePerYearnShare\n ) internal {\n uint256 underlyingTokensToWithdraw =\n DSMath.wmul(\n withdrawAmount.sub(yieldTokenBalance),\n pricePerYearnShare.mul(decimalShift(collateralToken))\n );\n transferAsset(\n weth,\n asset,\n payable(recipient),\n underlyingTokensToWithdraw\n );\n }\n\n /**\n * @notice Unwraps the necessary amount of the yield-bearing yearn token\n * and transfers amount to vault\n * @param amount is the amount of `asset` to withdraw\n * @param asset is the vault asset address\n * @param collateralToken is the address of the collateral token\n * @param yearnWithdrawalBuffer is the buffer for withdrawals from yearn vault\n * @param yearnWithdrawalSlippage is the slippage for withdrawals from yearn vault\n */\n function unwrapYieldToken(\n uint256 amount,\n address asset,\n address collateralToken,\n uint256 yearnWithdrawalBuffer,\n uint256 yearnWithdrawalSlippage\n ) external {\n uint256 assetBalance = IERC20(asset).balanceOf(address(this));\n IYearnVault collateral = IYearnVault(collateralToken);\n\n uint256 amountToUnwrap =\n DSMath.wdiv(\n DSMath.max(assetBalance, amount).sub(assetBalance),\n collateral.pricePerShare().mul(decimalShift(collateralToken))\n );\n\n if (amountToUnwrap > 0) {\n amountToUnwrap = amountToUnwrap\n .add(amountToUnwrap.mul(yearnWithdrawalBuffer).div(10000))\n .sub(1);\n\n collateral.withdraw(\n amountToUnwrap,\n address(this),\n yearnWithdrawalSlippage\n );\n }\n }\n\n /**\n * @notice Wraps the necessary amount of the base token to the yield-bearing yearn token\n * @param asset is the vault asset address\n * @param collateralToken is the address of the collateral token\n */\n function wrapToYieldToken(address asset, address collateralToken) external {\n uint256 amountToWrap = IERC20(asset).balanceOf(address(this));\n\n if (amountToWrap > 0) {\n IERC20(asset).safeApprove(collateralToken, amountToWrap);\n\n // there is a slight imprecision with regards to calculating back from yearn token -> underlying\n // that stems from miscoordination between ytoken .deposit() amount wrapped and pricePerShare\n // at that point in time.\n // ex: if I have 1 eth, deposit 1 eth into yearn vault and calculate value of yearn token balance\n // denominated in eth (via balance(yearn token) * pricePerShare) we will get 1 eth - 1 wei.\n IYearnVault(collateralToken).deposit(amountToWrap, address(this));\n }\n }\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param weth is the weth address\n * @param asset is the vault asset address\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(\n address weth,\n address asset,\n address recipient,\n uint256 amount\n ) public {\n if (asset == weth) {\n IWETH(weth).withdraw(amount);\n (bool success, ) = payable(recipient).call{value: amount}(\"\");\n require(success, \"!success\");\n return;\n }\n IERC20(asset).safeTransfer(recipient, amount);\n }\n\n /**\n * @notice Returns the decimal shift between 18 decimals and asset tokens\n * @param collateralToken is the address of the collateral token\n */\n function decimalShift(address collateralToken)\n public\n view\n returns (uint256)\n {\n return\n 10**(uint256(18).sub(IERC20Detailed(collateralToken).decimals()));\n }\n\n function getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount,\n address collateralAsset,\n bool isYearnPaused\n ) external view returns (uint256) {\n return\n _getOTokenPremium(\n oTokenAddress,\n optionsPremiumPricer,\n premiumDiscount,\n collateralAsset,\n isYearnPaused\n );\n }\n\n function _getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount,\n address collateralAsset,\n bool isYearnPaused\n ) internal view returns (uint256) {\n IOtoken newOToken = IOtoken(oTokenAddress);\n IOptionsPremiumPricer premiumPricer =\n IOptionsPremiumPricer(optionsPremiumPricer);\n\n // Apply black-scholes formula (from rvol library) to option given its features\n // and get price for 100 contracts denominated in the underlying asset for call option\n // and USDC for put option\n uint256 optionPremium =\n premiumPricer.getPremium(\n newOToken.strikePrice(),\n newOToken.expiryTimestamp(),\n newOToken.isPut()\n );\n\n // Apply a discount to incentivize arbitraguers\n optionPremium = optionPremium.mul(premiumDiscount).div(\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER\n );\n\n // get the black scholes premium of the option and adjust premium based on\n // collateral asset <-> asset exchange rate\n uint256 adjustedPremium =\n isYearnPaused\n ? optionPremium\n : DSMath.wmul(\n optionPremium,\n IYearnVault(collateralAsset).pricePerShare().mul(\n decimalShift(collateralAsset)\n )\n );\n\n require(\n adjustedPremium <= type(uint96).max,\n \"adjustedPremium > type(uint96) max value!\"\n );\n\n require(adjustedPremium > 0, \"!adjustedPremium\");\n\n return adjustedPremium;\n }\n}\n" + }, + "contracts/vaults/RETHVault/RibbonThetaRETHVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport {ISwap} from \"../../interfaces/ISwap.sol\";\nimport {\n RibbonThetaVaultStorage\n} from \"../../storage/RibbonThetaVaultStorage.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../libraries/VaultLifecycleWithSwap.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {ILiquidityGauge} from \"../../interfaces/ILiquidityGauge.sol\";\nimport {RibbonVault} from \"./base/RibbonVault.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\n\n/**\n * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe\n * the inheritance chain closely.\n * Any changes/appends in storage variable needs to happen in RibbonThetaVaultStorage.\n * RibbonThetaVault should not inherit from any other contract aside from RibbonVault, RibbonThetaVaultStorage\n */\ncontract RibbonThetaRETHVault is RibbonVault, RibbonThetaVaultStorage {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice oTokenFactory is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event NewOffer(\n uint256 swapId,\n address seller,\n address oToken,\n address biddingToken,\n uint256 minPrice,\n uint256 minBidSize,\n uint256 totalSize\n );\n\n /************************************************\n * STRUCTS\n ***********************************************/\n\n /**\n * @notice Initialization parameters for the vault.\n * @param _owner is the owner of the vault with critical permissions\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n */\n struct InitParams {\n address _owner;\n address _keeper;\n address _feeRecipient;\n uint256 _managementFee;\n uint256 _performanceFee;\n string _tokenName;\n string _tokenSymbol;\n address _optionsPremiumPricer;\n address _strikeSelection;\n }\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _swapContract\n ) RibbonVault(_weth, _usdc, _gammaController, _marginPool, _swapContract) {\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n OTOKEN_FACTORY = _oTokenFactory;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n * @param _initParams is the struct with vault initialization parameters\n * @param _vaultParams is the struct with vault general data\n */\n function initialize(\n InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n baseInitialize(\n _initParams._owner,\n _initParams._keeper,\n _initParams._feeRecipient,\n _initParams._managementFee,\n _initParams._performanceFee,\n _initParams._tokenName,\n _initParams._tokenSymbol,\n _vaultParams\n );\n require(\n _initParams._optionsPremiumPricer != address(0),\n \"!_optionsPremiumPricer\"\n );\n require(\n _initParams._strikeSelection != address(0),\n \"!_strikeSelection\"\n );\n\n optionsPremiumPricer = _initParams._optionsPremiumPricer;\n strikeSelection = _initParams._strikeSelection;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new strike selection contract\n * @param newStrikeSelection is the address of the new strike selection contract\n */\n function setStrikeSelection(address newStrikeSelection) external onlyOwner {\n require(newStrikeSelection != address(0), \"!newStrikeSelection\");\n strikeSelection = newStrikeSelection;\n }\n\n /**\n * @notice Sets the new options premium pricer contract\n * @param newOptionsPremiumPricer is the address of the new strike selection contract\n */\n function setOptionsPremiumPricer(address newOptionsPremiumPricer)\n external\n onlyOwner\n {\n require(\n newOptionsPremiumPricer != address(0),\n \"!newOptionsPremiumPricer\"\n );\n optionsPremiumPricer = newOptionsPremiumPricer;\n }\n\n /**\n * @notice Optionality to set strike price manually\n * Should be called after closeRound if we are setting current week's strike\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice) external onlyOwner {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Sets the new liquidityGauge contract for this vault\n * @param newLiquidityGauge is the address of the new liquidityGauge contract\n */\n function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {\n liquidityGauge = newLiquidityGauge;\n }\n\n /**\n * @notice Sets oToken Premium\n * @param minPrice is the new oToken Premium in the units of 10**18\n */\n function setMinPrice(uint256 minPrice) external onlyKeeper {\n require(minPrice > 0, \"!minPrice\");\n currentOtokenPremium = minPrice;\n }\n\n /**\n * @notice Sets the new Vault Pauser contract for this vault\n * @param newVaultPauser is the address of the new vaultPauser contract\n */\n function setVaultPauser(address newVaultPauser) external onlyOwner {\n vaultPauser = newVaultPauser;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n transferAsset(msg.sender, amount);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n _initiateWithdraw(numShares);\n currentQueuedWithdrawShares = currentQueuedWithdrawShares.add(\n numShares\n );\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /**\n * @notice Stakes a users vault shares\n * @param numShares is the number of shares to stake\n */\n function stake(uint256 numShares) external nonReentrant {\n address _liquidityGauge = liquidityGauge;\n require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit\n require(numShares > 0);\n uint256 heldByAccount = balanceOf(msg.sender);\n if (heldByAccount < numShares) {\n _redeem(numShares.sub(heldByAccount), false);\n }\n _transfer(msg.sender, address(this), numShares);\n _approve(address(this), _liquidityGauge, numShares);\n ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);\n }\n\n /**\n * @notice Closes the existing short and calculate the shares to mint, new price per share &\n amount of funds to re-allocate as collateral for the new round\n * Since we are incrementing the round here, the options are sold in the beginning of a round\n * instead of at the end of the round. For example, at round 1, we don't sell any options. We\n * start selling options at the beginning of round 2.\n */\n function closeRound() external nonReentrant {\n address oldOption = optionState.currentOption;\n require(\n oldOption != address(0) || vaultState.round == 1,\n \"Round closed\"\n );\n _closeShort(oldOption);\n\n uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares;\n (uint256 lockedBalance, uint256 queuedWithdrawAmount) =\n _closeRound(\n uint256(lastQueuedWithdrawAmount),\n currQueuedWithdrawShares\n );\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(\n currQueuedWithdrawShares\n );\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n currentQueuedWithdrawShares = 0;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycleWithSwap.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Sets the next option the vault will be shorting\n */\n function commitNextOption() external onlyKeeper nonReentrant {\n address currentOption = optionState.currentOption;\n require(\n currentOption == address(0) && vaultState.round != 1,\n \"Round not closed\"\n );\n\n VaultLifecycleWithSwap.CommitParams memory commitParams =\n VaultLifecycleWithSwap.CommitParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n collateralAsset: vaultParams.asset,\n currentOption: currentOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n strikeSelection: strikeSelection,\n optionsPremiumPricer: optionsPremiumPricer\n });\n\n (address otokenAddress, uint256 strikePrice, uint256 delta) =\n VaultLifecycleWithSwap.commitNextOption(\n commitParams,\n vaultParams,\n vaultState\n );\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n optionState.nextOption = otokenAddress;\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position and create a new offer.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n address newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n uint256 lockedBalance = vaultState.lockedAmount;\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n VaultLifecycleWithSwap.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n _createOffer();\n }\n\n function _createOffer() private {\n address currentOtoken = optionState.currentOption;\n uint256 currOtokenPremium = currentOtokenPremium;\n\n optionAuctionID = VaultLifecycleWithSwap.createOffer(\n currentOtoken,\n currOtokenPremium,\n SWAP_CONTRACT,\n vaultParams\n );\n }\n\n /**\n * @notice Settle current offer\n */\n function settleOffer(ISwap.Bid[] calldata bids)\n external\n onlyKeeper\n nonReentrant\n {\n ISwap(SWAP_CONTRACT).settleOffer(optionAuctionID, bids);\n }\n\n /**\n * @notice Burn the remaining oTokens left over\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n VaultLifecycleWithSwap.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n }\n\n /**\n * @notice pause a user's vault position\n */\n function pausePosition() external {\n address _vaultPauserAddress = vaultPauser;\n require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit\n _redeem(0, true);\n uint256 heldByAccount = balanceOf(msg.sender);\n _approve(msg.sender, _vaultPauserAddress, heldByAccount);\n IVaultPauser(_vaultPauserAddress).pausePosition(\n msg.sender,\n heldByAccount\n );\n }\n}\n" + }, + "contracts/vaults/RETHVault/base/RibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\n\nimport {Vault} from \"../../../libraries/Vault.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../../libraries/VaultLifecycleWithSwap.sol\";\nimport {ShareMath} from \"../../../libraries/ShareMath.sol\";\nimport {IWETH} from \"../../../interfaces/IWETH.sol\";\nimport {IRETH} from \"../../../interfaces/IRETH.sol\";\nimport {IRETHDepositPool} from \"../../../interfaces/IRETHDepositPool.sol\";\n\ncontract RibbonVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n /// @notice depositETH transactions within 24hr are paused\n /// DEPRECATED - BUT DO NOT REMOVE\n uint256 private _lastrETHMintCutoff;\n\n // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure.\n uint256[30] private ____gap;\n\n // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE\n // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaVaultStorage\n // or RibbonDeltaVaultStorage instead. Read this documentation to learn more:\n // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice Deprecated: 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n /// @notice 7 day period between each options sale.\n uint256 public constant PERIOD = 7 days;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // SWAP_CONTRACT is a contract for settling bids via signed messages\n // https://github.com/ribbon-finance/ribbon-v2/blob/master/contracts/utils/Swap.sol\n address public immutable SWAP_CONTRACT;\n\n address public immutable RETH_DEPOSIT_POOL = 0x2cac916b2A963Bf162f076C0a8a4a8200BCFBfb4;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectVaultFees(\n uint256 performanceFee,\n uint256 vaultFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _gammaController,\n address _marginPool,\n address _swapContract\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_swapContract != address(0), \"!_swapContract\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n\n WETH = _weth;\n USDC = _usdc;\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n SWAP_CONTRACT = _swapContract;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function baseInitialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) internal initializer {\n VaultLifecycleWithSwap.verifyInitializerParams(\n _owner,\n _keeper,\n _feeRecipient,\n _performanceFee,\n _managementFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_tokenName, _tokenSymbol);\n __Ownable_init();\n transferOwnership(_owner);\n\n keeper = _keeper;\n\n feeRecipient = _feeRecipient;\n performanceFee = _performanceFee;\n managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div(\n WEEKS_PER_YEAR\n );\n vaultParams = _vaultParams;\n\n uint256 assetBalance =\n IERC20(vaultParams.asset).balanceOf(address(this));\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n\n vaultState.round = 1;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n // We are dividing annualized management fee by num weeks in a year\n uint256 tmpManagementFee =\n newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n\n managementFee = tmpManagementFee;\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Deposits ETH into the contract and mint vault shares.\n */\n function depositETH() external payable nonReentrant {\n require(msg.value > 0, \"!value\");\n\n uint256 rETHAmount = IRETH(vaultParams.asset).getRethValue(msg.value);\n\n _depositFor(rETHAmount, msg.sender);\n\n // Deposit ETH for rETH\n IRETHDepositPool(RETH_DEPOSIT_POOL).deposit{\n value: msg.value\n }();\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender.\n * @param amount is the amount of `asset` to deposit\n */\n function deposit(uint256 amount) external nonReentrant {\n require(amount > 0, \"!amount\");\n\n _depositFor(amount, msg.sender);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.\n * @notice Used for vault -> vault deposits on the user's behalf\n * @param amount is the amount of `asset` to deposit\n * @param creditor is the address that can claim/withdraw deposited amount\n */\n function depositFor(uint256 amount, address creditor)\n external\n nonReentrant\n {\n require(amount > 0, \"!amount\");\n require(creditor != address(0));\n\n _depositFor(amount, creditor);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n */\n function _depositFor(uint256 amount, address creditor) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount = totalBalance().add(amount);\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n\n emit Deposit(creditor, amount, currentRound);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function _initiateWithdraw(uint256 numShares) internal {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n if (\n depositReceipts[msg.sender].amount > 0 ||\n depositReceipts[msg.sender].unredeemedShares > 0\n ) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n bool withdrawalIsSameRound = withdrawal.round == currentRound;\n\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n\n uint256 existingShares = uint256(withdrawal.shares);\n\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return withdrawAmount the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n require(withdrawAmount > 0, \"!withdrawAmount\");\n transferAsset(msg.sender, withdrawAmount);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map.\n * Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k.\n * Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0.\n * @param numRounds is the number of rounds to initialize in the map\n */\n function initRounds(uint256 numRounds) external nonReentrant {\n require(numRounds > 0, \"!numRounds\");\n\n uint256 _round = vaultState.round;\n for (uint256 i = 0; i < numRounds; i++) {\n uint256 index = _round + i;\n require(roundPricePerShare[index] == 0, \"Initialized\"); // AVOID OVERWRITING ACTUAL VALUES\n roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT;\n }\n }\n\n /**\n * @notice Helper function that performs most administrative tasks\n * such as setting next option, minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @param currentQueuedWithdrawShares is the queued withdraw shares for the current round\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _closeRound(\n uint256 lastQueuedWithdrawAmount,\n uint256 currentQueuedWithdrawShares\n ) internal returns (uint256 lockedBalance, uint256 queuedWithdrawAmount) {\n address recipient = feeRecipient;\n uint256 mintShares;\n uint256 performanceFeeInAsset;\n uint256 totalVaultFee;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n ) = VaultLifecycleWithSwap.closeRound(\n vaultState,\n VaultLifecycleWithSwap.CloseParams(\n vaultParams.decimals,\n IERC20(vaultParams.asset).balanceOf(address(this)),\n totalSupply(),\n lastQueuedWithdrawAmount,\n performanceFee,\n managementFee,\n currentQueuedWithdrawShares\n )\n );\n\n // Finalize the pricePerShare at the end of the round\n uint256 currentRound = vaultState.round;\n roundPricePerShare[currentRound] = newPricePerShare;\n\n emit CollectVaultFees(\n performanceFeeInAsset,\n totalVaultFee,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (totalVaultFee > 0) {\n transferAsset(payable(recipient), totalVaultFee);\n }\n\n return (lockedBalance, queuedWithdrawAmount);\n }\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) internal {\n address asset = vaultParams.asset;\n if (asset == WETH) {\n IWETH(WETH).withdraw(amount);\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Transfer failed\");\n return;\n }\n IERC20(asset).safeTransfer(recipient, amount);\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n external\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n // After calling closeRound, current option is set to none\n // We also commit the lockedAmount but do not deposit into Opyn\n // which results in double counting of asset balance and lockedAmount\n\n return\n optionState.currentOption != address(0)\n ? uint256(vaultState.lockedAmount).add(\n IERC20(vaultParams.asset).balanceOf(address(this))\n )\n : IERC20(vaultParams.asset).balanceOf(address(this));\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n}\n" + }, + "contracts/interfaces/IRETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IRETH is IERC20 {\n function getExchangeRate() external view returns (uint256);\n\n function getETHValue(uint256 rethAmount) external view returns (uint256);\n\n function getRethValue(uint256 ethAmount) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IRETHDepositPool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\ninterface IRETHDepositPool {\n function deposit() external payable;\n}\n" + }, + "contracts/vendor/CustomSafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure.\n * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n function safeTransfer(IERC20 token, address to, uint256 value) internal {\n require(_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)),\n \"ERC20 transfer failed\");\n }\n\n function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n require(_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)),\n \"ERC20 transferFrom failed\");\n }\n\n function safeApprove(IERC20 token, address spender, uint256 value) internal {\n if (_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value))) {\n return;\n }\n require(_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0))\n && _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)),\n \"ERC20 approve failed\");\n }\n\n function _callOptionalReturn(IERC20 token, bytes memory data) private returns (bool) {\n // solhint-disable-next-line avoid-low-level-calls\n (bool success, bytes memory returndata) = address(token).call(data);\n if (!success) {\n return false;\n }\n\n if (returndata.length >= 32) { // Return data is optional\n return abi.decode(returndata, (bool));\n }\n\n // In a wierd case when return data is 1-31 bytes long - return false.\n return returndata.length == 0;\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/ERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n\n uint256 currentAllowance = _allowances[sender][_msgSender()];\n require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n unchecked {\n _approve(sender, _msgSender(), currentAllowance - amount);\n }\n\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n uint256 currentAllowance = _allowances[_msgSender()][spender];\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n uint256 senderBalance = _balances[sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[sender] = senderBalance - amount;\n }\n _balances[recipient] += amount;\n\n emit Transfer(sender, recipient, amount);\n\n _afterTokenTransfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n _balances[account] += amount;\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n }\n _totalSupply -= amount;\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/tests/MockOtoken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockOtoken is ERC20 {\n address public underlyingAsset;\n bool public isPut;\n\n constructor(address _underlyingAsset, bool _isPut)\n ERC20(\"Otoken\", \"Otoken\")\n {\n underlyingAsset = _underlyingAsset;\n isPut = _isPut;\n }\n\n function mint(uint256 amount) public {\n ERC20._mint(msg.sender, amount);\n }\n}\n" + }, + "contracts/utils/PercentStrikeSelection.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {\n IPriceOracle\n} from \"@ribbon-finance/rvol/contracts/interfaces/IPriceOracle.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\nimport {\n IManualVolatilityOracle\n} from \"@ribbon-finance/rvol/contracts/interfaces/IManualVolatilityOracle.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\ncontract PercentStrikeSelection is Ownable {\n using SafeMath for uint256;\n\n /**\n * Immutables\n */\n IOptionsPremiumPricer public immutable optionsPremiumPricer;\n\n // step in absolute terms at which we will increment\n // (ex: 100 * 10 ** assetOracleDecimals means we will move at increments of 100 points)\n uint256 public step;\n\n // multiplier for strike selection\n uint256 public strikeMultiplier;\n\n // multiplier to shift asset prices\n uint256 private immutable assetOracleMultiplier;\n\n // Delta are in 4 decimal places. 1 * 10**4 = 1 delta.\n uint256 private constant DELTA_MULTIPLIER = 10**4;\n\n // ChainLink's USD Price oracles return results in 8 decimal places\n uint256 private constant ORACLE_PRICE_MULTIPLIER = 10**8;\n\n // Strike multiplier has 2 decimal places. For example: 150 = 1.5x spot price\n uint256 private constant STRIKE_MULTIPLIER = 10**2;\n\n event StepSet(uint256 oldStep, uint256 newStep, address indexed owner);\n\n constructor(\n address _optionsPremiumPricer,\n uint256 _strikeMultiplier,\n uint256 _step\n ) {\n require(_optionsPremiumPricer != address(0), \"!_optionsPremiumPricer\");\n require(\n _strikeMultiplier > STRIKE_MULTIPLIER,\n \"Multiplier must be bigger than 1!\"\n );\n require(_step > 0, \"!_step\");\n\n optionsPremiumPricer = IOptionsPremiumPricer(_optionsPremiumPricer);\n\n // ex: delta = 7500 (.75)\n uint256 _assetOracleMultiplier =\n 10 **\n IPriceOracle(\n IOptionsPremiumPricer(_optionsPremiumPricer).priceOracle()\n )\n .decimals();\n\n step = _step;\n\n strikeMultiplier = _strikeMultiplier;\n\n assetOracleMultiplier = _assetOracleMultiplier;\n }\n\n /**\n * @notice Gets the strike price by multiplying the current underlying price\n * with a multiplier\n * @param expiryTimestamp is the unix timestamp of expiration\n * @param isPut is whether option is put or call\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\n * @return newDelta will be set to zero for percent strike selection\n */\n\n function getStrikePrice(uint256 expiryTimestamp, bool isPut)\n external\n view\n returns (uint256 newStrikePrice, uint256 newDelta)\n {\n require(\n expiryTimestamp > block.timestamp,\n \"Expiry must be in the future!\"\n );\n\n // asset price\n uint256 strikePrice =\n optionsPremiumPricer.getUnderlyingPrice().mul(strikeMultiplier).div(\n STRIKE_MULTIPLIER\n );\n\n newStrikePrice = isPut\n ? strikePrice.sub(strikePrice % step)\n : strikePrice.add(step.sub(strikePrice % step));\n\n newDelta = 0;\n }\n\n /**\n * @notice Set the multiplier for setting the strike price\n * @param newStrikeMultiplier is the strike multiplier (decimals = 2)\n */\n function setStrikeMultiplier(uint256 newStrikeMultiplier)\n external\n onlyOwner\n {\n require(\n newStrikeMultiplier > STRIKE_MULTIPLIER,\n \"Multiplier must be bigger than 1!\"\n );\n strikeMultiplier = newStrikeMultiplier;\n }\n\n /**\n * @notice Sets new step value\n * @param newStep is the new step value\n */\n function setStep(uint256 newStep) external onlyOwner {\n require(newStep > 0, \"!newStep\");\n uint256 oldStep = step;\n step = newStep;\n emit StepSet(oldStep, newStep, msg.sender);\n }\n}\n" + }, + "@ribbon-finance/rvol/contracts/interfaces/IPriceOracle.sol": { + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >0.6.0 <0.8.7;\n\ninterface IPriceOracle {\n function decimals() external view returns (uint256 _decimals);\n\n function latestAnswer() external view returns (uint256 price);\n}\n" + }, + "@ribbon-finance/rvol/contracts/interfaces/IManualVolatilityOracle.sol": { + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >0.6.0 <0.8.7;\n\ninterface IManualVolatilityOracle {\n function vol(bytes32 optionId)\n external\n view\n returns (uint256 standardDeviation);\n\n function annualizedVol(bytes32 optionId)\n external\n view\n returns (uint256 annualStdev);\n\n function setAnnualizedVol(\n bytes32[] calldata optionIds,\n uint256[] calldata newAnnualizedVols\n ) external;\n}\n" + }, + "contracts/vaults/BaseVaultWithSwap/RibbonThetaVaultWithSwap.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport {ISwap} from \"../../interfaces/ISwap.sol\";\nimport {\n RibbonThetaVaultStorage\n} from \"../../storage/RibbonThetaVaultStorage.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../libraries/VaultLifecycleWithSwap.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {ILiquidityGauge} from \"../../interfaces/ILiquidityGauge.sol\";\nimport {RibbonVault} from \"./base/RibbonVault.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\n\n/**\n * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe\n * the inheritance chain closely.\n * Any changes/appends in storage variable needs to happen in RibbonThetaVaultStorage.\n * RibbonThetaVault should not inherit from any other contract aside from RibbonVault, RibbonThetaVaultStorage\n */\ncontract RibbonThetaVaultWithSwap is RibbonVault, RibbonThetaVaultStorage {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice oTokenFactory is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event NewOffer(\n uint256 swapId,\n address seller,\n address oToken,\n address biddingToken,\n uint256 minPrice,\n uint256 minBidSize,\n uint256 totalSize\n );\n\n /************************************************\n * STRUCTS\n ***********************************************/\n\n /**\n * @notice Initialization parameters for the vault.\n * @param _owner is the owner of the vault with critical permissions\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n */\n struct InitParams {\n address _owner;\n address _keeper;\n address _feeRecipient;\n uint256 _managementFee;\n uint256 _performanceFee;\n string _tokenName;\n string _tokenSymbol;\n address _optionsPremiumPricer;\n address _strikeSelection;\n }\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _swapContract\n ) RibbonVault(_weth, _usdc, _gammaController, _marginPool, _swapContract) {\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n OTOKEN_FACTORY = _oTokenFactory;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n * @param _initParams is the struct with vault initialization parameters\n * @param _vaultParams is the struct with vault general data\n */\n function initialize(\n InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n baseInitialize(\n _initParams._owner,\n _initParams._keeper,\n _initParams._feeRecipient,\n _initParams._managementFee,\n _initParams._performanceFee,\n _initParams._tokenName,\n _initParams._tokenSymbol,\n _vaultParams\n );\n require(\n _initParams._optionsPremiumPricer != address(0),\n \"!_optionsPremiumPricer\"\n );\n require(\n _initParams._strikeSelection != address(0),\n \"!_strikeSelection\"\n );\n\n optionsPremiumPricer = _initParams._optionsPremiumPricer;\n strikeSelection = _initParams._strikeSelection;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new strike selection contract\n * @param newStrikeSelection is the address of the new strike selection contract\n */\n function setStrikeSelection(address newStrikeSelection) external onlyOwner {\n require(newStrikeSelection != address(0), \"!newStrikeSelection\");\n strikeSelection = newStrikeSelection;\n }\n\n /**\n * @notice Sets the new options premium pricer contract\n * @param newOptionsPremiumPricer is the address of the new strike selection contract\n */\n function setOptionsPremiumPricer(address newOptionsPremiumPricer)\n external\n onlyOwner\n {\n require(\n newOptionsPremiumPricer != address(0),\n \"!newOptionsPremiumPricer\"\n );\n optionsPremiumPricer = newOptionsPremiumPricer;\n }\n\n /**\n * @notice Optionality to set strike price manually\n * Should be called after closeRound if we are setting current week's strike\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice) external onlyOwner {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Sets the new liquidityGauge contract for this vault\n * @param newLiquidityGauge is the address of the new liquidityGauge contract\n */\n function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {\n liquidityGauge = newLiquidityGauge;\n }\n\n /**\n * @notice Sets oToken Premium\n * @param minPrice is the new oToken Premium in the units of 10**18\n */\n function setMinPrice(uint256 minPrice) external onlyKeeper {\n require(minPrice > 0, \"!minPrice\");\n currentOtokenPremium = minPrice;\n }\n\n /**\n * @notice Sets the new Vault Pauser contract for this vault\n * @param newVaultPauser is the address of the new vaultPauser contract\n */\n function setVaultPauser(address newVaultPauser) external onlyOwner {\n vaultPauser = newVaultPauser;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n transferAsset(msg.sender, amount);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n _initiateWithdraw(numShares);\n currentQueuedWithdrawShares = currentQueuedWithdrawShares.add(\n numShares\n );\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /**\n * @notice Stakes a users vault shares\n * @param numShares is the number of shares to stake\n */\n function stake(uint256 numShares) external nonReentrant {\n address _liquidityGauge = liquidityGauge;\n require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit\n require(numShares > 0);\n uint256 heldByAccount = balanceOf(msg.sender);\n if (heldByAccount < numShares) {\n _redeem(numShares.sub(heldByAccount), false);\n }\n _transfer(msg.sender, address(this), numShares);\n _approve(address(this), _liquidityGauge, numShares);\n ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);\n }\n\n /**\n * @notice Closes the existing short and calculate the shares to mint, new price per share &\n amount of funds to re-allocate as collateral for the new round\n * Since we are incrementing the round here, the options are sold in the beginning of a round\n * instead of at the end of the round. For example, at round 1, we don't sell any options. We\n * start selling options at the beginning of round 2.\n */\n function closeRound() external nonReentrant {\n address oldOption = optionState.currentOption;\n require(\n oldOption != address(0) || vaultState.round == 1,\n \"Round closed\"\n );\n _closeShort(oldOption);\n\n uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares;\n (uint256 lockedBalance, uint256 queuedWithdrawAmount) =\n _closeRound(\n uint256(lastQueuedWithdrawAmount),\n currQueuedWithdrawShares\n );\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(\n currQueuedWithdrawShares\n );\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n currentQueuedWithdrawShares = 0;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycleWithSwap.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Sets the next option the vault will be shorting\n */\n function commitNextOption() external onlyKeeper nonReentrant {\n address currentOption = optionState.currentOption;\n require(\n currentOption == address(0) && vaultState.round != 1,\n \"Round not closed\"\n );\n\n VaultLifecycleWithSwap.CommitParams memory commitParams =\n VaultLifecycleWithSwap.CommitParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n collateralAsset: vaultParams.asset,\n currentOption: currentOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n strikeSelection: strikeSelection,\n optionsPremiumPricer: optionsPremiumPricer\n });\n\n (address otokenAddress, uint256 strikePrice, uint256 delta) =\n VaultLifecycleWithSwap.commitNextOption(\n commitParams,\n vaultParams,\n vaultState\n );\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n optionState.nextOption = otokenAddress;\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position and create a new offer.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n address newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n uint256 lockedBalance = vaultState.lockedAmount;\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n VaultLifecycleWithSwap.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n _createOffer();\n }\n\n function _createOffer() private {\n address currentOtoken = optionState.currentOption;\n uint256 currOtokenPremium = currentOtokenPremium;\n\n optionAuctionID = VaultLifecycleWithSwap.createOffer(\n currentOtoken,\n currOtokenPremium,\n SWAP_CONTRACT,\n vaultParams\n );\n }\n\n /**\n * @notice Settle current offer\n */\n function settleOffer(ISwap.Bid[] calldata bids)\n external\n onlyKeeper\n nonReentrant\n {\n ISwap(SWAP_CONTRACT).settleOffer(optionAuctionID, bids);\n }\n\n /**\n * @notice Burn the remaining oTokens left over\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n VaultLifecycleWithSwap.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n }\n\n /**\n * @notice pause a user's vault position\n */\n function pausePosition() external {\n address _vaultPauserAddress = vaultPauser;\n require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit\n _redeem(0, true);\n uint256 heldByAccount = balanceOf(msg.sender);\n _approve(msg.sender, _vaultPauserAddress, heldByAccount);\n IVaultPauser(_vaultPauserAddress).pausePosition(\n msg.sender,\n heldByAccount\n );\n }\n}\n" + }, + "contracts/vaults/BaseVaultWithSwap/base/RibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\n\nimport {Vault} from \"../../../libraries/Vault.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../../libraries/VaultLifecycleWithSwap.sol\";\nimport {ShareMath} from \"../../../libraries/ShareMath.sol\";\nimport {IWETH} from \"../../../interfaces/IWETH.sol\";\n\ncontract RibbonVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure.\n uint256[30] private ____gap;\n\n // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE\n // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaVaultStorage\n // or RibbonDeltaVaultStorage instead. Read this documentation to learn more:\n // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice Deprecated: 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n /// @notice 7 day period between each options sale.\n uint256 public constant PERIOD = 7 days;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // SWAP_CONTRACT is a contract for settling bids via signed messages\n // https://github.com/ribbon-finance/ribbon-v2/blob/master/contracts/utils/Swap.sol\n address public immutable SWAP_CONTRACT;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectVaultFees(\n uint256 performanceFee,\n uint256 vaultFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _gammaController,\n address _marginPool,\n address _swapContract\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_swapContract != address(0), \"!_swapContract\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n\n WETH = _weth;\n USDC = _usdc;\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n SWAP_CONTRACT = _swapContract;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function baseInitialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) internal initializer {\n VaultLifecycleWithSwap.verifyInitializerParams(\n _owner,\n _keeper,\n _feeRecipient,\n _performanceFee,\n _managementFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_tokenName, _tokenSymbol);\n __Ownable_init();\n transferOwnership(_owner);\n\n keeper = _keeper;\n\n feeRecipient = _feeRecipient;\n performanceFee = _performanceFee;\n managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div(\n WEEKS_PER_YEAR\n );\n vaultParams = _vaultParams;\n\n uint256 assetBalance =\n IERC20(vaultParams.asset).balanceOf(address(this));\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n\n vaultState.round = 1;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n // We are dividing annualized management fee by num weeks in a year\n uint256 tmpManagementFee =\n newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n\n managementFee = tmpManagementFee;\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Deposits ETH into the contract and mint vault shares. Reverts if the asset is not WETH.\n */\n function depositETH() external payable nonReentrant {\n require(vaultParams.asset == WETH, \"!WETH\");\n require(msg.value > 0, \"!value\");\n\n _depositFor(msg.value, msg.sender);\n\n IWETH(WETH).deposit{value: msg.value}();\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender.\n * @param amount is the amount of `asset` to deposit\n */\n function deposit(uint256 amount) external nonReentrant {\n require(amount > 0, \"!amount\");\n\n _depositFor(amount, msg.sender);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.\n * @notice Used for vault -> vault deposits on the user's behalf\n * @param amount is the amount of `asset` to deposit\n * @param creditor is the address that can claim/withdraw deposited amount\n */\n function depositFor(uint256 amount, address creditor)\n external\n nonReentrant\n {\n require(amount > 0, \"!amount\");\n require(creditor != address(0));\n\n _depositFor(amount, creditor);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n */\n function _depositFor(uint256 amount, address creditor) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount = totalBalance().add(amount);\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n\n emit Deposit(creditor, amount, currentRound);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function _initiateWithdraw(uint256 numShares) internal {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n if (\n depositReceipts[msg.sender].amount > 0 ||\n depositReceipts[msg.sender].unredeemedShares > 0\n ) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n bool withdrawalIsSameRound = withdrawal.round == currentRound;\n\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n\n uint256 existingShares = uint256(withdrawal.shares);\n\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return withdrawAmount the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n require(withdrawAmount > 0, \"!withdrawAmount\");\n transferAsset(msg.sender, withdrawAmount);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map.\n * Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k.\n * Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0.\n * @param numRounds is the number of rounds to initialize in the map\n */\n function initRounds(uint256 numRounds) external nonReentrant {\n require(numRounds > 0, \"!numRounds\");\n\n uint256 _round = vaultState.round;\n for (uint256 i = 0; i < numRounds; i++) {\n uint256 index = _round + i;\n require(roundPricePerShare[index] == 0, \"Initialized\"); // AVOID OVERWRITING ACTUAL VALUES\n roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT;\n }\n }\n\n /**\n * @notice Helper function that performs most administrative tasks\n * such as minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @param currentQueuedWithdrawShares is the queued withdraw shares for the current round\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _closeRound(\n uint256 lastQueuedWithdrawAmount,\n uint256 currentQueuedWithdrawShares\n ) internal returns (uint256 lockedBalance, uint256 queuedWithdrawAmount) {\n address recipient = feeRecipient;\n uint256 mintShares;\n uint256 performanceFeeInAsset;\n uint256 totalVaultFee;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n ) = VaultLifecycleWithSwap.closeRound(\n vaultState,\n VaultLifecycleWithSwap.CloseParams(\n vaultParams.decimals,\n IERC20(vaultParams.asset).balanceOf(address(this)),\n totalSupply(),\n lastQueuedWithdrawAmount,\n performanceFee,\n managementFee,\n currentQueuedWithdrawShares\n )\n );\n\n // Finalize the pricePerShare at the end of the round\n uint256 currentRound = vaultState.round;\n roundPricePerShare[currentRound] = newPricePerShare;\n\n emit CollectVaultFees(\n performanceFeeInAsset,\n totalVaultFee,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (totalVaultFee > 0) {\n transferAsset(payable(recipient), totalVaultFee);\n }\n\n return (lockedBalance, queuedWithdrawAmount);\n }\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) internal {\n address asset = vaultParams.asset;\n if (asset == WETH) {\n IWETH(WETH).withdraw(amount);\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Transfer failed\");\n return;\n }\n IERC20(asset).safeTransfer(recipient, amount);\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n external\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n // After calling closeRound, current option is set to none\n // We also commit the lockedAmount but do not deposit into Opyn\n // which results in double counting of asset balance and lockedAmount\n\n return\n optionState.currentOption != address(0)\n ? uint256(vaultState.lockedAmount).add(\n IERC20(vaultParams.asset).balanceOf(address(this))\n )\n : IERC20(vaultParams.asset).balanceOf(address(this));\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n}\n" + }, + "contracts/utils/STETHDepositHelper.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {ICurveETHSTETHPool} from \"../interfaces/ICurveETHSTETHPool.sol\";\nimport {IRibbonThetaVault} from \"../interfaces/IRibbonThetaVault.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\ncontract STETHDepositHelper {\n using SafeERC20 for IERC20;\n\n ICurveETHSTETHPool public immutable curveETHSTETHPool;\n IRibbonThetaVault public immutable stETHVault;\n IERC20 public immutable stETH;\n\n constructor(\n address _curveETHSTETHPool,\n address _stETHVault,\n address _stETH\n ) {\n require(_curveETHSTETHPool != address(0), \"!curveETHSTETH Pool\");\n require(_stETHVault != address(0), \"!stETHVault\");\n require(_stETH != address(0), \"!_stETH\");\n\n curveETHSTETHPool = ICurveETHSTETHPool(_curveETHSTETHPool);\n stETHVault = IRibbonThetaVault(_stETHVault);\n stETH = IERC20(_stETH);\n }\n\n /**\n * Swaps ETH -> stETH on Curve ETH-stETH pool, and deposits into stETH vault\n */\n function deposit(uint256 minSTETHAmount) external payable {\n curveETHSTETHPool.exchange{value: msg.value}(\n 0,\n 1,\n msg.value,\n minSTETHAmount\n );\n uint256 balance = stETH.balanceOf(address(this));\n stETH.safeApprove(address(stETHVault), balance);\n stETHVault.depositYieldTokenFor(balance, msg.sender);\n }\n}\n" + }, + "contracts/interfaces/ICurveETHSTETHPool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\ninterface ICurveETHSTETHPool {\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 dx,\n uint256 min_dy\n ) external payable returns (uint256);\n}\n" + }, + "contracts/utils/OptionsPurchaseQueue.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {SafeCast} from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport {IOptionsPurchaseQueue} from \"../interfaces/IOptionsPurchaseQueue.sol\";\nimport {IRibbonThetaVault} from \"../interfaces/IRibbonThetaVault.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\ncontract OptionsPurchaseQueue is IOptionsPurchaseQueue, Ownable {\n using SafeERC20 for IERC20;\n\n /************************************************\n * STORAGE\n ***********************************************/\n\n /// @notice Stores the purchase queue for each vault\n mapping(address => Purchase[]) public override purchases;\n\n /// @notice Stores the total options being purchased for each vault\n mapping(address => uint256) public override totalOptionsAmount;\n\n /// @notice Stores the amount of options the vault is allocating towards the purchase queue\n /// @dev When vaultAllocatedOptions != 0, new purchase requests for the vault are blocked since the vault has\n /// already allocated options for this contract\n mapping(address => uint256) public override vaultAllocatedOptions;\n\n /// @notice Stores whether a buyer is whitelisted\n mapping(address => bool) public override whitelistedBuyer;\n\n /// @notice Stores the ceiling price of a vaults options\n /// @dev If the ceilingPrice != 0, then the vault is available for requesting purchases\n mapping(address => uint256) public override ceilingPrice;\n\n /// @notice Minimum amount of options a buyer needs to request from a vault, necessary to prevent the purchase\n /// queue from getting griefed\n /// @dev Buyers on the whitelist are exempted from this requirement\n mapping(address => uint256) public override minPurchaseAmount;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n /**\n * @notice Emitted when a purchase is requested\n * @param buyer The buyer requesting the purchase\n * @param vault The vault the buyer is purchasing from\n * @param optionsAmount Amount of options requested\n * @param premiums Total premiums from the buyers (optionsAmount * ceilingPrice)\n */\n event PurchaseRequested(\n address indexed buyer,\n address indexed vault,\n uint256 optionsAmount,\n uint256 premiums\n );\n\n /**\n * @notice Emitted when a purchase is cancelled\n * @param buyer The buyer cancelling their purchase\n * @param vault The vault the buyer was purchasing from\n * @param optionsAmount Amount of options cancelled\n * @param premiums Total premiums transferred back to the buyer\n */\n event PurchaseCancelled(\n address indexed buyer,\n address indexed vault,\n uint256 optionsAmount,\n uint256 premiums\n );\n\n /**\n * @notice Emitted when the vault allocates options to be sold to the buyers\n * @param vault The vault allocating options\n * @param allocatedOptions Amount of options allocated\n */\n event OptionsAllocated(address indexed vault, uint256 allocatedOptions);\n\n /**\n * @notice Emitted when the vault sells options to the buyers\n * @param vault The vault selling the options\n * @param totalPremiums Total premiums earnt by the vault\n * @param totalOptions Total options transferred to the buyers (allocatedOptions)\n */\n event OptionsSold(\n address indexed vault,\n uint256 totalPremiums,\n uint256 totalOptions\n );\n\n /**\n * @notice Emitted when a buyer is whitelisted for purchasing options\n * @param buyer The whitelisted buyer\n */\n event AddWhitelist(address indexed buyer);\n\n /**\n * @notice Emitted when a buyer is removed from the whitelist for purchasing options\n * @param buyer The blacklisted buyer\n */\n event RemoveWhitelist(address indexed buyer);\n\n /**\n * @notice Emitted when the ceiling price for a vault is updated\n * @param vault The vault\n * @param ceilingPrice The new ceiling price\n */\n event CeilingPriceUpdated(address indexed vault, uint256 ceilingPrice);\n\n /**\n * @notice Emitted when the minimum purchase amount for a vault is updated\n * @param vault The vault\n * @param optionsAmount The new minimum purchase amount\n */\n event MinPurchaseAmountUpdated(\n address indexed vault,\n uint256 optionsAmount\n );\n\n /************************************************\n * BUYER OPERATIONS\n ***********************************************/\n\n /**\n * @notice Create a request to purchase options from a vault at the auction settlement price\n * @dev The buyer must be whitelisted to prevent the purchase queue from getting griefed (since sellToBuyers()\n * iterates through it). This function transfers the premiums for the options from the buyer at the ceiling\n * price (maximum price the buyer has to pay), however when the options are sold the buyer only pays the\n * auction settlement price and the leftover premiums are transferred back to the buyer. This function will\n * revert after the vault calls allocateOptions. New purchases can be made after the vault calls sellToBuyers().\n * The requests on the purchased queue are filled FIFO. Any unfilled/partially filled requests are refunded\n * their premiums, this can occur when the vault allocates less options than there are on the queue.\n * @param vault The vault to purchase options from\n * @param optionsAmount Amount of options requested\n * @return premiums Amount of premiums transferred from the buyer\n */\n function requestPurchase(address vault, uint256 optionsAmount)\n external\n override\n returns (uint256)\n {\n uint256 _ceilingPrice = ceilingPrice[vault];\n require(_ceilingPrice != 0, \"Invalid vault\");\n require(optionsAmount != 0, \"!optionsAmount\");\n // Exempt buyers on the whitelist from the minimum purchase requirement\n require(\n optionsAmount >= minPurchaseAmount[vault] ||\n whitelistedBuyer[msg.sender],\n \"Minimum purchase requirement\"\n );\n // This prevents new purchase requested after the vault has set its allocation\n require(vaultAllocatedOptions[vault] == 0, \"Vault allocated\");\n\n // premiums = optionsAmount * ceilingPrice\n uint256 premiums =\n (optionsAmount * _ceilingPrice) / (10**Vault.OTOKEN_DECIMALS);\n\n // Add purchase to queue\n purchases[vault].push(\n Purchase(\n SafeCast.toUint128(optionsAmount),\n SafeCast.toUint128(premiums),\n msg.sender\n )\n );\n\n totalOptionsAmount[vault] += optionsAmount;\n\n // Transfer premiums from the buyer to this contract\n IERC20(IRibbonThetaVault(vault).vaultParams().asset).safeTransferFrom(\n msg.sender,\n address(this),\n premiums\n );\n\n emit PurchaseRequested(msg.sender, vault, optionsAmount, premiums);\n\n return premiums;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Allocate options to the purchase queue\n * @dev Only callable by the vault selling options. Since we cannot allocate more options than there are on the\n * purchase queue, we cap the allocated options at the totalOptionsAmount. The vault decides how many options\n * of its options it wants to allocate. Allows allocating additional options if already called. Transfers the\n * options from the vault to this contract.\n * @param allocatedOptions Maximum amount of options the vault can allocate to buyers\n * @return allocatedOptions The actual amount of options allocated\n */\n function allocateOptions(uint256 allocatedOptions)\n external\n override\n returns (uint256)\n {\n require(ceilingPrice[msg.sender] != 0, \"Not vault\");\n\n // Prevent the vault from allocating more options than there are requested\n allocatedOptions = getOptionsAllocation(msg.sender, allocatedOptions);\n\n // Blocks new purchase requests until sellToBuyers() is called\n vaultAllocatedOptions[msg.sender] += allocatedOptions;\n\n if (allocatedOptions != 0) {\n // Transfer allocated options from the vault to this contract\n IERC20(IRibbonThetaVault(msg.sender).currentOption())\n .safeTransferFrom(msg.sender, address(this), allocatedOptions);\n }\n\n emit OptionsAllocated(msg.sender, allocatedOptions);\n\n return allocatedOptions;\n }\n\n /**\n * @notice Sells allocated options to the buyers on the purchase queue\n * @dev Only callable by the vault. Lets say the vault starts an auction and it doesn't fully fill and\n * settles at a poor price. If this function were callable by anyone, then they could sell the allocated\n * options to the buyers at the poor price. Hence the vault should call this once its auction has settled at a\n * good price. The vault must allocate options first, otherwise all the buyers are returned their premiums.\n * The buyers receive their options at the auction settlement price and any leftover premiums are refunded.\n * If the auction settles above the ceiling price, the vault receives the premiums at the ceiling price (so it\n * receives premiums at a worse price than the auction) and the buyers are not refunded.\n * @param settlementPrice The vault passes in the settlement price of the options\n * @return totalPremiums The total premiums the vault received from the purchase queue\n */\n function sellToBuyers(uint256 settlementPrice)\n external\n override\n returns (uint256)\n {\n require(ceilingPrice[msg.sender] != 0, \"Not vault\");\n\n uint256 totalPremiums;\n uint256 allocatedOptions = vaultAllocatedOptions[msg.sender];\n uint256 totalOptions = allocatedOptions; // Cache allocatedOptions here for emitting an event later\n IERC20 currentOption =\n IERC20(IRibbonThetaVault(msg.sender).currentOption());\n IERC20 asset =\n IERC20(IRibbonThetaVault(msg.sender).vaultParams().asset);\n Purchase[] memory purchaseQueue = purchases[msg.sender];\n\n for (uint256 i; i < purchaseQueue.length; i++) {\n if (allocatedOptions == 0) {\n // Transfer premiums back to the buyer if no options are left\n asset.safeTransfer(\n purchaseQueue[i].buyer,\n purchaseQueue[i].premiums\n );\n } else {\n // Prevent transferring more options than there are allocated\n // optionsAmount = min(purchase.optionsAmount, allocatedOptions)\n uint256 optionsAmount =\n purchaseQueue[i].optionsAmount < allocatedOptions\n ? purchaseQueue[i].optionsAmount\n : allocatedOptions;\n\n // premiums = optionsAmount * settlementPrice\n uint256 premiums =\n (optionsAmount * settlementPrice) /\n (10**Vault.OTOKEN_DECIMALS);\n\n if (premiums < purchaseQueue[i].premiums) {\n // Transfer leftover premiums back to the buyer\n asset.safeTransfer(\n purchaseQueue[i].buyer,\n purchaseQueue[i].premiums - premiums\n );\n\n totalPremiums += premiums;\n } else {\n // If the settlement price exceed the buyer's price (ceiling price), the vault receives all\n // of the buyer's premiums at a worse price than the auction\n totalPremiums += purchaseQueue[i].premiums;\n }\n\n // Transfer options to the buyer\n currentOption.safeTransfer(\n purchaseQueue[i].buyer,\n optionsAmount\n );\n\n // Deduct transferred options from allocatedOptions\n allocatedOptions -= optionsAmount;\n }\n }\n\n // Transfer premiums to the vault\n if (totalPremiums != 0) asset.safeTransfer(msg.sender, totalPremiums);\n\n // Clear purchase queue\n delete purchases[msg.sender];\n totalOptionsAmount[msg.sender] = 0;\n // Purchase requests are unblocked\n vaultAllocatedOptions[msg.sender] = 0;\n\n emit OptionsSold(msg.sender, totalPremiums, totalOptions);\n\n return totalPremiums;\n }\n\n /************************************************\n * OWNER OPERATIONS\n ***********************************************/\n\n /**\n * @notice Cancels all purchase requests for a delisted vault\n * @dev Only callable by the owner. Will revert if options have already been allocated by the vault.\n * @param vault The vault to cancel all purchases for\n */\n function cancelAllPurchases(address vault) external override onlyOwner {\n // Revert if the vault is still listed\n require(ceilingPrice[vault] == 0, \"Vault listed\");\n // This prevents cancellations after the vault has set its allocation\n require(vaultAllocatedOptions[vault] == 0, \"Vault allocated\");\n\n IERC20 asset = IERC20(IRibbonThetaVault(vault).vaultParams().asset);\n Purchase[] memory purchaseQueue = purchases[vault];\n\n for (uint256 i; i < purchaseQueue.length; i++) {\n // Refund premiums to the buyer\n asset.safeTransfer(\n purchaseQueue[i].buyer,\n purchaseQueue[i].premiums\n );\n\n emit PurchaseCancelled(\n purchaseQueue[i].buyer,\n vault,\n purchaseQueue[i].optionsAmount,\n purchaseQueue[i].premiums\n );\n }\n\n // Clear purchase queue\n delete purchases[vault];\n totalOptionsAmount[vault] = 0;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Adds a buyer to the purchase queue whitelist\n * @dev Only callable by the owner\n * @param buyer The buyer to whitelist\n */\n function addWhitelist(address buyer) external override onlyOwner {\n require(buyer != address(0), \"!buyer\");\n\n whitelistedBuyer[buyer] = true;\n\n emit AddWhitelist(buyer);\n }\n\n /**\n * @notice Removes a buyer from the purchase queue whitelist\n * @dev Only callable by the owner\n * @param buyer The buyer to remove from the whitelist\n */\n function removeWhitelist(address buyer) external override onlyOwner {\n require(buyer != address(0), \"!buyer\");\n\n whitelistedBuyer[buyer] = false;\n\n emit RemoveWhitelist(buyer);\n }\n\n /**\n * @notice Set the ceiling price for a vault\n * @dev Only callable by the owner\n * @param vault The vault to set a ceiling price for\n * @param price The ceiling price\n */\n function setCeilingPrice(address vault, uint256 price)\n external\n override\n onlyOwner\n {\n require(vault != address(0), \"!vault\");\n\n // Setting the ceiling price to 0 is the same as delisting a vault\n ceilingPrice[vault] = price;\n\n emit CeilingPriceUpdated(vault, price);\n }\n\n /**\n * @notice Sets the minimum purchase amount for a vault\n * @dev Only callable by the owner\n * @param vault The vault to set the minimum purchase amount for\n * @param optionsAmount The minimum options purchase amount\n */\n function setMinPurchaseAmount(address vault, uint256 optionsAmount)\n external\n override\n onlyOwner\n {\n require(vault != address(0), \"!vault\");\n\n minPurchaseAmount[vault] = optionsAmount;\n\n emit MinPurchaseAmountUpdated(vault, optionsAmount);\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Gets all the purchase requests for a vault\n * @param vault The vault to get purchase requests for\n * @return purchases The purchase array\n */\n function getPurchases(address vault)\n external\n view\n override\n returns (Purchase[] memory)\n {\n return purchases[vault];\n }\n\n /**\n * @notice Gets the premiums the buyer needs to deposit to request a certain amount of options\n * @param vault The vault to purchase options from\n * @param optionsAmount Amount of options requested\n * @return premiums Premiums required to request a purchase\n */\n function getPremiums(address vault, uint256 optionsAmount)\n external\n view\n override\n returns (uint256)\n {\n // premiums = optionsAmount * ceilingPrice\n return\n (optionsAmount * ceilingPrice[vault]) / (10**Vault.OTOKEN_DECIMALS);\n }\n\n /**\n * @notice Gets the amount of options the vault can allocate to the queue\n * @param vault The vault selling options to the queue\n * @param allocatedOptions Maximum amount of options the vault can allocate to the queue\n * @return allocatedOptions Actual amount of options the vault allocates to the queue\n */\n function getOptionsAllocation(address vault, uint256 allocatedOptions)\n public\n view\n override\n returns (uint256)\n {\n // Prevent the vault from allocating more options than there are requested\n uint256 optionsAmount =\n totalOptionsAmount[vault] - vaultAllocatedOptions[vault];\n // allocatedOptions = min(allocatedOptions, totalOptionsAmount[vault] - vaultAllocatedOptions[vault])\n return\n optionsAmount < allocatedOptions ? optionsAmount : allocatedOptions;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/tests/MockRibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {IOptionsPurchaseQueue} from \"../interfaces/IOptionsPurchaseQueue.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\ncontract MockRibbonVault {\n Vault.VaultParams public vaultParams;\n\n address public currentOption;\n\n function setAsset(address asset) external {\n vaultParams.asset = asset;\n }\n\n function setCurrentOption(address option) external {\n currentOption = option;\n }\n\n function allocateOptions(\n address optionsPurchaseQueue,\n address option,\n uint256 optionsAmount\n ) external {\n IERC20(option).approve(optionsPurchaseQueue, optionsAmount);\n IOptionsPurchaseQueue(optionsPurchaseQueue).allocateOptions(\n optionsAmount\n );\n }\n\n function sellToBuyers(address optionsPurchaseQueue, uint256 settlementPrice)\n external\n {\n IOptionsPurchaseQueue(optionsPurchaseQueue).sellToBuyers(\n settlementPrice\n );\n }\n}\n" + }, + "contracts/tests/TestVaultLifecycle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {VaultLifecycle} from \"../libraries/VaultLifecycle.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\ncontract TestVaultLifecycle {\n Vault.VaultState public vaultState;\n\n function getNextFriday(uint256 currentExpiry)\n external\n pure\n returns (uint256 nextFriday)\n {\n return VaultLifecycle.getNextFriday(currentExpiry);\n }\n\n function getNextExpiry(address currentOption)\n external\n view\n returns (uint256 nextExpiry)\n {\n return VaultLifecycle.getNextExpiry(currentOption);\n }\n\n function balanceOf(address account) public view returns (uint256) {\n if (account == address(this)) {\n return 1 ether;\n }\n return 0;\n }\n\n function setVaultState(Vault.VaultState calldata newVaultState) public {\n vaultState.totalPending = newVaultState.totalPending;\n vaultState.queuedWithdrawShares = newVaultState.queuedWithdrawShares;\n }\n\n function rollover(VaultLifecycle.RolloverParams calldata params)\n external\n view\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares,\n uint256 performanceFeeInAsset,\n uint256 totalVaultFee\n )\n {\n return VaultLifecycle.rollover(vaultState, params);\n }\n\n function getAuctionSettlementPrice(\n address gnosisEasyAuction,\n uint256 optionAuctionID\n ) external view returns (uint256) {\n return\n VaultLifecycle.getAuctionSettlementPrice(\n gnosisEasyAuction,\n optionAuctionID\n );\n }\n}\n" + }, + "contracts/tests/TestVaultLifecycleTreasury.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {VaultLifecycleTreasury} from \"../libraries/VaultLifecycleTreasury.sol\";\n\ncontract TestVaultLifecycleTreasury {\n function getNextExpiry(uint256 currentExpiry, uint256 interval)\n external\n pure\n returns (uint256)\n {\n return VaultLifecycleTreasury.getNextExpiry(currentExpiry, interval);\n }\n}\n" + }, + "contracts/utils/DeltaStrikeSelection.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {\n IPriceOracle\n} from \"@ribbon-finance/rvol/contracts/interfaces/IPriceOracle.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\nimport {\n IManualVolatilityOracle\n} from \"@ribbon-finance/rvol/contracts/interfaces/IManualVolatilityOracle.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\ncontract DeltaStrikeSelection is Ownable {\n using SafeMath for uint256;\n\n /**\n * Immutables\n */\n IOptionsPremiumPricer public immutable optionsPremiumPricer;\n\n IManualVolatilityOracle public immutable volatilityOracle;\n\n // delta for options strike price selection. 1 is 10000 (10**4)\n uint256 public delta;\n\n // step in absolute terms at which we will increment\n // (ex: 100 * 10 ** assetOracleDecimals means we will move at increments of 100 points)\n uint256 public step;\n\n // multiplier to shift asset prices\n uint256 private immutable assetOracleMultiplier;\n\n // Delta are in 4 decimal places. 1 * 10**4 = 1 delta.\n uint256 private constant DELTA_MULTIPLIER = 10**4;\n\n // ChainLink's USD Price oracles return results in 8 decimal places\n uint256 private constant ORACLE_PRICE_MULTIPLIER = 10**8;\n\n event DeltaSet(uint256 oldDelta, uint256 newDelta, address indexed owner);\n event StepSet(uint256 oldStep, uint256 newStep, address indexed owner);\n\n constructor(\n address _optionsPremiumPricer,\n uint256 _delta,\n uint256 _step\n ) {\n require(_optionsPremiumPricer != address(0), \"!_optionsPremiumPricer\");\n require(_delta > 0, \"!_delta\");\n require(_delta <= DELTA_MULTIPLIER, \"newDelta cannot be more than 1\");\n require(_step > 0, \"!_step\");\n\n optionsPremiumPricer = IOptionsPremiumPricer(_optionsPremiumPricer);\n volatilityOracle = IManualVolatilityOracle(\n IOptionsPremiumPricer(_optionsPremiumPricer).volatilityOracle()\n );\n // ex: delta = 7500 (.75)\n delta = _delta;\n uint256 _assetOracleMultiplier =\n 10 **\n IPriceOracle(\n IOptionsPremiumPricer(_optionsPremiumPricer).priceOracle()\n )\n .decimals();\n\n step = _step;\n\n assetOracleMultiplier = _assetOracleMultiplier;\n }\n\n /**\n * @notice Gets the strike price satisfying the delta value\n * given the expiry timestamp and whether option is call or put\n * @param expiryTimestamp is the unix timestamp of expiration\n * @param isPut is whether option is put or call\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\n * @return newDelta is the delta of the option given its parameters\n */\n function getStrikePrice(uint256 expiryTimestamp, bool isPut)\n external\n view\n returns (uint256 newStrikePrice, uint256 newDelta)\n {\n // asset's annualized volatility\n uint256 annualizedVol =\n volatilityOracle.annualizedVol(optionsPremiumPricer.optionId()).mul(\n 10**10\n );\n return _getStrikePrice(expiryTimestamp, isPut, annualizedVol);\n }\n\n /**\n * @notice Gets the strike price satisfying the delta value\n * given the expiry timestamp and whether option is call or put\n * @param expiryTimestamp is the unix timestamp of expiration\n * @param isPut is whether option is put or call\n * @param annualizedVol is IV of the asset at the specified delta\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\n * @return newDelta is the delta of the option given its parameters\n */\n function getStrikePriceWithVol(\n uint256 expiryTimestamp,\n bool isPut,\n uint256 annualizedVol\n ) external view returns (uint256 newStrikePrice, uint256 newDelta) {\n return\n _getStrikePrice(expiryTimestamp, isPut, annualizedVol.mul(10**10));\n }\n\n /**\n * @notice Gets the strike price satisfying the delta value\n * given the expiry timestamp and whether option is call or put\n * @param expiryTimestamp is the unix timestamp of expiration\n * @param isPut is whether option is put or call\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\n * @return newDelta is the delta of the option given its parameters\n */\n\n function _getStrikePrice(\n uint256 expiryTimestamp,\n bool isPut,\n uint256 annualizedVol\n ) internal view returns (uint256 newStrikePrice, uint256 newDelta) {\n require(\n expiryTimestamp > block.timestamp,\n \"Expiry must be in the future!\"\n );\n\n // asset price\n uint256 assetPrice = optionsPremiumPricer.getUnderlyingPrice();\n\n // For each asset prices with step of 'step' (down if put, up if call)\n // if asset's getOptionDelta(currStrikePrice, spotPrice, annualizedVol, t) == (isPut ? 1 - delta:delta)\n // with certain margin of error\n // return strike price\n\n uint256 strike =\n isPut\n ? assetPrice.sub(assetPrice % step).sub(step)\n : assetPrice.add(step - (assetPrice % step)).add(step);\n uint256 targetDelta = isPut ? DELTA_MULTIPLIER.sub(delta) : delta;\n uint256 prevDelta = isPut ? 0 : DELTA_MULTIPLIER;\n\n while (true) {\n uint256 currDelta =\n optionsPremiumPricer.getOptionDelta(\n assetPrice.mul(ORACLE_PRICE_MULTIPLIER).div(\n assetOracleMultiplier\n ),\n strike,\n annualizedVol,\n expiryTimestamp\n );\n // If the current delta is between the previous\n // strike price delta and current strike price delta\n // then we are done\n bool foundTargetStrikePrice =\n isPut\n ? targetDelta >= prevDelta && targetDelta <= currDelta\n : targetDelta <= prevDelta && targetDelta >= currDelta;\n\n if (foundTargetStrikePrice) {\n uint256 finalDelta =\n _getBestDelta(prevDelta, currDelta, targetDelta, isPut);\n uint256 finalStrike =\n _getBestStrike(finalDelta, prevDelta, strike, isPut);\n require(\n isPut\n ? finalStrike <= assetPrice\n : finalStrike >= assetPrice,\n \"Invalid strike price\"\n );\n // make decimals consistent with oToken strike price decimals (10 ** 8)\n return (\n finalStrike.mul(ORACLE_PRICE_MULTIPLIER).div(\n assetOracleMultiplier\n ),\n finalDelta\n );\n }\n\n strike = isPut ? strike.sub(step) : strike.add(step);\n\n prevDelta = currDelta;\n }\n }\n\n /**\n * @notice Rounds to best delta value\n * @param prevDelta is the delta of the previous strike price\n * @param currDelta is delta of the current strike price\n * @param targetDelta is the delta we are targeting\n * @param isPut is whether its a put\n * @return the best delta value\n */\n function _getBestDelta(\n uint256 prevDelta,\n uint256 currDelta,\n uint256 targetDelta,\n bool isPut\n ) private pure returns (uint256) {\n uint256 finalDelta;\n\n // for tie breaks (ex: 0.05 <= 0.1 <= 0.15) round to higher strike price\n // for calls and lower strike price for puts for deltas\n if (isPut) {\n uint256 upperBoundDiff = currDelta.sub(targetDelta);\n uint256 lowerBoundDiff = targetDelta.sub(prevDelta);\n finalDelta = lowerBoundDiff <= upperBoundDiff\n ? prevDelta\n : currDelta;\n } else {\n uint256 upperBoundDiff = prevDelta.sub(targetDelta);\n uint256 lowerBoundDiff = targetDelta.sub(currDelta);\n finalDelta = lowerBoundDiff <= upperBoundDiff\n ? currDelta\n : prevDelta;\n }\n\n return finalDelta;\n }\n\n /**\n * @notice Rounds to best delta value\n * @param finalDelta is the best delta value we found\n * @param prevDelta is delta of the previous strike price\n * @param strike is the strike of the previous iteration\n * @param isPut is whether its a put\n * @return the best strike\n */\n function _getBestStrike(\n uint256 finalDelta,\n uint256 prevDelta,\n uint256 strike,\n bool isPut\n ) private view returns (uint256) {\n if (finalDelta != prevDelta) {\n return strike;\n }\n return isPut ? strike.add(step) : strike.sub(step);\n }\n\n /**\n * @notice Sets new delta value\n * @param newDelta is the new delta value\n */\n function setDelta(uint256 newDelta) external onlyOwner {\n require(newDelta > 0, \"!newDelta\");\n require(newDelta <= DELTA_MULTIPLIER, \"newDelta cannot be more than 1\");\n uint256 oldDelta = delta;\n delta = newDelta;\n emit DeltaSet(oldDelta, newDelta, msg.sender);\n }\n\n /**\n * @notice Sets new step value\n * @param newStep is the new step value\n */\n function setStep(uint256 newStep) external onlyOwner {\n require(newStep > 0, \"!newStep\");\n uint256 oldStep = step;\n step = newStep;\n emit StepSet(oldStep, newStep, msg.sender);\n }\n}\n" + }, + "contracts/libraries/UniswapRouter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {ISwapRouter} from \"../interfaces/ISwapRouter.sol\";\nimport {IUniswapV3Factory} from \"../interfaces/IUniswapV3Factory.sol\";\nimport \"./Path.sol\";\n\nlibrary UniswapRouter {\n using Path for bytes;\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n /**\n * @notice Check if the path set for swap is valid\n * @param swapPath is the swap path e.g. encodePacked(tokenIn, poolFee, tokenOut)\n * @param validTokenIn is the contract address of the correct tokenIn\n * @param validTokenOut is the contract address of the correct tokenOut\n * @param uniswapFactory is the contract address of UniswapV3 factory\n * @return isValidPath is whether the path is valid\n */\n function checkPath(\n bytes memory swapPath,\n address validTokenIn,\n address validTokenOut,\n address uniswapFactory\n ) internal view returns (bool isValidPath) {\n // Function checks if the tokenIn and tokenOut in the swapPath\n // matches the validTokenIn and validTokenOut specified.\n address tokenIn;\n address tokenOut;\n address tempTokenIn;\n uint24 fee;\n IUniswapV3Factory factory = IUniswapV3Factory(uniswapFactory);\n\n // Return early if swapPath is below the bare minimum (43)\n require(swapPath.length >= 43, \"Path too short\");\n // Return early if swapPath is above the max (66)\n // At worst we have 2 hops e.g. USDC > WETH > asset\n require(swapPath.length <= 66, \"Path too long\");\n\n // Decode the first pool in path\n (tokenIn, tokenOut, fee) = swapPath.decodeFirstPool();\n\n // Check to factory if pool exists\n require(\n factory.getPool(tokenIn, tokenOut, fee) != address(0),\n \"Pool does not exist\"\n );\n\n // Check next pool if multiple pools\n while (swapPath.hasMultiplePools()) {\n // Remove the first pool from path\n swapPath = swapPath.skipToken();\n // Check the next pool and update tokenOut\n (tempTokenIn, tokenOut, fee) = swapPath.decodeFirstPool();\n\n require(\n factory.getPool(tokenIn, tokenOut, fee) != address(0),\n \"Pool does not exist\"\n );\n }\n\n return tokenIn == validTokenIn && tokenOut == validTokenOut;\n }\n\n /**\n * @notice Swaps assets by calling UniswapV3 router\n * @param recipient is the address of recipient of the tokenOut\n * @param tokenIn is the address of the token given to the router\n * @param amountIn is the amount of tokenIn given to the router\n * @param minAmountOut is the minimum acceptable amount of tokenOut received from swap\n * @param router is the contract address of UniswapV3 router\n * @param swapPath is the swap path e.g. encodePacked(tokenIn, poolFee, tokenOut)\n * @return amountOut is the amount of tokenOut received from the swap\n */\n function swap(\n address recipient,\n address tokenIn,\n uint256 amountIn,\n uint256 minAmountOut,\n address router,\n bytes calldata swapPath\n ) internal returns (uint256 amountOut) {\n // Approve router to spend tokenIn\n IERC20(tokenIn).safeApprove(router, amountIn);\n\n // Swap assets using UniswapV3 router\n ISwapRouter.ExactInputParams memory swapParams =\n ISwapRouter.ExactInputParams({\n recipient: recipient,\n path: swapPath,\n deadline: block.timestamp.add(10 minutes),\n amountIn: amountIn,\n amountOutMinimum: minAmountOut\n });\n\n amountOut = ISwapRouter(router).exactInput(swapParams);\n\n return amountOut;\n }\n}\n" + }, + "contracts/interfaces/ISwapRouter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity =0.8.4;\npragma abicoder v2;\n\n/// Source: https://github.com/Uniswap/v3-core/blob/main/contracts/interfaces/callback/IUniswapV3SwapCallback.sol\n\n/// @title Callback for IUniswapV3PoolActions#swap\n/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface\ninterface IUniswapV3SwapCallback {\n /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.\n /// @dev In the implementation you must pay the pool tokens owed for the swap.\n /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.\n /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.\n /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.\n /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.\n /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call\n function uniswapV3SwapCallback(\n int256 amount0Delta,\n int256 amount1Delta,\n bytes calldata data\n ) external;\n}\n\n/// Source: https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/ISwapRouter.sol\n\n/// @title Router token swapping functionality\n/// @notice Functions for swapping tokens via Uniswap V3\ninterface ISwapRouter is IUniswapV3SwapCallback {\n struct ExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n uint24 fee;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInputSingle(ExactInputSingleParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n uint24 fee;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutputSingle(ExactOutputSingleParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n\n struct ExactOutputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutput(ExactOutputParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n}\n" + }, + "contracts/interfaces/IUniswapV3Factory.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// Source: https://github.com/Uniswap/v3-core/blob/main/contracts/interfaces/IUniswapV3Factory.sol\npragma solidity =0.8.4;\n\n/// @title The interface for the Uniswap V3 Factory\n/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees\ninterface IUniswapV3Factory {\n /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist\n /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order\n /// @param tokenA The contract address of either token0 or token1\n /// @param tokenB The contract address of the other token\n /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip\n /// @return pool The pool address\n function getPool(\n address tokenA,\n address tokenB,\n uint24 fee\n ) external view returns (address pool);\n}\n" + }, + "contracts/libraries/Path.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// Source: https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/Path.sol\npragma solidity =0.8.4;\n\nimport \"./BytesLib.sol\";\n\n/// @title Functions for manipulating path data for multihop swaps\nlibrary Path {\n using BytesLib for bytes;\n\n /// @dev The length of the bytes encoded address\n uint256 private constant ADDR_SIZE = 20;\n /// @dev The length of the bytes encoded fee\n uint256 private constant FEE_SIZE = 3;\n\n /// @dev The offset of a single token address and pool fee\n uint256 private constant NEXT_OFFSET = ADDR_SIZE + FEE_SIZE;\n /// @dev The offset of an encoded pool key\n uint256 private constant POP_OFFSET = NEXT_OFFSET + ADDR_SIZE;\n /// @dev The minimum length of an encoding that contains 2 or more pools\n uint256 private constant MULTIPLE_POOLS_MIN_LENGTH =\n POP_OFFSET + NEXT_OFFSET;\n\n /// @notice Returns true iff the path contains two or more pools\n /// @param path The encoded swap path\n /// @return True if path contains two or more pools, otherwise false\n function hasMultiplePools(bytes memory path) internal pure returns (bool) {\n return path.length >= MULTIPLE_POOLS_MIN_LENGTH;\n }\n\n /// @notice Returns the number of pools in the path\n /// @param path The encoded swap path\n /// @return The number of pools in the path\n function numPools(bytes memory path) internal pure returns (uint256) {\n // Ignore the first token address. From then on every fee and token offset indicates a pool.\n return ((path.length - ADDR_SIZE) / NEXT_OFFSET);\n }\n\n /// @notice Decodes the first pool in path\n /// @param path The bytes encoded swap path\n /// @return tokenA The first token of the given pool\n /// @return tokenB The second token of the given pool\n /// @return fee The fee level of the pool\n function decodeFirstPool(bytes memory path)\n internal\n pure\n returns (\n address tokenA,\n address tokenB,\n uint24 fee\n )\n {\n tokenA = path.toAddress(0);\n fee = path.toUint24(ADDR_SIZE);\n tokenB = path.toAddress(NEXT_OFFSET);\n }\n\n /// @notice Gets the segment corresponding to the first pool in the path\n /// @param path The bytes encoded swap path\n /// @return The segment containing all data necessary to target the first pool in the path\n function getFirstPool(bytes memory path)\n internal\n pure\n returns (bytes memory)\n {\n return path.slice(0, POP_OFFSET);\n }\n\n /// @notice Skips a token + fee element from the buffer and returns the remainder\n /// @param path The swap path\n /// @return The remaining token + fee elements in the path\n function skipToken(bytes memory path) internal pure returns (bytes memory) {\n return path.slice(NEXT_OFFSET, path.length - NEXT_OFFSET);\n }\n}\n" + }, + "contracts/libraries/BytesLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Source: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol\n/*\n * @title Solidity Bytes Arrays Utils\n * @author Gonçalo Sá \n *\n * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.\n * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.\n */\npragma solidity =0.8.4;\n\nlibrary BytesLib {\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n ) internal pure returns (bytes memory) {\n require(_length + 31 >= _length, \"slice_overflow\");\n require(_bytes.length >= _start + _length, \"slice_outOfBounds\");\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(\n add(tempBytes, lengthmod),\n mul(0x20, iszero(lengthmod))\n )\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(\n add(\n add(_bytes, lengthmod),\n mul(0x20, iszero(lengthmod))\n ),\n _start\n )\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n\n function toAddress(bytes memory _bytes, uint256 _start)\n internal\n pure\n returns (address)\n {\n require(_bytes.length >= _start + 20, \"toAddress_outOfBounds\");\n address tempAddress;\n\n assembly {\n tempAddress := div(\n mload(add(add(_bytes, 0x20), _start)),\n 0x1000000000000000000000000\n )\n }\n\n return tempAddress;\n }\n\n function toUint24(bytes memory _bytes, uint256 _start)\n internal\n pure\n returns (uint24)\n {\n require(_bytes.length >= _start + 3, \"toUint24_outOfBounds\");\n uint24 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x3), _start))\n }\n\n return tempUint;\n }\n}\n" + }, + "contracts/storage/RibbonDeltaVaultStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport {IRibbonThetaVault} from \"../interfaces/IRibbonThetaVault.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\nabstract contract RibbonDeltaVaultStorageV1 {\n // Ribbon counterparty theta vault\n IRibbonThetaVault public counterpartyThetaVault;\n // % of funds to be used for weekly option purchase\n uint256 public optionAllocation;\n // Delta vault equivalent of lockedAmount\n uint256 public balanceBeforePremium;\n // User Id of delta vault in latest gnosis auction\n Vault.AuctionSellOrder public auctionSellOrder;\n}\n\nabstract contract RibbonDeltaVaultStorageV2 {\n // Amount locked for scheduled withdrawals last week;\n uint128 public lastQueuedWithdrawAmount;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of RibbonDeltaVaultStorage\n// e.g. RibbonDeltaVaultStorage, so finally it would look like\n// contract RibbonDeltaVaultStorage is RibbonDeltaVaultStorageV1, RibbonDeltaVaultStorageV2\nabstract contract RibbonDeltaVaultStorage is\n RibbonDeltaVaultStorageV1,\n RibbonDeltaVaultStorageV2\n{\n\n}\n" + }, + "contracts/tests/MockLiquidityGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\ncontract MockLiquidityGauge {\n using SafeERC20 for IERC20;\n\n address public lp_token;\n uint256 public totalSupply;\n mapping(address => uint256) public balanceOf;\n\n constructor(address _lp_token) {\n lp_token = _lp_token;\n }\n\n function deposit(\n uint256 _value,\n address _addr,\n bool\n ) external {\n if (_value != 0) {\n totalSupply += _value;\n balanceOf[_addr] += _value;\n\n IERC20(lp_token).safeTransferFrom(\n msg.sender,\n address(this),\n _value\n );\n }\n }\n}\n" + }, + "contracts/vendor/upgrades/UpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport './Proxy.sol';\nimport '@openzeppelin/contracts/utils/Address.sol';\n\n/**\n * @title UpgradeabilityProxy\n * @dev This contract implements a proxy that allows to change the\n * implementation address to which it will delegate.\n * Such a change is called an implementation upgrade.\n */\ncontract UpgradeabilityProxy is Proxy {\n /**\n * @dev Contract constructor.\n * @param _logic Address of the initial implementation.\n * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\n */\n constructor(address _logic, bytes memory _data) payable {\n assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));\n _setImplementation(_logic);\n if(_data.length > 0) {\n (bool success,) = _logic.delegatecall(_data);\n require(success);\n }\n }\n\n /**\n * @dev Emitted when the implementation is upgraded.\n * @param implementation Address of the new implementation.\n */\n event Upgraded(address indexed implementation);\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Returns the current implementation.\n * @return impl Address of the current implementation\n */\n function _implementation() internal override view returns (address impl) {\n bytes32 slot = IMPLEMENTATION_SLOT;\n assembly {\n impl := sload(slot)\n }\n }\n\n /**\n * @dev Upgrades the proxy to a new implementation.\n * @param newImplementation Address of the new implementation.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Sets the implementation address of the proxy.\n * @param newImplementation Address of the new implementation.\n */\n function _setImplementation(address newImplementation) internal {\n require(Address.isContract(newImplementation), \"Cannot set a proxy implementation to a non-contract address\");\n\n bytes32 slot = IMPLEMENTATION_SLOT;\n\n assembly {\n sstore(slot, newImplementation)\n }\n }\n}\n" + }, + "contracts/vendor/upgrades/Proxy.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\n/**\n * @title Proxy\n * @dev Implements delegation of calls to other contracts, with proper\n * forwarding of return values and bubbling of failures.\n * It defines a fallback function that delegates all calls to the address\n * returned by the abstract _implementation() internal function.\n */\nabstract contract Proxy {\n /**\n * @dev Fallback function.\n * Implemented entirely in `_fallback`.\n */\n fallback () payable external {\n _fallback();\n }\n\n /**\n * @dev Receive function.\n * Implemented entirely in `_fallback`.\n */\n receive () payable external {\n // _fallback();\n }\n\n /**\n * @return The Address of the implementation.\n */\n function _implementation() internal virtual view returns (address);\n\n /**\n * @dev Delegates execution to an implementation contract.\n * This is a low level function that doesn't return to its internal call site.\n * It will return to the external caller whatever the implementation returns.\n * @param implementation Address to delegate.\n */\n function _delegate(address implementation) internal {\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 { revert(0, returndatasize()) }\n default { return(0, returndatasize()) }\n }\n }\n\n /**\n * @dev Function that is run as the first thing in the fallback function.\n * Can be redefined in derived contracts to add functionality.\n * Redefinitions must call super._willFallback().\n */\n function _willFallback() internal virtual {\n }\n\n /**\n * @dev fallback implementation.\n * Extracted to enable manual triggering.\n */\n function _fallback() internal {\n _willFallback();\n _delegate(_implementation());\n }\n}\n" + }, + "contracts/vendor/upgrades/AdminUpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.8;\n\nimport './UpgradeabilityProxy.sol';\n\n/**\n * @title AdminUpgradeabilityProxy\n * @dev This contract combines an upgradeability proxy with an authorization\n * mechanism for administrative tasks.\n * All external functions in this contract must be guarded by the\n * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity\n * feature proposal that would enable this to be done automatically.\n */\ncontract AdminUpgradeabilityProxy is UpgradeabilityProxy {\n /**\n * Contract constructor.\n * @param _logic address of the initial implementation.\n * @param admin_ Address of the proxy administrator.\n * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\n */\n constructor(address _logic, address admin_, bytes memory _data) UpgradeabilityProxy(_logic, _data) payable {\n assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));\n _setAdmin(admin_);\n }\n\n /**\n * @dev Emitted when the administration has been transferred.\n * @param previousAdmin Address of the previous admin.\n * @param newAdmin Address of the new admin.\n */\n event AdminChanged(address previousAdmin, address newAdmin);\n\n /**\n * @dev Storage slot with the admin of the contract.\n * This is the keccak-256 hash of \"eip1967.proxy.admin\" subtracted by 1, and is\n * validated in the constructor.\n */\n\n bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n /**\n * @dev Modifier to check whether the `msg.sender` is the admin.\n * If it is, it will run the function. Otherwise, it will delegate the call\n * to the implementation.\n */\n modifier ifAdmin() {\n if (msg.sender == _admin()) {\n _;\n } else {\n _fallback();\n }\n }\n\n /**\n * @return adminAddress The address of the proxy admin.\n */\n function admin() external ifAdmin returns (address adminAddress) {\n return _admin();\n }\n\n /**\n * @return implementationAddress The address of the implementation.\n */\n function implementation() external ifAdmin returns (address implementationAddress) {\n return _implementation();\n }\n\n /**\n * @dev Changes the admin of the proxy.\n * Only the current admin can call this function.\n * @param newAdmin Address to transfer proxy administration to.\n */\n function changeAdmin(address newAdmin) external ifAdmin {\n require(newAdmin != address(0), \"Cannot change the admin of a proxy to the zero address\");\n emit AdminChanged(_admin(), newAdmin);\n _setAdmin(newAdmin);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy.\n * Only the admin can call this function.\n * @param newImplementation Address of the new implementation.\n */\n function upgradeTo(address newImplementation) external ifAdmin {\n _upgradeTo(newImplementation);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy and call a function\n * on the new implementation.\n * This is useful to initialize the proxied contract.\n * @param newImplementation Address of the new implementation.\n * @param data Data to send as msg.data in the low level call.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {\n _upgradeTo(newImplementation);\n (bool success,) = newImplementation.delegatecall(data);\n require(success);\n }\n\n /**\n * @return adm The admin slot.\n */\n function _admin() internal view returns (address adm) {\n bytes32 slot = ADMIN_SLOT;\n assembly {\n adm := sload(slot)\n }\n }\n\n /**\n * @dev Sets the address of the proxy admin.\n * @param newAdmin Address of the new proxy admin.\n */\n function _setAdmin(address newAdmin) internal {\n bytes32 slot = ADMIN_SLOT;\n\n assembly {\n sstore(slot, newAdmin)\n }\n }\n\n /**\n * @dev Only fall back when the sender is not the admin.\n */\n function _willFallback() internal override virtual {\n require(msg.sender != _admin(), \"Cannot call fallback function from the proxy admin\");\n super._willFallback();\n }\n}\n" + }, + "contracts/utils/ManualStrikeSelection.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract ManualStrikeSelection is Ownable {\n /// @dev Selected strike price\n uint256 public strikePrice;\n\n /// @dev Delta for options strike price selection. 1 is 10000 (10**4)\n uint256 public constant delta = 1000;\n\n /**\n * @notice Sets the strike price, only callable by the owner\n * @param _strikePrice is the strike price of the option\n */\n function setStrikePrice(uint256 _strikePrice) external onlyOwner {\n strikePrice = _strikePrice;\n }\n\n /**\n * @notice Gets the strike price satisfying the delta value\n * given the expiry timestamp and whether option is call or put\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\n * @return newDelta is the delta of the option given its parameters\n */\n function getStrikePrice(uint256, bool)\n external\n view\n returns (uint256, uint256)\n {\n return (strikePrice, delta);\n }\n}\n" + }, + "contracts/tests/MockERC20.sol": { + "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockERC20 is ERC20 {\n constructor(string memory _name, string memory _symbol)\n ERC20(_name, _symbol)\n {}\n\n function mint(address to, uint256 value) external {\n _mint(to, value);\n }\n\n function burn(address from, uint256 value) external {\n _burn(from, value);\n }\n}\n" + }, + "contracts/oracles/OpynOracle.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\nimport {IOracle} from \"../interfaces/GammaInterface.sol\";\nimport {IPriceOracle} from \"../interfaces/IPriceOracle.sol\";\n\ncontract OpynOracle is IPriceOracle {\n /// @dev base decimals\n uint256 public constant override decimals = 8;\n\n /// @notice Gamma Protocol oracle\n IOracle public immutable oracle;\n\n /// @notice Asset to get the price of\n address public immutable asset;\n\n constructor(address _oracle, address _asset) {\n require(_oracle != address(0), \"!oracle\");\n require(_asset != address(0), \"!asset\");\n\n oracle = IOracle(_oracle);\n asset = _asset;\n }\n\n function latestAnswer() external view override returns (uint256) {\n return oracle.getPrice(asset);\n }\n}\n" + }, + "contracts/tests/TestShareMath.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {ShareMath} from \"../libraries/ShareMath.sol\";\n\ncontract TestShareMath {\n function assetToShares(\n uint256 assetAmount,\n uint256 pps,\n uint256 decimals\n ) external pure returns (uint256) {\n return ShareMath.assetToShares(assetAmount, pps, decimals);\n }\n\n function sharesToAsset(\n uint256 shares,\n uint256 pps,\n uint256 decimals\n ) external pure returns (uint256) {\n return ShareMath.sharesToAsset(shares, pps, decimals);\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/deployments/mainnet/OptionsPremiumPricerETHWithSwap.json b/deployments/mainnet/OptionsPremiumPricerETHWithSwap.json new file mode 100644 index 000000000..e22e9f5a7 --- /dev/null +++ b/deployments/mainnet/OptionsPremiumPricerETHWithSwap.json @@ -0,0 +1,235 @@ +{ + "address": "0xB38f51BeaE3718011CFBA1Aee51C4c9B05681286", + "abi": [ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_optionId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_volatilityOracle", + "type": "address" + }, + { + "internalType": "address", + "name": "_priceOracle", + "type": "address" + }, + { + "internalType": "address", + "name": "_stablesOracle", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "st", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expiryTimestamp", + "type": "uint256" + } + ], + "name": "getOptionDelta", + "outputs": [ + { + "internalType": "uint256", + "name": "delta", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "sp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "st", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "v", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expiryTimestamp", + "type": "uint256" + } + ], + "name": "getOptionDelta", + "outputs": [ + { + "internalType": "uint256", + "name": "delta", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "st", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expiryTimestamp", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isPut", + "type": "bool" + } + ], + "name": "getPremium", + "outputs": [ + { + "internalType": "uint256", + "name": "premium", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "st", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expiryTimestamp", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isPut", + "type": "bool" + } + ], + "name": "getPremiumInStables", + "outputs": [ + { + "internalType": "uint256", + "name": "premium", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getUnderlyingPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "optionId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "priceOracle", + "outputs": [ + { + "internalType": "contract IPriceOracle", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stablesOracle", + "outputs": [ + { + "internalType": "contract IPriceOracle", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "volatilityOracle", + "outputs": [ + { + "internalType": "contract IManualVolatilityOracle", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "transactionHash": "0xb29c7194f933751aeab0e41f564c8980dea24de285ad9d234327aedba33d60d3", + "receipt": { + "to": null, + "from": "0xF8368119Bb1073Cf01B841848725d81b542A4c19", + "contractAddress": "0xB38f51BeaE3718011CFBA1Aee51C4c9B05681286", + "transactionIndex": 94, + "gasUsed": "1260068", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x3e416df15ac2b09243635d58bd7c2cf7242f257c86ba2728ab80ae41bdc524ad", + "transactionHash": "0xb29c7194f933751aeab0e41f564c8980dea24de285ad9d234327aedba33d60d3", + "logs": [], + "blockNumber": 15132612, + "cumulativeGasUsed": "5194068", + "status": 1, + "byzantium": true + }, + "args": [ + "0x707b0fae9188af93a8511f3348818b9caf5c4b157addc8cbeb129328dcf8cb80", + "0xe0b7Cf788d8B8dFd2960D61eF34f7DD16c08e098", + "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419", + "0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6" + ], + "bytecode": "0x6101406040523480156200001257600080fd5b506040516200183938038062001839833981810160405260808110156200003857600080fd5b50805160208201516040830151606090930151919290916001600160a01b038316620000a0576040805162461bcd60e51b8152602060048201526012602482015271215f766f6c6174696c6974794f7261636c6560701b604482015290519081900360640190fd5b6001600160a01b038216620000ec576040805162461bcd60e51b815260206004820152600d60248201526c215f70726963654f7261636c6560981b604482015290519081900360640190fd5b6001600160a01b0381166200013a576040805162461bcd60e51b815260206004820152600f60248201526e215f737461626c65734f7261636c6560881b604482015290519081900360640190fd5b60808490526001600160601b0319606084811b821660a05283811b821660c05282901b1660e0526040805163313ce56760e01b815290516001600160a01b0384169163313ce567916004808301926020929190829003018186803b158015620001a257600080fd5b505afa158015620001b7573d6000803e3d6000fd5b505050506040513d6020811015620001ce57600080fd5b5051610100526040805163313ce56760e01b815290516001600160a01b0383169163313ce567916004808301926020929190829003018186803b1580156200021557600080fd5b505afa1580156200022a573d6000803e3d6000fd5b505050506040513d60208110156200024157600080fd5b5051610120525050505060805160a05160601c60c05160601c60e05160601c6101005161012051611569620002d0600039806102d652806106ad52508061060252806107ca52508061026152806104bc528061062852508061019852806101c2528061035d528061040e5280610572525080610498528061080d52508061054b528061083c52506115696000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c806355b13a4f1161006657806355b13a4f14610124578063562c9c571461012c5780639380576f14610134578063ad27179614610163578063fc3d42e81461016b57610093565b80632630c12f1461009857806330a75871146100bc578063365cab0a146100f9578063468f02d21461011c575b600080fd5b6100a0610196565b604080516001600160a01b039092168252519081900360200190f35b6100e7600480360360608110156100d257600080fd5b508035906020810135906040013515156101ba565b60408051918252519081900360200190f35b6100e76004803603604081101561010f57600080fd5b5080359060200135610303565b6100e761040a565b6100a0610496565b6100a06104ba565b6100e76004803603608081101561014a57600080fd5b50803590602081013590604081013590606001356104de565b6100e7610549565b6100e76004803603606081101561018157600080fd5b5080359060208101359060400135151561056d565b7f000000000000000000000000000000000000000000000000000000000000000081565b60006102fb847f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166350d25bcd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561021957600080fd5b505afa15801561022d573d6000803e3d6000fd5b505050506040513d602081101561024357600080fd5b5051604080516350d25bcd60e01b8152905187916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916350d25bcd91600480820192602092909190829003018186803b1580156102a857600080fd5b505afa1580156102bc573d6000803e3d6000fd5b505050506040513d60208110156102d257600080fd5b50517f0000000000000000000000000000000000000000000000000000000000000000876106ea565b949350505050565b6000428211610359576040805162461bcd60e51b815260206004820152601d60248201527f457870697279206d75737420626520696e207468652066757475726521000000604482015290519081900360640190fd5b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166350d25bcd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156103b457600080fd5b505afa1580156103c8573d6000803e3d6000fd5b505050506040513d60208110156103de57600080fd5b505190506000806103ef83866107c1565b5091509150610400828783886108dd565b9695505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166350d25bcd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561046557600080fd5b505afa158015610479573d6000803e3d6000fd5b505050506040513d602081101561048f57600080fd5b5051919050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000428211610534576040805162461bcd60e51b815260206004820152601d60248201527f457870697279206d75737420626520696e207468652066757475726521000000604482015290519081900360640190fd5b610540858585856108dd565b95945050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166350d25bcd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156105c957600080fd5b505afa1580156105dd573d6000803e3d6000fd5b505050506040513d60208110156105f357600080fd5b505190506000808461062657827f00000000000000000000000000000000000000000000000000000000000000006106cd565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166350d25bcd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561067f57600080fd5b505afa158015610693573d6000803e3d6000fd5b505050506040513d60208110156106a957600080fd5b50517f00000000000000000000000000000000000000000000000000000000000000005b915091506106df87848885858a6106ea565b979650505050505050565b6000428511610740576040805162461bcd60e51b815260206004820152601d60248201527f457870697279206d75737420626520696e207468652066757475726521000000604482015290519081900360640190fd5b60008061074d88886107c1565b9199509250905060008061076383858c8e610992565b90925090506000610775601289610a31565b600a0a905086610797576107928361078d8b84610a93565b610af3565b6107a5565b6107a58261078d8b84610a93565b95506107b18682610a93565b9c9b505050505050505050505050565b600080806108007f0000000000000000000000000000000000000000000000000000000000000000600a0a6107fa876305f5e100610a93565b90610b23565b92506108c16402540be4007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635ecfdf1e7f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561088f57600080fd5b505afa1580156108a3573d6000803e3d6000fd5b505050506040513d60208110156108b957600080fd5b505190610a93565b91506108d4620151806107fa8642610a31565b90509250925092565b6000806108f1620151806107fa8542610a31565b90506000808688106109385761090983878a8a610b82565b90925090506109316402540be4006107fa670de0b6b3a76400006001607f1b86025b04610ce9565b9350610987565b6109448387898b610b82565b90925090506109846402540be4006107fa61096d670de0b6b3a76400006001607f1b860261092b565b61097e600a6509184e72a000610a93565b90610a31565b93505b505050949350505050565b600080600080848611156109e4576109ac88888888610db6565b9150856109c26109bc8488610e2a565b88610e84565b146109da576109d58661097e8488610e2a565b6109dd565b60005b9050610a24565b6109f088888789610db6565b905084610a06610a008389610e2a565b87610e84565b14610a1e57610a198561097e8389610e2a565b610a21565b60005b91505b9097909650945050505050565b600082821115610a88576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b508082035b92915050565b600082610aa257506000610a8d565b82820282848281610aaf57fe5b0414610aec5760405162461bcd60e51b81526004018080602001828103825260218152602001806115136021913960400191505060405180910390fd5b9392505050565b600081610b14610b0b85670de0b6b3a7640000610e9b565b60028504610efe565b81610b1b57fe5b049392505050565b6000808211610b79576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b818381610b1b57fe5b60008060008411610bc0576040805162461bcd60e51b815260206004820152600360248201526202173760ec1b604482015290519081900360640190fd5b60008311610bfb576040805162461bcd60e51b8152602060048201526003602482015262085cdd60ea1b604482015290519081900360640190fd5b600280860a046ec097ce7bc90715b34b9f1000000000600061016d670de0b6b3a764000084028390048a020490506000633b9aca00610c4761016d670de0b6b3a76400008d0204610f4d565b8a0281610c5057fe5b04905060008111610c91576040805162461bcd60e51b8152602060048201526006602482015265085cd4d4549560d21b604482015290519081900360640190fd5b6001607f1b610cae888a6001607f1b0281610ca857fe5b04610f84565b670de0b6b3a76400000281610cbf57fe5b04955080828701670de0b6b3a76400000281610cd757fe5b049a908b039950975050505050505050565b6000806001607f1b622358838402046298968001905060006001607f1b8460028681610d1157fe5b040281610d1a57fe5b0490506000610d2882611018565b623cdfaf607f1b81610d3657fe5b049050600083848586876578fcdaec220081610d4e57fe5b05630115e6cf1901629896800281610d6257fe5b0563010fd4fc01629896800281610d7557fe5b05623668451901629896800281610d8857fe5b056230bbd7018302629896800281610d9c57fe5b059050851561054057655af3107a40000395945050505050565b6000806000610dc787878787610b82565b915091506000610de7670de0b6b3a7640000846001607f1b028161092b57fe5b90506000610e04670de0b6b3a76400006001607f1b8502056113bc565b9050655af3107a400086820204655af3107a400088840204039998505050505050505050565b600082820183811015610aec576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600081831015610e945781610aec565b5090919050565b6000811580610eb657505080820282828281610eb357fe5b04145b610a8d576040805162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6d756c2d6f766572666c6f7760601b604482015290519081900360640190fd5b80820182811015610a8d576040805162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6164642d6f766572666c6f7760601b604482015290519081900360640190fd5b80600260018201045b81811015610f7e57809150600281828581610f6d57fe5b040181610f7657fe5b049050610f56565b50919050565b600080600160801b8310610fb4576000610fa36001607f1b850461149b565b60ff1693841c936001607f1b029150505b6001607f1b83111561100257607f5b60ff811615611000576001607f1b848002049350600160801b8410610ff757600193841c9360ff6000198301161b91909101905b60001901610fc3565b505b6402540be40064019d25ddbe8202049392505050565b6000670168244fdac780006001607f1b6f0fffffffffffffffffffffffffffffff84168080028290048082028390048083028490049485026710e1b3be415a00009092026705a0913f6b1e000091909102010192909181830204905080664807432bc1800002830192506001607f1b8282028161109157fe5b04905080660c0135dca0400002830192506001607f1b828202816110b157fe5b049050806601b707b1cdc00002830192506001607f1b828202816110d157fe5b049050806536e0f639b80002830192506001607f1b828202816110f057fe5b04905080650618fee9f80002830192506001607f1b8282028161110f57fe5b04905080649c197dcc0002830192506001607f1b8282028161112d57fe5b04905080640e30dce40002830192506001607f1b8282028161114b57fe5b0490508064012ebd130002830192506001607f1b8282028161116957fe5b049050806317499f0002830192506001607f1b8282028161118657fe5b049050806301a9d48002830192506001607f1b828202816111a357fe5b04905080621c638002830192506001607f1b828202816111bf57fe5b049050806201c63802830192506001607f1b828202816111db57fe5b04905080611ab802830192506001607f1b828202816111f657fe5b0490508061017c02830192506001607f1b8282028161121157fe5b04905080601402830192506001607f1b8282028161122b57fe5b6721c3677c82b400009190049384010482016001607f1b019290506001607c1b85161561127c5770018ebef9eac820ae8682b9793ac6d1e7767001c3d6a24ed82218787d624d3e5eba95f984020492505b6001607d1b8516156112b2577001368b2fc6f9609fe7aceb46aa619baed470018ebef9eac820ae8682b9793ac6d1e77884020492505b6001607e1b8516156112e7576fbc5ab1b16779be3575bd8f0520a9f21f7001368b2fc6f9609fe7aceb46aa619baed584020492505b6001607f1b85161561131b576f454aaa8efe072e7f6ddbab84b40a55c96fbc5ab1b16779be3575bd8f0520a9f21e84020492505b600160801b85161561134f576f0960aadc109e7a3bf4578099615711ea6f454aaa8efe072e7f6ddbab84b40a55c584020492505b600160811b851615611382576e2bf84208204f5977f9a8cf01fdce3d6f0960aadc109e7a3bf4578099615711d784020492505b600160821b8516156113b3576d03c6ab775dd0b95b4cbee7e65d116e2bf84208204f5977f9a8cf01fdc30784020492505b50909392505050565b6000806001607f1b6113cd846114fc565b6223588302816113d957fe5b046298968001905060006001607f1b84600286816113f357fe5b0502816113fc57fe5b049050600061140a82611018565b623cdfaf607f1b8161141857fe5b049050600083848586876578fcdaec22008161143057fe5b05630115e6cf190162989680028161144457fe5b0563010fd4fc0162989680028161145757fe5b0562366845190162989680028161146a57fe5b056230bbd701830262989680028161147e57fe5b059050600086131561054057655af3107a40000395945050505050565b6000806101008310156114c3575b60018311156114be57600192831c92016114a9565b610a8d565b60805b60ff8116156114f557600160ff82161b84106114ea5760ff81169390931c92908117905b60011c607f166114c6565b5092915050565b600080821261150b5781610a8d565b506000039056fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a2646970667358221220a352615e761603819ede79e44f84f031942c8a2258a20468126f32e80e5ebf7e64736f6c63430007030033" +} \ No newline at end of file diff --git a/deployments/mainnet/RibbonThetaVaultETHCallWithSwap.json b/deployments/mainnet/RibbonThetaVaultETHCallWithSwap.json new file mode 100644 index 000000000..4fa73942f --- /dev/null +++ b/deployments/mainnet/RibbonThetaVaultETHCallWithSwap.json @@ -0,0 +1,261 @@ +{ + "address": "0xa413CcF8e9EB83c0d50EB8c69C524C927381DCf9", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_logic", + "type": "address" + }, + { + "internalType": "address", + "name": "admin_", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "adminAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "changeAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "implementationAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0xddfa61d3582d704da607d91b3df5a07dd2ebc0192b9506fcb4d75d11145b0ce3", + "receipt": { + "to": null, + "from": "0xF8368119Bb1073Cf01B841848725d81b542A4c19", + "contractAddress": "0xa413CcF8e9EB83c0d50EB8c69C524C927381DCf9", + "transactionIndex": 66, + "gasUsed": "842716", + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000008000000000800000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010200000000000000000000000000000000000400000000000000000000000000020000000000000002000000000000000000000000000000000000000000000000000", + "blockHash": "0x6fa3743f840673c8a7196b6a35b111a82ea15c343d9bc6bef4ace738bc7581b6", + "transactionHash": "0xddfa61d3582d704da607d91b3df5a07dd2ebc0192b9506fcb4d75d11145b0ce3", + "logs": [ + { + "transactionIndex": 66, + "blockNumber": 15132961, + "transactionHash": "0xddfa61d3582d704da607d91b3df5a07dd2ebc0192b9506fcb4d75d11145b0ce3", + "address": "0xa413CcF8e9EB83c0d50EB8c69C524C927381DCf9", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000f8368119bb1073cf01b841848725d81b542a4c19" + ], + "data": "0x", + "logIndex": 106, + "blockHash": "0x6fa3743f840673c8a7196b6a35b111a82ea15c343d9bc6bef4ace738bc7581b6" + }, + { + "transactionIndex": 66, + "blockNumber": 15132961, + "transactionHash": "0xddfa61d3582d704da607d91b3df5a07dd2ebc0192b9506fcb4d75d11145b0ce3", + "address": "0xa413CcF8e9EB83c0d50EB8c69C524C927381DCf9", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x000000000000000000000000f8368119bb1073cf01b841848725d81b542a4c19", + "0x000000000000000000000000f8368119bb1073cf01b841848725d81b542a4c19" + ], + "data": "0x", + "logIndex": 107, + "blockHash": "0x6fa3743f840673c8a7196b6a35b111a82ea15c343d9bc6bef4ace738bc7581b6" + } + ], + "blockNumber": 15132961, + "cumulativeGasUsed": "5893147", + "status": 1, + "byzantium": true + }, + "args": [ + "0x7a6A812188D01D08DEF011356b98425997800429", + "0x223d59FA315D7693dF4238d1a5748c964E615923", + "0xf001382700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000003635c9adc5dea00000000000000000000000000000f8368119bb1073cf01b841848725d81b542a4c19000000000000000000000000f8368119bb1073cf01b841848725d81b542a4c19000000000000000000000000daeada3d210d2f45874724beea03c7d4bbd4167400000000000000000000000000000000000000000000000000000000001e84800000000000000000000000000000000000000000000000000000000000989680000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001600000000000000000000000005ba2a42b74a72a1a3ccc37cf03802a0b7a551139000000000000000000000000ab40513b6f0a33a68b59ccf90cb6f892b4be15730000000000000000000000000000000000000000000000000000000000000016526962626f6e20455448205468657461205661756c7400000000000000000000000000000000000000000000000000000000000000000000000000000000000a724554482d544845544100000000000000000000000000000000000000000000" + ], + "solcInputHash": "7c5737198e0350df334dd243d1d73ef5", + "metadata": "{\"compiler\":{\"version\":\"0.8.4+commit.c7e474f2\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_logic\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"admin_\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"adminAddress\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"changeAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"implementationAddress\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"upgradeToAndCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"details\":\"This contract combines an upgradeability proxy with an authorization mechanism for administrative tasks. All external functions in this contract must be guarded by the `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity feature proposal that would enable this to be done automatically.\",\"events\":{\"AdminChanged(address,address)\":{\"details\":\"Emitted when the administration has been transferred.\",\"params\":{\"newAdmin\":\"Address of the new admin.\",\"previousAdmin\":\"Address of the previous admin.\"}}},\"kind\":\"dev\",\"methods\":{\"admin()\":{\"returns\":{\"adminAddress\":\"The address of the proxy admin.\"}},\"changeAdmin(address)\":{\"details\":\"Changes the admin of the proxy. Only the current admin can call this function.\",\"params\":{\"newAdmin\":\"Address to transfer proxy administration to.\"}},\"constructor\":{\"params\":{\"_data\":\"Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\",\"_logic\":\"address of the initial implementation.\",\"admin_\":\"Address of the proxy administrator.\"}},\"implementation()\":{\"returns\":{\"implementationAddress\":\"The address of the implementation.\"}},\"upgradeTo(address)\":{\"details\":\"Upgrade the backing implementation of the proxy. Only the admin can call this function.\",\"params\":{\"newImplementation\":\"Address of the new implementation.\"}},\"upgradeToAndCall(address,bytes)\":{\"details\":\"Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.\",\"params\":{\"data\":\"Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\",\"newImplementation\":\"Address of the new implementation.\"}}},\"stateVariables\":{\"ADMIN_SLOT\":{\"details\":\"Storage slot with the admin of the contract. This is the keccak-256 hash of \\\"eip1967.proxy.admin\\\" subtracted by 1, and is validated in the constructor.\"}},\"title\":\"AdminUpgradeabilityProxy\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"constructor\":{\"notice\":\"Contract constructor.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/vendor/upgrades/AdminUpgradeabilityProxy.sol\":\"AdminUpgradeabilityProxy\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x3336baae5cf23e94274d75336e2d412193be508504aee185e61dc7d58cd05c8a\",\"license\":\"MIT\"},\"contracts/vendor/upgrades/AdminUpgradeabilityProxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity >=0.6.8;\\n\\nimport './UpgradeabilityProxy.sol';\\n\\n/**\\n * @title AdminUpgradeabilityProxy\\n * @dev This contract combines an upgradeability proxy with an authorization\\n * mechanism for administrative tasks.\\n * All external functions in this contract must be guarded by the\\n * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity\\n * feature proposal that would enable this to be done automatically.\\n */\\ncontract AdminUpgradeabilityProxy is UpgradeabilityProxy {\\n /**\\n * Contract constructor.\\n * @param _logic address of the initial implementation.\\n * @param admin_ Address of the proxy administrator.\\n * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.\\n * It should include the signature and the parameters of the function to be called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\\n */\\n constructor(address _logic, address admin_, bytes memory _data) UpgradeabilityProxy(_logic, _data) payable {\\n assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));\\n _setAdmin(admin_);\\n }\\n\\n /**\\n * @dev Emitted when the administration has been transferred.\\n * @param previousAdmin Address of the previous admin.\\n * @param newAdmin Address of the new admin.\\n */\\n event AdminChanged(address previousAdmin, address newAdmin);\\n\\n /**\\n * @dev Storage slot with the admin of the contract.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.admin\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n\\n bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\\n\\n /**\\n * @dev Modifier to check whether the `msg.sender` is the admin.\\n * If it is, it will run the function. Otherwise, it will delegate the call\\n * to the implementation.\\n */\\n modifier ifAdmin() {\\n if (msg.sender == _admin()) {\\n _;\\n } else {\\n _fallback();\\n }\\n }\\n\\n /**\\n * @return adminAddress The address of the proxy admin.\\n */\\n function admin() external ifAdmin returns (address adminAddress) {\\n return _admin();\\n }\\n\\n /**\\n * @return implementationAddress The address of the implementation.\\n */\\n function implementation() external ifAdmin returns (address implementationAddress) {\\n return _implementation();\\n }\\n\\n /**\\n * @dev Changes the admin of the proxy.\\n * Only the current admin can call this function.\\n * @param newAdmin Address to transfer proxy administration to.\\n */\\n function changeAdmin(address newAdmin) external ifAdmin {\\n require(newAdmin != address(0), \\\"Cannot change the admin of a proxy to the zero address\\\");\\n emit AdminChanged(_admin(), newAdmin);\\n _setAdmin(newAdmin);\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy.\\n * Only the admin can call this function.\\n * @param newImplementation Address of the new implementation.\\n */\\n function upgradeTo(address newImplementation) external ifAdmin {\\n _upgradeTo(newImplementation);\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy and call a function\\n * on the new implementation.\\n * This is useful to initialize the proxied contract.\\n * @param newImplementation Address of the new implementation.\\n * @param data Data to send as msg.data in the low level call.\\n * It should include the signature and the parameters of the function to be called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n */\\n function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {\\n _upgradeTo(newImplementation);\\n (bool success,) = newImplementation.delegatecall(data);\\n require(success);\\n }\\n\\n /**\\n * @return adm The admin slot.\\n */\\n function _admin() internal view returns (address adm) {\\n bytes32 slot = ADMIN_SLOT;\\n assembly {\\n adm := sload(slot)\\n }\\n }\\n\\n /**\\n * @dev Sets the address of the proxy admin.\\n * @param newAdmin Address of the new proxy admin.\\n */\\n function _setAdmin(address newAdmin) internal {\\n bytes32 slot = ADMIN_SLOT;\\n\\n assembly {\\n sstore(slot, newAdmin)\\n }\\n }\\n\\n /**\\n * @dev Only fall back when the sender is not the admin.\\n */\\n function _willFallback() internal override virtual {\\n require(msg.sender != _admin(), \\\"Cannot call fallback function from the proxy admin\\\");\\n super._willFallback();\\n }\\n}\\n\",\"keccak256\":\"0xb4950866bd640d9348e0b7cc56218d9f24312b7ec3c35fcf7ec26753bb3e48ce\",\"license\":\"MIT\"},\"contracts/vendor/upgrades/Proxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity =0.8.4;\\n\\n/**\\n * @title Proxy\\n * @dev Implements delegation of calls to other contracts, with proper\\n * forwarding of return values and bubbling of failures.\\n * It defines a fallback function that delegates all calls to the address\\n * returned by the abstract _implementation() internal function.\\n */\\nabstract contract Proxy {\\n /**\\n * @dev Fallback function.\\n * Implemented entirely in `_fallback`.\\n */\\n fallback () payable external {\\n _fallback();\\n }\\n\\n /**\\n * @dev Receive function.\\n * Implemented entirely in `_fallback`.\\n */\\n receive () payable external {\\n // _fallback();\\n }\\n\\n /**\\n * @return The Address of the implementation.\\n */\\n function _implementation() internal virtual view returns (address);\\n\\n /**\\n * @dev Delegates execution to an implementation contract.\\n * This is a low level function that doesn't return to its internal call site.\\n * It will return to the external caller whatever the implementation returns.\\n * @param implementation Address to delegate.\\n */\\n function _delegate(address implementation) internal {\\n assembly {\\n // Copy msg.data. We take full control of memory in this inline assembly\\n // block because it will not return to Solidity code. We overwrite the\\n // Solidity scratch pad at memory position 0.\\n calldatacopy(0, 0, calldatasize())\\n\\n // Call the implementation.\\n // out and outsize are 0 because we don't know the size yet.\\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\\n\\n // Copy the returned data.\\n returndatacopy(0, 0, returndatasize())\\n\\n switch result\\n // delegatecall returns 0 on error.\\n case 0 { revert(0, returndatasize()) }\\n default { return(0, returndatasize()) }\\n }\\n }\\n\\n /**\\n * @dev Function that is run as the first thing in the fallback function.\\n * Can be redefined in derived contracts to add functionality.\\n * Redefinitions must call super._willFallback().\\n */\\n function _willFallback() internal virtual {\\n }\\n\\n /**\\n * @dev fallback implementation.\\n * Extracted to enable manual triggering.\\n */\\n function _fallback() internal {\\n _willFallback();\\n _delegate(_implementation());\\n }\\n}\\n\",\"keccak256\":\"0xb7343b1d10f8f15fa7588e6d867d172a8f4aa03ab7390bfe5b6d387f21e85870\",\"license\":\"MIT\"},\"contracts/vendor/upgrades/UpgradeabilityProxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity =0.8.4;\\n\\nimport './Proxy.sol';\\nimport '@openzeppelin/contracts/utils/Address.sol';\\n\\n/**\\n * @title UpgradeabilityProxy\\n * @dev This contract implements a proxy that allows to change the\\n * implementation address to which it will delegate.\\n * Such a change is called an implementation upgrade.\\n */\\ncontract UpgradeabilityProxy is Proxy {\\n /**\\n * @dev Contract constructor.\\n * @param _logic Address of the initial implementation.\\n * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.\\n * It should include the signature and the parameters of the function to be called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\\n */\\n constructor(address _logic, bytes memory _data) payable {\\n assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));\\n _setImplementation(_logic);\\n if(_data.length > 0) {\\n (bool success,) = _logic.delegatecall(_data);\\n require(success);\\n }\\n }\\n\\n /**\\n * @dev Emitted when the implementation is upgraded.\\n * @param implementation Address of the new implementation.\\n */\\n event Upgraded(address indexed implementation);\\n\\n /**\\n * @dev Storage slot with the address of the current implementation.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.implementation\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n\\n /**\\n * @dev Returns the current implementation.\\n * @return impl Address of the current implementation\\n */\\n function _implementation() internal override view returns (address impl) {\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n assembly {\\n impl := sload(slot)\\n }\\n }\\n\\n /**\\n * @dev Upgrades the proxy to a new implementation.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _upgradeTo(address newImplementation) internal {\\n _setImplementation(newImplementation);\\n emit Upgraded(newImplementation);\\n }\\n\\n /**\\n * @dev Sets the implementation address of the proxy.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _setImplementation(address newImplementation) internal {\\n require(Address.isContract(newImplementation), \\\"Cannot set a proxy implementation to a non-contract address\\\");\\n\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n\\n assembly {\\n sstore(slot, newImplementation)\\n }\\n }\\n}\\n\",\"keccak256\":\"0xbc609da5fd7f73c75e06d3239203a36f51f1b0507c08ed0e728887ed6ade8d31\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x608060405260405162000a8038038062000a80833981016040819052620000269162000243565b82816200005560017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6200033d565b60008051602062000a60833981519152146200008157634e487b7160e01b600052600160045260246000fd5b6200008c8262000180565b80511562000103576000826001600160a01b031682604051620000b091906200031f565b600060405180830381855af49150503d8060008114620000ed576040519150601f19603f3d011682016040523d82523d6000602084013e620000f2565b606091505b50509050806200010157600080fd5b505b5062000133905060017fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61046200033d565b60008051602062000a40833981519152146200015f57634e487b7160e01b600052600160045260246000fd5b620001778260008051602062000a4083398151915255565b505050620003aa565b62000196816200022060201b620003d71760201c565b6200020d5760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e747261637420616464726573730000000000606482015260840160405180910390fd5b60008051602062000a6083398151915255565b3b151590565b80516001600160a01b03811681146200023e57600080fd5b919050565b60008060006060848603121562000258578283fd5b620002638462000226565b9250620002736020850162000226565b60408501519092506001600160401b038082111562000290578283fd5b818601915086601f830112620002a4578283fd5b815181811115620002b957620002b962000394565b604051601f8201601f19908116603f01168101908382118183101715620002e457620002e462000394565b81604052828152896020848701011115620002fd578586fd5b6200031083602083016020880162000361565b80955050505050509250925092565b600082516200033381846020870162000361565b9190910192915050565b6000828210156200035c57634e487b7160e01b81526011600452602481fd5b500390565b60005b838110156200037e57818101518382015260200162000364565b838111156200038e576000848401525b50505050565b634e487b7160e01b600052604160045260246000fd5b61068680620003ba6000396000f3fe60806040526004361061004e5760003560e01c80633659cfe61461005f5780634f1ef2861461007f5780635c60da1b146100925780638f283970146100c3578063f851a440146100e357610055565b3661005557005b61005d6100f8565b005b34801561006b57600080fd5b5061005d61007a366004610581565b610132565b61005d61008d3660046105a2565b61016f565b34801561009e57600080fd5b506100a761021e565b6040516001600160a01b03909116815260200160405180910390f35b3480156100cf57600080fd5b5061005d6100de366004610581565b610280565b3480156100ef57600080fd5b506100a7610392565b6101006103dd565b61013061012b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b610469565b565b600080516020610631833981519152546001600160a01b0316336001600160a01b03161415610167576101648161048d565b50565b6101646100f8565b600080516020610631833981519152546001600160a01b0316336001600160a01b03161415610211576101a18361048d565b6000836001600160a01b031683836040516101bd929190610620565b600060405180830381855af49150503d80600081146101f8576040519150601f19603f3d011682016040523d82523d6000602084013e6101fd565b606091505b505090508061020b57600080fd5b50505050565b6102196100f8565b505050565b60006102366000805160206106318339815191525490565b6001600160a01b0316336001600160a01b0316141561027557507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b61027d6100f8565b90565b600080516020610631833981519152546001600160a01b0316336001600160a01b03161415610167576001600160a01b0381166103235760405162461bcd60e51b815260206004820152603660248201527f43616e6e6f74206368616e6765207468652061646d696e206f6620612070726f604482015275787920746f20746865207a65726f206164647265737360501b60648201526084015b60405180910390fd5b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f61035a6000805160206106318339815191525490565b604080516001600160a01b03928316815291841660208301520160405180910390a16101648160008051602061063183398151915255565b60006103aa6000805160206106318339815191525490565b6001600160a01b0316336001600160a01b0316141561027557506000805160206106318339815191525490565b3b151590565b600080516020610631833981519152546001600160a01b0316336001600160a01b031614156101305760405162461bcd60e51b815260206004820152603260248201527f43616e6e6f742063616c6c2066616c6c6261636b2066756e6374696f6e20667260448201527137b6903a343290383937bc3c9030b236b4b760711b606482015260840161031a565b3660008037600080366000845af43d6000803e808015610488573d6000f35b3d6000fd5b610496816104cd565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b803b6105415760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e747261637420616464726573730000000000606482015260840161031a565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55565b80356001600160a01b038116811461057c57600080fd5b919050565b600060208284031215610592578081fd5b61059b82610565565b9392505050565b6000806000604084860312156105b6578182fd5b6105bf84610565565b9250602084013567ffffffffffffffff808211156105db578384fd5b818601915086601f8301126105ee578384fd5b8135818111156105fc578485fd5b87602082850101111561060d578485fd5b6020830194508093505050509250925092565b818382376000910190815291905056feb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103a2646970667358221220ccbb94ef5092a0bf84853cefa22901724a90c6dc70692625ee3f5c86b34819a464736f6c63430008040033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + "deployedBytecode": "0x60806040526004361061004e5760003560e01c80633659cfe61461005f5780634f1ef2861461007f5780635c60da1b146100925780638f283970146100c3578063f851a440146100e357610055565b3661005557005b61005d6100f8565b005b34801561006b57600080fd5b5061005d61007a366004610581565b610132565b61005d61008d3660046105a2565b61016f565b34801561009e57600080fd5b506100a761021e565b6040516001600160a01b03909116815260200160405180910390f35b3480156100cf57600080fd5b5061005d6100de366004610581565b610280565b3480156100ef57600080fd5b506100a7610392565b6101006103dd565b61013061012b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b610469565b565b600080516020610631833981519152546001600160a01b0316336001600160a01b03161415610167576101648161048d565b50565b6101646100f8565b600080516020610631833981519152546001600160a01b0316336001600160a01b03161415610211576101a18361048d565b6000836001600160a01b031683836040516101bd929190610620565b600060405180830381855af49150503d80600081146101f8576040519150601f19603f3d011682016040523d82523d6000602084013e6101fd565b606091505b505090508061020b57600080fd5b50505050565b6102196100f8565b505050565b60006102366000805160206106318339815191525490565b6001600160a01b0316336001600160a01b0316141561027557507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b61027d6100f8565b90565b600080516020610631833981519152546001600160a01b0316336001600160a01b03161415610167576001600160a01b0381166103235760405162461bcd60e51b815260206004820152603660248201527f43616e6e6f74206368616e6765207468652061646d696e206f6620612070726f604482015275787920746f20746865207a65726f206164647265737360501b60648201526084015b60405180910390fd5b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f61035a6000805160206106318339815191525490565b604080516001600160a01b03928316815291841660208301520160405180910390a16101648160008051602061063183398151915255565b60006103aa6000805160206106318339815191525490565b6001600160a01b0316336001600160a01b0316141561027557506000805160206106318339815191525490565b3b151590565b600080516020610631833981519152546001600160a01b0316336001600160a01b031614156101305760405162461bcd60e51b815260206004820152603260248201527f43616e6e6f742063616c6c2066616c6c6261636b2066756e6374696f6e20667260448201527137b6903a343290383937bc3c9030b236b4b760711b606482015260840161031a565b3660008037600080366000845af43d6000803e808015610488573d6000f35b3d6000fd5b610496816104cd565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b803b6105415760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e747261637420616464726573730000000000606482015260840161031a565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55565b80356001600160a01b038116811461057c57600080fd5b919050565b600060208284031215610592578081fd5b61059b82610565565b9392505050565b6000806000604084860312156105b6578182fd5b6105bf84610565565b9250602084013567ffffffffffffffff808211156105db578384fd5b818601915086601f8301126105ee578384fd5b8135818111156105fc578485fd5b87602082850101111561060d578485fd5b6020830194508093505050509250925092565b818382376000910190815291905056feb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103a2646970667358221220ccbb94ef5092a0bf84853cefa22901724a90c6dc70692625ee3f5c86b34819a464736f6c63430008040033", + "devdoc": { + "details": "This contract combines an upgradeability proxy with an authorization mechanism for administrative tasks. All external functions in this contract must be guarded by the `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity feature proposal that would enable this to be done automatically.", + "events": { + "AdminChanged(address,address)": { + "details": "Emitted when the administration has been transferred.", + "params": { + "newAdmin": "Address of the new admin.", + "previousAdmin": "Address of the previous admin." + } + } + }, + "kind": "dev", + "methods": { + "admin()": { + "returns": { + "adminAddress": "The address of the proxy admin." + } + }, + "changeAdmin(address)": { + "details": "Changes the admin of the proxy. Only the current admin can call this function.", + "params": { + "newAdmin": "Address to transfer proxy administration to." + } + }, + "constructor": { + "params": { + "_data": "Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.", + "_logic": "address of the initial implementation.", + "admin_": "Address of the proxy administrator." + } + }, + "implementation()": { + "returns": { + "implementationAddress": "The address of the implementation." + } + }, + "upgradeTo(address)": { + "details": "Upgrade the backing implementation of the proxy. Only the admin can call this function.", + "params": { + "newImplementation": "Address of the new implementation." + } + }, + "upgradeToAndCall(address,bytes)": { + "details": "Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.", + "params": { + "data": "Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.", + "newImplementation": "Address of the new implementation." + } + } + }, + "stateVariables": { + "ADMIN_SLOT": { + "details": "Storage slot with the admin of the contract. This is the keccak-256 hash of \"eip1967.proxy.admin\" subtracted by 1, and is validated in the constructor." + } + }, + "title": "AdminUpgradeabilityProxy", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "constructor": { + "notice": "Contract constructor." + } + }, + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/deployments/mainnet/StrikeSelectionETHWithSwap.json b/deployments/mainnet/StrikeSelectionETHWithSwap.json new file mode 100644 index 000000000..8d61ffc85 --- /dev/null +++ b/deployments/mainnet/StrikeSelectionETHWithSwap.json @@ -0,0 +1,416 @@ +{ + "address": "0xF6010Dc7475547221f97c28F8ed6f5EC0969024b", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_optionsPremiumPricer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_delta", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_step", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldDelta", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newDelta", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "DeltaSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldStep", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newStep", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "StepSet", + "type": "event" + }, + { + "inputs": [], + "name": "delta", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "expiryTimestamp", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isPut", + "type": "bool" + } + ], + "name": "getStrikePrice", + "outputs": [ + { + "internalType": "uint256", + "name": "newStrikePrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "newDelta", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "expiryTimestamp", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isPut", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "annualizedVol", + "type": "uint256" + } + ], + "name": "getStrikePriceWithVol", + "outputs": [ + { + "internalType": "uint256", + "name": "newStrikePrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "newDelta", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "optionsPremiumPricer", + "outputs": [ + { + "internalType": "contract IOptionsPremiumPricer", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newDelta", + "type": "uint256" + } + ], + "name": "setDelta", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newStep", + "type": "uint256" + } + ], + "name": "setStep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "step", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "volatilityOracle", + "outputs": [ + { + "internalType": "contract IManualVolatilityOracle", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "transactionHash": "0xb9b482882678b3ff308a5870e6491cd30b08432eea5568925f1b77b5cc7d2a5a", + "receipt": { + "to": null, + "from": "0xF8368119Bb1073Cf01B841848725d81b542A4c19", + "contractAddress": "0xF6010Dc7475547221f97c28F8ed6f5EC0969024b", + "transactionIndex": 83, + "gasUsed": "837751", + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000020000000000000000000000200000000000000010000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000008000000000800000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000080002000000000000000000000000000000000000000000000000000", + "blockHash": "0x6c23d258971b4dd085f9c2312a2af3d8e972f7d1fee9bc7f23a6e1b9e0c61283", + "transactionHash": "0xb9b482882678b3ff308a5870e6491cd30b08432eea5568925f1b77b5cc7d2a5a", + "logs": [ + { + "transactionIndex": 83, + "blockNumber": 15132713, + "transactionHash": "0xb9b482882678b3ff308a5870e6491cd30b08432eea5568925f1b77b5cc7d2a5a", + "address": "0xF6010Dc7475547221f97c28F8ed6f5EC0969024b", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000f8368119bb1073cf01b841848725d81b542a4c19" + ], + "data": "0x", + "logIndex": 174, + "blockHash": "0x6c23d258971b4dd085f9c2312a2af3d8e972f7d1fee9bc7f23a6e1b9e0c61283" + } + ], + "blockNumber": 15132713, + "cumulativeGasUsed": "8411855", + "status": 1, + "byzantium": true + }, + "args": [ + "0xB38f51BeaE3718011CFBA1Aee51C4c9B05681286", + 1000, + 10000000000 + ], + "solcInputHash": "7c5737198e0350df334dd243d1d73ef5", + "metadata": "{\"compiler\":{\"version\":\"0.8.4+commit.c7e474f2\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_optionsPremiumPricer\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_delta\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_step\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldDelta\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newDelta\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"DeltaSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldStep\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newStep\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"StepSet\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"delta\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expiryTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isPut\",\"type\":\"bool\"}],\"name\":\"getStrikePrice\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"newStrikePrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"newDelta\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expiryTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isPut\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"annualizedVol\",\"type\":\"uint256\"}],\"name\":\"getStrikePriceWithVol\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"newStrikePrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"newDelta\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"optionsPremiumPricer\",\"outputs\":[{\"internalType\":\"contract IOptionsPremiumPricer\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newDelta\",\"type\":\"uint256\"}],\"name\":\"setDelta\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newStep\",\"type\":\"uint256\"}],\"name\":\"setStep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"step\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"volatilityOracle\",\"outputs\":[{\"internalType\":\"contract IManualVolatilityOracle\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"getStrikePrice(uint256,bool)\":{\"params\":{\"expiryTimestamp\":\"is the unix timestamp of expiration\",\"isPut\":\"is whether option is put or call\"},\"returns\":{\"newDelta\":\"is the delta of the option given its parameters\",\"newStrikePrice\":\"is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\"}},\"getStrikePriceWithVol(uint256,bool,uint256)\":{\"params\":{\"annualizedVol\":\"is IV of the asset at the specified delta\",\"expiryTimestamp\":\"is the unix timestamp of expiration\",\"isPut\":\"is whether option is put or call\"},\"returns\":{\"newDelta\":\"is the delta of the option given its parameters\",\"newStrikePrice\":\"is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\"}},\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"renounceOwnership()\":{\"details\":\"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner.\"},\"setDelta(uint256)\":{\"params\":{\"newDelta\":\"is the new delta value\"}},\"setStep(uint256)\":{\"params\":{\"newStep\":\"is the new step value\"}},\"transferOwnership(address)\":{\"details\":\"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"getStrikePrice(uint256,bool)\":{\"notice\":\"Gets the strike price satisfying the delta value given the expiry timestamp and whether option is call or put\"},\"getStrikePriceWithVol(uint256,bool,uint256)\":{\"notice\":\"Gets the strike price satisfying the delta value given the expiry timestamp and whether option is call or put\"},\"optionsPremiumPricer()\":{\"notice\":\"Immutables\"},\"setDelta(uint256)\":{\"notice\":\"Sets new delta value\"},\"setStep(uint256)\":{\"notice\":\"Sets new step value\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/utils/DeltaStrikeSelection.sol\":\"DeltaStrikeSelection\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/access/Ownable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../utils/Context.sol\\\";\\n\\n/**\\n * @dev Contract module which provides a basic access control mechanism, where\\n * there is an account (an owner) that can be granted exclusive access to\\n * specific functions.\\n *\\n * By default, the owner account will be the one that deploys the contract. This\\n * can later be changed with {transferOwnership}.\\n *\\n * This module is used through inheritance. It will make available the modifier\\n * `onlyOwner`, which can be applied to your functions to restrict their use to\\n * the owner.\\n */\\nabstract contract Ownable is Context {\\n address private _owner;\\n\\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\\n\\n /**\\n * @dev Initializes the contract setting the deployer as the initial owner.\\n */\\n constructor() {\\n _setOwner(_msgSender());\\n }\\n\\n /**\\n * @dev Returns the address of the current owner.\\n */\\n function owner() public view virtual returns (address) {\\n return _owner;\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the owner.\\n */\\n modifier onlyOwner() {\\n require(owner() == _msgSender(), \\\"Ownable: caller is not the owner\\\");\\n _;\\n }\\n\\n /**\\n * @dev Leaves the contract without owner. It will not be possible to call\\n * `onlyOwner` functions anymore. Can only be called by the current owner.\\n *\\n * NOTE: Renouncing ownership will leave the contract without an owner,\\n * thereby removing any functionality that is only available to the owner.\\n */\\n function renounceOwnership() public virtual onlyOwner {\\n _setOwner(address(0));\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Can only be called by the current owner.\\n */\\n function transferOwnership(address newOwner) public virtual onlyOwner {\\n require(newOwner != address(0), \\\"Ownable: new owner is the zero address\\\");\\n _setOwner(newOwner);\\n }\\n\\n function _setOwner(address newOwner) private {\\n address oldOwner = _owner;\\n _owner = newOwner;\\n emit OwnershipTransferred(oldOwner, newOwner);\\n }\\n}\\n\",\"keccak256\":\"0x6bb804a310218875e89d12c053e94a13a4607cdf7cc2052f3e52bd32a0dc50a1\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Context.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Provides information about the current execution context, including the\\n * sender of the transaction and its data. While these are generally available\\n * via msg.sender and msg.data, they should not be accessed in such a direct\\n * manner, since when dealing with meta-transactions the account sending and\\n * paying for execution may not be the actual sender (as far as an application\\n * is concerned).\\n *\\n * This contract is only required for intermediate, library-like contracts.\\n */\\nabstract contract Context {\\n function _msgSender() internal view virtual returns (address) {\\n return msg.sender;\\n }\\n\\n function _msgData() internal view virtual returns (bytes calldata) {\\n return msg.data;\\n }\\n}\\n\",\"keccak256\":\"0x90565a39ae45c80f0468dc96c7b20d0afc3055f344c8203a0c9258239f350b9f\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/SafeMath.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\n\\n// CAUTION\\n// This version of SafeMath should only be used with Solidity 0.8 or later,\\n// because it relies on the compiler's built in overflow checks.\\n\\n/**\\n * @dev Wrappers over Solidity's arithmetic operations.\\n *\\n * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler\\n * now has built in overflow checking.\\n */\\nlibrary SafeMath {\\n /**\\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n uint256 c = a + b;\\n if (c < a) return (false, 0);\\n return (true, c);\\n }\\n }\\n\\n /**\\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\\n *\\n * _Available since v3.4._\\n */\\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n if (b > a) return (false, 0);\\n return (true, a - b);\\n }\\n }\\n\\n /**\\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\\n // benefit is lost if 'b' is also tested.\\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\\n if (a == 0) return (true, 0);\\n uint256 c = a * b;\\n if (c / a != b) return (false, 0);\\n return (true, c);\\n }\\n }\\n\\n /**\\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n if (b == 0) return (false, 0);\\n return (true, a / b);\\n }\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n if (b == 0) return (false, 0);\\n return (true, a % b);\\n }\\n }\\n\\n /**\\n * @dev Returns the addition of two unsigned integers, reverting on\\n * overflow.\\n *\\n * Counterpart to Solidity's `+` operator.\\n *\\n * Requirements:\\n *\\n * - Addition cannot overflow.\\n */\\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a + b;\\n }\\n\\n /**\\n * @dev Returns the subtraction of two unsigned integers, reverting on\\n * overflow (when the result is negative).\\n *\\n * Counterpart to Solidity's `-` operator.\\n *\\n * Requirements:\\n *\\n * - Subtraction cannot overflow.\\n */\\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a - b;\\n }\\n\\n /**\\n * @dev Returns the multiplication of two unsigned integers, reverting on\\n * overflow.\\n *\\n * Counterpart to Solidity's `*` operator.\\n *\\n * Requirements:\\n *\\n * - Multiplication cannot overflow.\\n */\\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a * b;\\n }\\n\\n /**\\n * @dev Returns the integer division of two unsigned integers, reverting on\\n * division by zero. The result is rounded towards zero.\\n *\\n * Counterpart to Solidity's `/` operator.\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a / b;\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\\n * reverting when dividing by zero.\\n *\\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\\n * opcode (which leaves remaining gas untouched) while Solidity uses an\\n * invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a % b;\\n }\\n\\n /**\\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\\n * overflow (when the result is negative).\\n *\\n * CAUTION: This function is deprecated because it requires allocating memory for the error\\n * message unnecessarily. For custom revert reasons use {trySub}.\\n *\\n * Counterpart to Solidity's `-` operator.\\n *\\n * Requirements:\\n *\\n * - Subtraction cannot overflow.\\n */\\n function sub(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n unchecked {\\n require(b <= a, errorMessage);\\n return a - b;\\n }\\n }\\n\\n /**\\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\\n * division by zero. The result is rounded towards zero.\\n *\\n * Counterpart to Solidity's `/` operator. Note: this function uses a\\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\\n * uses an invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function div(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n unchecked {\\n require(b > 0, errorMessage);\\n return a / b;\\n }\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\\n * reverting with custom message when dividing by zero.\\n *\\n * CAUTION: This function is deprecated because it requires allocating memory for the error\\n * message unnecessarily. For custom revert reasons use {tryMod}.\\n *\\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\\n * opcode (which leaves remaining gas untouched) while Solidity uses an\\n * invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function mod(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n unchecked {\\n require(b > 0, errorMessage);\\n return a % b;\\n }\\n }\\n}\\n\",\"keccak256\":\"0x8666f020bd8fc9dc14f07e2ebc52b5f236ab4cdde7c77679b08cb2f94730043b\",\"license\":\"MIT\"},\"@ribbon-finance/rvol/contracts/interfaces/IManualVolatilityOracle.sol\":{\"content\":\"//SPDX-License-Identifier: GPL-3.0\\npragma solidity >0.6.0 <0.8.7;\\n\\ninterface IManualVolatilityOracle {\\n function vol(bytes32 optionId)\\n external\\n view\\n returns (uint256 standardDeviation);\\n\\n function annualizedVol(bytes32 optionId)\\n external\\n view\\n returns (uint256 annualStdev);\\n\\n function setAnnualizedVol(\\n bytes32[] calldata optionIds,\\n uint256[] calldata newAnnualizedVols\\n ) external;\\n}\\n\",\"keccak256\":\"0xc854383f49a9bdb33532fa363556896a1088479dddd8827f12e4fd297c07f433\",\"license\":\"GPL-3.0\"},\"@ribbon-finance/rvol/contracts/interfaces/IPriceOracle.sol\":{\"content\":\"//SPDX-License-Identifier: GPL-3.0\\npragma solidity >0.6.0 <0.8.7;\\n\\ninterface IPriceOracle {\\n function decimals() external view returns (uint256 _decimals);\\n\\n function latestAnswer() external view returns (uint256 price);\\n}\\n\",\"keccak256\":\"0xabed5bf667e5c972fbfd9b2f06a8169b10dcaf4841bcc91caf937ed3c27eea1f\",\"license\":\"GPL-3.0\"},\"contracts/interfaces/IRibbon.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity =0.8.4;\\nimport {Vault} from \\\"../libraries/Vault.sol\\\";\\n\\ninterface IRibbonVault {\\n function deposit(uint256 amount) external;\\n\\n function depositETH() external payable;\\n\\n function cap() external view returns (uint256);\\n\\n function depositFor(uint256 amount, address creditor) external;\\n\\n function vaultParams() external view returns (Vault.VaultParams memory);\\n}\\n\\ninterface IStrikeSelection {\\n function getStrikePrice(uint256 expiryTimestamp, bool isPut)\\n external\\n view\\n returns (uint256, uint256);\\n\\n function delta() external view returns (uint256);\\n}\\n\\ninterface IOptionsPremiumPricer {\\n function getPremium(\\n uint256 strikePrice,\\n uint256 timeToExpiry,\\n bool isPut\\n ) external view returns (uint256);\\n\\n function getPremiumInStables(\\n uint256 strikePrice,\\n uint256 timeToExpiry,\\n bool isPut\\n ) external view returns (uint256);\\n\\n function getOptionDelta(\\n uint256 spotPrice,\\n uint256 strikePrice,\\n uint256 volatility,\\n uint256 expiryTimestamp\\n ) external view returns (uint256 delta);\\n\\n function getUnderlyingPrice() external view returns (uint256);\\n\\n function priceOracle() external view returns (address);\\n\\n function volatilityOracle() external view returns (address);\\n\\n function optionId() external view returns (bytes32);\\n}\\n\",\"keccak256\":\"0xd9ffea587ce992000d8a9f8d4b1bc8c40008633c35121b3d11ab8c6a7dd5ecbc\",\"license\":\"MIT\"},\"contracts/libraries/Vault.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity =0.8.4;\\n\\nlibrary Vault {\\n /************************************************\\n * IMMUTABLES & CONSTANTS\\n ***********************************************/\\n\\n // Fees are 6-decimal places. For example: 20 * 10**6 = 20%\\n uint256 internal constant FEE_MULTIPLIER = 10**6;\\n\\n // Premium discount has 1-decimal place. For example: 80 * 10**1 = 80%. Which represents a 20% discount.\\n uint256 internal constant PREMIUM_DISCOUNT_MULTIPLIER = 10;\\n\\n // Otokens have 8 decimal places.\\n uint256 internal constant OTOKEN_DECIMALS = 8;\\n\\n // Percentage of funds allocated to options is 2 decimal places. 10 * 10**2 = 10%\\n uint256 internal constant OPTION_ALLOCATION_MULTIPLIER = 10**2;\\n\\n // Placeholder uint value to prevent cold writes\\n uint256 internal constant PLACEHOLDER_UINT = 1;\\n\\n struct VaultParams {\\n // Option type the vault is selling\\n bool isPut;\\n // Token decimals for vault shares\\n uint8 decimals;\\n // Asset used in Theta / Delta Vault\\n address asset;\\n // Underlying asset of the options sold by vault\\n address underlying;\\n // Minimum supply of the vault shares issued, for ETH it's 10**10\\n uint56 minimumSupply;\\n // Vault cap\\n uint104 cap;\\n }\\n\\n struct OptionState {\\n // Option that the vault is shorting / longing in the next cycle\\n address nextOption;\\n // Option that the vault is currently shorting / longing\\n address currentOption;\\n // The timestamp when the `nextOption` can be used by the vault\\n uint32 nextOptionReadyAt;\\n }\\n\\n struct VaultState {\\n // 32 byte slot 1\\n // Current round number. `round` represents the number of `period`s elapsed.\\n uint16 round;\\n // Amount that is currently locked for selling options\\n uint104 lockedAmount;\\n // Amount that was locked for selling options in the previous round\\n // used for calculating performance fee deduction\\n uint104 lastLockedAmount;\\n // 32 byte slot 2\\n // Stores the total tally of how much of `asset` there is\\n // to be used to mint rTHETA tokens\\n uint128 totalPending;\\n // Total amount of queued withdrawal shares from previous rounds (doesn't include the current round)\\n uint128 queuedWithdrawShares;\\n }\\n\\n struct DepositReceipt {\\n // Maximum of 65535 rounds. Assuming 1 round is 7 days, maximum is 1256 years.\\n uint16 round;\\n // Deposit amount, max 20,282,409,603,651 or 20 trillion ETH deposit\\n uint104 amount;\\n // Unredeemed shares balance\\n uint128 unredeemedShares;\\n }\\n\\n struct Withdrawal {\\n // Maximum of 65535 rounds. Assuming 1 round is 7 days, maximum is 1256 years.\\n uint16 round;\\n // Number of shares withdrawn\\n uint128 shares;\\n }\\n\\n struct AuctionSellOrder {\\n // Amount of `asset` token offered in auction\\n uint96 sellAmount;\\n // Amount of oToken requested in auction\\n uint96 buyAmount;\\n // User Id of delta vault in latest gnosis auction\\n uint64 userId;\\n }\\n}\\n\",\"keccak256\":\"0x3119c096a02be6137015be605caf65412e0dd4b7df5f25e73bab4f88fddfa5d6\",\"license\":\"MIT\"},\"contracts/utils/DeltaStrikeSelection.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity =0.8.4;\\n\\nimport {SafeMath} from \\\"@openzeppelin/contracts/utils/math/SafeMath.sol\\\";\\nimport {\\n IPriceOracle\\n} from \\\"@ribbon-finance/rvol/contracts/interfaces/IPriceOracle.sol\\\";\\nimport {IOptionsPremiumPricer} from \\\"../interfaces/IRibbon.sol\\\";\\nimport {\\n IManualVolatilityOracle\\n} from \\\"@ribbon-finance/rvol/contracts/interfaces/IManualVolatilityOracle.sol\\\";\\nimport \\\"@openzeppelin/contracts/access/Ownable.sol\\\";\\nimport {Vault} from \\\"../libraries/Vault.sol\\\";\\n\\ncontract DeltaStrikeSelection is Ownable {\\n using SafeMath for uint256;\\n\\n /**\\n * Immutables\\n */\\n IOptionsPremiumPricer public immutable optionsPremiumPricer;\\n\\n IManualVolatilityOracle public immutable volatilityOracle;\\n\\n // delta for options strike price selection. 1 is 10000 (10**4)\\n uint256 public delta;\\n\\n // step in absolute terms at which we will increment\\n // (ex: 100 * 10 ** assetOracleDecimals means we will move at increments of 100 points)\\n uint256 public step;\\n\\n // multiplier to shift asset prices\\n uint256 private immutable assetOracleMultiplier;\\n\\n // Delta are in 4 decimal places. 1 * 10**4 = 1 delta.\\n uint256 private constant DELTA_MULTIPLIER = 10**4;\\n\\n // ChainLink's USD Price oracles return results in 8 decimal places\\n uint256 private constant ORACLE_PRICE_MULTIPLIER = 10**8;\\n\\n event DeltaSet(uint256 oldDelta, uint256 newDelta, address indexed owner);\\n event StepSet(uint256 oldStep, uint256 newStep, address indexed owner);\\n\\n constructor(\\n address _optionsPremiumPricer,\\n uint256 _delta,\\n uint256 _step\\n ) {\\n require(_optionsPremiumPricer != address(0), \\\"!_optionsPremiumPricer\\\");\\n require(_delta > 0, \\\"!_delta\\\");\\n require(_delta <= DELTA_MULTIPLIER, \\\"newDelta cannot be more than 1\\\");\\n require(_step > 0, \\\"!_step\\\");\\n\\n optionsPremiumPricer = IOptionsPremiumPricer(_optionsPremiumPricer);\\n volatilityOracle = IManualVolatilityOracle(\\n IOptionsPremiumPricer(_optionsPremiumPricer).volatilityOracle()\\n );\\n // ex: delta = 7500 (.75)\\n delta = _delta;\\n uint256 _assetOracleMultiplier =\\n 10 **\\n IPriceOracle(\\n IOptionsPremiumPricer(_optionsPremiumPricer).priceOracle()\\n )\\n .decimals();\\n\\n step = _step;\\n\\n assetOracleMultiplier = _assetOracleMultiplier;\\n }\\n\\n /**\\n * @notice Gets the strike price satisfying the delta value\\n * given the expiry timestamp and whether option is call or put\\n * @param expiryTimestamp is the unix timestamp of expiration\\n * @param isPut is whether option is put or call\\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\\n * @return newDelta is the delta of the option given its parameters\\n */\\n function getStrikePrice(uint256 expiryTimestamp, bool isPut)\\n external\\n view\\n returns (uint256 newStrikePrice, uint256 newDelta)\\n {\\n // asset's annualized volatility\\n uint256 annualizedVol =\\n volatilityOracle.annualizedVol(optionsPremiumPricer.optionId()).mul(\\n 10**10\\n );\\n return _getStrikePrice(expiryTimestamp, isPut, annualizedVol);\\n }\\n\\n /**\\n * @notice Gets the strike price satisfying the delta value\\n * given the expiry timestamp and whether option is call or put\\n * @param expiryTimestamp is the unix timestamp of expiration\\n * @param isPut is whether option is put or call\\n * @param annualizedVol is IV of the asset at the specified delta\\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\\n * @return newDelta is the delta of the option given its parameters\\n */\\n function getStrikePriceWithVol(\\n uint256 expiryTimestamp,\\n bool isPut,\\n uint256 annualizedVol\\n ) external view returns (uint256 newStrikePrice, uint256 newDelta) {\\n return\\n _getStrikePrice(expiryTimestamp, isPut, annualizedVol.mul(10**10));\\n }\\n\\n /**\\n * @notice Gets the strike price satisfying the delta value\\n * given the expiry timestamp and whether option is call or put\\n * @param expiryTimestamp is the unix timestamp of expiration\\n * @param isPut is whether option is put or call\\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\\n * @return newDelta is the delta of the option given its parameters\\n */\\n\\n function _getStrikePrice(\\n uint256 expiryTimestamp,\\n bool isPut,\\n uint256 annualizedVol\\n ) internal view returns (uint256 newStrikePrice, uint256 newDelta) {\\n require(\\n expiryTimestamp > block.timestamp,\\n \\\"Expiry must be in the future!\\\"\\n );\\n\\n // asset price\\n uint256 assetPrice = optionsPremiumPricer.getUnderlyingPrice();\\n\\n // For each asset prices with step of 'step' (down if put, up if call)\\n // if asset's getOptionDelta(currStrikePrice, spotPrice, annualizedVol, t) == (isPut ? 1 - delta:delta)\\n // with certain margin of error\\n // return strike price\\n\\n uint256 strike =\\n isPut\\n ? assetPrice.sub(assetPrice % step).sub(step)\\n : assetPrice.add(step - (assetPrice % step)).add(step);\\n uint256 targetDelta = isPut ? DELTA_MULTIPLIER.sub(delta) : delta;\\n uint256 prevDelta = isPut ? 0 : DELTA_MULTIPLIER;\\n\\n while (true) {\\n uint256 currDelta =\\n optionsPremiumPricer.getOptionDelta(\\n assetPrice.mul(ORACLE_PRICE_MULTIPLIER).div(\\n assetOracleMultiplier\\n ),\\n strike,\\n annualizedVol,\\n expiryTimestamp\\n );\\n // If the current delta is between the previous\\n // strike price delta and current strike price delta\\n // then we are done\\n bool foundTargetStrikePrice =\\n isPut\\n ? targetDelta >= prevDelta && targetDelta <= currDelta\\n : targetDelta <= prevDelta && targetDelta >= currDelta;\\n\\n if (foundTargetStrikePrice) {\\n uint256 finalDelta =\\n _getBestDelta(prevDelta, currDelta, targetDelta, isPut);\\n uint256 finalStrike =\\n _getBestStrike(finalDelta, prevDelta, strike, isPut);\\n require(\\n isPut\\n ? finalStrike <= assetPrice\\n : finalStrike >= assetPrice,\\n \\\"Invalid strike price\\\"\\n );\\n // make decimals consistent with oToken strike price decimals (10 ** 8)\\n return (\\n finalStrike.mul(ORACLE_PRICE_MULTIPLIER).div(\\n assetOracleMultiplier\\n ),\\n finalDelta\\n );\\n }\\n\\n strike = isPut ? strike.sub(step) : strike.add(step);\\n\\n prevDelta = currDelta;\\n }\\n }\\n\\n /**\\n * @notice Rounds to best delta value\\n * @param prevDelta is the delta of the previous strike price\\n * @param currDelta is delta of the current strike price\\n * @param targetDelta is the delta we are targeting\\n * @param isPut is whether its a put\\n * @return the best delta value\\n */\\n function _getBestDelta(\\n uint256 prevDelta,\\n uint256 currDelta,\\n uint256 targetDelta,\\n bool isPut\\n ) private pure returns (uint256) {\\n uint256 finalDelta;\\n\\n // for tie breaks (ex: 0.05 <= 0.1 <= 0.15) round to higher strike price\\n // for calls and lower strike price for puts for deltas\\n if (isPut) {\\n uint256 upperBoundDiff = currDelta.sub(targetDelta);\\n uint256 lowerBoundDiff = targetDelta.sub(prevDelta);\\n finalDelta = lowerBoundDiff <= upperBoundDiff\\n ? prevDelta\\n : currDelta;\\n } else {\\n uint256 upperBoundDiff = prevDelta.sub(targetDelta);\\n uint256 lowerBoundDiff = targetDelta.sub(currDelta);\\n finalDelta = lowerBoundDiff <= upperBoundDiff\\n ? currDelta\\n : prevDelta;\\n }\\n\\n return finalDelta;\\n }\\n\\n /**\\n * @notice Rounds to best delta value\\n * @param finalDelta is the best delta value we found\\n * @param prevDelta is delta of the previous strike price\\n * @param strike is the strike of the previous iteration\\n * @param isPut is whether its a put\\n * @return the best strike\\n */\\n function _getBestStrike(\\n uint256 finalDelta,\\n uint256 prevDelta,\\n uint256 strike,\\n bool isPut\\n ) private view returns (uint256) {\\n if (finalDelta != prevDelta) {\\n return strike;\\n }\\n return isPut ? strike.add(step) : strike.sub(step);\\n }\\n\\n /**\\n * @notice Sets new delta value\\n * @param newDelta is the new delta value\\n */\\n function setDelta(uint256 newDelta) external onlyOwner {\\n require(newDelta > 0, \\\"!newDelta\\\");\\n require(newDelta <= DELTA_MULTIPLIER, \\\"newDelta cannot be more than 1\\\");\\n uint256 oldDelta = delta;\\n delta = newDelta;\\n emit DeltaSet(oldDelta, newDelta, msg.sender);\\n }\\n\\n /**\\n * @notice Sets new step value\\n * @param newStep is the new step value\\n */\\n function setStep(uint256 newStep) external onlyOwner {\\n require(newStep > 0, \\\"!newStep\\\");\\n uint256 oldStep = step;\\n step = newStep;\\n emit StepSet(oldStep, newStep, msg.sender);\\n }\\n}\\n\",\"keccak256\":\"0x5ff1e071671b729eee403668cff36a6df25a8eb5ad71aee4b630f195ebac49d6\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x60e06040523480156200001157600080fd5b50604051620011e0380380620011e08339810160408190526200003491620003b7565b6200003f3362000326565b6001600160a01b0383166200009b5760405162461bcd60e51b815260206004820152601660248201527f215f6f7074696f6e735072656d69756d5072696365720000000000000000000060448201526064015b60405180910390fd5b60008211620000d75760405162461bcd60e51b8152602060048201526007602482015266215f64656c746160c81b604482015260640162000092565b6127108211156200012b5760405162461bcd60e51b815260206004820152601e60248201527f6e657744656c74612063616e6e6f74206265206d6f7265207468616e20310000604482015260640162000092565b60008111620001665760405162461bcd60e51b81526020600482015260066024820152650215f737465760d41b604482015260640162000092565b826001600160a01b03166080816001600160a01b031660601b81525050826001600160a01b03166355b13a4f6040518163ffffffff1660e01b815260040160206040518083038186803b158015620001bd57600080fd5b505afa158015620001d2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001f8919062000393565b6001600160a01b031660a0816001600160a01b031660601b81525050816001819055506000836001600160a01b0316632630c12f6040518163ffffffff1660e01b815260040160206040518083038186803b1580156200025757600080fd5b505afa1580156200026c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000292919062000393565b6001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015620002cb57600080fd5b505afa158015620002e0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003069190620003ee565b6200031390600a62000450565b6002929092555060c05250620005249050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b03811681146200038e57600080fd5b919050565b600060208284031215620003a5578081fd5b620003b08262000376565b9392505050565b600080600060608486031215620003cc578182fd5b620003d78462000376565b925060208401519150604084015190509250925092565b60006020828403121562000400578081fd5b5051919050565b600181815b80851115620004485781600019048211156200042c576200042c6200050e565b808516156200043a57918102915b93841c93908002906200040c565b509250929050565b6000620003b08383600082620004695750600162000508565b81620004785750600062000508565b81600181146200049157600281146200049c57620004bc565b600191505062000508565b60ff841115620004b057620004b06200050e565b50506001821b62000508565b5060208310610133831016604e8410600b8410161715620004e1575081810a62000508565b620004ed838362000407565b80600019048211156200050457620005046200050e565b0290505b92915050565b634e487b7160e01b600052601160045260246000fd5b60805160601c60a05160601c60c051610c636200057d6000396000818161078e01526108f601526000818161010c01526103400152600081816101640152818161036f0152818161063401526107640152610c636000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80638da5cb5b116100715780638da5cb5b1461014e578063afa662641461015f578063e25fe17514610186578063e436b8911461018f578063f2fde38b146101a2578063f8dcbddb146101b557600080fd5b8063025ff0a5146100ae5780630eaf0e7a146100c357806312b495a8146100f057806355b13a4f14610107578063715018a614610146575b600080fd5b6100c16100bc366004610adf565b6101c8565b005b6100d66100d1366004610b22565b6102d1565b604080519283526020830191909152015b60405180910390f35b6100f960015481565b6040519081526020016100e7565b61012e7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100e7565b6100c16102fa565b6000546001600160a01b031661012e565b61012e7f000000000000000000000000000000000000000000000000000000000000000081565b6100f960025481565b6100d661019d366004610af7565b610330565b6100c16101b0366004610aa0565b61048b565b6100c16101c3366004610adf565b610526565b6000546001600160a01b031633146101fb5760405162461bcd60e51b81526004016101f290610b56565b60405180910390fd5b600081116102375760405162461bcd60e51b8152602060048201526009602482015268216e657744656c746160b81b60448201526064016101f2565b6127108111156102895760405162461bcd60e51b815260206004820152601e60248201527f6e657744656c74612063616e6e6f74206265206d6f7265207468616e2031000060448201526064016101f2565b6001805490829055604080518281526020810184905233917f760f0242cb96fbbbd0cf6647501a2763dfd1ee22287ca1a688cb1d5f1ca6026d91015b60405180910390a25050565b6000806102ed85856102e8866402540be4006105cb565b6105de565b915091505b935093915050565b6000546001600160a01b031633146103245760405162461bcd60e51b81526004016101f290610b56565b61032e6000610967565b565b60008060006104726402540be4007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635ecfdf1e7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ad2717966040518163ffffffff1660e01b815260040160206040518083038186803b1580156103c657600080fd5b505afa1580156103da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103fe9190610ac7565b6040518263ffffffff1660e01b815260040161041c91815260200190565b60206040518083038186803b15801561043457600080fd5b505afa158015610448573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061046c9190610ac7565b906105cb565b905061047f8585836105de565b92509250509250929050565b6000546001600160a01b031633146104b55760405162461bcd60e51b81526004016101f290610b56565b6001600160a01b03811661051a5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016101f2565b61052381610967565b50565b6000546001600160a01b031633146105505760405162461bcd60e51b81526004016101f290610b56565b6000811161058b5760405162461bcd60e51b81526020600482015260086024820152670216e6577537465760c41b60448201526064016101f2565b6002805490829055604080518281526020810184905233917f08e332c6ae0c42d16506516b7ec987c8101ae8c5e930012ecd0c23142930518c91016102c5565b60006105d78284610bb7565b9392505050565b6000804285116106305760405162461bcd60e51b815260206004820152601d60248201527f457870697279206d75737420626520696e20746865206675747572652100000060448201526064016101f2565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663468f02d26040518163ffffffff1660e01b815260040160206040518083038186803b15801561068b57600080fd5b505afa15801561069f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c39190610ac7565b90506000856106ff576002546106fa906106f46106e08286610bed565b6002546106ed9190610bd6565b85906109b7565b906109b7565b610720565b6002546107209061071a6107138286610bed565b85906109c3565b906109c3565b905060008661073157600154610741565b60015461074190612710906109c3565b905060008761075257612710610755565b60005b90505b60006001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016639380576f6107c17f00000000000000000000000000000000000000000000000000000000000000006107bb896305f5e1006105cb565b906109cf565b6040516001600160e01b031960e084901b168152600481019190915260248101879052604481018b9052606481018d905260840160206040518083038186803b15801561080d57600080fd5b505afa158015610821573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108459190610ac7565b90506000896108635782841115801561085e5750818410155b610873565b8284101580156108735750818411155b905080156109355760006108898484878e6109db565b905060006108998286898f610a55565b90508b6108a957878110156108ae565b878111155b6108f15760405162461bcd60e51b8152602060048201526014602482015273496e76616c696420737472696b6520707269636560601b60448201526064016101f2565b6109237f00000000000000000000000000000000000000000000000000000000000000006107bb836305f5e1006105cb565b829950995050505050505050506102f2565b8961094d576002546109489086906109b7565b61095b565b60025461095b9086906109c3565b94508192505050610758565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006105d78284610b8b565b60006105d78284610bd6565b60006105d78284610ba3565b6000808215610a195760006109f086866109c3565b905060006109fe86896109c3565b905081811115610a0e5786610a10565b875b92505050610a4a565b6000610a2587866109c3565b90506000610a3386886109c3565b905081811115610a435787610a45565b865b925050505b90505b949350505050565b6000838514610a65575081610a4d565b81610a7d57600254610a789084906109c3565b610a4a565b600254610a4a9084906109b7565b80358015158114610a9b57600080fd5b919050565b600060208284031215610ab1578081fd5b81356001600160a01b03811681146105d7578182fd5b600060208284031215610ad8578081fd5b5051919050565b600060208284031215610af0578081fd5b5035919050565b60008060408385031215610b09578081fd5b82359150610b1960208401610a8b565b90509250929050565b600080600060608486031215610b36578081fd5b83359250610b4660208501610a8b565b9150604084013590509250925092565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60008219821115610b9e57610b9e610c01565b500190565b600082610bb257610bb2610c17565b500490565b6000816000190483118215151615610bd157610bd1610c01565b500290565b600082821015610be857610be8610c01565b500390565b600082610bfc57610bfc610c17565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fdfea2646970667358221220276d79f218416d4393930f8fd195b86715e3ce75343229b3d92d70b2cf38bb7564736f6c63430008040033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80638da5cb5b116100715780638da5cb5b1461014e578063afa662641461015f578063e25fe17514610186578063e436b8911461018f578063f2fde38b146101a2578063f8dcbddb146101b557600080fd5b8063025ff0a5146100ae5780630eaf0e7a146100c357806312b495a8146100f057806355b13a4f14610107578063715018a614610146575b600080fd5b6100c16100bc366004610adf565b6101c8565b005b6100d66100d1366004610b22565b6102d1565b604080519283526020830191909152015b60405180910390f35b6100f960015481565b6040519081526020016100e7565b61012e7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100e7565b6100c16102fa565b6000546001600160a01b031661012e565b61012e7f000000000000000000000000000000000000000000000000000000000000000081565b6100f960025481565b6100d661019d366004610af7565b610330565b6100c16101b0366004610aa0565b61048b565b6100c16101c3366004610adf565b610526565b6000546001600160a01b031633146101fb5760405162461bcd60e51b81526004016101f290610b56565b60405180910390fd5b600081116102375760405162461bcd60e51b8152602060048201526009602482015268216e657744656c746160b81b60448201526064016101f2565b6127108111156102895760405162461bcd60e51b815260206004820152601e60248201527f6e657744656c74612063616e6e6f74206265206d6f7265207468616e2031000060448201526064016101f2565b6001805490829055604080518281526020810184905233917f760f0242cb96fbbbd0cf6647501a2763dfd1ee22287ca1a688cb1d5f1ca6026d91015b60405180910390a25050565b6000806102ed85856102e8866402540be4006105cb565b6105de565b915091505b935093915050565b6000546001600160a01b031633146103245760405162461bcd60e51b81526004016101f290610b56565b61032e6000610967565b565b60008060006104726402540be4007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635ecfdf1e7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ad2717966040518163ffffffff1660e01b815260040160206040518083038186803b1580156103c657600080fd5b505afa1580156103da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103fe9190610ac7565b6040518263ffffffff1660e01b815260040161041c91815260200190565b60206040518083038186803b15801561043457600080fd5b505afa158015610448573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061046c9190610ac7565b906105cb565b905061047f8585836105de565b92509250509250929050565b6000546001600160a01b031633146104b55760405162461bcd60e51b81526004016101f290610b56565b6001600160a01b03811661051a5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016101f2565b61052381610967565b50565b6000546001600160a01b031633146105505760405162461bcd60e51b81526004016101f290610b56565b6000811161058b5760405162461bcd60e51b81526020600482015260086024820152670216e6577537465760c41b60448201526064016101f2565b6002805490829055604080518281526020810184905233917f08e332c6ae0c42d16506516b7ec987c8101ae8c5e930012ecd0c23142930518c91016102c5565b60006105d78284610bb7565b9392505050565b6000804285116106305760405162461bcd60e51b815260206004820152601d60248201527f457870697279206d75737420626520696e20746865206675747572652100000060448201526064016101f2565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663468f02d26040518163ffffffff1660e01b815260040160206040518083038186803b15801561068b57600080fd5b505afa15801561069f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c39190610ac7565b90506000856106ff576002546106fa906106f46106e08286610bed565b6002546106ed9190610bd6565b85906109b7565b906109b7565b610720565b6002546107209061071a6107138286610bed565b85906109c3565b906109c3565b905060008661073157600154610741565b60015461074190612710906109c3565b905060008761075257612710610755565b60005b90505b60006001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016639380576f6107c17f00000000000000000000000000000000000000000000000000000000000000006107bb896305f5e1006105cb565b906109cf565b6040516001600160e01b031960e084901b168152600481019190915260248101879052604481018b9052606481018d905260840160206040518083038186803b15801561080d57600080fd5b505afa158015610821573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108459190610ac7565b90506000896108635782841115801561085e5750818410155b610873565b8284101580156108735750818411155b905080156109355760006108898484878e6109db565b905060006108998286898f610a55565b90508b6108a957878110156108ae565b878111155b6108f15760405162461bcd60e51b8152602060048201526014602482015273496e76616c696420737472696b6520707269636560601b60448201526064016101f2565b6109237f00000000000000000000000000000000000000000000000000000000000000006107bb836305f5e1006105cb565b829950995050505050505050506102f2565b8961094d576002546109489086906109b7565b61095b565b60025461095b9086906109c3565b94508192505050610758565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006105d78284610b8b565b60006105d78284610bd6565b60006105d78284610ba3565b6000808215610a195760006109f086866109c3565b905060006109fe86896109c3565b905081811115610a0e5786610a10565b875b92505050610a4a565b6000610a2587866109c3565b90506000610a3386886109c3565b905081811115610a435787610a45565b865b925050505b90505b949350505050565b6000838514610a65575081610a4d565b81610a7d57600254610a789084906109c3565b610a4a565b600254610a4a9084906109b7565b80358015158114610a9b57600080fd5b919050565b600060208284031215610ab1578081fd5b81356001600160a01b03811681146105d7578182fd5b600060208284031215610ad8578081fd5b5051919050565b600060208284031215610af0578081fd5b5035919050565b60008060408385031215610b09578081fd5b82359150610b1960208401610a8b565b90509250929050565b600080600060608486031215610b36578081fd5b83359250610b4660208501610a8b565b9150604084013590509250925092565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60008219821115610b9e57610b9e610c01565b500190565b600082610bb257610bb2610c17565b500490565b6000816000190483118215151615610bd157610bd1610c01565b500290565b600082821015610be857610be8610c01565b500390565b600082610bfc57610bfc610c17565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fdfea2646970667358221220276d79f218416d4393930f8fd195b86715e3ce75343229b3d92d70b2cf38bb7564736f6c63430008040033", + "devdoc": { + "kind": "dev", + "methods": { + "getStrikePrice(uint256,bool)": { + "params": { + "expiryTimestamp": "is the unix timestamp of expiration", + "isPut": "is whether option is put or call" + }, + "returns": { + "newDelta": "is the delta of the option given its parameters", + "newStrikePrice": "is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)" + } + }, + "getStrikePriceWithVol(uint256,bool,uint256)": { + "params": { + "annualizedVol": "is IV of the asset at the specified delta", + "expiryTimestamp": "is the unix timestamp of expiration", + "isPut": "is whether option is put or call" + }, + "returns": { + "newDelta": "is the delta of the option given its parameters", + "newStrikePrice": "is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)" + } + }, + "owner()": { + "details": "Returns the address of the current owner." + }, + "renounceOwnership()": { + "details": "Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner." + }, + "setDelta(uint256)": { + "params": { + "newDelta": "is the new delta value" + } + }, + "setStep(uint256)": { + "params": { + "newStep": "is the new step value" + } + }, + "transferOwnership(address)": { + "details": "Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner." + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "getStrikePrice(uint256,bool)": { + "notice": "Gets the strike price satisfying the delta value given the expiry timestamp and whether option is call or put" + }, + "getStrikePriceWithVol(uint256,bool,uint256)": { + "notice": "Gets the strike price satisfying the delta value given the expiry timestamp and whether option is call or put" + }, + "optionsPremiumPricer()": { + "notice": "Immutables" + }, + "setDelta(uint256)": { + "notice": "Sets new delta value" + }, + "setStep(uint256)": { + "notice": "Sets new step value" + } + }, + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 1002, + "contract": "contracts/utils/DeltaStrikeSelection.sol:DeltaStrikeSelection", + "label": "_owner", + "offset": 0, + "slot": "0", + "type": "t_address" + }, + { + "astId": 14997, + "contract": "contracts/utils/DeltaStrikeSelection.sol:DeltaStrikeSelection", + "label": "delta", + "offset": 0, + "slot": "1", + "type": "t_uint256" + }, + { + "astId": 14999, + "contract": "contracts/utils/DeltaStrikeSelection.sol:DeltaStrikeSelection", + "label": "step", + "offset": 0, + "slot": "2", + "type": "t_uint256" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } +} \ No newline at end of file diff --git a/deployments/mainnet/solcInputs/863e1da1fc0c6524664d6bdf38c5110b.json b/deployments/mainnet/solcInputs/863e1da1fc0c6524664d6bdf38c5110b.json new file mode 100644 index 000000000..b7b6acfc8 --- /dev/null +++ b/deployments/mainnet/solcInputs/863e1da1fc0c6524664d6bdf38c5110b.json @@ -0,0 +1,308 @@ +{ + "language": "Solidity", + "sources": { + "contracts/interfaces/GammaInterface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nlibrary GammaTypes {\n // vault is a struct of 6 arrays that describe a position a user has, a user can have multiple vaults.\n struct Vault {\n // addresses of oTokens a user has shorted (i.e. written) against this vault\n address[] shortOtokens;\n // addresses of oTokens a user has bought and deposited in this vault\n // user can be long oTokens without opening a vault (e.g. by buying on a DEX)\n // generally, long oTokens will be 'deposited' in vaults to act as collateral\n // in order to write oTokens against (i.e. in spreads)\n address[] longOtokens;\n // addresses of other ERC-20s a user has deposited as collateral in this vault\n address[] collateralAssets;\n // quantity of oTokens minted/written for each oToken address in shortOtokens\n uint256[] shortAmounts;\n // quantity of oTokens owned and held in the vault for each oToken address in longOtokens\n uint256[] longAmounts;\n // quantity of ERC-20 deposited as collateral in the vault for each ERC-20 address in collateralAssets\n uint256[] collateralAmounts;\n }\n}\n\ninterface IOtoken {\n function underlyingAsset() external view returns (address);\n\n function strikeAsset() external view returns (address);\n\n function collateralAsset() external view returns (address);\n\n function strikePrice() external view returns (uint256);\n\n function expiryTimestamp() external view returns (uint256);\n\n function isPut() external view returns (bool);\n}\n\ninterface IOtokenFactory {\n function getOtoken(\n address _underlyingAsset,\n address _strikeAsset,\n address _collateralAsset,\n uint256 _strikePrice,\n uint256 _expiry,\n bool _isPut\n ) external view returns (address);\n\n function createOtoken(\n address _underlyingAsset,\n address _strikeAsset,\n address _collateralAsset,\n uint256 _strikePrice,\n uint256 _expiry,\n bool _isPut\n ) external returns (address);\n\n function getTargetOtokenAddress(\n address _underlyingAsset,\n address _strikeAsset,\n address _collateralAsset,\n uint256 _strikePrice,\n uint256 _expiry,\n bool _isPut\n ) external view returns (address);\n\n event OtokenCreated(\n address tokenAddress,\n address creator,\n address indexed underlying,\n address indexed strike,\n address indexed collateral,\n uint256 strikePrice,\n uint256 expiry,\n bool isPut\n );\n}\n\ninterface IController {\n // possible actions that can be performed\n enum ActionType {\n OpenVault,\n MintShortOption,\n BurnShortOption,\n DepositLongOption,\n WithdrawLongOption,\n DepositCollateral,\n WithdrawCollateral,\n SettleVault,\n Redeem,\n Call,\n Liquidate\n }\n\n struct ActionArgs {\n // type of action that is being performed on the system\n ActionType actionType;\n // address of the account owner\n address owner;\n // address which we move assets from or to (depending on the action type)\n address secondAddress;\n // asset that is to be transfered\n address asset;\n // index of the vault that is to be modified (if any)\n uint256 vaultId;\n // amount of asset that is to be transfered\n uint256 amount;\n // each vault can hold multiple short / long / collateral assets\n // but we are restricting the scope to only 1 of each in this version\n // in future versions this would be the index of the short / long / collateral asset that needs to be modified\n uint256 index;\n // any other data that needs to be passed in for arbitrary function calls\n bytes data;\n }\n\n struct RedeemArgs {\n // address to which we pay out the oToken proceeds\n address receiver;\n // oToken that is to be redeemed\n address otoken;\n // amount of oTokens that is to be redeemed\n uint256 amount;\n }\n\n function getPayout(address _otoken, uint256 _amount)\n external\n view\n returns (uint256);\n\n function operate(ActionArgs[] calldata _actions) external;\n\n function getAccountVaultCounter(address owner)\n external\n view\n returns (uint256);\n\n function oracle() external view returns (address);\n\n function getVault(address _owner, uint256 _vaultId)\n external\n view\n returns (GammaTypes.Vault memory);\n\n function getProceed(address _owner, uint256 _vaultId)\n external\n view\n returns (uint256);\n\n function isSettlementAllowed(\n address _underlying,\n address _strike,\n address _collateral,\n uint256 _expiry\n ) external view returns (bool);\n}\n\ninterface IOracle {\n function setAssetPricer(address _asset, address _pricer) external;\n\n function updateAssetPricer(address _asset, address _pricer) external;\n\n function getPrice(address _asset) external view returns (uint256);\n}\n" + }, + "contracts/oracles/OpynOracle.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\nimport {IOracle} from \"../interfaces/GammaInterface.sol\";\nimport {IPriceOracle} from \"../interfaces/IPriceOracle.sol\";\n\ncontract OpynOracle is IPriceOracle {\n /// @dev base decimals\n uint256 public constant override decimals = 8;\n\n /// @notice Gamma Protocol oracle\n IOracle public immutable oracle;\n\n /// @notice Asset to get the price of\n address public immutable asset;\n\n constructor(address _oracle, address _asset) {\n require(_oracle != address(0), \"!oracle\");\n require(_asset != address(0), \"!asset\");\n\n oracle = IOracle(_oracle);\n asset = _asset;\n }\n\n function latestAnswer() external view override returns (uint256) {\n return oracle.getPrice(asset);\n }\n}\n" + }, + "contracts/interfaces/IPriceOracle.sol": { + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.4;\n\ninterface IPriceOracle {\n function decimals() external view returns (uint256 _decimals);\n\n function latestAnswer() external view returns (uint256 price);\n}\n" + }, + "contracts/oracles/SAvaxOracle.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\nimport \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IPriceOracle} from \"../interfaces/IPriceOracle.sol\";\nimport {ISAVAX} from \"../interfaces/ISAVAX.sol\";\nimport {DSMath} from \"../vendor/DSMath.sol\";\n\ncontract SAvaxOracle is IPriceOracle {\n using SafeMath for uint256;\n\n AggregatorV3Interface public immutable WAVAXOracle;\n ISAVAX public immutable sAVAX;\n\n constructor(address _sAVAX, address _WAVAXOracle) {\n require(_sAVAX != address(0), \"!sAVAX\");\n require(_WAVAXOracle != address(0), \"!WAVAXOracle\");\n\n sAVAX = ISAVAX(_sAVAX);\n WAVAXOracle = AggregatorV3Interface(_WAVAXOracle);\n }\n\n function _underlyingPriceToSAvaxPrice(uint256 underlyingPrice)\n private\n view\n returns (uint256)\n {\n // Passing 1e18 to getPooledAvaxByShares() gives us the number of AVAX per sAVAX.\n uint256 sAvaxPerAvax = sAVAX.getPooledAvaxByShares(1e18);\n return sAvaxPerAvax.mul(underlyingPrice).div(1e18);\n }\n\n function latestAnswer() external view override returns (uint256) {\n (\n uint80 roundID,\n int256 price,\n ,\n uint256 timeStamp,\n uint80 answeredInRound\n ) = WAVAXOracle.latestRoundData();\n\n require(answeredInRound >= roundID, \"Stale oracle price\");\n require(timeStamp != 0, \"!timeStamp\");\n uint256 underlyingPrice = uint256(DSMath.imax(price, 0));\n return _underlyingPriceToSAvaxPrice(underlyingPrice);\n }\n\n function decimals() external view override returns (uint256) {\n return WAVAXOracle.decimals();\n }\n}\n" + }, + "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n// CAUTION\n// This version of SafeMath should only be used with Solidity 0.8 or later,\n// because it relies on the compiler's built in overflow checks.\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations.\n *\n * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler\n * now has built in overflow checking.\n */\nlibrary SafeMath {\n /**\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n uint256 c = a + b;\n if (c < a) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b > a) return (false, 0);\n return (true, a - b);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) return (true, 0);\n uint256 c = a * b;\n if (c / a != b) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a / b);\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a % b);\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n return a + b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n return a - b;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n return a * b;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator.\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n return a % b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n * overflow (when the result is negative).\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {trySub}.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b <= a, errorMessage);\n return a - b;\n }\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a / b;\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting with custom message when dividing by zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryMod}.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a % b;\n }\n }\n}\n" + }, + "contracts/interfaces/ISAVAX.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface ISAVAX is IERC20 {\n function getSharesByPooledAvax(uint256 avaxAmount)\n external\n view\n returns (uint256);\n\n function getPooledAvaxByShares(uint256 shareAmount)\n external\n view\n returns (uint256);\n\n function submit() external payable returns (uint256);\n}\n" + }, + "contracts/vendor/DSMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n\n/// math.sol -- mixin for inline numerical wizardry\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >0.4.13;\n\nlibrary DSMath {\n function add(uint256 x, uint256 y) internal pure returns (uint256 z) {\n require((z = x + y) >= x, \"ds-math-add-overflow\");\n }\n\n function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {\n require((z = x - y) <= x, \"ds-math-sub-underflow\");\n }\n\n function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {\n require(y == 0 || (z = x * y) / y == x, \"ds-math-mul-overflow\");\n }\n\n function min(uint256 x, uint256 y) internal pure returns (uint256 z) {\n return x <= y ? x : y;\n }\n\n function max(uint256 x, uint256 y) internal pure returns (uint256 z) {\n return x >= y ? x : y;\n }\n\n function imin(int256 x, int256 y) internal pure returns (int256 z) {\n return x <= y ? x : y;\n }\n\n function imax(int256 x, int256 y) internal pure returns (int256 z) {\n return x >= y ? x : y;\n }\n\n uint256 constant WAD = 10**18;\n uint256 constant RAY = 10**27;\n\n //rounds to zero if x*y < WAD / 2\n function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {\n z = add(mul(x, y), WAD / 2) / WAD;\n }\n\n //rounds to zero if x*y < WAD / 2\n function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {\n z = add(mul(x, y), RAY / 2) / RAY;\n }\n\n //rounds to zero if x*y < WAD / 2\n function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {\n z = add(mul(x, WAD), y / 2) / y;\n }\n\n //rounds to zero if x*y < RAY / 2\n function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {\n z = add(mul(x, RAY), y / 2) / y;\n }\n\n // This famous algorithm is called \"exponentiation by squaring\"\n // and calculates x^n with x as fixed-point and n as regular unsigned.\n //\n // It's O(log n), instead of O(n) for naive repeated multiplication.\n //\n // These facts are why it works:\n //\n // If n is even, then x^n = (x^2)^(n/2).\n // If n is odd, then x^n = x * x^(n-1),\n // and applying the equation for even x gives\n // x^n = x * (x^2)^((n-1) / 2).\n //\n // Also, EVM division is flooring and\n // floor[(n-1) / 2] = floor[n / 2].\n //\n function rpow(uint256 x, uint256 n) internal pure returns (uint256 z) {\n z = n % 2 != 0 ? x : RAY;\n\n for (n /= 2; n != 0; n /= 2) {\n x = rmul(x, x);\n\n if (n % 2 != 0) {\n z = rmul(z, x);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "contracts/libraries/VaultLifecycleYearn.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {DSMath} from \"../vendor/DSMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {VaultLifecycle} from \"./VaultLifecycle.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {ShareMath} from \"./ShareMath.sol\";\nimport {IYearnVault} from \"../interfaces/IYearn.sol\";\nimport {IWETH} from \"../interfaces/IWETH.sol\";\nimport {IStrikeSelection} from \"../interfaces/IRibbon.sol\";\nimport {\n IOtokenFactory,\n IOtoken,\n IController,\n GammaTypes\n} from \"../interfaces/GammaInterface.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\nimport {SupportsNonCompliantERC20} from \"./SupportsNonCompliantERC20.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\n\nlibrary VaultLifecycleYearn {\n using SafeMath for uint256;\n using SupportsNonCompliantERC20 for IERC20;\n using SafeERC20 for IERC20;\n\n /**\n * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @param collateralAsset is the address of the collateral asset\n * @param isYearnPaused is whether yearn is enabled in contract\n * @return otokenAddress is the address of the new option\n * @return strikePrice is the strike price of the new option\n * @return delta is the delta of the new option\n */\n function commitAndClose(\n VaultLifecycle.CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n Vault.VaultState storage vaultState,\n address collateralAsset,\n bool isYearnPaused\n )\n external\n returns (\n address otokenAddress,\n uint256 strikePrice,\n uint256 delta\n )\n {\n uint256 expiry =\n VaultLifecycle.getNextExpiry(closeParams.currentOption);\n\n bool isPut = vaultParams.isPut;\n\n IStrikeSelection selection =\n IStrikeSelection(closeParams.strikeSelection);\n\n // calculate strike and delta\n (strikePrice, delta) = closeParams.lastStrikeOverrideRound ==\n vaultState.round\n ? (closeParams.overriddenStrikePrice, selection.delta())\n : selection.getStrikePrice(expiry, isPut);\n\n require(strikePrice != 0, \"!strikePrice\");\n\n // retrieve address if option already exists, or deploy it\n otokenAddress = VaultLifecycle.getOrDeployOtoken(\n closeParams,\n vaultParams,\n vaultParams.underlying,\n isYearnPaused ? vaultParams.asset : collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n return (otokenAddress, strikePrice, delta);\n }\n\n /**\n * @notice Calculate the shares to mint, new price per share, and\n amount of funds to re-allocate as collateral for the new round\n * @param currentShareSupply is the total supply of shares\n * @param currentBalance is the total balance of the vault\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @return newLockedAmount is the amount of funds to allocate for the new round\n * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal\n * @return newPricePerShare is the price per share of the new round\n * @return mintShares is the amount of shares to mint from deposits\n */\n function rollover(\n uint256 currentShareSupply,\n uint256 currentBalance,\n Vault.VaultParams calldata vaultParams,\n Vault.VaultState calldata vaultState\n )\n external\n pure\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares\n )\n {\n uint256 pendingAmount = uint256(vaultState.totalPending);\n uint256 _decimals = vaultParams.decimals;\n\n newPricePerShare = ShareMath.pricePerShare(\n currentShareSupply,\n currentBalance,\n pendingAmount,\n _decimals\n );\n\n // After closing the short, if the options expire in-the-money\n // vault pricePerShare would go down because vault's asset balance decreased.\n // This ensures that the newly-minted shares do not take on the loss.\n uint256 _mintShares =\n ShareMath.assetToShares(pendingAmount, newPricePerShare, _decimals);\n\n uint256 newSupply = currentShareSupply.add(_mintShares);\n\n uint256 queuedAmount =\n newSupply > 0\n ? ShareMath.sharesToAsset(\n vaultState.queuedWithdrawShares,\n newPricePerShare,\n _decimals\n )\n : 0;\n\n return (\n currentBalance.sub(queuedAmount),\n queuedAmount,\n newPricePerShare,\n _mintShares\n );\n }\n\n /**\n * @notice Withdraws yvWETH + WETH (if necessary) from vault using vault shares\n * @param weth is the weth address\n * @param asset is the vault asset address\n * @param collateralToken is the address of the collateral token\n * @param recipient is the recipient\n * @param amount is the withdraw amount in `asset`\n * @return withdrawAmount is the withdraw amount in `collateralToken`\n */\n function withdrawYieldAndBaseToken(\n address weth,\n address asset,\n address collateralToken,\n address recipient,\n uint256 amount\n ) external returns (uint256) {\n uint256 pricePerYearnShare =\n IYearnVault(collateralToken).pricePerShare();\n uint256 withdrawAmount =\n DSMath.wdiv(\n amount,\n pricePerYearnShare.mul(decimalShift(collateralToken))\n );\n uint256 yieldTokenBalance =\n withdrawYieldToken(collateralToken, recipient, withdrawAmount);\n\n // If there is not enough yvWETH in the vault, it withdraws as much as possible and\n // transfers the rest in `asset`\n if (withdrawAmount > yieldTokenBalance) {\n withdrawBaseToken(\n weth,\n asset,\n collateralToken,\n recipient,\n withdrawAmount,\n yieldTokenBalance,\n pricePerYearnShare\n );\n }\n\n return withdrawAmount;\n }\n\n /**\n * @notice Withdraws yvWETH from vault\n * @param collateralToken is the address of the collateral token\n * @param recipient is the recipient\n * @param withdrawAmount is the withdraw amount in terms of yearn tokens\n * @return yieldTokenBalance is the balance of the yield token\n */\n function withdrawYieldToken(\n address collateralToken,\n address recipient,\n uint256 withdrawAmount\n ) internal returns (uint256) {\n IERC20 collateral = IERC20(collateralToken);\n\n uint256 yieldTokenBalance = collateral.balanceOf(address(this));\n uint256 yieldTokensToWithdraw =\n DSMath.min(yieldTokenBalance, withdrawAmount);\n if (yieldTokensToWithdraw > 0) {\n collateral.safeTransfer(recipient, yieldTokensToWithdraw);\n }\n\n return yieldTokenBalance;\n }\n\n /**\n * @notice Withdraws `asset` from vault\n * @param weth is the weth address\n * @param asset is the vault asset address\n * @param collateralToken is the address of the collateral token\n * @param recipient is the recipient\n * @param withdrawAmount is the withdraw amount in terms of yearn tokens\n * @param yieldTokenBalance is the collateral token (yvWETH) balance of the vault\n * @param pricePerYearnShare is the yvWETH<->WETH price ratio\n */\n function withdrawBaseToken(\n address weth,\n address asset,\n address collateralToken,\n address recipient,\n uint256 withdrawAmount,\n uint256 yieldTokenBalance,\n uint256 pricePerYearnShare\n ) internal {\n uint256 underlyingTokensToWithdraw =\n DSMath.wmul(\n withdrawAmount.sub(yieldTokenBalance),\n pricePerYearnShare.mul(decimalShift(collateralToken))\n );\n transferAsset(\n weth,\n asset,\n payable(recipient),\n underlyingTokensToWithdraw\n );\n }\n\n /**\n * @notice Unwraps the necessary amount of the yield-bearing yearn token\n * and transfers amount to vault\n * @param amount is the amount of `asset` to withdraw\n * @param asset is the vault asset address\n * @param collateralToken is the address of the collateral token\n * @param yearnWithdrawalBuffer is the buffer for withdrawals from yearn vault\n * @param yearnWithdrawalSlippage is the slippage for withdrawals from yearn vault\n */\n function unwrapYieldToken(\n uint256 amount,\n address asset,\n address collateralToken,\n uint256 yearnWithdrawalBuffer,\n uint256 yearnWithdrawalSlippage\n ) external {\n uint256 assetBalance = IERC20(asset).balanceOf(address(this));\n IYearnVault collateral = IYearnVault(collateralToken);\n\n uint256 amountToUnwrap =\n DSMath.wdiv(\n DSMath.max(assetBalance, amount).sub(assetBalance),\n collateral.pricePerShare().mul(decimalShift(collateralToken))\n );\n\n if (amountToUnwrap > 0) {\n amountToUnwrap = amountToUnwrap\n .add(amountToUnwrap.mul(yearnWithdrawalBuffer).div(10000))\n .sub(1);\n\n collateral.withdraw(\n amountToUnwrap,\n address(this),\n yearnWithdrawalSlippage\n );\n }\n }\n\n /**\n * @notice Wraps the necessary amount of the base token to the yield-bearing yearn token\n * @param asset is the vault asset address\n * @param collateralToken is the address of the collateral token\n */\n function wrapToYieldToken(address asset, address collateralToken) external {\n uint256 amountToWrap = IERC20(asset).balanceOf(address(this));\n\n if (amountToWrap > 0) {\n IERC20(asset).safeApprove(collateralToken, amountToWrap);\n\n // there is a slight imprecision with regards to calculating back from yearn token -> underlying\n // that stems from miscoordination between ytoken .deposit() amount wrapped and pricePerShare\n // at that point in time.\n // ex: if I have 1 eth, deposit 1 eth into yearn vault and calculate value of yearn token balance\n // denominated in eth (via balance(yearn token) * pricePerShare) we will get 1 eth - 1 wei.\n IYearnVault(collateralToken).deposit(amountToWrap, address(this));\n }\n }\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param weth is the weth address\n * @param asset is the vault asset address\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(\n address weth,\n address asset,\n address recipient,\n uint256 amount\n ) public {\n if (asset == weth) {\n IWETH(weth).withdraw(amount);\n (bool success, ) = payable(recipient).call{value: amount}(\"\");\n require(success, \"!success\");\n return;\n }\n IERC20(asset).safeTransfer(recipient, amount);\n }\n\n /**\n * @notice Returns the decimal shift between 18 decimals and asset tokens\n * @param collateralToken is the address of the collateral token\n */\n function decimalShift(address collateralToken)\n public\n view\n returns (uint256)\n {\n return\n 10**(uint256(18).sub(IERC20Detailed(collateralToken).decimals()));\n }\n\n function getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount,\n address collateralAsset,\n bool isYearnPaused\n ) external view returns (uint256) {\n return\n _getOTokenPremium(\n oTokenAddress,\n optionsPremiumPricer,\n premiumDiscount,\n collateralAsset,\n isYearnPaused\n );\n }\n\n function _getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount,\n address collateralAsset,\n bool isYearnPaused\n ) internal view returns (uint256) {\n IOtoken newOToken = IOtoken(oTokenAddress);\n IOptionsPremiumPricer premiumPricer =\n IOptionsPremiumPricer(optionsPremiumPricer);\n\n // Apply black-scholes formula (from rvol library) to option given its features\n // and get price for 100 contracts denominated in the underlying asset for call option\n // and USDC for put option\n uint256 optionPremium =\n premiumPricer.getPremium(\n newOToken.strikePrice(),\n newOToken.expiryTimestamp(),\n newOToken.isPut()\n );\n\n // Apply a discount to incentivize arbitraguers\n optionPremium = optionPremium.mul(premiumDiscount).div(\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER\n );\n\n // get the black scholes premium of the option and adjust premium based on\n // collateral asset <-> asset exchange rate\n uint256 adjustedPremium =\n isYearnPaused\n ? optionPremium\n : DSMath.wmul(\n optionPremium,\n IYearnVault(collateralAsset).pricePerShare().mul(\n decimalShift(collateralAsset)\n )\n );\n\n require(\n adjustedPremium <= type(uint96).max,\n \"adjustedPremium > type(uint96) max value!\"\n );\n\n require(adjustedPremium > 0, \"!adjustedPremium\");\n\n return adjustedPremium;\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "contracts/libraries/VaultLifecycle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {ShareMath} from \"./ShareMath.sol\";\nimport {IStrikeSelection} from \"../interfaces/IRibbon.sol\";\nimport {GnosisAuction} from \"./GnosisAuction.sol\";\nimport {\n IOtokenFactory,\n IOtoken,\n IController,\n GammaTypes\n} from \"../interfaces/GammaInterface.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\nimport {IGnosisAuction} from \"../interfaces/IGnosisAuction.sol\";\nimport {IOptionsPurchaseQueue} from \"../interfaces/IOptionsPurchaseQueue.sol\";\nimport {SupportsNonCompliantERC20} from \"./SupportsNonCompliantERC20.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\n\nlibrary VaultLifecycle {\n using SafeMath for uint256;\n using SupportsNonCompliantERC20 for IERC20;\n\n struct CloseParams {\n address OTOKEN_FACTORY;\n address USDC;\n address currentOption;\n uint256 delay;\n uint16 lastStrikeOverrideRound;\n uint256 overriddenStrikePrice;\n address strikeSelection;\n address optionsPremiumPricer;\n uint256 premiumDiscount;\n }\n\n /// @notice Default maximum option allocation for the queue (50%)\n uint256 internal constant QUEUE_OPTION_ALLOCATION = 5000;\n\n /**\n * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @return otokenAddress is the address of the new option\n * @return strikePrice is the strike price of the new option\n * @return delta is the delta of the new option\n */\n function commitAndClose(\n CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n Vault.VaultState storage vaultState\n )\n external\n returns (\n address otokenAddress,\n uint256 strikePrice,\n uint256 delta\n )\n {\n uint256 expiry = getNextExpiry(closeParams.currentOption);\n\n IStrikeSelection selection =\n IStrikeSelection(closeParams.strikeSelection);\n\n bool isPut = vaultParams.isPut;\n address underlying = vaultParams.underlying;\n address asset = vaultParams.asset;\n\n (strikePrice, delta) = closeParams.lastStrikeOverrideRound ==\n vaultState.round\n ? (closeParams.overriddenStrikePrice, selection.delta())\n : selection.getStrikePrice(expiry, isPut);\n\n require(strikePrice != 0, \"!strikePrice\");\n\n // retrieve address if option already exists, or deploy it\n otokenAddress = getOrDeployOtoken(\n closeParams,\n vaultParams,\n underlying,\n asset,\n strikePrice,\n expiry,\n isPut\n );\n\n return (otokenAddress, strikePrice, delta);\n }\n\n /**\n * @notice Verify the otoken has the correct parameters to prevent vulnerability to opyn contract changes\n * @param otokenAddress is the address of the otoken\n * @param vaultParams is the struct with vault general data\n * @param collateralAsset is the address of the collateral asset\n * @param USDC is the address of usdc\n * @param delay is the delay between commitAndClose and rollToNextOption\n */\n function verifyOtoken(\n address otokenAddress,\n Vault.VaultParams storage vaultParams,\n address collateralAsset,\n address USDC,\n uint256 delay\n ) private view {\n require(otokenAddress != address(0), \"!otokenAddress\");\n\n IOtoken otoken = IOtoken(otokenAddress);\n require(otoken.isPut() == vaultParams.isPut, \"Type mismatch\");\n require(\n otoken.underlyingAsset() == vaultParams.underlying,\n \"Wrong underlyingAsset\"\n );\n require(\n otoken.collateralAsset() == collateralAsset,\n \"Wrong collateralAsset\"\n );\n\n // we just assume all options use USDC as the strike\n require(otoken.strikeAsset() == USDC, \"strikeAsset != USDC\");\n\n uint256 readyAt = block.timestamp.add(delay);\n require(otoken.expiryTimestamp() >= readyAt, \"Expiry before delay\");\n }\n\n /**\n * @param decimals is the decimals of the asset\n * @param totalBalance is the vaults total balance of the asset\n * @param currentShareSupply is the supply of the shares invoked with totalSupply()\n * @param lastQueuedWithdrawAmount is the total amount queued for withdrawals\n * @param performanceFee is the perf fee percent to charge on premiums\n * @param managementFee is the management fee percent to charge on the AUM\n * @param currentQueuedWithdrawShares is amount of queued withdrawals from the current round\n */\n struct RolloverParams {\n uint256 decimals;\n uint256 totalBalance;\n uint256 currentShareSupply;\n uint256 lastQueuedWithdrawAmount;\n uint256 performanceFee;\n uint256 managementFee;\n uint256 currentQueuedWithdrawShares;\n }\n\n /**\n * @notice Calculate the shares to mint, new price per share, and\n amount of funds to re-allocate as collateral for the new round\n * @param vaultState is the storage variable vaultState passed from RibbonVault\n * @param params is the rollover parameters passed to compute the next state\n * @return newLockedAmount is the amount of funds to allocate for the new round\n * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal\n * @return newPricePerShare is the price per share of the new round\n * @return mintShares is the amount of shares to mint from deposits\n * @return performanceFeeInAsset is the performance fee charged by vault\n * @return totalVaultFee is the total amount of fee charged by vault\n */\n function rollover(\n Vault.VaultState storage vaultState,\n RolloverParams calldata params\n )\n external\n view\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares,\n uint256 performanceFeeInAsset,\n uint256 totalVaultFee\n )\n {\n uint256 currentBalance = params.totalBalance;\n uint256 pendingAmount = vaultState.totalPending;\n // Total amount of queued withdrawal shares from previous rounds (doesn't include the current round)\n uint256 lastQueuedWithdrawShares = vaultState.queuedWithdrawShares;\n\n // Deduct older queued withdraws so we don't charge fees on them\n uint256 balanceForVaultFees =\n currentBalance.sub(params.lastQueuedWithdrawAmount);\n\n {\n (performanceFeeInAsset, , totalVaultFee) = VaultLifecycle\n .getVaultFees(\n balanceForVaultFees,\n vaultState.lastLockedAmount,\n vaultState.totalPending,\n params.performanceFee,\n params.managementFee\n );\n }\n\n // Take into account the fee\n // so we can calculate the newPricePerShare\n currentBalance = currentBalance.sub(totalVaultFee);\n\n {\n newPricePerShare = ShareMath.pricePerShare(\n params.currentShareSupply.sub(lastQueuedWithdrawShares),\n currentBalance.sub(params.lastQueuedWithdrawAmount),\n pendingAmount,\n params.decimals\n );\n\n queuedWithdrawAmount = params.lastQueuedWithdrawAmount.add(\n ShareMath.sharesToAsset(\n params.currentQueuedWithdrawShares,\n newPricePerShare,\n params.decimals\n )\n );\n\n // After closing the short, if the options expire in-the-money\n // vault pricePerShare would go down because vault's asset balance decreased.\n // This ensures that the newly-minted shares do not take on the loss.\n mintShares = ShareMath.assetToShares(\n pendingAmount,\n newPricePerShare,\n params.decimals\n );\n }\n\n return (\n currentBalance.sub(queuedWithdrawAmount), // new locked balance subtracts the queued withdrawals\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n );\n }\n\n /**\n * @notice Creates the actual Opyn short position by depositing collateral and minting otokens\n * @param gammaController is the address of the opyn controller contract\n * @param marginPool is the address of the opyn margin contract which holds the collateral\n * @param oTokenAddress is the address of the otoken to mint\n * @param depositAmount is the amount of collateral to deposit\n * @return the otoken mint amount\n */\n function createShort(\n address gammaController,\n address marginPool,\n address oTokenAddress,\n uint256 depositAmount\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n uint256 newVaultID =\n (controller.getAccountVaultCounter(address(this))).add(1);\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IOtoken oToken = IOtoken(oTokenAddress);\n address collateralAsset = oToken.collateralAsset();\n\n uint256 collateralDecimals =\n uint256(IERC20Detailed(collateralAsset).decimals());\n uint256 mintAmount;\n\n if (oToken.isPut()) {\n // For minting puts, there will be instances where the full depositAmount will not be used for minting.\n // This is because of an issue with precision.\n //\n // For ETH put options, we are calculating the mintAmount (10**8 decimals) using\n // the depositAmount (10**18 decimals), which will result in truncation of decimals when scaling down.\n // As a result, there will be tiny amounts of dust left behind in the Opyn vault when minting put otokens.\n //\n // For simplicity's sake, we do not refund the dust back to the address(this) on minting otokens.\n // We retain the dust in the vault so the calling contract can withdraw the\n // actual locked amount + dust at settlement.\n //\n // To test this behavior, we can console.log\n // MarginCalculatorInterface(0x7A48d10f372b3D7c60f6c9770B91398e4ccfd3C7).getExcessCollateral(vault)\n // to see how much dust (or excess collateral) is left behind.\n mintAmount = depositAmount\n .mul(10**Vault.OTOKEN_DECIMALS)\n .mul(10**18) // we use 10**18 to give extra precision\n .div(oToken.strikePrice().mul(10**(10 + collateralDecimals)));\n } else {\n mintAmount = depositAmount;\n\n if (collateralDecimals > 8) {\n uint256 scaleBy = 10**(collateralDecimals.sub(8)); // oTokens have 8 decimals\n if (mintAmount > scaleBy) {\n mintAmount = depositAmount.div(scaleBy); // scale down from 10**18 to 10**8\n }\n }\n }\n\n // double approve to fix non-compliant ERC20s\n IERC20 collateralToken = IERC20(collateralAsset);\n collateralToken.safeApproveNonCompliant(marginPool, depositAmount);\n\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](3);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.OpenVault,\n address(this), // owner\n address(this), // receiver\n address(0), // asset, otoken\n newVaultID, // vaultId\n 0, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.DepositCollateral,\n address(this), // owner\n address(this), // address to transfer from\n collateralAsset, // deposited asset\n newVaultID, // vaultId\n depositAmount, // amount\n 0, //index\n \"\" //data\n );\n\n actions[2] = IController.ActionArgs(\n IController.ActionType.MintShortOption,\n address(this), // owner\n address(this), // address to transfer to\n oTokenAddress, // option address\n newVaultID, // vaultId\n mintAmount, // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n return mintAmount;\n }\n\n /**\n * @notice Close the existing short otoken position. Currently this implementation is simple.\n * It closes the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time. Since calling `_closeShort` deletes vaults by\n calling SettleVault action, this assumption should hold.\n * @param gammaController is the address of the opyn controller contract\n * @return amount of collateral redeemed from the vault\n */\n function settleShort(address gammaController) external returns (uint256) {\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n // The short position has been previously closed, or all the otokens have been burned.\n // So we return early.\n if (address(collateralToken) == address(0)) {\n return 0;\n }\n\n // This is equivalent to doing IERC20(vault.asset).balanceOf(address(this))\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // If it is after expiry, we need to settle the short position using the normal way\n // Delete the vault and withdraw all remaining collateral from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.SettleVault,\n address(this), // owner\n address(this), // address to transfer to\n address(0), // not used\n vaultID, // vaultId\n 0, // not used\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Exercises the ITM option using existing long otoken position. Currently this implementation is simple.\n * It calls the `Redeem` action to claim the payout.\n * @param gammaController is the address of the opyn controller contract\n * @param oldOption is the address of the old option\n * @param asset is the address of the vault's asset\n * @return amount of asset received by exercising the option\n */\n function settleLong(\n address gammaController,\n address oldOption,\n address asset\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n\n uint256 oldOptionBalance = IERC20(oldOption).balanceOf(address(this));\n\n if (controller.getPayout(oldOption, oldOptionBalance) == 0) {\n return 0;\n }\n\n uint256 startAssetBalance = IERC20(asset).balanceOf(address(this));\n\n // If it is after expiry, we need to redeem the profits\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.Redeem,\n address(0), // not used\n address(this), // address to send profits to\n oldOption, // address of otoken\n 0, // not used\n oldOptionBalance, // otoken balance\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endAssetBalance = IERC20(asset).balanceOf(address(this));\n\n return endAssetBalance.sub(startAssetBalance);\n }\n\n /**\n * @notice Burn the remaining oTokens left over from auction. Currently this implementation is simple.\n * It burns oTokens from the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time.\n * @param gammaController is the address of the opyn controller contract\n * @param currentOption is the address of the current option\n * @return amount of collateral redeemed by burning otokens\n */\n function burnOtokens(address gammaController, address currentOption)\n external\n returns (uint256)\n {\n uint256 numOTokensToBurn =\n IERC20(currentOption).balanceOf(address(this));\n\n require(numOTokensToBurn > 0, \"No oTokens to burn\");\n\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // Burning `amount` of oTokens from the ribbon vault,\n // then withdrawing the corresponding collateral amount from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](2);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.BurnShortOption,\n address(this), // owner\n address(this), // address to transfer from\n address(vault.shortOtokens[0]), // otoken address\n vaultID, // vaultId\n numOTokensToBurn, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.WithdrawCollateral,\n address(this), // owner\n address(this), // address to transfer to\n address(collateralToken), // withdrawn asset\n vaultID, // vaultId\n vault.collateralAmounts[0].mul(numOTokensToBurn).div(\n vault.shortAmounts[0]\n ), // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Calculates the performance and management fee for this week's round\n * @param currentBalance is the balance of funds held on the vault after closing short\n * @param lastLockedAmount is the amount of funds locked from the previous round\n * @param pendingAmount is the pending deposit amount\n * @param performanceFeePercent is the performance fee pct.\n * @param managementFeePercent is the management fee pct.\n * @return performanceFeeInAsset is the performance fee\n * @return managementFeeInAsset is the management fee\n * @return vaultFee is the total fees\n */\n function getVaultFees(\n uint256 currentBalance,\n uint256 lastLockedAmount,\n uint256 pendingAmount,\n uint256 performanceFeePercent,\n uint256 managementFeePercent\n )\n internal\n pure\n returns (\n uint256 performanceFeeInAsset,\n uint256 managementFeeInAsset,\n uint256 vaultFee\n )\n {\n // At the first round, currentBalance=0, pendingAmount>0\n // so we just do not charge anything on the first round\n uint256 lockedBalanceSansPending =\n currentBalance > pendingAmount\n ? currentBalance.sub(pendingAmount)\n : 0;\n\n uint256 _performanceFeeInAsset;\n uint256 _managementFeeInAsset;\n uint256 _vaultFee;\n\n // Take performance fee and management fee ONLY if difference between\n // last week and this week's vault deposits, taking into account pending\n // deposits and withdrawals, is positive. If it is negative, last week's\n // option expired ITM past breakeven, and the vault took a loss so we\n // do not collect performance fee for last week\n if (lockedBalanceSansPending > lastLockedAmount) {\n _performanceFeeInAsset = performanceFeePercent > 0\n ? lockedBalanceSansPending\n .sub(lastLockedAmount)\n .mul(performanceFeePercent)\n .div(100 * Vault.FEE_MULTIPLIER)\n : 0;\n _managementFeeInAsset = managementFeePercent > 0\n ? lockedBalanceSansPending.mul(managementFeePercent).div(\n 100 * Vault.FEE_MULTIPLIER\n )\n : 0;\n\n _vaultFee = _performanceFeeInAsset.add(_managementFeeInAsset);\n }\n\n return (_performanceFeeInAsset, _managementFeeInAsset, _vaultFee);\n }\n\n /**\n * @notice Either retrieves the option token if it already exists, or deploy it\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param underlying is the address of the underlying asset of the option\n * @param collateralAsset is the address of the collateral asset of the option\n * @param strikePrice is the strike price of the option\n * @param expiry is the expiry timestamp of the option\n * @param isPut is whether the option is a put\n * @return the address of the option\n */\n function getOrDeployOtoken(\n CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n address underlying,\n address collateralAsset,\n uint256 strikePrice,\n uint256 expiry,\n bool isPut\n ) internal returns (address) {\n IOtokenFactory factory = IOtokenFactory(closeParams.OTOKEN_FACTORY);\n\n address otokenFromFactory =\n factory.getOtoken(\n underlying,\n closeParams.USDC,\n collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n if (otokenFromFactory != address(0)) {\n return otokenFromFactory;\n }\n\n address otoken =\n factory.createOtoken(\n underlying,\n closeParams.USDC,\n collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n verifyOtoken(\n otoken,\n vaultParams,\n collateralAsset,\n closeParams.USDC,\n closeParams.delay\n );\n\n return otoken;\n }\n\n function getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount\n ) external view returns (uint256) {\n return\n _getOTokenPremium(\n oTokenAddress,\n optionsPremiumPricer,\n premiumDiscount\n );\n }\n\n function _getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount\n ) internal view returns (uint256) {\n IOtoken newOToken = IOtoken(oTokenAddress);\n IOptionsPremiumPricer premiumPricer =\n IOptionsPremiumPricer(optionsPremiumPricer);\n\n // Apply black-scholes formula (from rvol library) to option given its features\n // and get price for 100 contracts denominated in the underlying asset for call option\n // and USDC for put option\n uint256 optionPremium =\n premiumPricer.getPremium(\n newOToken.strikePrice(),\n newOToken.expiryTimestamp(),\n newOToken.isPut()\n );\n\n // Apply a discount to incentivize arbitraguers\n optionPremium = optionPremium.mul(premiumDiscount).div(\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER\n );\n\n require(\n optionPremium <= type(uint96).max,\n \"optionPremium > type(uint96) max value!\"\n );\n require(optionPremium > 0, \"!optionPremium\");\n\n return optionPremium;\n }\n\n /**\n * @notice Starts the gnosis auction\n * @param auctionDetails is the struct with all the custom parameters of the auction\n * @return the auction id of the newly created auction\n */\n function startAuction(GnosisAuction.AuctionDetails calldata auctionDetails)\n external\n returns (uint256)\n {\n return GnosisAuction.startAuction(auctionDetails);\n }\n\n /**\n * @notice Settles the gnosis auction\n * @param gnosisEasyAuction is the contract address of Gnosis easy auction protocol\n * @param auctionID is the auction ID of the gnosis easy auction\n */\n function settleAuction(address gnosisEasyAuction, uint256 auctionID)\n internal\n {\n IGnosisAuction(gnosisEasyAuction).settleAuction(auctionID);\n }\n\n /**\n * @notice Places a bid in an auction\n * @param bidDetails is the struct with all the details of the\n bid including the auction's id and how much to bid\n */\n function placeBid(GnosisAuction.BidDetails calldata bidDetails)\n external\n returns (\n uint256 sellAmount,\n uint256 buyAmount,\n uint64 userId\n )\n {\n return GnosisAuction.placeBid(bidDetails);\n }\n\n /**\n * @notice Claims the oTokens belonging to the vault\n * @param auctionSellOrder is the sell order of the bid\n * @param gnosisEasyAuction is the address of the gnosis auction contract\n holding custody to the funds\n * @param counterpartyThetaVault is the address of the counterparty theta\n vault of this delta vault\n */\n function claimAuctionOtokens(\n Vault.AuctionSellOrder calldata auctionSellOrder,\n address gnosisEasyAuction,\n address counterpartyThetaVault\n ) external {\n GnosisAuction.claimAuctionOtokens(\n auctionSellOrder,\n gnosisEasyAuction,\n counterpartyThetaVault\n );\n }\n\n /**\n * @notice Allocates the vault's minted options to the OptionsPurchaseQueue contract\n * @dev Skipped if the optionsPurchaseQueue doesn't exist\n * @param optionsPurchaseQueue is the OptionsPurchaseQueue contract\n * @param option is the minted option\n * @param optionsAmount is the amount of options minted\n * @param optionAllocation is the maximum % of options to allocate towards the purchase queue (will only allocate\n * up to the amount that is on the queue)\n * @return allocatedOptions is the amount of options that ended up getting allocated to the OptionsPurchaseQueue\n */\n function allocateOptions(\n address optionsPurchaseQueue,\n address option,\n uint256 optionsAmount,\n uint256 optionAllocation\n ) external returns (uint256 allocatedOptions) {\n // Skip if optionsPurchaseQueue is address(0)\n if (optionsPurchaseQueue != address(0)) {\n allocatedOptions = optionsAmount.mul(optionAllocation).div(\n 100 * Vault.OPTION_ALLOCATION_MULTIPLIER\n );\n allocatedOptions = IOptionsPurchaseQueue(optionsPurchaseQueue)\n .getOptionsAllocation(address(this), allocatedOptions);\n\n if (allocatedOptions != 0) {\n IERC20(option).approve(optionsPurchaseQueue, allocatedOptions);\n IOptionsPurchaseQueue(optionsPurchaseQueue).allocateOptions(\n allocatedOptions\n );\n }\n }\n\n return allocatedOptions;\n }\n\n /**\n * @notice Sell the allocated options to the purchase queue post auction settlement\n * @dev Reverts if the auction hasn't settled yet\n * @param optionsPurchaseQueue is the OptionsPurchaseQueue contract\n * @param gnosisEasyAuction The address of the Gnosis Easy Auction contract\n * @return totalPremiums Total premiums earnt by the vault\n */\n function sellOptionsToQueue(\n address optionsPurchaseQueue,\n address gnosisEasyAuction,\n uint256 optionAuctionID\n ) external returns (uint256) {\n uint256 settlementPrice =\n getAuctionSettlementPrice(gnosisEasyAuction, optionAuctionID);\n require(settlementPrice != 0, \"!settlementPrice\");\n\n return\n IOptionsPurchaseQueue(optionsPurchaseQueue).sellToBuyers(\n settlementPrice\n );\n }\n\n /**\n * @notice Gets the settlement price of a settled auction\n * @param gnosisEasyAuction The address of the Gnosis Easy Auction contract\n * @return settlementPrice Auction settlement price\n */\n function getAuctionSettlementPrice(\n address gnosisEasyAuction,\n uint256 optionAuctionID\n ) public view returns (uint256) {\n bytes32 clearingPriceOrder =\n IGnosisAuction(gnosisEasyAuction)\n .auctionData(optionAuctionID)\n .clearingPriceOrder;\n\n if (clearingPriceOrder == bytes32(0)) {\n // Current auction hasn't settled yet\n return 0;\n } else {\n // We decode the clearingPriceOrder to find the auction settlement price\n // settlementPrice = clearingPriceOrder.sellAmount / clearingPriceOrder.buyAmount\n return\n (10**Vault.OTOKEN_DECIMALS)\n .mul(\n uint96(uint256(clearingPriceOrder)) // sellAmount\n )\n .div(\n uint96(uint256(clearingPriceOrder) >> 96) // buyAmount\n );\n }\n }\n\n /**\n * @notice Verify the constructor params satisfy requirements\n * @param owner is the owner of the vault with critical permissions\n * @param feeRecipient is the address to recieve vault performance and management fees\n * @param performanceFee is the perfomance fee pct.\n * @param tokenName is the name of the token\n * @param tokenSymbol is the symbol of the token\n * @param _vaultParams is the struct with vault general data\n */\n function verifyInitializerParams(\n address owner,\n address keeper,\n address feeRecipient,\n uint256 performanceFee,\n uint256 managementFee,\n string calldata tokenName,\n string calldata tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) external pure {\n require(owner != address(0), \"!owner\");\n require(keeper != address(0), \"!keeper\");\n require(feeRecipient != address(0), \"!feeRecipient\");\n require(\n performanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"performanceFee >= 100%\"\n );\n require(\n managementFee < 100 * Vault.FEE_MULTIPLIER,\n \"managementFee >= 100%\"\n );\n require(bytes(tokenName).length > 0, \"!tokenName\");\n require(bytes(tokenSymbol).length > 0, \"!tokenSymbol\");\n\n require(_vaultParams.asset != address(0), \"!asset\");\n require(_vaultParams.underlying != address(0), \"!underlying\");\n require(_vaultParams.minimumSupply > 0, \"!minimumSupply\");\n require(_vaultParams.cap > 0, \"!cap\");\n require(\n _vaultParams.cap > _vaultParams.minimumSupply,\n \"cap has to be higher than minimumSupply\"\n );\n }\n\n /**\n * @notice Gets the next option expiry timestamp\n * @param currentOption is the otoken address that the vault is currently writing\n */\n function getNextExpiry(address currentOption)\n internal\n view\n returns (uint256)\n {\n // uninitialized state\n if (currentOption == address(0)) {\n return getNextFriday(block.timestamp);\n }\n uint256 currentExpiry = IOtoken(currentOption).expiryTimestamp();\n\n // After options expiry if no options are written for >1 week\n // We need to give the ability continue writing options\n if (block.timestamp > currentExpiry + 7 days) {\n return getNextFriday(block.timestamp);\n }\n return getNextFriday(currentExpiry);\n }\n\n /**\n * @notice Gets the next options expiry timestamp\n * @param timestamp is the expiry timestamp of the current option\n * Reference: https://codereview.stackexchange.com/a/33532\n * Examples:\n * getNextFriday(week 1 thursday) -> week 1 friday\n * getNextFriday(week 1 friday) -> week 2 friday\n * getNextFriday(week 1 saturday) -> week 2 friday\n */\n function getNextFriday(uint256 timestamp) internal pure returns (uint256) {\n // dayOfWeek = 0 (sunday) - 6 (saturday)\n uint256 dayOfWeek = ((timestamp / 1 days) + 4) % 7;\n uint256 nextFriday = timestamp + ((7 + 5 - dayOfWeek) % 7) * 1 days;\n uint256 friday8am = nextFriday - (nextFriday % (24 hours)) + (8 hours);\n\n // If the passed timestamp is day=Friday hour>8am, we simply increment it by a week to next Friday\n if (timestamp >= friday8am) {\n friday8am += 7 days;\n }\n return friday8am;\n }\n}\n" + }, + "contracts/libraries/Vault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nlibrary Vault {\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n // Fees are 6-decimal places. For example: 20 * 10**6 = 20%\n uint256 internal constant FEE_MULTIPLIER = 10**6;\n\n // Premium discount has 1-decimal place. For example: 80 * 10**1 = 80%. Which represents a 20% discount.\n uint256 internal constant PREMIUM_DISCOUNT_MULTIPLIER = 10;\n\n // Otokens have 8 decimal places.\n uint256 internal constant OTOKEN_DECIMALS = 8;\n\n // Percentage of funds allocated to options is 2 decimal places. 10 * 10**2 = 10%\n uint256 internal constant OPTION_ALLOCATION_MULTIPLIER = 10**2;\n\n // Placeholder uint value to prevent cold writes\n uint256 internal constant PLACEHOLDER_UINT = 1;\n\n struct VaultParams {\n // Option type the vault is selling\n bool isPut;\n // Token decimals for vault shares\n uint8 decimals;\n // Asset used in Theta / Delta Vault\n address asset;\n // Underlying asset of the options sold by vault\n address underlying;\n // Minimum supply of the vault shares issued, for ETH it's 10**10\n uint56 minimumSupply;\n // Vault cap\n uint104 cap;\n }\n\n struct OptionState {\n // Option that the vault is shorting / longing in the next cycle\n address nextOption;\n // Option that the vault is currently shorting / longing\n address currentOption;\n // The timestamp when the `nextOption` can be used by the vault\n uint32 nextOptionReadyAt;\n }\n\n struct VaultState {\n // 32 byte slot 1\n // Current round number. `round` represents the number of `period`s elapsed.\n uint16 round;\n // Amount that is currently locked for selling options\n uint104 lockedAmount;\n // Amount that was locked for selling options in the previous round\n // used for calculating performance fee deduction\n uint104 lastLockedAmount;\n // 32 byte slot 2\n // Stores the total tally of how much of `asset` there is\n // to be used to mint rTHETA tokens\n uint128 totalPending;\n // Total amount of queued withdrawal shares from previous rounds (doesn't include the current round)\n uint128 queuedWithdrawShares;\n }\n\n struct DepositReceipt {\n // Maximum of 65535 rounds. Assuming 1 round is 7 days, maximum is 1256 years.\n uint16 round;\n // Deposit amount, max 20,282,409,603,651 or 20 trillion ETH deposit\n uint104 amount;\n // Unredeemed shares balance\n uint128 unredeemedShares;\n }\n\n struct Withdrawal {\n // Maximum of 65535 rounds. Assuming 1 round is 7 days, maximum is 1256 years.\n uint16 round;\n // Number of shares withdrawn\n uint128 shares;\n }\n\n struct AuctionSellOrder {\n // Amount of `asset` token offered in auction\n uint96 sellAmount;\n // Amount of oToken requested in auction\n uint96 buyAmount;\n // User Id of delta vault in latest gnosis auction\n uint64 userId;\n }\n}\n" + }, + "contracts/libraries/ShareMath.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {Vault} from \"./Vault.sol\";\n\nlibrary ShareMath {\n using SafeMath for uint256;\n\n uint256 internal constant PLACEHOLDER_UINT = 1;\n\n function assetToShares(\n uint256 assetAmount,\n uint256 assetPerShare,\n uint256 decimals\n ) internal pure returns (uint256) {\n // If this throws, it means that vault's roundPricePerShare[currentRound] has not been set yet\n // which should never happen.\n // Has to be larger than 1 because `1` is used in `initRoundPricePerShares` to prevent cold writes.\n require(assetPerShare > PLACEHOLDER_UINT, \"Invalid assetPerShare\");\n\n return assetAmount.mul(10**decimals).div(assetPerShare);\n }\n\n function sharesToAsset(\n uint256 shares,\n uint256 assetPerShare,\n uint256 decimals\n ) internal pure returns (uint256) {\n // If this throws, it means that vault's roundPricePerShare[currentRound] has not been set yet\n // which should never happen.\n // Has to be larger than 1 because `1` is used in `initRoundPricePerShares` to prevent cold writes.\n require(assetPerShare > PLACEHOLDER_UINT, \"Invalid assetPerShare\");\n\n return shares.mul(assetPerShare).div(10**decimals);\n }\n\n /**\n * @notice Returns the shares unredeemed by the user given their DepositReceipt\n * @param depositReceipt is the user's deposit receipt\n * @param currentRound is the `round` stored on the vault\n * @param assetPerShare is the price in asset per share\n * @param decimals is the number of decimals the asset/shares use\n * @return unredeemedShares is the user's virtual balance of shares that are owed\n */\n function getSharesFromReceipt(\n Vault.DepositReceipt memory depositReceipt,\n uint256 currentRound,\n uint256 assetPerShare,\n uint256 decimals\n ) internal pure returns (uint256 unredeemedShares) {\n if (depositReceipt.round > 0 && depositReceipt.round < currentRound) {\n uint256 sharesFromRound =\n assetToShares(depositReceipt.amount, assetPerShare, decimals);\n\n return\n uint256(depositReceipt.unredeemedShares).add(sharesFromRound);\n }\n return depositReceipt.unredeemedShares;\n }\n\n function pricePerShare(\n uint256 totalSupply,\n uint256 totalBalance,\n uint256 pendingAmount,\n uint256 decimals\n ) internal pure returns (uint256) {\n uint256 singleShare = 10**decimals;\n return\n totalSupply > 0\n ? singleShare.mul(totalBalance.sub(pendingAmount)).div(\n totalSupply\n )\n : singleShare;\n }\n\n /************************************************\n * HELPERS\n ***********************************************/\n\n function assertUint104(uint256 num) internal pure {\n require(num <= type(uint104).max, \"Overflow uint104\");\n }\n\n function assertUint128(uint256 num) internal pure {\n require(num <= type(uint128).max, \"Overflow uint128\");\n }\n}\n" + }, + "contracts/interfaces/IYearn.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface IYearnVault {\n function pricePerShare() external view returns (uint256);\n\n function deposit(uint256 _amount, address _recipient)\n external\n returns (uint256);\n\n function withdraw(\n uint256 _maxShares,\n address _recipient,\n uint256 _maxLoss\n ) external returns (uint256);\n\n function approve(address _recipient, uint256 _amount)\n external\n returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n}\n\ninterface IYearnRegistry {\n function latestVault(address token) external returns (address);\n}\n\ninterface IYearnPricer {\n function setExpiryPriceInOracle(uint256 _expiryTimestamp) external;\n\n function getPrice() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IWETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface IWETH {\n function deposit() external payable;\n\n function withdraw(uint256) external;\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function decimals() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IRibbon.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport {Vault} from \"../libraries/Vault.sol\";\n\ninterface IRibbonVault {\n function deposit(uint256 amount) external;\n\n function depositETH() external payable;\n\n function cap() external view returns (uint256);\n\n function depositFor(uint256 amount, address creditor) external;\n\n function vaultParams() external view returns (Vault.VaultParams memory);\n}\n\ninterface IStrikeSelection {\n function getStrikePrice(uint256 expiryTimestamp, bool isPut)\n external\n view\n returns (uint256, uint256);\n\n function delta() external view returns (uint256);\n}\n\ninterface IOptionsPremiumPricer {\n function getPremium(\n uint256 strikePrice,\n uint256 timeToExpiry,\n bool isPut\n ) external view returns (uint256);\n\n function getPremiumInStables(\n uint256 strikePrice,\n uint256 timeToExpiry,\n bool isPut\n ) external view returns (uint256);\n\n function getOptionDelta(\n uint256 spotPrice,\n uint256 strikePrice,\n uint256 volatility,\n uint256 expiryTimestamp\n ) external view returns (uint256 delta);\n\n function getUnderlyingPrice() external view returns (uint256);\n\n function priceOracle() external view returns (address);\n\n function volatilityOracle() external view returns (address);\n\n function optionId() external view returns (bytes32);\n}\n" + }, + "contracts/interfaces/IERC20Detailed.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IERC20Detailed is IERC20 {\n function decimals() external view returns (uint8);\n\n function symbol() external view returns (string calldata);\n\n function name() external view returns (string calldata);\n}\n" + }, + "contracts/libraries/SupportsNonCompliantERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\n/**\n * This library supports ERC20s that have quirks in their behavior.\n * One such ERC20 is USDT, which requires allowance to be 0 before calling approve.\n * We plan to update this library with ERC20s that display such idiosyncratic behavior.\n */\nlibrary SupportsNonCompliantERC20 {\n address private constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;\n\n function safeApproveNonCompliant(\n IERC20 token,\n address spender,\n uint256 amount\n ) internal {\n if (address(token) == USDT) {\n SafeERC20.safeApprove(token, spender, 0);\n }\n SafeERC20.safeApprove(token, spender, amount);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "contracts/libraries/GnosisAuction.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {DSMath} from \"../vendor/DSMath.sol\";\nimport {IGnosisAuction} from \"../interfaces/IGnosisAuction.sol\";\nimport {IOtoken} from \"../interfaces/GammaInterface.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {IRibbonThetaVault} from \"../interfaces/IRibbonThetaVault.sol\";\n\nlibrary GnosisAuction {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n event InitiateGnosisAuction(\n address indexed auctioningToken,\n address indexed biddingToken,\n uint256 auctionCounter,\n address indexed manager\n );\n\n event PlaceAuctionBid(\n uint256 auctionId,\n address indexed auctioningToken,\n uint256 sellAmount,\n uint256 buyAmount,\n address indexed bidder\n );\n\n struct AuctionDetails {\n address oTokenAddress;\n address gnosisEasyAuction;\n address asset;\n uint256 assetDecimals;\n uint256 oTokenPremium;\n uint256 duration;\n }\n\n struct BidDetails {\n address oTokenAddress;\n address gnosisEasyAuction;\n address asset;\n uint256 assetDecimals;\n uint256 auctionId;\n uint256 lockedBalance;\n uint256 optionAllocation;\n uint256 optionPremium;\n address bidder;\n }\n\n function startAuction(AuctionDetails calldata auctionDetails)\n internal\n returns (uint256 auctionID)\n {\n uint256 oTokenSellAmount =\n getOTokenSellAmount(auctionDetails.oTokenAddress);\n require(oTokenSellAmount > 0, \"No otokens to sell\");\n\n IERC20(auctionDetails.oTokenAddress).safeApprove(\n auctionDetails.gnosisEasyAuction,\n IERC20(auctionDetails.oTokenAddress).balanceOf(address(this))\n );\n\n // minBidAmount is total oTokens to sell * premium per oToken\n // shift decimals to correspond to decimals of USDC for puts\n // and underlying for calls\n uint256 minBidAmount =\n DSMath.wmul(\n oTokenSellAmount.mul(10**10),\n auctionDetails.oTokenPremium\n );\n\n minBidAmount = auctionDetails.assetDecimals > 18\n ? minBidAmount.mul(10**(auctionDetails.assetDecimals.sub(18)))\n : minBidAmount.div(\n 10**(uint256(18).sub(auctionDetails.assetDecimals))\n );\n\n require(\n minBidAmount <= type(uint96).max,\n \"optionPremium * oTokenSellAmount > type(uint96) max value!\"\n );\n\n uint256 auctionEnd = block.timestamp.add(auctionDetails.duration);\n\n auctionID = IGnosisAuction(auctionDetails.gnosisEasyAuction)\n .initiateAuction(\n // address of oToken we minted and are selling\n auctionDetails.oTokenAddress,\n // address of asset we want in exchange for oTokens. Should match vault `asset`\n auctionDetails.asset,\n // orders can be cancelled at any time during the auction\n auctionEnd,\n // order will last for `duration`\n auctionEnd,\n // we are selling all of the otokens minus a fee taken by gnosis\n uint96(oTokenSellAmount),\n // the minimum we are willing to sell all the oTokens for. A discount is applied on black-scholes price\n uint96(minBidAmount),\n // the minimum bidding amount must be 1 * 10 ** -assetDecimals\n 1,\n // the min funding threshold\n 0,\n // no atomic closure\n false,\n // access manager contract\n address(0),\n // bytes for storing info like a whitelist for who can bid\n bytes(\"\")\n );\n\n emit InitiateGnosisAuction(\n auctionDetails.oTokenAddress,\n auctionDetails.asset,\n auctionID,\n msg.sender\n );\n }\n\n function placeBid(BidDetails calldata bidDetails)\n internal\n returns (\n uint256 sellAmount,\n uint256 buyAmount,\n uint64 userId\n )\n {\n // calculate how much to allocate\n sellAmount = bidDetails\n .lockedBalance\n .mul(bidDetails.optionAllocation)\n .div(100 * Vault.OPTION_ALLOCATION_MULTIPLIER);\n\n // divide the `asset` sellAmount by the target premium per oToken to\n // get the number of oTokens to buy (8 decimals)\n buyAmount = sellAmount\n .mul(10**(bidDetails.assetDecimals.add(Vault.OTOKEN_DECIMALS)))\n .div(bidDetails.optionPremium)\n .div(10**bidDetails.assetDecimals);\n\n require(\n sellAmount <= type(uint96).max,\n \"sellAmount > type(uint96) max value!\"\n );\n require(\n buyAmount <= type(uint96).max,\n \"buyAmount > type(uint96) max value!\"\n );\n\n // approve that amount\n IERC20(bidDetails.asset).safeApprove(\n bidDetails.gnosisEasyAuction,\n sellAmount\n );\n\n uint96[] memory _minBuyAmounts = new uint96[](1);\n uint96[] memory _sellAmounts = new uint96[](1);\n bytes32[] memory _prevSellOrders = new bytes32[](1);\n _minBuyAmounts[0] = uint96(buyAmount);\n _sellAmounts[0] = uint96(sellAmount);\n _prevSellOrders[\n 0\n ] = 0x0000000000000000000000000000000000000000000000000000000000000001;\n\n // place sell order with that amount\n userId = IGnosisAuction(bidDetails.gnosisEasyAuction).placeSellOrders(\n bidDetails.auctionId,\n _minBuyAmounts,\n _sellAmounts,\n _prevSellOrders,\n \"0x\"\n );\n\n emit PlaceAuctionBid(\n bidDetails.auctionId,\n bidDetails.oTokenAddress,\n sellAmount,\n buyAmount,\n bidDetails.bidder\n );\n\n return (sellAmount, buyAmount, userId);\n }\n\n function claimAuctionOtokens(\n Vault.AuctionSellOrder calldata auctionSellOrder,\n address gnosisEasyAuction,\n address counterpartyThetaVault\n ) internal {\n bytes32 order =\n encodeOrder(\n auctionSellOrder.userId,\n auctionSellOrder.buyAmount,\n auctionSellOrder.sellAmount\n );\n bytes32[] memory orders = new bytes32[](1);\n orders[0] = order;\n IGnosisAuction(gnosisEasyAuction).claimFromParticipantOrder(\n IRibbonThetaVault(counterpartyThetaVault).optionAuctionID(),\n orders\n );\n }\n\n function getOTokenSellAmount(address oTokenAddress)\n internal\n view\n returns (uint256)\n {\n // We take our current oToken balance. That will be our sell amount\n // but otokens will be transferred to gnosis.\n uint256 oTokenSellAmount =\n IERC20(oTokenAddress).balanceOf(address(this));\n\n require(\n oTokenSellAmount <= type(uint96).max,\n \"oTokenSellAmount > type(uint96) max value!\"\n );\n\n return oTokenSellAmount;\n }\n\n function getOTokenPremiumInStables(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount\n ) internal view returns (uint256) {\n IOtoken newOToken = IOtoken(oTokenAddress);\n IOptionsPremiumPricer premiumPricer =\n IOptionsPremiumPricer(optionsPremiumPricer);\n\n // Apply black-scholes formula (from rvol library) to option given its features\n // and get price for 100 contracts denominated USDC for both call and put options\n uint256 optionPremium =\n premiumPricer.getPremiumInStables(\n newOToken.strikePrice(),\n newOToken.expiryTimestamp(),\n newOToken.isPut()\n );\n\n // Apply a discount to incentivize arbitraguers\n optionPremium = optionPremium.mul(premiumDiscount).div(\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER\n );\n\n require(\n optionPremium <= type(uint96).max,\n \"optionPremium > type(uint96) max value!\"\n );\n\n return optionPremium;\n }\n\n function encodeOrder(\n uint64 userId,\n uint96 buyAmount,\n uint96 sellAmount\n ) internal pure returns (bytes32) {\n return\n bytes32(\n (uint256(userId) << 192) +\n (uint256(buyAmount) << 96) +\n uint256(sellAmount)\n );\n }\n}\n" + }, + "contracts/interfaces/IGnosisAuction.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nlibrary AuctionType {\n struct AuctionData {\n IERC20 auctioningToken;\n IERC20 biddingToken;\n uint256 orderCancellationEndDate;\n uint256 auctionEndDate;\n bytes32 initialAuctionOrder;\n uint256 minimumBiddingAmountPerOrder;\n uint256 interimSumBidAmount;\n bytes32 interimOrder;\n bytes32 clearingPriceOrder;\n uint96 volumeClearingPriceOrder;\n bool minFundingThresholdNotReached;\n bool isAtomicClosureAllowed;\n uint256 feeNumerator;\n uint256 minFundingThreshold;\n }\n}\n\ninterface IGnosisAuction {\n function initiateAuction(\n address _auctioningToken,\n address _biddingToken,\n uint256 orderCancellationEndDate,\n uint256 auctionEndDate,\n uint96 _auctionedSellAmount,\n uint96 _minBuyAmount,\n uint256 minimumBiddingAmountPerOrder,\n uint256 minFundingThreshold,\n bool isAtomicClosureAllowed,\n address accessManagerContract,\n bytes memory accessManagerContractData\n ) external returns (uint256);\n\n function auctionCounter() external view returns (uint256);\n\n function auctionData(uint256 auctionId)\n external\n view\n returns (AuctionType.AuctionData memory);\n\n function auctionAccessManager(uint256 auctionId)\n external\n view\n returns (address);\n\n function auctionAccessData(uint256 auctionId)\n external\n view\n returns (bytes memory);\n\n function FEE_DENOMINATOR() external view returns (uint256);\n\n function feeNumerator() external view returns (uint256);\n\n function settleAuction(uint256 auctionId) external returns (bytes32);\n\n function placeSellOrders(\n uint256 auctionId,\n uint96[] memory _minBuyAmounts,\n uint96[] memory _sellAmounts,\n bytes32[] memory _prevSellOrders,\n bytes calldata allowListCallData\n ) external returns (uint64);\n\n function claimFromParticipantOrder(\n uint256 auctionId,\n bytes32[] memory orders\n ) external returns (uint256, uint256);\n}\n" + }, + "contracts/interfaces/IOptionsPurchaseQueue.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\ninterface IOptionsPurchaseQueue {\n /**\n * @dev Contains purchase request info\n * @param optionsAmount Amount of options to purchase\n * @param premiums Total premiums the buyer is spending to purchase the options (optionsAmount * ceilingPrice)\n * We need to track the premiums here since the ceilingPrice could change between the time the purchase was\n * requested and when the options are sold\n * @param buyer The buyer requesting this purchase\n */\n struct Purchase {\n uint128 optionsAmount; // Slot 0\n uint128 premiums;\n address buyer; // Slot 1\n }\n\n function purchases(address, uint256)\n external\n view\n returns (\n uint128,\n uint128,\n address\n );\n\n function totalOptionsAmount(address) external view returns (uint256);\n\n function vaultAllocatedOptions(address) external view returns (uint256);\n\n function whitelistedBuyer(address) external view returns (bool);\n\n function minPurchaseAmount(address) external view returns (uint256);\n\n function ceilingPrice(address) external view returns (uint256);\n\n function getPurchases(address vault)\n external\n view\n returns (Purchase[] memory);\n\n function getPremiums(address vault, uint256 optionsAmount)\n external\n view\n returns (uint256);\n\n function getOptionsAllocation(address vault, uint256 allocatedOptions)\n external\n view\n returns (uint256);\n\n function requestPurchase(address vault, uint256 optionsAmount)\n external\n returns (uint256);\n\n function allocateOptions(uint256 allocatedOptions)\n external\n returns (uint256);\n\n function sellToBuyers(uint256 settlementPrice) external returns (uint256);\n\n function cancelAllPurchases(address vault) external;\n\n function addWhitelist(address buyer) external;\n\n function removeWhitelist(address buyer) external;\n\n function setCeilingPrice(address vault, uint256 price) external;\n\n function setMinPurchaseAmount(address vault, uint256 amount) external;\n}\n" + }, + "contracts/interfaces/IRibbonThetaVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {Vault} from \"../libraries/Vault.sol\";\n\ninterface IRibbonThetaVault {\n function currentOption() external view returns (address);\n\n function nextOption() external view returns (address);\n\n function vaultParams() external view returns (Vault.VaultParams memory);\n\n function vaultState() external view returns (Vault.VaultState memory);\n\n function optionState() external view returns (Vault.OptionState memory);\n\n function optionAuctionID() external view returns (uint256);\n\n function pricePerShare() external view returns (uint256);\n\n function roundPricePerShare(uint256) external view returns (uint256);\n\n function depositFor(uint256 amount, address creditor) external;\n\n function initiateWithdraw(uint256 numShares) external;\n\n function completeWithdraw() external;\n\n function maxRedeem() external;\n\n function depositYieldTokenFor(uint256 amount, address creditor) external;\n\n function symbol() external view returns (string calldata);\n}\n" + }, + "contracts/libraries/VaultLifecycleWithSwap.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {ShareMath} from \"./ShareMath.sol\";\nimport {IStrikeSelection} from \"../interfaces/IRibbon.sol\";\nimport {GnosisAuction} from \"./GnosisAuction.sol\";\nimport {\n IOtokenFactory,\n IOtoken,\n IController,\n GammaTypes\n} from \"../interfaces/GammaInterface.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\nimport {ISwap} from \"../interfaces/ISwap.sol\";\nimport {IOptionsPurchaseQueue} from \"../interfaces/IOptionsPurchaseQueue.sol\";\nimport {SupportsNonCompliantERC20} from \"./SupportsNonCompliantERC20.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\nimport \"hardhat/console.sol\";\n\nlibrary VaultLifecycleWithSwap {\n using SafeMath for uint256;\n using SupportsNonCompliantERC20 for IERC20;\n using SafeERC20 for IERC20;\n\n struct CommitParams {\n address OTOKEN_FACTORY;\n address USDC;\n address collateralAsset;\n address currentOption;\n uint256 delay;\n uint16 lastStrikeOverrideRound;\n uint256 overriddenStrikePrice;\n address strikeSelection;\n address optionsPremiumPricer;\n }\n\n /**\n * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction\n * @param commitParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @return otokenAddress is the address of the new option\n * @return strikePrice is the strike price of the new option\n * @return delta is the delta of the new option\n */\n function commitNextOption(\n CommitParams calldata commitParams,\n Vault.VaultParams storage vaultParams,\n Vault.VaultState storage vaultState\n )\n external\n returns (\n address otokenAddress,\n uint256 strikePrice,\n uint256 delta\n )\n {\n uint256 expiry = getNextExpiry(commitParams.currentOption);\n\n IStrikeSelection selection =\n IStrikeSelection(commitParams.strikeSelection);\n\n bool isPut = vaultParams.isPut;\n address underlying = vaultParams.underlying;\n\n (strikePrice, delta) = commitParams.lastStrikeOverrideRound ==\n vaultState.round\n ? (commitParams.overriddenStrikePrice, selection.delta())\n : selection.getStrikePrice(expiry, isPut);\n\n require(strikePrice != 0, \"!strikePrice\");\n\n // retrieve address if option already exists, or deploy it\n otokenAddress = getOrDeployOtoken(\n commitParams,\n vaultParams,\n underlying,\n strikePrice,\n expiry,\n isPut\n );\n\n return (otokenAddress, strikePrice, delta);\n }\n\n /**\n * @notice Verify the otoken has the correct parameters to prevent vulnerability to opyn contract changes\n * @param otokenAddress is the address of the otoken\n * @param vaultParams is the struct with vault general data\n * @param collateralAsset is the address of the collateral asset\n * @param USDC is the address of usdc\n * @param delay is the delay between commitAndClose and rollToNextOption\n */\n function verifyOtoken(\n address otokenAddress,\n Vault.VaultParams storage vaultParams,\n address collateralAsset,\n address USDC,\n uint256 delay\n ) private view {\n require(otokenAddress != address(0), \"!otokenAddress\");\n\n IOtoken otoken = IOtoken(otokenAddress);\n require(otoken.isPut() == vaultParams.isPut, \"Type mismatch\");\n require(\n otoken.underlyingAsset() == vaultParams.underlying,\n \"Wrong underlyingAsset\"\n );\n require(\n otoken.collateralAsset() == collateralAsset,\n \"Wrong collateralAsset\"\n );\n\n // we just assume all options use USDC as the strike\n require(otoken.strikeAsset() == USDC, \"strikeAsset != USDC\");\n\n uint256 readyAt = block.timestamp.add(delay);\n require(otoken.expiryTimestamp() >= readyAt, \"Expiry before delay\");\n }\n\n /**\n * @param decimals is the decimals of the asset\n * @param totalBalance is the vault's total asset balance\n * @param currentShareSupply is the supply of the shares invoked with totalSupply()\n * @param lastQueuedWithdrawAmount is the amount queued for withdrawals from last round\n * @param performanceFee is the perf fee percent to charge on premiums\n * @param managementFee is the management fee percent to charge on the AUM\n */\n struct CloseParams {\n uint256 decimals;\n uint256 totalBalance;\n uint256 currentShareSupply;\n uint256 lastQueuedWithdrawAmount;\n uint256 performanceFee;\n uint256 managementFee;\n uint256 currentQueuedWithdrawShares;\n }\n\n /**\n * @notice Calculate the shares to mint, new price per share, and\n amount of funds to re-allocate as collateral for the new round\n * @param vaultState is the storage variable vaultState passed from RibbonVault\n * @param params is the rollover parameters passed to compute the next state\n * @return newLockedAmount is the amount of funds to allocate for the new round\n * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal\n * @return newPricePerShare is the price per share of the new round\n * @return mintShares is the amount of shares to mint from deposits\n * @return performanceFeeInAsset is the performance fee charged by vault\n * @return totalVaultFee is the total amount of fee charged by vault\n */\n function closeRound(\n Vault.VaultState storage vaultState,\n CloseParams calldata params\n )\n external\n view\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares,\n uint256 performanceFeeInAsset,\n uint256 totalVaultFee\n )\n {\n uint256 currentBalance = params.totalBalance;\n uint256 pendingAmount = vaultState.totalPending;\n // Total amount of queued withdrawal shares from previous rounds (doesn't include the current round)\n uint256 lastQueuedWithdrawShares = vaultState.queuedWithdrawShares;\n\n // Deduct older queued withdraws so we don't charge fees on them\n uint256 balanceForVaultFees =\n currentBalance.sub(params.lastQueuedWithdrawAmount);\n\n {\n (performanceFeeInAsset, , totalVaultFee) = getVaultFees(\n balanceForVaultFees,\n vaultState.lastLockedAmount,\n vaultState.totalPending,\n params.performanceFee,\n params.managementFee\n );\n }\n\n // Take into account the fee\n // so we can calculate the newPricePerShare\n currentBalance = currentBalance.sub(totalVaultFee);\n\n {\n newPricePerShare = ShareMath.pricePerShare(\n params.currentShareSupply.sub(lastQueuedWithdrawShares),\n currentBalance.sub(params.lastQueuedWithdrawAmount),\n pendingAmount,\n params.decimals\n );\n\n queuedWithdrawAmount = params.lastQueuedWithdrawAmount.add(\n ShareMath.sharesToAsset(\n params.currentQueuedWithdrawShares,\n newPricePerShare,\n params.decimals\n )\n );\n\n // After closing the short, if the options expire in-the-money\n // vault pricePerShare would go down because vault's asset balance decreased.\n // This ensures that the newly-minted shares do not take on the loss.\n mintShares = ShareMath.assetToShares(\n pendingAmount,\n newPricePerShare,\n params.decimals\n );\n }\n\n return (\n currentBalance.sub(queuedWithdrawAmount), // new locked balance subtracts the queued withdrawals\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n );\n }\n\n /**\n * @notice Creates the actual Opyn short position by depositing collateral and minting otokens\n * @param gammaController is the address of the opyn controller contract\n * @param marginPool is the address of the opyn margin contract which holds the collateral\n * @param oTokenAddress is the address of the otoken to mint\n * @param depositAmount is the amount of collateral to deposit\n * @return the otoken mint amount\n */\n function createShort(\n address gammaController,\n address marginPool,\n address oTokenAddress,\n uint256 depositAmount\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n uint256 newVaultID =\n (controller.getAccountVaultCounter(address(this))).add(1);\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IOtoken oToken = IOtoken(oTokenAddress);\n address collateralAsset = oToken.collateralAsset();\n\n uint256 collateralDecimals =\n uint256(IERC20Detailed(collateralAsset).decimals());\n uint256 mintAmount;\n\n if (oToken.isPut()) {\n // For minting puts, there will be instances where the full depositAmount will not be used for minting.\n // This is because of an issue with precision.\n //\n // For ETH put options, we are calculating the mintAmount (10**8 decimals) using\n // the depositAmount (10**18 decimals), which will result in truncation of decimals when scaling down.\n // As a result, there will be tiny amounts of dust left behind in the Opyn vault when minting put otokens.\n //\n // For simplicity's sake, we do not refund the dust back to the address(this) on minting otokens.\n // We retain the dust in the vault so the calling contract can withdraw the\n // actual locked amount + dust at settlement.\n //\n // To test this behavior, we can console.log\n // MarginCalculatorInterface(0x7A48d10f372b3D7c60f6c9770B91398e4ccfd3C7).getExcessCollateral(vault)\n // to see how much dust (or excess collateral) is left behind.\n mintAmount = depositAmount\n .mul(10**Vault.OTOKEN_DECIMALS)\n .mul(10**18) // we use 10**18 to give extra precision\n .div(oToken.strikePrice().mul(10**(10 + collateralDecimals)));\n } else {\n mintAmount = depositAmount;\n\n if (collateralDecimals > 8) {\n uint256 scaleBy = 10**(collateralDecimals.sub(8)); // oTokens have 8 decimals\n if (mintAmount > scaleBy) {\n mintAmount = depositAmount.div(scaleBy); // scale down from 10**18 to 10**8\n }\n }\n }\n\n // double approve to fix non-compliant ERC20s\n IERC20 collateralToken = IERC20(collateralAsset);\n collateralToken.safeApproveNonCompliant(marginPool, depositAmount);\n\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](3);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.OpenVault,\n address(this), // owner\n address(this), // receiver\n address(0), // asset, otoken\n newVaultID, // vaultId\n 0, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.DepositCollateral,\n address(this), // owner\n address(this), // address to transfer from\n collateralAsset, // deposited asset\n newVaultID, // vaultId\n depositAmount, // amount\n 0, //index\n \"\" //data\n );\n\n actions[2] = IController.ActionArgs(\n IController.ActionType.MintShortOption,\n address(this), // owner\n address(this), // address to transfer to\n oTokenAddress, // option address\n newVaultID, // vaultId\n mintAmount, // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n return mintAmount;\n }\n\n /**\n * @notice Close the existing short otoken position. Currently this implementation is simple.\n * It closes the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time. Since calling `_closeShort` deletes vaults by\n calling SettleVault action, this assumption should hold.\n * @param gammaController is the address of the opyn controller contract\n * @return amount of collateral redeemed from the vault\n */\n function settleShort(address gammaController) external returns (uint256) {\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n // The short position has been previously closed, or all the otokens have been burned.\n // So we return early.\n if (address(collateralToken) == address(0)) {\n return 0;\n }\n\n // This is equivalent to doing IERC20(vault.asset).balanceOf(address(this))\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // If it is after expiry, we need to settle the short position using the normal way\n // Delete the vault and withdraw all remaining collateral from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.SettleVault,\n address(this), // owner\n address(this), // address to transfer to\n address(0), // not used\n vaultID, // vaultId\n 0, // not used\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Exercises the ITM option using existing long otoken position. Currently this implementation is simple.\n * It calls the `Redeem` action to claim the payout.\n * @param gammaController is the address of the opyn controller contract\n * @param oldOption is the address of the old option\n * @param asset is the address of the vault's asset\n * @return amount of asset received by exercising the option\n */\n function settleLong(\n address gammaController,\n address oldOption,\n address asset\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n\n uint256 oldOptionBalance = IERC20(oldOption).balanceOf(address(this));\n\n if (controller.getPayout(oldOption, oldOptionBalance) == 0) {\n return 0;\n }\n\n uint256 startAssetBalance = IERC20(asset).balanceOf(address(this));\n\n // If it is after expiry, we need to redeem the profits\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.Redeem,\n address(0), // not used\n address(this), // address to send profits to\n oldOption, // address of otoken\n 0, // not used\n oldOptionBalance, // otoken balance\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endAssetBalance = IERC20(asset).balanceOf(address(this));\n\n return endAssetBalance.sub(startAssetBalance);\n }\n\n /**\n * @notice Burn the remaining oTokens left over from auction. Currently this implementation is simple.\n * It burns oTokens from the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time.\n * @param gammaController is the address of the opyn controller contract\n * @param currentOption is the address of the current option\n * @return amount of collateral redeemed by burning otokens\n */\n function burnOtokens(address gammaController, address currentOption)\n external\n returns (uint256)\n {\n uint256 numOTokensToBurn =\n IERC20(currentOption).balanceOf(address(this));\n\n require(numOTokensToBurn > 0, \"No oTokens to burn\");\n\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n console.log(\"collateralTokenBurn\", vault.collateralAssets[0]);\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // Burning `amount` of oTokens from the ribbon vault,\n // then withdrawing the corresponding collateral amount from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](2);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.BurnShortOption,\n address(this), // owner\n address(this), // address to transfer from\n address(vault.shortOtokens[0]), // otoken address\n vaultID, // vaultId\n numOTokensToBurn, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.WithdrawCollateral,\n address(this), // owner\n address(this), // address to transfer to\n address(collateralToken), // withdrawn asset\n vaultID, // vaultId\n vault.collateralAmounts[0].mul(numOTokensToBurn).div(\n vault.shortAmounts[0]\n ), // amount\n 0, //index\n \"\" //data\n );\n\n console.log(collateralToken.balanceOf(address(this)));\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n console.log(\"endCollateralBalance\", endCollateralBalance);\n console.log(\"startCollateralBalance\", startCollateralBalance);\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Calculates the performance and management fee for this week's round\n * @param currentBalance is the balance of funds held on the vault after closing short\n * @param lastLockedAmount is the amount of funds locked from the previous round\n * @param pendingAmount is the pending deposit amount\n * @param performanceFeePercent is the performance fee pct.\n * @param managementFeePercent is the management fee pct.\n * @return performanceFeeInAsset is the performance fee\n * @return managementFeeInAsset is the management fee\n * @return vaultFee is the total fees\n */\n function getVaultFees(\n uint256 currentBalance,\n uint256 lastLockedAmount,\n uint256 pendingAmount,\n uint256 performanceFeePercent,\n uint256 managementFeePercent\n )\n internal\n pure\n returns (\n uint256 performanceFeeInAsset,\n uint256 managementFeeInAsset,\n uint256 vaultFee\n )\n {\n // At the first round, currentBalance=0, pendingAmount>0\n // so we just do not charge anything on the first round\n uint256 lockedBalanceSansPending =\n currentBalance > pendingAmount\n ? currentBalance.sub(pendingAmount)\n : 0;\n\n uint256 _performanceFeeInAsset;\n uint256 _managementFeeInAsset;\n uint256 _vaultFee;\n\n // Take performance fee and management fee ONLY if difference between\n // last week and this week's vault deposits, taking into account pending\n // deposits and withdrawals, is positive. If it is negative, last week's\n // option expired ITM past breakeven, and the vault took a loss so we\n // do not collect performance fee for last week\n if (lockedBalanceSansPending > lastLockedAmount) {\n _performanceFeeInAsset = performanceFeePercent > 0\n ? lockedBalanceSansPending\n .sub(lastLockedAmount)\n .mul(performanceFeePercent)\n .div(100 * Vault.FEE_MULTIPLIER)\n : 0;\n _managementFeeInAsset = managementFeePercent > 0\n ? lockedBalanceSansPending.mul(managementFeePercent).div(\n 100 * Vault.FEE_MULTIPLIER\n )\n : 0;\n\n _vaultFee = _performanceFeeInAsset.add(_managementFeeInAsset);\n }\n\n return (_performanceFeeInAsset, _managementFeeInAsset, _vaultFee);\n }\n\n /**\n * @notice Either retrieves the option token if it already exists, or deploy it\n * @param commitParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param underlying is the address of the underlying asset of the option\n * @param strikePrice is the strike price of the option\n * @param expiry is the expiry timestamp of the option\n * @param isPut is whether the option is a put\n * @return the address of the option\n */\n function getOrDeployOtoken(\n CommitParams calldata commitParams,\n Vault.VaultParams storage vaultParams,\n address underlying,\n uint256 strikePrice,\n uint256 expiry,\n bool isPut\n ) internal returns (address) {\n IOtokenFactory factory = IOtokenFactory(commitParams.OTOKEN_FACTORY);\n\n address otokenFromFactory =\n factory.getOtoken(\n underlying,\n commitParams.USDC,\n commitParams.collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n if (otokenFromFactory != address(0)) {\n return otokenFromFactory;\n }\n\n address otoken =\n factory.createOtoken(\n underlying,\n commitParams.USDC,\n commitParams.collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n verifyOtoken(\n otoken,\n vaultParams,\n commitParams.collateralAsset,\n commitParams.USDC,\n commitParams.delay\n );\n\n return otoken;\n }\n\n /**\n * @notice Creates an offer in the Swap Contract\n * @param currentOtoken is the current otoken address\n * @param currOtokenPremium is premium for each otoken\n * @param swapContract the address of the swap contract\n * @param vaultParams is the struct with vault general data\n * @return optionAuctionID auction id of the newly created offer\n */\n function createOffer(\n address currentOtoken,\n uint256 currOtokenPremium,\n address swapContract,\n Vault.VaultParams storage vaultParams\n ) external returns (uint256 optionAuctionID) {\n require(\n currOtokenPremium <= type(uint96).max,\n \"currentOtokenPremium > type(uint96) max value!\"\n );\n require(currOtokenPremium > 0, \"!currentOtokenPremium\");\n\n uint256 oTokenBalance = IERC20(currentOtoken).balanceOf(address(this));\n require(\n oTokenBalance <= type(uint128).max,\n \"oTokenBalance > type(uint128) max value!\"\n );\n\n // Use safeIncrease instead of safeApproval because safeApproval is only used for initial\n // approval and cannot be called again. Using safeIncrease allow us to call _createOffer\n // even when we are approving the same oTokens we have used before. This might happen if\n // we accidentally burn the oTokens before settlement.\n uint256 allowance =\n IERC20(currentOtoken).allowance(address(this), swapContract);\n\n if (allowance < oTokenBalance) {\n IERC20(currentOtoken).safeIncreaseAllowance(\n swapContract,\n oTokenBalance.sub(allowance)\n );\n }\n\n uint256 decimals = vaultParams.decimals;\n\n // If total size is larger than 1, set minimum bid as 1\n // Otherwise, set minimum bid to one tenth the total size\n uint256 minBidSize =\n oTokenBalance > 10**decimals ? 10**decimals : oTokenBalance.div(10);\n\n require(\n minBidSize <= type(uint96).max,\n \"minBidSize > type(uint96) max value!\"\n );\n\n currOtokenPremium = decimals > 18\n ? currOtokenPremium.mul(10**(decimals.sub(18)))\n : currOtokenPremium.div(10**(uint256(18).sub(decimals)));\n\n optionAuctionID = ISwap(swapContract).createOffer(\n currentOtoken,\n vaultParams.asset,\n uint96(currOtokenPremium),\n uint96(minBidSize),\n uint128(oTokenBalance)\n );\n }\n\n /**\n * @notice Allocates the vault's minted options to the OptionsPurchaseQueue contract\n * @dev Skipped if the optionsPurchaseQueue doesn't exist\n * @param optionsPurchaseQueue is the OptionsPurchaseQueue contract\n * @param option is the minted option\n * @param optionsAmount is the amount of options minted\n * @param optionAllocation is the maximum % of options to allocate towards the purchase queue (will only allocate\n * up to the amount that is on the queue)\n * @return allocatedOptions is the amount of options that ended up getting allocated to the OptionsPurchaseQueue\n */\n function allocateOptions(\n address optionsPurchaseQueue,\n address option,\n uint256 optionsAmount,\n uint256 optionAllocation\n ) external returns (uint256 allocatedOptions) {\n // Skip if optionsPurchaseQueue is address(0)\n if (optionsPurchaseQueue != address(0)) {\n allocatedOptions = optionsAmount.mul(optionAllocation).div(\n 100 * Vault.OPTION_ALLOCATION_MULTIPLIER\n );\n allocatedOptions = IOptionsPurchaseQueue(optionsPurchaseQueue)\n .getOptionsAllocation(address(this), allocatedOptions);\n\n if (allocatedOptions != 0) {\n IERC20(option).approve(optionsPurchaseQueue, allocatedOptions);\n IOptionsPurchaseQueue(optionsPurchaseQueue).allocateOptions(\n allocatedOptions\n );\n }\n }\n\n return allocatedOptions;\n }\n\n /**\n * @notice Sell the allocated options to the purchase queue post auction settlement\n * @dev Reverts if the auction hasn't settled yet\n * @param optionsPurchaseQueue is the OptionsPurchaseQueue contract\n * @param swapContract The address of the swap settlement contract\n * @return totalPremiums Total premiums earnt by the vault\n */\n function sellOptionsToQueue(\n address optionsPurchaseQueue,\n address swapContract,\n uint256 optionAuctionID\n ) external returns (uint256) {\n uint256 settlementPrice =\n getAuctionSettlementPrice(swapContract, optionAuctionID);\n require(settlementPrice != 0, \"!settlementPrice\");\n\n return\n IOptionsPurchaseQueue(optionsPurchaseQueue).sellToBuyers(\n settlementPrice\n );\n }\n\n /**\n * @notice Gets the settlement price of a settled auction\n * @param swapContract The address of the swap settlement contract\n * @param optionAuctionID is the offer ID\n * @return settlementPrice Auction settlement price\n */\n function getAuctionSettlementPrice(\n address swapContract,\n uint256 optionAuctionID\n ) public view returns (uint256) {\n return ISwap(swapContract).averagePriceForOffer(optionAuctionID);\n }\n\n /**\n * @notice Verify the constructor params satisfy requirements\n * @param owner is the owner of the vault with critical permissions\n * @param feeRecipient is the address to recieve vault performance and management fees\n * @param performanceFee is the perfomance fee pct.\n * @param tokenName is the name of the token\n * @param tokenSymbol is the symbol of the token\n * @param _vaultParams is the struct with vault general data\n */\n function verifyInitializerParams(\n address owner,\n address keeper,\n address feeRecipient,\n uint256 performanceFee,\n uint256 managementFee,\n string calldata tokenName,\n string calldata tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) external pure {\n require(owner != address(0), \"!owner\");\n require(keeper != address(0), \"!keeper\");\n require(feeRecipient != address(0), \"!feeRecipient\");\n require(\n performanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"performanceFee >= 100%\"\n );\n require(\n managementFee < 100 * Vault.FEE_MULTIPLIER,\n \"managementFee >= 100%\"\n );\n require(bytes(tokenName).length > 0, \"!tokenName\");\n require(bytes(tokenSymbol).length > 0, \"!tokenSymbol\");\n\n require(_vaultParams.asset != address(0), \"!asset\");\n require(_vaultParams.underlying != address(0), \"!underlying\");\n require(_vaultParams.minimumSupply > 0, \"!minimumSupply\");\n require(_vaultParams.cap > 0, \"!cap\");\n require(\n _vaultParams.cap > _vaultParams.minimumSupply,\n \"cap has to be higher than minimumSupply\"\n );\n }\n\n /**\n * @notice Gets the next option expiry timestamp\n * @param currentOption is the otoken address that the vault is currently writing\n */\n function getNextExpiry(address currentOption)\n internal\n view\n returns (uint256)\n {\n // uninitialized state\n if (currentOption == address(0)) {\n return getNextFriday(block.timestamp);\n }\n uint256 currentExpiry = IOtoken(currentOption).expiryTimestamp();\n\n // After options expiry if no options are written for >1 week\n // We need to give the ability continue writing options\n if (block.timestamp > currentExpiry + 7 days) {\n return getNextFriday(block.timestamp);\n }\n return getNextFriday(currentExpiry);\n }\n\n /**\n * @notice Gets the next options expiry timestamp\n * @param timestamp is the expiry timestamp of the current option\n * Reference: https://codereview.stackexchange.com/a/33532\n * Examples:\n * getNextFriday(week 1 thursday) -> week 1 friday\n * getNextFriday(week 1 friday) -> week 2 friday\n * getNextFriday(week 1 saturday) -> week 2 friday\n */\n function getNextFriday(uint256 timestamp) internal pure returns (uint256) {\n // dayOfWeek = 0 (sunday) - 6 (saturday)\n uint256 dayOfWeek = ((timestamp / 1 days) + 4) % 7;\n uint256 nextFriday = timestamp + ((7 + 5 - dayOfWeek) % 7) * 1 days;\n uint256 friday8am = nextFriday - (nextFriday % (24 hours)) + (8 hours);\n\n // If the passed timestamp is day=Friday hour>8am, we simply increment it by a week to next Friday\n if (timestamp >= friday8am) {\n friday8am += 7 days;\n }\n return friday8am;\n }\n}\n" + }, + "contracts/interfaces/ISwap.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity =0.8.4;\n\ninterface ISwap {\n struct Offer {\n // 32 byte slot 1, partial fill\n // Seller wallet address\n address seller;\n // 32 byte slot 2\n // Addess of oToken\n address oToken;\n // Price per oToken denominated in biddingToken\n uint96 minPrice;\n // 32 byte slot 3\n // ERC20 Token to bid for oToken\n address biddingToken;\n // Minimum oToken amount acceptable for a single bid\n uint96 minBidSize;\n // 32 byte slot 4\n // Total available oToken amount\n uint128 totalSize;\n // Remaining available oToken amount\n // This figure is updated after each successfull swap\n uint128 availableSize;\n // 32 byte slot 5\n // Amount of biddingToken received\n // This figure is updated after each successfull swap\n uint256 totalSales;\n }\n\n struct Bid {\n // ID assigned to offers\n uint256 swapId;\n // Number only used once for each wallet\n uint256 nonce;\n // Signer wallet address\n address signerWallet;\n // Amount of biddingToken offered by signer\n uint256 sellAmount;\n // Amount of oToken requested by signer\n uint256 buyAmount;\n // Referrer wallet address\n address referrer;\n // Signature recovery id\n uint8 v;\n // r portion of the ECSDA signature\n bytes32 r;\n // s portion of the ECSDA signature\n bytes32 s;\n }\n\n struct OfferDetails {\n // Seller wallet address\n address seller;\n // Addess of oToken\n address oToken;\n // Price per oToken denominated in biddingToken\n uint256 minPrice;\n // ERC20 Token to bid for oToken\n address biddingToken;\n // Minimum oToken amount acceptable for a single bid\n uint256 minBidSize;\n }\n\n event Swap(\n uint256 indexed swapId,\n uint256 nonce,\n address indexed signerWallet,\n uint256 signerAmount,\n uint256 sellerAmount,\n address referrer,\n uint256 feeAmount\n );\n\n event NewOffer(\n uint256 swapId,\n address seller,\n address oToken,\n address biddingToken,\n uint256 minPrice,\n uint256 minBidSize,\n uint256 totalSize\n );\n\n event SetFee(address referrer, uint256 fee);\n\n event SettleOffer(uint256 swapId);\n\n event Cancel(uint256 indexed nonce, address indexed signerWallet);\n\n event Authorize(address indexed signer, address indexed signerWallet);\n\n event Revoke(address indexed signer, address indexed signerWallet);\n\n function createOffer(\n address oToken,\n address biddingToken,\n uint96 minPrice,\n uint96 minBidSize,\n uint128 totalSize\n ) external returns (uint256 swapId);\n\n function settleOffer(uint256 swapId, Bid[] calldata bids) external;\n\n function cancelNonce(uint256[] calldata nonces) external;\n\n function check(Bid calldata bid)\n external\n view\n returns (uint256, bytes32[] memory);\n\n function averagePriceForOffer(uint256 swapId)\n external\n view\n returns (uint256);\n\n function authorize(address sender) external;\n\n function revoke() external;\n\n function nonceUsed(address, uint256) external view returns (bool);\n}\n" + }, + "hardhat/console.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >= 0.4.22 <0.9.0;\n\nlibrary console {\n\taddress constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);\n\n\tfunction _sendLogPayload(bytes memory payload) private view {\n\t\tuint256 payloadLength = payload.length;\n\t\taddress consoleAddress = CONSOLE_ADDRESS;\n\t\tassembly {\n\t\t\tlet payloadStart := add(payload, 32)\n\t\t\tlet r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)\n\t\t}\n\t}\n\n\tfunction log() internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log()\"));\n\t}\n\n\tfunction logInt(int p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(int)\", p0));\n\t}\n\n\tfunction logUint(uint p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint)\", p0));\n\t}\n\n\tfunction logString(string memory p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n\t}\n\n\tfunction logBool(bool p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n\t}\n\n\tfunction logAddress(address p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n\t}\n\n\tfunction logBytes(bytes memory p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes)\", p0));\n\t}\n\n\tfunction logBytes1(bytes1 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes1)\", p0));\n\t}\n\n\tfunction logBytes2(bytes2 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes2)\", p0));\n\t}\n\n\tfunction logBytes3(bytes3 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes3)\", p0));\n\t}\n\n\tfunction logBytes4(bytes4 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes4)\", p0));\n\t}\n\n\tfunction logBytes5(bytes5 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes5)\", p0));\n\t}\n\n\tfunction logBytes6(bytes6 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes6)\", p0));\n\t}\n\n\tfunction logBytes7(bytes7 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes7)\", p0));\n\t}\n\n\tfunction logBytes8(bytes8 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes8)\", p0));\n\t}\n\n\tfunction logBytes9(bytes9 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes9)\", p0));\n\t}\n\n\tfunction logBytes10(bytes10 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes10)\", p0));\n\t}\n\n\tfunction logBytes11(bytes11 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes11)\", p0));\n\t}\n\n\tfunction logBytes12(bytes12 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes12)\", p0));\n\t}\n\n\tfunction logBytes13(bytes13 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes13)\", p0));\n\t}\n\n\tfunction logBytes14(bytes14 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes14)\", p0));\n\t}\n\n\tfunction logBytes15(bytes15 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes15)\", p0));\n\t}\n\n\tfunction logBytes16(bytes16 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes16)\", p0));\n\t}\n\n\tfunction logBytes17(bytes17 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes17)\", p0));\n\t}\n\n\tfunction logBytes18(bytes18 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes18)\", p0));\n\t}\n\n\tfunction logBytes19(bytes19 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes19)\", p0));\n\t}\n\n\tfunction logBytes20(bytes20 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes20)\", p0));\n\t}\n\n\tfunction logBytes21(bytes21 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes21)\", p0));\n\t}\n\n\tfunction logBytes22(bytes22 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes22)\", p0));\n\t}\n\n\tfunction logBytes23(bytes23 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes23)\", p0));\n\t}\n\n\tfunction logBytes24(bytes24 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes24)\", p0));\n\t}\n\n\tfunction logBytes25(bytes25 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes25)\", p0));\n\t}\n\n\tfunction logBytes26(bytes26 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes26)\", p0));\n\t}\n\n\tfunction logBytes27(bytes27 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes27)\", p0));\n\t}\n\n\tfunction logBytes28(bytes28 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes28)\", p0));\n\t}\n\n\tfunction logBytes29(bytes29 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes29)\", p0));\n\t}\n\n\tfunction logBytes30(bytes30 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes30)\", p0));\n\t}\n\n\tfunction logBytes31(bytes31 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes31)\", p0));\n\t}\n\n\tfunction logBytes32(bytes32 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes32)\", p0));\n\t}\n\n\tfunction log(uint p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint)\", p0));\n\t}\n\n\tfunction log(string memory p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n\t}\n\n\tfunction log(bool p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n\t}\n\n\tfunction log(address p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n\t}\n\n\tfunction log(uint p0, uint p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint)\", p0, p1));\n\t}\n\n\tfunction log(uint p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string)\", p0, p1));\n\t}\n\n\tfunction log(uint p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool)\", p0, p1));\n\t}\n\n\tfunction log(uint p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, uint p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, uint p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address)\", p0, p1));\n\t}\n\n\tfunction log(address p0, uint p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint)\", p0, p1));\n\t}\n\n\tfunction log(address p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string)\", p0, p1));\n\t}\n\n\tfunction log(address p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool)\", p0, p1));\n\t}\n\n\tfunction log(address p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address)\", p0, p1));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n}\n" + }, + "contracts/vaults/YearnVaults/RibbonThetaYearnVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {ISwap} from \"../../interfaces/ISwap.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../libraries/VaultLifecycleWithSwap.sol\";\nimport {ILiquidityGauge} from \"../../interfaces/ILiquidityGauge.sol\";\nimport {RibbonVault} from \"./base/RibbonVault.sol\";\nimport {\n RibbonThetaYearnVaultStorage\n} from \"../../storage/RibbonThetaYearnVaultStorage.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\n\n/**\n * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe\n * the inheritance chain closely.\n * Any changes/appends in storage variable needs to happen in RibbonThetaYearnVaultStorage.\n * RibbonThetaYearnVault should not inherit from any other contract aside from RibbonVault, RibbonThetaYearnVaultStorage\n */\ncontract RibbonThetaYearnVault is RibbonVault, RibbonThetaYearnVaultStorage {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice oTokenFactory is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event PremiumDiscountSet(\n uint256 premiumDiscount,\n uint256 newPremiumDiscount\n );\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event NewOffer(\n uint256 swapId,\n address seller,\n address oToken,\n address biddingToken,\n uint256 minPrice,\n uint256 minBidSize,\n uint256 totalSize\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _swapContract\n ) RibbonVault(_weth, _usdc, _gammaController, _marginPool, _swapContract) {\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n OTOKEN_FACTORY = _oTokenFactory;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n * @param _owner is the owner of the vault with critical permissions\n * @param _keeper is the keeper of the vault with medium permissions (weekly actions)\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n * @param _premiumDiscount is the vault's discount applied to the premium\n * @param _auctionDuration is the duration of the auction\n * @param _vaultParams is the struct with vault general data\n */\n function initialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n address _optionsPremiumPricer,\n address _strikeSelection,\n uint32 _premiumDiscount,\n uint256 _auctionDuration,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n baseInitialize(\n _owner,\n _keeper,\n _feeRecipient,\n _managementFee,\n _performanceFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n require(_optionsPremiumPricer != address(0), \"!_optionsPremiumPricer\");\n require(_strikeSelection != address(0), \"!_strikeSelection\");\n require(\n _premiumDiscount > 0 &&\n _premiumDiscount < 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"!_premiumDiscount\"\n );\n require(_auctionDuration >= MIN_AUCTION_DURATION, \"!_auctionDuration\");\n optionsPremiumPricer = _optionsPremiumPricer;\n strikeSelection = _strikeSelection;\n premiumDiscount = _premiumDiscount;\n auctionDuration = _auctionDuration;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new strike selection contract\n * @param newStrikeSelection is the address of the new strike selection contract\n */\n function setStrikeSelection(address newStrikeSelection) external onlyOwner {\n require(newStrikeSelection != address(0), \"!newStrikeSelection\");\n strikeSelection = newStrikeSelection;\n }\n\n /**\n * @notice Sets the new options premium pricer contract\n * @param newOptionsPremiumPricer is the address of the new strike selection contract\n */\n function setOptionsPremiumPricer(address newOptionsPremiumPricer)\n external\n onlyOwner\n {\n require(\n newOptionsPremiumPricer != address(0),\n \"!newOptionsPremiumPricer\"\n );\n optionsPremiumPricer = newOptionsPremiumPricer;\n }\n\n /**\n * @notice Optionality to set strike price manually\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice) external onlyOwner {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Sets the new liquidityGauge contract for this vault\n * @param newLiquidityGauge is the address of the new liquidityGauge contract\n */\n function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {\n liquidityGauge = newLiquidityGauge;\n }\n\n /**\n * @notice Sets oToken Premium\n * @param minPrice is the new oToken Premium in the units of 10**18\n */\n function setMinPrice(uint256 minPrice) external onlyKeeper {\n require(minPrice > 0, \"!minPrice\");\n currentOtokenPremium = minPrice;\n }\n\n /**\n * @notice Sets the new Vault Pauser contract for this vault\n * @param newVaultPauser is the address of the new vaultPauser contract\n */\n function setVaultPauser(address newVaultPauser) external onlyOwner {\n vaultPauser = newVaultPauser;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n transferAsset(msg.sender, amount);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n _initiateWithdraw(numShares);\n currentQueuedWithdrawShares = currentQueuedWithdrawShares.add(\n numShares\n );\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /**\n * @notice Stakes a users vault shares\n * @param numShares is the number of shares to stake\n */\n function stake(uint256 numShares) external nonReentrant {\n address _liquidityGauge = liquidityGauge;\n require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit\n require(numShares > 0);\n uint256 heldByAccount = balanceOf(msg.sender);\n if (heldByAccount < numShares) {\n _redeem(numShares.sub(heldByAccount), false);\n }\n _transfer(msg.sender, address(this), numShares);\n _approve(address(this), _liquidityGauge, numShares);\n ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);\n }\n\n /**\n * @notice Closes the existing short and calculate the shares to mint, new price per share &\n amount of funds to re-allocate as collateral for the new round\n * Since we are incrementing the round here, the options are sold in the beginning of a round\n * instead of at the end of the round. For example, at round 1, we don't sell any options. We\n * start selling options at the beginning of round 2.\n */\n function closeRound() external nonReentrant {\n address oldOption = optionState.currentOption;\n require(\n oldOption != address(0) || vaultState.round == 1,\n \"Round closed\"\n );\n _closeShort(oldOption);\n\n uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares;\n (uint256 lockedBalance, uint256 queuedWithdrawAmount) =\n _closeRound(\n uint256(lastQueuedWithdrawAmount),\n currQueuedWithdrawShares\n );\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(\n currQueuedWithdrawShares\n );\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n currentQueuedWithdrawShares = 0;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycleWithSwap.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Sets the next option the vault will be shorting\n */\n function commitNextOption() external onlyKeeper nonReentrant {\n address currentOption = optionState.currentOption;\n require(\n currentOption == address(0) && vaultState.round != 1,\n \"Round not closed\"\n );\n\n VaultLifecycleWithSwap.CommitParams memory commitParams =\n VaultLifecycleWithSwap.CommitParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n collateralAsset: vaultParams.asset,\n currentOption: currentOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n strikeSelection: strikeSelection,\n optionsPremiumPricer: optionsPremiumPricer\n });\n\n (address otokenAddress, uint256 strikePrice, uint256 delta) =\n VaultLifecycleWithSwap.commitNextOption(\n commitParams,\n vaultParams,\n vaultState\n );\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n optionState.nextOption = otokenAddress;\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position and create a new offer.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n address newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n uint256 lockedBalance = vaultState.lockedAmount;\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n VaultLifecycleWithSwap.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n _createOffer();\n }\n\n function _createOffer() private {\n address currentOtoken = optionState.currentOption;\n uint256 currOtokenPremium = currentOtokenPremium;\n\n optionAuctionID = VaultLifecycleWithSwap.createOffer(\n currentOtoken,\n currOtokenPremium,\n SWAP_CONTRACT,\n vaultParams\n );\n }\n\n /**\n * @notice Settle current offer\n */\n function settleOffer(ISwap.Bid[] calldata bids)\n external\n onlyKeeper\n nonReentrant\n {\n ISwap(SWAP_CONTRACT).settleOffer(optionAuctionID, bids);\n }\n\n /**\n * @notice Burn the remaining oTokens left over\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n uint256 unlockedAssetAmount =\n VaultLifecycleWithSwap.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n\n vaultState.lockedAmount = uint104(\n uint256(vaultState.lockedAmount).sub(unlockedAssetAmount)\n );\n }\n\n /**\n * @notice pause a user's vault position\n */\n function pausePosition() external {\n address _vaultPauserAddress = vaultPauser;\n require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit\n _redeem(0, true);\n uint256 heldByAccount = balanceOf(msg.sender);\n _approve(msg.sender, _vaultPauserAddress, heldByAccount);\n IVaultPauser(_vaultPauserAddress).pausePosition(\n msg.sender,\n heldByAccount\n );\n }\n}\n" + }, + "contracts/interfaces/ILiquidityGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface ILiquidityGauge {\n function balanceOf(address) external view returns (uint256);\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function withdraw(uint256 _value) external;\n}\n" + }, + "contracts/vaults/YearnVaults/base/RibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\nimport {IYearnVault} from \"../../../interfaces/IYearn.sol\";\nimport {Vault} from \"../../../libraries/Vault.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../../libraries/VaultLifecycleWithSwap.sol\";\nimport {ShareMath} from \"../../../libraries/ShareMath.sol\";\nimport {IWETH} from \"../../../interfaces/IWETH.sol\";\n\ncontract RibbonVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n /// @notice Yearn vault contract\n IYearnVault public collateralToken;\n\n // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure.\n uint256[30] private ____gap;\n\n // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE\n // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaYearnVaultStorage\n // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n /// @notice 7 day period between each options sale.\n uint256 public constant PERIOD = 7 days;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // SWAP_CONTRACT is a contract for settling bids via signed messages\n // https://github.com/ribbon-finance/ribbon-v2/blob/master/contracts/utils/Swap.sol\n address public immutable SWAP_CONTRACT;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectVaultFees(\n uint256 performanceFee,\n uint256 vaultFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _gammaController,\n address _marginPool,\n address _swapContract\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_swapContract != address(0), \"!_swapContract\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n\n WETH = _weth;\n USDC = _usdc;\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n SWAP_CONTRACT = _swapContract;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function baseInitialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) internal initializer {\n VaultLifecycleWithSwap.verifyInitializerParams(\n _owner,\n _keeper,\n _feeRecipient,\n _performanceFee,\n _managementFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_tokenName, _tokenSymbol);\n __Ownable_init();\n transferOwnership(_owner);\n\n keeper = _keeper;\n\n feeRecipient = _feeRecipient;\n performanceFee = _performanceFee;\n managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div(\n WEEKS_PER_YEAR\n );\n vaultParams = _vaultParams;\n\n uint256 assetBalance = totalBalance();\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n\n vaultState.round = 1;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n // We are dividing annualized management fee by num weeks in a year\n uint256 tmpManagementFee =\n newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n\n managementFee = tmpManagementFee;\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Deposits the `asset` from msg.sender.\n * @param amount is the amount of `asset` to deposit\n */\n function deposit(uint256 amount) external nonReentrant {\n require(amount > 0, \"!amount\");\n\n _depositFor(amount, msg.sender);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.\n * @notice Used for vault -> vault deposits on the user's behalf\n * @param amount is the amount of `asset` to deposit\n * @param creditor is the address that can claim/withdraw deposited amount\n */\n function depositFor(uint256 amount, address creditor)\n external\n nonReentrant\n {\n require(amount > 0, \"!amount\");\n require(creditor != address(0), \"!creditor\");\n\n _depositFor(amount, creditor);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n */\n function _depositFor(uint256 amount, address creditor) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount = totalBalance().add(amount);\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n\n emit Deposit(creditor, amount, currentRound);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function _initiateWithdraw(uint256 numShares) internal {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n if (\n depositReceipts[msg.sender].amount > 0 ||\n depositReceipts[msg.sender].unredeemedShares > 0\n ) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n bool withdrawalIsSameRound = withdrawal.round == currentRound;\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n uint256 existingShares = uint256(withdrawal.shares);\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return withdrawAmount the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n require(withdrawAmount > 0, \"!withdrawAmount\");\n\n transferAsset(msg.sender, withdrawAmount);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Helper function that performs most administrative tasks\n * such as minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @param currentQueuedWithdrawShares is the queued withdraw shares for the current round\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _closeRound(\n uint256 lastQueuedWithdrawAmount,\n uint256 currentQueuedWithdrawShares\n ) internal returns (uint256 lockedBalance, uint256 queuedWithdrawAmount) {\n address recipient = feeRecipient;\n uint256 mintShares;\n uint256 performanceFeeInAsset;\n uint256 totalVaultFee;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n ) = VaultLifecycleWithSwap.closeRound(\n vaultState,\n VaultLifecycleWithSwap.CloseParams(\n vaultParams.decimals,\n IERC20(vaultParams.asset).balanceOf(address(this)),\n totalSupply(),\n lastQueuedWithdrawAmount,\n performanceFee,\n managementFee,\n currentQueuedWithdrawShares\n )\n );\n\n // Finalize the pricePerShare at the end of the round\n uint256 currentRound = vaultState.round;\n roundPricePerShare[currentRound] = newPricePerShare;\n\n emit CollectVaultFees(\n performanceFeeInAsset,\n totalVaultFee,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (totalVaultFee > 0) {\n IERC20(vaultParams.asset).safeTransfer(\n payable(recipient),\n totalVaultFee\n );\n }\n\n return (lockedBalance, queuedWithdrawAmount);\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n external\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n // After calling closeRound, current option is set to none\n // We also commit the lockedAmount but do not deposit into Opyn\n // which results in double counting of asset balance and lockedAmount\n\n return\n optionState.currentOption != address(0)\n ? uint256(vaultState.lockedAmount).add(\n IERC20(vaultParams.asset).balanceOf(address(this))\n )\n : IERC20(vaultParams.asset).balanceOf(address(this));\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n\n /************************************************\n * HELPERS\n ***********************************************/\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) internal {\n address asset = vaultParams.asset;\n if (asset == WETH) {\n IWETH(WETH).withdraw(amount);\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Transfer failed\");\n return;\n }\n IERC20(asset).safeTransfer(recipient, amount);\n }\n}\n" + }, + "contracts/storage/RibbonThetaYearnVaultStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nabstract contract RibbonThetaYearnVaultStorageV1 {\n // Logic contract used to price options\n address public optionsPremiumPricer;\n // Logic contract used to select strike prices\n address public strikeSelection;\n // Premium discount on options we are selling (thousandths place: 000 - 999)\n uint256 public premiumDiscount;\n // Current oToken premium\n uint256 public currentOtokenPremium;\n // Last round id at which the strike was manually overridden\n uint16 public lastStrikeOverrideRound;\n // Price last overridden strike set to\n uint256 public overriddenStrikePrice;\n // Auction duration\n uint256 public auctionDuration;\n // Auction id of current option\n uint256 public optionAuctionID;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV2 {\n // Amount locked for scheduled withdrawals last week;\n uint256 public lastQueuedWithdrawAmount;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV3 {\n // LiquidityGauge contract for the vault\n address public liquidityGauge;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV4 {\n // OptionsPurchaseQueue contract for selling options\n address public optionsPurchaseQueue;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV5 {\n // Queued withdraw shares for the current round\n uint256 public currentQueuedWithdrawShares;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV6 {\n // Vault Pauser Contract for the vault\n address public vaultPauser;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV7 {\n // Check if yearn is used in vault\n bool public isYearnPaused;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of RibbonThetaVaultStorage\n// e.g. RibbonThetaVaultStorage, so finally it would look like\n// contract RibbonThetaVaultStorage is RibbonThetaVaultStorageV1, RibbonThetaVaultStorageV2\nabstract contract RibbonThetaYearnVaultStorage is\n RibbonThetaYearnVaultStorageV1,\n RibbonThetaYearnVaultStorageV2,\n RibbonThetaYearnVaultStorageV3,\n RibbonThetaYearnVaultStorageV4,\n RibbonThetaYearnVaultStorageV5,\n RibbonThetaYearnVaultStorageV6,\n RibbonThetaYearnVaultStorageV7\n{\n\n}\n" + }, + "contracts/interfaces/IVaultPauser.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface IVaultPauser {\n /// @notice pause vault position of an account with max amount\n /// @param _account the address of user\n /// @param _amount amount of shares\n function pausePosition(address _account, uint256 _amount) external;\n\n /// @notice resume vault position of an account with max amount\n /// @param _vaultAddress the address of vault\n function resumePosition(address _vaultAddress) external;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuardUpgradeable is Initializable {\n // Booleans are more expensive than uint256 or any type that takes up a full\n // word because each write operation emits an extra SLOAD to first read the\n // slot's contents, replace the bits taken up by the boolean, and then write\n // back. This is the compiler's defense against contract upgrades and\n // pointer aliasing, and it cannot be disabled.\n\n // The values being non-zero value makes deployment a bit more expensive,\n // but in exchange the refund on every call to nonReentrant will be lower in\n // amount. Since refunds are capped to a percentage of the total\n // transaction's gas, it is best to keep them low in cases like this one, to\n // increase the likelihood of the full refund coming into effect.\n uint256 private constant _NOT_ENTERED = 1;\n uint256 private constant _ENTERED = 2;\n\n uint256 private _status;\n\n function __ReentrancyGuard_init() internal initializer {\n __ReentrancyGuard_init_unchained();\n }\n\n function __ReentrancyGuard_init_unchained() internal initializer {\n _status = _NOT_ENTERED;\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n // On the first call to nonReentrant, _notEntered will be true\n require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n _status = _ENTERED;\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = _NOT_ENTERED;\n }\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../utils/ContextUpgradeable.sol\";\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n function __Ownable_init() internal initializer {\n __Context_init_unchained();\n __Ownable_init_unchained();\n }\n\n function __Ownable_init_unchained() internal initializer {\n _setOwner(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _setOwner(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _setOwner(newOwner);\n }\n\n function _setOwner(address newOwner) private {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20Upgradeable.sol\";\nimport \"./extensions/IERC20MetadataUpgradeable.sol\";\nimport \"../../utils/ContextUpgradeable.sol\";\nimport \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n function __ERC20_init(string memory name_, string memory symbol_) internal initializer {\n __Context_init_unchained();\n __ERC20_init_unchained(name_, symbol_);\n }\n\n function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n\n uint256 currentAllowance = _allowances[sender][_msgSender()];\n require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n unchecked {\n _approve(sender, _msgSender(), currentAllowance - amount);\n }\n\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n uint256 currentAllowance = _allowances[_msgSender()][spender];\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n uint256 senderBalance = _balances[sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[sender] = senderBalance - amount;\n }\n _balances[recipient] += amount;\n\n emit Transfer(sender, recipient, amount);\n\n _afterTokenTransfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n _balances[account] += amount;\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n }\n _totalSupply -= amount;\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n uint256[45] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private _initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private _initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(_initializing || !_initialized, \"Initializable: contract is already initialized\");\n\n bool isTopLevelCall = !_initializing;\n if (isTopLevelCall) {\n _initializing = true;\n _initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n _initializing = false;\n }\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n function __Context_init() internal initializer {\n __Context_init_unchained();\n }\n\n function __Context_init_unchained() internal initializer {\n }\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n uint256[50] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20Upgradeable {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20Upgradeable.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20MetadataUpgradeable is IERC20Upgradeable {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/vaults/VaultPauser/RibbonVaultPauser.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {IRibbonThetaVault} from \"../../interfaces/IRibbonThetaVault.sol\";\nimport {IWETH} from \"../../interfaces/IWETH.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\n\ncontract RibbonVaultPauser is Ownable, IVaultPauser {\n using SafeERC20 for IERC20;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores all the vault's paused positions\n struct PauseReceipt {\n uint16 round;\n uint128 shares;\n }\n\n mapping(address => mapping(address => PauseReceipt)) public pausedPositions;\n mapping(address => bool) private registeredVaults;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n address public immutable STETH;\n address public immutable STETH_VAULT;\n\n address public keeper;\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Pause(\n address indexed account,\n address indexed vaultAddress,\n uint256 share,\n uint256 round\n );\n\n event Resume(\n address indexed account,\n address indexed vaultAddress,\n uint256 withdrawAmount\n );\n\n event ProcessWithdrawal(address indexed vaultAddress, uint256 round);\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n */\n constructor(\n address _keeper,\n address _weth,\n address _steth,\n address _steth_vault\n ) {\n require(_keeper != address(0), \"!_keeper\");\n require(_weth != address(0), \"!_weth\");\n require(_steth != address(0), \"!_steth\");\n require(_steth_vault != address(0), \"!_steth_vault\");\n\n keeper = _keeper;\n WETH = _weth;\n STETH = _steth;\n STETH_VAULT = _steth_vault;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n function getPausePosition(address _vaultAddress, address _userAddress)\n external\n view\n returns (PauseReceipt memory)\n {\n return pausedPositions[_vaultAddress][_userAddress];\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param _newKeeper is the address of the new keeper\n */\n function setNewKeeper(address _newKeeper) external onlyOwner {\n require(_newKeeper != address(0), \"!newKeeper\");\n keeper = _newKeeper;\n }\n\n /**\n * @notice add vault into registered vaults\n * @param _vaultAddress is the address of the new vault to be registered\n */\n function addVault(address _vaultAddress) external onlyOwner {\n registeredVaults[_vaultAddress] = true;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice pause position from vault by redeem all the shares from vault to Pauser\n * @param _account user's address\n * @param _amount the amount of shares\n */\n function pausePosition(address _account, uint256 _amount)\n external\n override\n {\n address currentVaultAddress = msg.sender;\n IRibbonThetaVault currentVault = IRibbonThetaVault(currentVaultAddress);\n\n // check if vault is registered\n require(\n registeredVaults[currentVaultAddress],\n \"Vault is not registered\"\n );\n\n PauseReceipt storage pausedPosition =\n pausedPositions[currentVaultAddress][_account];\n\n // check if position is paused\n require(\n pausedPosition.shares == 0 && pausedPosition.round == 0,\n \"Position is paused\"\n );\n\n uint16 round = currentVault.vaultState().round;\n\n require(_amount < type(uint128).max, \"_amount overflow\");\n\n pausedPositions[currentVaultAddress][_account] = PauseReceipt({\n round: round,\n shares: uint128(_amount)\n });\n\n emit Pause(_account, currentVaultAddress, _amount, round);\n\n // transfer from user to pauser\n IERC20(currentVaultAddress).safeTransferFrom(\n _account,\n address(this),\n _amount\n );\n\n currentVault.initiateWithdraw(_amount);\n }\n\n /**\n * @notice resume user's position into vault by making a deposit\n * @param _vaultAddress vault's address\n */\n function resumePosition(address _vaultAddress) external override {\n IRibbonThetaVault currentVault = IRibbonThetaVault(_vaultAddress);\n\n // check if vault is registered\n require(registeredVaults[_vaultAddress], \"Vault is not registered\");\n\n // get params and round\n Vault.VaultParams memory currentParams = currentVault.vaultParams();\n uint256 round = currentVault.vaultState().round;\n\n PauseReceipt storage pauseReceipt =\n pausedPositions[_vaultAddress][msg.sender];\n uint256 pauseReceiptRound = pauseReceipt.round;\n\n // check if roun is closed before resuming position\n require(pauseReceiptRound < round, \"Round not closed yet\");\n uint256 totalWithdrawAmount =\n ShareMath.sharesToAsset(\n pauseReceipt.shares,\n currentVault.roundPricePerShare(pauseReceiptRound),\n currentParams.decimals\n );\n\n // delete position once transfer (revert to zero)\n delete pausedPositions[_vaultAddress][msg.sender];\n\n // stETH transfers suffer from an off-by-1 error\n // since we received STETH , we shall deposit using STETH instead of ETH\n if (_vaultAddress == STETH_VAULT) {\n totalWithdrawAmount = totalWithdrawAmount - 3;\n\n emit Resume(msg.sender, _vaultAddress, totalWithdrawAmount - 1);\n IERC20(STETH).safeApprove(_vaultAddress, totalWithdrawAmount);\n currentVault.depositYieldTokenFor(totalWithdrawAmount, msg.sender);\n } else {\n emit Resume(msg.sender, _vaultAddress, totalWithdrawAmount);\n\n // if asset is ETH, we will convert it into WETH before depositing\n if (currentParams.asset == WETH) {\n IWETH(WETH).deposit{value: totalWithdrawAmount}();\n }\n IERC20(currentParams.asset).safeApprove(\n _vaultAddress,\n totalWithdrawAmount\n );\n\n currentVault.depositFor(totalWithdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice process withdrawals by completing in a batch\n * @param _vaultAddress vault's address to be processed\n */\n function processWithdrawal(address _vaultAddress) external onlyKeeper {\n IRibbonThetaVault currentVault = IRibbonThetaVault(_vaultAddress);\n // we can only process withdrawal after closing the previous round\n // hence round should be - 1\n emit ProcessWithdrawal(\n _vaultAddress,\n currentVault.vaultState().round - 1\n );\n currentVault.completeWithdraw();\n }\n\n fallback() external payable {}\n\n receive() external payable {}\n}\n" + }, + "@openzeppelin/contracts/access/Ownable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _setOwner(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _setOwner(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _setOwner(newOwner);\n }\n\n function _setOwner(address newOwner) private {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "contracts/vaults/TreasuryVault/RibbonTreasuryVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\n\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {\n VaultLifecycleTreasury\n} from \"../../libraries/VaultLifecycleTreasury.sol\";\nimport {\n RibbonTreasuryVaultStorage\n} from \"../../storage/RibbonTreasuryVaultStorage.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {IWETH} from \"../../interfaces/IWETH.sol\";\nimport {GnosisAuction} from \"../../libraries/GnosisAuction.sol\";\nimport {IERC20Detailed} from \"../../interfaces/IERC20Detailed.sol\";\n\ncontract RibbonTreasuryVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable,\n RibbonTreasuryVaultStorage\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // GNOSIS_EASY_AUCTION is Gnosis protocol's contract for initiating auctions and placing bids\n // https://github.com/gnosis/ido-contracts/blob/main/contracts/EasyAuction.sol\n address public immutable GNOSIS_EASY_AUCTION;\n\n /// OTOKEN_FACTORY is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n // The minimum amount above which premium distribution will occur during commitAndClose\n uint256 private constant MIN_DUST_AMOUNT = 10000000;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectManagementFee(\n uint256 managementFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n event CollectPerformanceFee(\n uint256 performanceFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n event DistributePremium(\n uint256 amount,\n uint256[] amounts,\n address[] recipients,\n uint256 round\n );\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event PremiumDiscountSet(\n uint256 premiumDiscount,\n uint256 newPremiumDiscount\n );\n\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event InitiateGnosisAuction(\n address indexed auctioningToken,\n address indexed biddingToken,\n uint256 auctionCounter,\n address indexed manager\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions\n */\n constructor(\n address _weth,\n address _usdc,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _gnosisEasyAuction\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n require(_gnosisEasyAuction != address(0), \"!_gnosisEasyAuction\");\n\n WETH = _weth;\n USDC = _usdc;\n OTOKEN_FACTORY = _oTokenFactory;\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n GNOSIS_EASY_AUCTION = _gnosisEasyAuction;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function initialize(\n VaultLifecycleTreasury.InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n VaultLifecycleTreasury.verifyInitializerParams(\n _initParams,\n _vaultParams,\n MIN_AUCTION_DURATION\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_initParams._tokenName, _initParams._tokenSymbol);\n __Ownable_init();\n transferOwnership(_initParams._owner);\n\n keeper = _initParams._keeper;\n period = _initParams._period;\n optionsPremiumPricer = _initParams._optionsPremiumPricer;\n strikeSelection = _initParams._strikeSelection;\n premiumDiscount = _initParams._premiumDiscount;\n auctionDuration = _initParams._auctionDuration;\n feeRecipient = _initParams._feeRecipient;\n performanceFee = _initParams._performanceFee;\n managementFee = _perRoundManagementFee(_initParams._managementFee);\n maxDepositors = _initParams._maxDepositors;\n minDeposit = _initParams._minDeposit;\n\n vaultParams = _vaultParams;\n vaultState.round = 1;\n\n uint256 assetBalance =\n IERC20(vaultParams.asset).balanceOf(address(this));\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n managementFee = _perRoundManagementFee(newManagementFee);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n }\n\n /**\n * @notice Internal function to set the management fee for the vault\n * @param managementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2\n * @return perRoundManagementFee is the management divided by the number of rounds per year\n */\n function _perRoundManagementFee(uint256 managementFee)\n internal\n view\n returns (uint256)\n {\n uint256 _period = period;\n uint256 feeDivider =\n _period % 30 == 0\n ? Vault.FEE_MULTIPLIER * (12 / (_period / 30))\n : WEEKS_PER_YEAR / (_period / 7);\n\n // We are dividing annualized management fee by num weeks in a year\n return managementFee.mul(Vault.FEE_MULTIPLIER).div(feeDivider);\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /**\n * @notice Sets the new discount on premiums for options we are selling\n * @param newPremiumDiscount is the premium discount\n */\n function setPremiumDiscount(uint256 newPremiumDiscount) external onlyOwner {\n require(\n newPremiumDiscount > 0 &&\n newPremiumDiscount < 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"Invalid discount\"\n );\n\n emit PremiumDiscountSet(premiumDiscount, newPremiumDiscount);\n\n premiumDiscount = newPremiumDiscount;\n }\n\n /**\n * @notice Sets the new auction duration\n * @param newAuctionDuration is the auction duration\n */\n function setAuctionDuration(uint256 newAuctionDuration) external onlyOwner {\n require(\n newAuctionDuration >= MIN_AUCTION_DURATION,\n \"Invalid auction duration\"\n );\n\n emit AuctionDurationSet(auctionDuration, newAuctionDuration);\n\n auctionDuration = newAuctionDuration;\n }\n\n /**\n * @notice Sets the new strike selection contract\n * @param newStrikeSelection is the address of the new strike selection contract\n */\n function setStrikeSelection(address newStrikeSelection) external onlyOwner {\n require(newStrikeSelection != address(0), \"!newStrikeSelection\");\n strikeSelection = newStrikeSelection;\n }\n\n /**\n * @notice Sets the new options premium pricer contract\n * @param newOptionsPremiumPricer is the address of the new strike selection contract\n */\n function setOptionsPremiumPricer(address newOptionsPremiumPricer)\n external\n onlyOwner\n {\n require(\n newOptionsPremiumPricer != address(0),\n \"!newOptionsPremiumPricer\"\n );\n optionsPremiumPricer = newOptionsPremiumPricer;\n }\n\n /**\n * @notice Optionality to set strike price manually\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice)\n external\n onlyOwner\n nonReentrant\n {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Set the maximum number of depositors\n * @param newMaxDepositors is the new cap for number of depositors\n */\n function setMaxDepositors(uint256 newMaxDepositors)\n external\n onlyOwner\n nonReentrant\n {\n require(newMaxDepositors > 0, \"!newMaxDepositors\");\n maxDepositors = newMaxDepositors;\n }\n\n /**\n * @notice Set the minimum deposit amount\n * @param newMinDeposit is the new minimum amount for deposit\n */\n function setMinDeposit(uint256 newMinDeposit)\n external\n onlyOwner\n nonReentrant\n {\n require(newMinDeposit > 0, \"!newMinDeposit\");\n minDeposit = newMinDeposit;\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Internal function to add new depositor address\n * @param newDepositor is the address to include in the depositors list\n */\n function _addDepositor(address newDepositor) internal {\n if (!depositorsMap[newDepositor]) {\n require(newDepositor != address(0), \"Depositor address null\");\n require(\n (depositorsArray.length + 1) <= maxDepositors,\n \"Number of depositors exceeds limit\"\n );\n\n depositorsMap[newDepositor] = true;\n depositorsArray.push(newDepositor);\n }\n }\n\n /**\n * @notice Remove addresses from depositors list\n * @param excludeDepositor is the address to exclude from the depositors list\n */\n function _removeDepositor(address excludeDepositor) internal {\n address[] storage array = depositorsArray;\n uint256 arrayLength = array.length;\n\n require(depositorsMap[excludeDepositor], \"Depositor does not exist\");\n\n depositorsMap[excludeDepositor] = false;\n\n for (uint256 i = 0; i < arrayLength - 1; i++) {\n if (excludeDepositor == array[i]) {\n (array[i], array[arrayLength - 1]) = (\n array[arrayLength - 1],\n array[i]\n );\n }\n }\n array.pop();\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender.\n * @param amount is the amount of `asset` to deposit\n */\n function deposit(uint256 amount) external nonReentrant {\n require(amount > 0, \"!amount\");\n\n _addDepositor(msg.sender);\n\n _depositFor(amount, msg.sender);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n */\n function _depositFor(uint256 amount, address creditor) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount = totalBalance().add(amount);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n uint256 totalUserDeposit =\n accountVaultBalance(msg.sender).add(depositReceipt.amount).add(\n amount\n );\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n require(totalUserDeposit >= minDeposit, \"Minimum deposit not reached\");\n\n emit Deposit(creditor, amount, currentRound);\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n if (depositReceipt.amount > 0 || depositReceipt.unredeemedShares > 0) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n uint256 withdrawalRound = withdrawal.round;\n\n bool withdrawalIsSameRound = withdrawalRound == currentRound;\n\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n\n uint256 existingShares = uint256(withdrawal.shares);\n\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n\n // Ensure withdrawal does not reduce user deposit below the minimum amount\n uint256 vaultDecimals = vaultParams.decimals;\n uint256 userBalance = accountVaultBalance(msg.sender);\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n numShares,\n currentRound != 1\n ? roundPricePerShare[currentRound - 1]\n : 10**vaultDecimals,\n vaultDecimals\n );\n\n if (userBalance > withdrawAmount) {\n uint256 totalDeposit = userBalance.sub(withdrawAmount);\n require(totalDeposit >= minDeposit, \"Minimum deposit not reached\");\n }\n\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(numShares);\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n if (depositReceipt.amount == 0 && balanceOf(msg.sender) == numShares) {\n _removeDepositor(msg.sender);\n }\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return withdrawAmount the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n require(withdrawAmount > 0, \"!withdrawAmount\");\n transferAsset(msg.sender, withdrawAmount);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n uint256 userBalance =\n accountVaultBalance(msg.sender).add(receiptAmount);\n\n if (userBalance > amount) {\n uint256 totalUserDeposit = userBalance.sub(amount);\n require(\n totalUserDeposit >= minDeposit,\n \"Minimum deposit not reached\"\n );\n }\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n if (depositReceipt.amount == 0 && shares(msg.sender) == 0) {\n _removeDepositor(msg.sender);\n }\n\n transferAsset(msg.sender, amount);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /*\n * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map.\n * Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k.\n * Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0.\n * @param numRounds is the number of rounds to initialize in the map\n */\n function initRounds(uint256 numRounds) external nonReentrant {\n require(numRounds > 0, \"!numRounds\");\n\n uint256 _round = vaultState.round;\n for (uint256 i = 0; i < numRounds; i++) {\n uint256 index = _round + i;\n require(roundPricePerShare[index] == 0, \"Initialized\"); // AVOID OVERWRITING ACTUAL VALUES\n roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT;\n }\n }\n\n /*\n * @notice Helper function that performs most administrative tasks\n * such as setting next option, minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @return newOption is the new option address\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _rollToNextOption(uint256 lastQueuedWithdrawAmount)\n internal\n returns (\n address newOption,\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount\n )\n {\n require(block.timestamp >= optionState.nextOptionReadyAt, \"!ready\");\n\n newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n uint256 currentRound = vaultState.round;\n address recipient = feeRecipient;\n uint256 mintShares;\n uint256 managementFeeInAsset;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n managementFeeInAsset\n ) = VaultLifecycleTreasury.rollover(\n vaultState,\n VaultLifecycleTreasury.RolloverParams(\n vaultParams.decimals,\n IERC20(vaultParams.asset).balanceOf(address(this)),\n totalSupply(),\n lastQueuedWithdrawAmount,\n currentRound != 1 ? managementFee : 0\n )\n );\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n\n // Finalize the pricePerShare at the end of the round\n\n roundPricePerShare[currentRound] = newPricePerShare;\n\n emit CollectManagementFee(\n managementFeeInAsset,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (managementFeeInAsset > 0) {\n transferAsset(payable(recipient), managementFeeInAsset);\n }\n\n return (newOption, lockedBalance, queuedWithdrawAmount);\n }\n\n /**\n * @notice Helper function to make an ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) internal {\n address asset = vaultParams.asset;\n IERC20(asset).safeTransfer(recipient, amount);\n }\n\n /**\n * @notice Sets the next option the vault will be shorting, and closes the existing short.\n * This allows all the users to withdraw if the next option is malicious.\n */\n function commitAndClose() external nonReentrant {\n address oldOption = optionState.currentOption;\n\n VaultLifecycleTreasury.CloseParams memory closeParams =\n VaultLifecycleTreasury.CloseParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n currentOption: oldOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n period: period\n });\n\n (\n address otokenAddress,\n uint256 premium,\n uint256 strikePrice,\n uint256 delta\n ) =\n VaultLifecycleTreasury.commitAndClose(\n strikeSelection,\n optionsPremiumPricer,\n premiumDiscount,\n closeParams,\n vaultParams,\n vaultState\n );\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n ShareMath.assertUint104(premium);\n currentOtokenPremium = uint104(premium);\n optionState.nextOption = otokenAddress;\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n\n _closeShort(oldOption);\n\n // In case chargeAndDistribute was not called last round, call\n // the function to conclude last round's performance fee and distribution\n if (IERC20(USDC).balanceOf(address(this)) > MIN_DUST_AMOUNT) {\n _chargeAndDistribute();\n }\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycleTreasury.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n (\n address newOption,\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount\n ) = _rollToNextOption(uint256(lastQueuedWithdrawAmount));\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n VaultLifecycleTreasury.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n _startAuction();\n }\n\n /**\n * @notice Initiate the gnosis auction.\n */\n function startAuction() external onlyKeeper nonReentrant {\n _startAuction();\n }\n\n function _startAuction() private {\n GnosisAuction.AuctionDetails memory auctionDetails;\n\n uint256 currOtokenPremium = currentOtokenPremium;\n\n require(currOtokenPremium > 0, \"!currentOtokenPremium\");\n\n uint256 stableDecimals = IERC20Detailed(USDC).decimals();\n\n auctionDetails.oTokenAddress = optionState.currentOption;\n auctionDetails.gnosisEasyAuction = GNOSIS_EASY_AUCTION;\n auctionDetails.asset = USDC;\n auctionDetails.assetDecimals = stableDecimals;\n auctionDetails.oTokenPremium = currOtokenPremium;\n auctionDetails.duration = auctionDuration;\n\n optionAuctionID = VaultLifecycleTreasury.startAuction(auctionDetails);\n }\n\n /**\n * @notice Burn the remaining oTokens left over from gnosis auction.\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n uint256 unlockedAssetAmount =\n VaultLifecycleTreasury.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n\n vaultState.lockedAmount = uint104(\n uint256(vaultState.lockedAmount).sub(unlockedAssetAmount)\n );\n }\n\n /**\n * @notice Settles the round's Gnosis auction and distribute the premiums earned\n */\n function concludeOptionsSale() external onlyKeeper nonReentrant {\n VaultLifecycleTreasury.settleAuction(\n GNOSIS_EASY_AUCTION,\n optionAuctionID\n );\n\n if (IERC20(USDC).balanceOf(address(this)) > MIN_DUST_AMOUNT) {\n _chargeAndDistribute();\n }\n }\n\n /**\n * @notice Charge performance fee and distribute remaining to depositors addresses\n */\n function chargeAndDistribute() external onlyKeeper nonReentrant {\n _chargeAndDistribute();\n }\n\n /**\n * @notice Calculate performance fee and transfer to fee recipient\n */\n function _chargeAndDistribute() internal {\n IERC20 stableAsset = IERC20(USDC);\n uint256 stableBalance = stableAsset.balanceOf(address(this));\n\n require(stableBalance > 0, \"no premium to distribute\");\n\n _chargePerformanceFee(stableAsset, stableBalance);\n\n _distributePremium(\n stableAsset,\n stableAsset.balanceOf(address(this)) // Get the new balance\n );\n }\n\n /**\n * @notice Charge performance fee\n */\n function _chargePerformanceFee(IERC20 token, uint256 amount) internal {\n address recipient = feeRecipient;\n uint256 transferAmount =\n amount.mul(performanceFee).div(100 * Vault.FEE_MULTIPLIER);\n\n token.safeTransfer(recipient, transferAmount);\n\n // Performance fee for the round is charged after rollover\n // hence we need to adjust the round to the previous\n emit CollectPerformanceFee(\n transferAmount,\n vaultState.round - 1,\n recipient\n );\n }\n\n /**\n * @notice Distribute the premium to depositor addresses\n */\n function _distributePremium(IERC20 token, uint256 amount) internal {\n // Distribute to depositor address\n address[] storage _depositors = depositorsArray;\n uint256[] memory _amounts = new uint256[](_depositors.length);\n uint256 totalSupply = totalSupply() - lastQueuedWithdrawAmount;\n\n for (uint256 i = 0; i < _depositors.length; i++) {\n // Distribute to depositors proportional to the amount of\n // shares they own\n address depositorAddress = _depositors[i];\n _amounts[i] = shares(depositorAddress).mul(amount).div(totalSupply);\n\n token.safeTransfer(depositorAddress, _amounts[i]);\n }\n\n emit DistributePremium(\n amount,\n _amounts,\n _depositors,\n vaultState.round - 1\n );\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n public\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n return\n uint256(vaultState.lockedAmount).add(\n IERC20(vaultParams.asset).balanceOf(address(this))\n );\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n /**\n * @notice Returns the maximum capacity of the vault in terms of the vault's asset\n */\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n /**\n * @notice Returns the date and time for the next options sale\n */\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n /**\n * @notice Returns the options specification for the current round\n */\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n /**\n * @notice Returns the options specification for the next round\n */\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n /**\n * @notice Returns total pending deposit for the current round\n */\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n\n /**\n * @notice ERC20 _transfer override function\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal override {\n require(\n recipient == address(this) || sender == address(this),\n \"Treasury rToken is not transferrable\"\n );\n return ERC20Upgradeable._transfer(sender, recipient, amount);\n }\n}\n" + }, + "contracts/libraries/VaultLifecycleTreasury.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {ShareMath} from \"./ShareMath.sol\";\nimport {IStrikeSelection} from \"../interfaces/IRibbon.sol\";\nimport {GnosisAuction} from \"./GnosisAuction.sol\";\nimport {DateTime} from \"./DateTime.sol\";\nimport {\n IOtokenFactory,\n IOtoken,\n IController,\n GammaTypes\n} from \"../interfaces/GammaInterface.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\nimport {IGnosisAuction} from \"../interfaces/IGnosisAuction.sol\";\nimport {SupportsNonCompliantERC20} from \"./SupportsNonCompliantERC20.sol\";\n\nlibrary VaultLifecycleTreasury {\n using SafeMath for uint256;\n using SupportsNonCompliantERC20 for IERC20;\n\n struct CloseParams {\n address OTOKEN_FACTORY;\n address USDC;\n address currentOption;\n uint256 delay;\n uint16 lastStrikeOverrideRound;\n uint256 overriddenStrikePrice;\n uint256 period;\n }\n\n /**\n * @notice Initialization parameters for the vault.\n * @param _owner is the owner of the vault with critical permissions\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n * @param _premiumDiscount is the vault's discount applied to the premium\n * @param _auctionDuration is the duration of the gnosis auction\n * @param _period is the period between each option sales\n */\n struct InitParams {\n address _owner;\n address _keeper;\n address _feeRecipient;\n uint256 _managementFee;\n uint256 _performanceFee;\n string _tokenName;\n string _tokenSymbol;\n address _optionsPremiumPricer;\n address _strikeSelection;\n uint32 _premiumDiscount;\n uint256 _auctionDuration;\n uint256 _period;\n uint256 _maxDepositors;\n uint256 _minDeposit;\n }\n\n /**\n * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction\n * @param strikeSelection is the address of the contract with strike selection logic\n * @param optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param premiumDiscount is the vault's discount applied to the premium\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @return otokenAddress is the address of the new option\n * @return premium is the premium of the new option\n * @return strikePrice is the strike price of the new option\n * @return delta is the delta of the new option\n */\n function commitAndClose(\n address strikeSelection,\n address optionsPremiumPricer,\n uint256 premiumDiscount,\n CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n Vault.VaultState storage vaultState\n )\n external\n returns (\n address otokenAddress,\n uint256 premium,\n uint256 strikePrice,\n uint256 delta\n )\n {\n uint256 expiry;\n\n // uninitialized state\n if (closeParams.currentOption == address(0)) {\n expiry = getNextExpiry(block.timestamp, closeParams.period);\n } else {\n expiry = getNextExpiry(\n IOtoken(closeParams.currentOption).expiryTimestamp(),\n closeParams.period\n );\n }\n\n IStrikeSelection selection = IStrikeSelection(strikeSelection);\n\n bool isPut = vaultParams.isPut;\n address underlying = vaultParams.underlying;\n address asset = vaultParams.asset;\n\n (strikePrice, delta) = closeParams.lastStrikeOverrideRound ==\n vaultState.round\n ? (closeParams.overriddenStrikePrice, 0)\n : selection.getStrikePrice(expiry, isPut);\n\n require(strikePrice != 0, \"!strikePrice\");\n\n // retrieve address if option already exists, or deploy it\n otokenAddress = getOrDeployOtoken(\n closeParams,\n vaultParams,\n underlying,\n asset,\n strikePrice,\n expiry,\n isPut\n );\n\n // get the black scholes premium of the option\n premium = GnosisAuction.getOTokenPremiumInStables(\n otokenAddress,\n optionsPremiumPricer,\n premiumDiscount\n );\n\n require(premium > 0, \"!premium\");\n\n return (otokenAddress, premium, strikePrice, delta);\n }\n\n /**\n * @notice Verify the otoken has the correct parameters to prevent vulnerability to opyn contract changes\n * @param otokenAddress is the address of the otoken\n * @param vaultParams is the struct with vault general data\n * @param collateralAsset is the address of the collateral asset\n * @param USDC is the address of usdc\n * @param delay is the delay between commitAndClose and rollToNextOption\n */\n function verifyOtoken(\n address otokenAddress,\n Vault.VaultParams storage vaultParams,\n address collateralAsset,\n address USDC,\n uint256 delay\n ) private view {\n require(otokenAddress != address(0), \"!otokenAddress\");\n\n IOtoken otoken = IOtoken(otokenAddress);\n require(otoken.isPut() == vaultParams.isPut, \"Type mismatch\");\n require(\n otoken.underlyingAsset() == vaultParams.underlying,\n \"Wrong underlyingAsset\"\n );\n require(\n otoken.collateralAsset() == collateralAsset,\n \"Wrong collateralAsset\"\n );\n\n // we just assume all options use USDC as the strike\n require(otoken.strikeAsset() == USDC, \"strikeAsset != USDC\");\n\n uint256 readyAt = block.timestamp.add(delay);\n require(otoken.expiryTimestamp() >= readyAt, \"Expiry before delay\");\n }\n\n /**\n * @param currentShareSupply is the supply of the shares invoked with totalSupply()\n * @param asset is the address of the vault's asset\n * @param decimals is the decimals of the asset\n * @param lastQueuedWithdrawAmount is the amount queued for withdrawals from last round\n * @param managementFee is the management fee percent to charge on the AUM\n */\n struct RolloverParams {\n uint256 decimals;\n uint256 totalBalance;\n uint256 currentShareSupply;\n uint256 lastQueuedWithdrawAmount;\n uint256 managementFee;\n }\n\n /**\n * @notice Calculate the shares to mint, new price per share, and\n amount of funds to re-allocate as collateral for the new round\n * @param vaultState is the storage variable vaultState passed from RibbonVault\n * @param params is the rollover parameters passed to compute the next state\n * @return newLockedAmount is the amount of funds to allocate for the new round\n * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal\n * @return newPricePerShare is the price per share of the new round\n * @return mintShares is the amount of shares to mint from deposits\n * @return managementFeeInAsset is the amount of management fee charged by vault\n */\n function rollover(\n Vault.VaultState storage vaultState,\n RolloverParams calldata params\n )\n external\n view\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares,\n uint256 managementFeeInAsset\n )\n {\n uint256 currentBalance = params.totalBalance;\n uint256 pendingAmount = vaultState.totalPending;\n uint256 queuedWithdrawShares = vaultState.queuedWithdrawShares;\n\n uint256 balanceForVaultFees;\n {\n uint256 pricePerShareBeforeFee =\n ShareMath.pricePerShare(\n params.currentShareSupply,\n currentBalance,\n pendingAmount,\n params.decimals\n );\n\n uint256 queuedWithdrawBeforeFee =\n params.currentShareSupply > 0\n ? ShareMath.sharesToAsset(\n queuedWithdrawShares,\n pricePerShareBeforeFee,\n params.decimals\n )\n : 0;\n\n // Deduct the difference between the newly scheduled withdrawals\n // and the older withdrawals\n // so we can charge them fees before they leave\n uint256 withdrawAmountDiff =\n queuedWithdrawBeforeFee > params.lastQueuedWithdrawAmount\n ? queuedWithdrawBeforeFee.sub(\n params.lastQueuedWithdrawAmount\n )\n : 0;\n\n balanceForVaultFees = currentBalance\n .sub(queuedWithdrawBeforeFee)\n .add(withdrawAmountDiff);\n }\n\n managementFeeInAsset = getManagementFee(\n balanceForVaultFees,\n vaultState.totalPending,\n params.managementFee\n );\n\n // Take into account the fee\n // so we can calculate the newPricePerShare\n currentBalance = currentBalance.sub(managementFeeInAsset);\n\n {\n newPricePerShare = ShareMath.pricePerShare(\n params.currentShareSupply,\n currentBalance,\n pendingAmount,\n params.decimals\n );\n\n // After closing the short, if the options expire in-the-money\n // vault pricePerShare would go down because vault's asset balance decreased.\n // This ensures that the newly-minted shares do not take on the loss.\n mintShares = ShareMath.assetToShares(\n pendingAmount,\n newPricePerShare,\n params.decimals\n );\n\n uint256 newSupply = params.currentShareSupply.add(mintShares);\n\n queuedWithdrawAmount = newSupply > 0\n ? ShareMath.sharesToAsset(\n queuedWithdrawShares,\n newPricePerShare,\n params.decimals\n )\n : 0;\n }\n\n return (\n currentBalance.sub(queuedWithdrawAmount), // new locked balance subtracts the queued withdrawals\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n managementFeeInAsset\n );\n }\n\n /**\n * @notice Creates the actual Opyn short position by depositing collateral and minting otokens\n * @param gammaController is the address of the opyn controller contract\n * @param marginPool is the address of the opyn margin contract which holds the collateral\n * @param oTokenAddress is the address of the otoken to mint\n * @param depositAmount is the amount of collateral to deposit\n * @return the otoken mint amount\n */\n function createShort(\n address gammaController,\n address marginPool,\n address oTokenAddress,\n uint256 depositAmount\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n uint256 newVaultID =\n (controller.getAccountVaultCounter(address(this))).add(1);\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IOtoken oToken = IOtoken(oTokenAddress);\n address collateralAsset = oToken.collateralAsset();\n\n uint256 collateralDecimals =\n uint256(IERC20Detailed(collateralAsset).decimals());\n uint256 mintAmount;\n\n if (oToken.isPut()) {\n // For minting puts, there will be instances where the full depositAmount will not be used for minting.\n // This is because of an issue with precision.\n //\n // For ETH put options, we are calculating the mintAmount (10**8 decimals) using\n // the depositAmount (10**18 decimals), which will result in truncation of decimals when scaling down.\n // As a result, there will be tiny amounts of dust left behind in the Opyn vault when minting put otokens.\n //\n // For simplicity's sake, we do not refund the dust back to the address(this) on minting otokens.\n // We retain the dust in the vault so the calling contract can withdraw the\n // actual locked amount + dust at settlement.\n //\n // To test this behavior, we can console.log\n // MarginCalculatorInterface(0x7A48d10f372b3D7c60f6c9770B91398e4ccfd3C7).getExcessCollateral(vault)\n // to see how much dust (or excess collateral) is left behind.\n mintAmount = depositAmount\n .mul(10**Vault.OTOKEN_DECIMALS)\n .mul(10**18) // we use 10**18 to give extra precision\n .div(oToken.strikePrice().mul(10**(10 + collateralDecimals)));\n } else {\n mintAmount = depositAmount;\n\n if (collateralDecimals > 8) {\n uint256 scaleBy = 10**(collateralDecimals.sub(8)); // oTokens have 8 decimals\n if (mintAmount > scaleBy) {\n mintAmount = depositAmount.div(scaleBy); // scale down from 10**18 to 10**8\n }\n }\n }\n\n // double approve to fix non-compliant ERC20s\n IERC20 collateralToken = IERC20(collateralAsset);\n collateralToken.safeApproveNonCompliant(marginPool, depositAmount);\n\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](3);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.OpenVault,\n address(this), // owner\n address(this), // receiver\n address(0), // asset, otoken\n newVaultID, // vaultId\n 0, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.DepositCollateral,\n address(this), // owner\n address(this), // address to transfer from\n collateralAsset, // deposited asset\n newVaultID, // vaultId\n depositAmount, // amount\n 0, //index\n \"\" //data\n );\n\n actions[2] = IController.ActionArgs(\n IController.ActionType.MintShortOption,\n address(this), // owner\n address(this), // address to transfer to\n oTokenAddress, // option address\n newVaultID, // vaultId\n mintAmount, // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n return mintAmount;\n }\n\n /**\n * @notice Close the existing short otoken position. Currently this implementation is simple.\n * It closes the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time. Since calling `_closeShort` deletes vaults by\n calling SettleVault action, this assumption should hold.\n * @param gammaController is the address of the opyn controller contract\n * @return amount of collateral redeemed from the vault\n */\n function settleShort(address gammaController) external returns (uint256) {\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n // The short position has been previously closed, or all the otokens have been burned.\n // So we return early.\n if (address(collateralToken) == address(0)) {\n return 0;\n }\n\n // This is equivalent to doing IERC20(vault.asset).balanceOf(address(this))\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // If it is after expiry, we need to settle the short position using the normal way\n // Delete the vault and withdraw all remaining collateral from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.SettleVault,\n address(this), // owner\n address(this), // address to transfer to\n address(0), // not used\n vaultID, // vaultId\n 0, // not used\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Exercises the ITM option using existing long otoken position. Currently this implementation is simple.\n * It calls the `Redeem` action to claim the payout.\n * @param gammaController is the address of the opyn controller contract\n * @param oldOption is the address of the old option\n * @param asset is the address of the vault's asset\n * @return amount of asset received by exercising the option\n */\n function settleLong(\n address gammaController,\n address oldOption,\n address asset\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n\n uint256 oldOptionBalance = IERC20(oldOption).balanceOf(address(this));\n\n if (controller.getPayout(oldOption, oldOptionBalance) == 0) {\n return 0;\n }\n\n uint256 startAssetBalance = IERC20(asset).balanceOf(address(this));\n\n // If it is after expiry, we need to redeem the profits\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.Redeem,\n address(0), // not used\n address(this), // address to send profits to\n oldOption, // address of otoken\n 0, // not used\n oldOptionBalance, // otoken balance\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endAssetBalance = IERC20(asset).balanceOf(address(this));\n\n return endAssetBalance.sub(startAssetBalance);\n }\n\n /**\n * @notice Burn the remaining oTokens left over from auction. Currently this implementation is simple.\n * It burns oTokens from the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time.\n * @param gammaController is the address of the opyn controller contract\n * @param currentOption is the address of the current option\n * @return amount of collateral redeemed by burning otokens\n */\n function burnOtokens(address gammaController, address currentOption)\n external\n returns (uint256)\n {\n uint256 numOTokensToBurn =\n IERC20(currentOption).balanceOf(address(this));\n\n require(numOTokensToBurn > 0, \"No oTokens to burn\");\n\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // Burning `amount` of oTokens from the ribbon vault,\n // then withdrawing the corresponding collateral amount from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](2);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.BurnShortOption,\n address(this), // owner\n address(this), // address to transfer from\n address(vault.shortOtokens[0]), // otoken address\n vaultID, // vaultId\n numOTokensToBurn, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.WithdrawCollateral,\n address(this), // owner\n address(this), // address to transfer to\n address(collateralToken), // withdrawn asset\n vaultID, // vaultId\n vault.collateralAmounts[0].mul(numOTokensToBurn).div(\n vault.shortAmounts[0]\n ), // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Calculates the management fee for this week's round\n * @param currentBalance is the balance of funds held on the vault after closing short\n * @param pendingAmount is the pending deposit amount\n * @param managementFeePercent is the management fee pct.\n * @return managementFeeInAsset is the management fee\n */\n function getManagementFee(\n uint256 currentBalance,\n uint256 pendingAmount,\n uint256 managementFeePercent\n ) internal pure returns (uint256 managementFeeInAsset) {\n // At the first round, currentBalance=0, pendingAmount>0\n // so we just do not charge anything on the first round\n uint256 lockedBalanceSansPending =\n currentBalance > pendingAmount\n ? currentBalance.sub(pendingAmount)\n : 0;\n\n uint256 _managementFeeInAsset;\n\n // Always charge management fee regardless of whether the vault is\n // making a profit from the previous options sale\n _managementFeeInAsset = managementFeePercent > 0\n ? lockedBalanceSansPending.mul(managementFeePercent).div(\n 100 * Vault.FEE_MULTIPLIER\n )\n : 0;\n\n return _managementFeeInAsset;\n }\n\n /**\n * @notice Either retrieves the option token if it already exists, or deploy it\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param underlying is the address of the underlying asset of the option\n * @param collateralAsset is the address of the collateral asset of the option\n * @param strikePrice is the strike price of the option\n * @param expiry is the expiry timestamp of the option\n * @param isPut is whether the option is a put\n * @return the address of the option\n */\n function getOrDeployOtoken(\n CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n address underlying,\n address collateralAsset,\n uint256 strikePrice,\n uint256 expiry,\n bool isPut\n ) internal returns (address) {\n IOtokenFactory factory = IOtokenFactory(closeParams.OTOKEN_FACTORY);\n\n address otokenFromFactory =\n factory.getOtoken(\n underlying,\n closeParams.USDC,\n collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n if (otokenFromFactory != address(0)) {\n return otokenFromFactory;\n }\n\n address otoken =\n factory.createOtoken(\n underlying,\n closeParams.USDC,\n collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n verifyOtoken(\n otoken,\n vaultParams,\n collateralAsset,\n closeParams.USDC,\n closeParams.delay\n );\n\n return otoken;\n }\n\n /**\n * @notice Starts the gnosis auction\n * @param auctionDetails is the struct with all the custom parameters of the auction\n * @return the auction id of the newly created auction\n */\n function startAuction(GnosisAuction.AuctionDetails calldata auctionDetails)\n external\n returns (uint256)\n {\n return GnosisAuction.startAuction(auctionDetails);\n }\n\n /**\n * @notice Settles the gnosis auction\n * @param gnosisEasyAuction is the contract address of Gnosis easy auction protocol\n * @param auctionID is the auction ID of the gnosis easy auction\n */\n function settleAuction(address gnosisEasyAuction, uint256 auctionID)\n internal\n {\n IGnosisAuction(gnosisEasyAuction).settleAuction(auctionID);\n }\n\n /**\n * @notice Places a bid in an auction\n * @param bidDetails is the struct with all the details of the\n bid including the auction's id and how much to bid\n */\n function placeBid(GnosisAuction.BidDetails calldata bidDetails)\n external\n returns (\n uint256 sellAmount,\n uint256 buyAmount,\n uint64 userId\n )\n {\n return GnosisAuction.placeBid(bidDetails);\n }\n\n /**\n * @notice Claims the oTokens belonging to the vault\n * @param auctionSellOrder is the sell order of the bid\n * @param gnosisEasyAuction is the address of the gnosis auction contract\n holding custody to the funds\n * @param counterpartyThetaVault is the address of the counterparty theta\n vault of this delta vault\n */\n function claimAuctionOtokens(\n Vault.AuctionSellOrder calldata auctionSellOrder,\n address gnosisEasyAuction,\n address counterpartyThetaVault\n ) external {\n GnosisAuction.claimAuctionOtokens(\n auctionSellOrder,\n gnosisEasyAuction,\n counterpartyThetaVault\n );\n }\n\n /**\n * @notice Verify the constructor params satisfy requirements\n * @param _initParams is the initialization parameter including owner, keeper, etc.\n * @param _vaultParams is the struct with vault general data\n */\n function verifyInitializerParams(\n InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams,\n uint256 _min_auction_duration\n ) external pure {\n require(_initParams._owner != address(0), \"!_owner\");\n require(_initParams._keeper != address(0), \"!_keeper\");\n require(_initParams._feeRecipient != address(0), \"!_feeRecipient\");\n require(\n _initParams._performanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"performanceFee >= 100%\"\n );\n require(\n _initParams._managementFee < 100 * Vault.FEE_MULTIPLIER,\n \"managementFee >= 100%\"\n );\n require(bytes(_initParams._tokenName).length > 0, \"!_tokenName\");\n require(bytes(_initParams._tokenSymbol).length > 0, \"!_tokenSymbol\");\n require(\n (_initParams._period == 7) ||\n (_initParams._period == 14) ||\n (_initParams._period == 30) ||\n (_initParams._period == 90) ||\n (_initParams._period == 180),\n \"!_period\"\n );\n require(\n _initParams._optionsPremiumPricer != address(0),\n \"!_optionsPremiumPricer\"\n );\n require(\n _initParams._strikeSelection != address(0),\n \"!_strikeSelection\"\n );\n require(\n _initParams._premiumDiscount > 0 &&\n _initParams._premiumDiscount <\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"!_premiumDiscount\"\n );\n require(\n _initParams._auctionDuration >= _min_auction_duration,\n \"!_auctionDuration\"\n );\n require(_initParams._maxDepositors > 0, \"!_maxDepositors\");\n require(_initParams._minDeposit > 0, \"!_minDeposit\");\n\n require(_vaultParams.asset != address(0), \"!asset\");\n require(_vaultParams.underlying != address(0), \"!underlying\");\n require(_vaultParams.minimumSupply > 0, \"!minimumSupply\");\n require(_vaultParams.cap > 0, \"!cap\");\n require(\n _vaultParams.cap > _vaultParams.minimumSupply,\n \"cap has to be higher than minimumSupply\"\n );\n }\n\n /**\n * @notice Gets the next options expiry timestamp, this function should be called\n when there is sufficient guard to ensure valid period\n * @param timestamp is the expiry timestamp of the current option\n * @param period is no. of days in between option sales. Available periods are: \n * 7(1w), 14(2w), 30(1m), 90(3m), 180(6m)\n */\n function getNextExpiry(uint256 timestamp, uint256 period)\n internal\n pure\n returns (uint256 nextExpiry)\n {\n if (period == 7) {\n nextExpiry = DateTime.getNextFriday(timestamp);\n nextExpiry = nextExpiry <= timestamp\n ? nextExpiry + 1 weeks\n : nextExpiry;\n } else if (period == 14) {\n nextExpiry = DateTime.getNextFriday(timestamp);\n nextExpiry = nextExpiry <= timestamp\n ? nextExpiry + 2 weeks\n : nextExpiry;\n } else if (period == 30) {\n nextExpiry = DateTime.getMonthLastFriday(timestamp);\n nextExpiry = nextExpiry <= timestamp\n ? DateTime.getMonthLastFriday(nextExpiry + 1 weeks)\n : nextExpiry;\n } else if (period == 90) {\n nextExpiry = DateTime.getQuarterLastFriday(timestamp);\n nextExpiry = nextExpiry <= timestamp\n ? DateTime.getQuarterLastFriday(nextExpiry + 1 weeks)\n : nextExpiry;\n } else if (period == 180) {\n nextExpiry = DateTime.getBiannualLastFriday(timestamp);\n nextExpiry = nextExpiry <= timestamp\n ? DateTime.getBiannualLastFriday(nextExpiry + 1 weeks)\n : nextExpiry;\n }\n\n nextExpiry = nextExpiry - (nextExpiry % (24 hours)) + (8 hours);\n }\n}\n" + }, + "contracts/storage/RibbonTreasuryVaultStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {Vault} from \"../libraries/Vault.sol\";\n\nabstract contract RibbonTreasuryVaultStorageV1 {\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Mapping of depositors in the vault\n mapping(address => bool) public depositorsMap;\n\n /// @notice Array of depositors in the vault\n address[] public depositorsArray;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Logic contract used to price options\n address public optionsPremiumPricer;\n\n /// @notice Logic contract used to select strike prices\n address public strikeSelection;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n /// @notice Premium discount on options we are selling (thousandths place: 000 - 999)\n uint256 public premiumDiscount;\n\n /// @notice Current oToken premium\n uint256 public currentOtokenPremium;\n\n /// @notice Price last overridden strike set to\n uint256 public overriddenStrikePrice;\n\n /// @notice Auction duration\n uint256 public auctionDuration;\n\n /// @notice Auction id of current option\n uint256 public optionAuctionID;\n\n /// @notice Amount locked for scheduled withdrawals last week;\n uint256 public lastQueuedWithdrawAmount;\n\n /// @notice Period between each options sale.\n /// Available options 7 (weekly), 14 (biweekly), 30 (monthly), 90 (quarterly), 180 (biannually)\n uint256 public period;\n\n /// @notice Maximum number of depositors\n uint256 public maxDepositors;\n\n /// @notice Minimum amount to deposit\n uint256 public minDeposit;\n\n /// @notice Last round id at which the strike was manually overridden\n uint16 public lastStrikeOverrideRound;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of RibbonTreasuryVaultStorage\n// e.g. RibbonTreasuryVaultStorage, so finally it would look like\n// contract RibbonTreasuryVaultStorage is RibbonTreasuryVaultStorageV1, RibbonTreasuryVaultStorageV2\nabstract contract RibbonTreasuryVaultStorage is RibbonTreasuryVaultStorageV1 {\n\n}\n" + }, + "contracts/libraries/DateTime.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Source: https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary\n// ----------------------------------------------------------------------------\n// BokkyPooBah's DateTime Library v1.01\n// ----------------------------------------------------------------------------\n\npragma solidity =0.8.4;\n\nlibrary DateTime {\n uint256 constant SECONDS_PER_DAY = 24 * 60 * 60;\n uint256 constant SECONDS_PER_HOUR = 60 * 60;\n uint256 constant SECONDS_PER_MINUTE = 60;\n int256 constant OFFSET19700101 = 2440588;\n\n uint256 constant DOW_MON = 1;\n uint256 constant DOW_TUE = 2;\n uint256 constant DOW_WED = 3;\n uint256 constant DOW_THU = 4;\n uint256 constant DOW_FRI = 5;\n uint256 constant DOW_SAT = 6;\n uint256 constant DOW_SUN = 7;\n\n // ------------------------------------------------------------------------\n // Calculate the number of days from 1970/01/01 to year/month/day using\n // the date conversion algorithm from\n // http://aa.usno.navy.mil/faq/docs/JD_Formula.php\n // and subtracting the offset 2440588 so that 1970/01/01 is day 0\n //\n // days = day\n // - 32075\n // + 1461 * (year + 4800 + (month - 14) / 12) / 4\n // + 367 * (month - 2 - (month - 14) / 12 * 12) / 12\n // - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4\n // - offset\n // ------------------------------------------------------------------------\n function _daysFromDate(\n uint256 year,\n uint256 month,\n uint256 day\n ) internal pure returns (uint256 _days) {\n require(year >= 1970);\n int256 _year = int256(year);\n int256 _month = int256(month);\n int256 _day = int256(day);\n\n int256 __days =\n _day -\n 32075 +\n (1461 * (_year + 4800 + (_month - 14) / 12)) /\n 4 +\n (367 * (_month - 2 - ((_month - 14) / 12) * 12)) /\n 12 -\n (3 * ((_year + 4900 + (_month - 14) / 12) / 100)) /\n 4 -\n OFFSET19700101;\n\n _days = uint256(__days);\n }\n\n // ------------------------------------------------------------------------\n // Calculate year/month/day from the number of days since 1970/01/01 using\n // the date conversion algorithm from\n // http://aa.usno.navy.mil/faq/docs/JD_Formula.php\n // and adding the offset 2440588 so that 1970/01/01 is day 0\n //\n // int L = days + 68569 + offset\n // int N = 4 * L / 146097\n // L = L - (146097 * N + 3) / 4\n // year = 4000 * (L + 1) / 1461001\n // L = L - 1461 * year / 4 + 31\n // month = 80 * L / 2447\n // dd = L - 2447 * month / 80\n // L = month / 11\n // month = month + 2 - 12 * L\n // year = 100 * (N - 49) + year + L\n // ------------------------------------------------------------------------\n function _daysToDate(uint256 _days)\n internal\n pure\n returns (\n uint256 year,\n uint256 month,\n uint256 day\n )\n {\n int256 __days = int256(_days);\n\n int256 L = __days + 68569 + OFFSET19700101;\n int256 N = (4 * L) / 146097;\n L = L - (146097 * N + 3) / 4;\n int256 _year = (4000 * (L + 1)) / 1461001;\n L = L - (1461 * _year) / 4 + 31;\n int256 _month = (80 * L) / 2447;\n int256 _day = L - (2447 * _month) / 80;\n L = _month / 11;\n _month = _month + 2 - 12 * L;\n _year = 100 * (N - 49) + _year + L;\n\n year = uint256(_year);\n month = uint256(_month);\n day = uint256(_day);\n }\n\n function isLeapYear(uint256 timestamp)\n internal\n pure\n returns (bool leapYear)\n {\n (uint256 year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);\n leapYear = _isLeapYear(year);\n }\n\n function _isLeapYear(uint256 year) internal pure returns (bool leapYear) {\n leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);\n }\n\n function getDaysInMonth(uint256 timestamp)\n internal\n pure\n returns (uint256 daysInMonth)\n {\n (uint256 year, uint256 month, ) =\n _daysToDate(timestamp / SECONDS_PER_DAY);\n daysInMonth = _getDaysInMonth(year, month);\n }\n\n function _getDaysInMonth(uint256 year, uint256 month)\n internal\n pure\n returns (uint256 daysInMonth)\n {\n if (\n month == 1 ||\n month == 3 ||\n month == 5 ||\n month == 7 ||\n month == 8 ||\n month == 10 ||\n month == 12\n ) {\n daysInMonth = 31;\n } else if (month != 2) {\n daysInMonth = 30;\n } else {\n daysInMonth = _isLeapYear(year) ? 29 : 28;\n }\n }\n\n // 1 = Monday, 7 = Sunday\n function getDayOfWeek(uint256 timestamp)\n internal\n pure\n returns (uint256 dayOfWeek)\n {\n uint256 _days = timestamp / SECONDS_PER_DAY;\n dayOfWeek = ((_days + 3) % 7) + 1;\n }\n\n function getYear(uint256 timestamp) internal pure returns (uint256 year) {\n (year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);\n }\n\n function getMonth(uint256 timestamp) internal pure returns (uint256 month) {\n (, month, ) = _daysToDate(timestamp / SECONDS_PER_DAY);\n }\n\n function getDay(uint256 timestamp) internal pure returns (uint256 day) {\n (, , day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n }\n\n function timestampFromDate(\n uint256 year,\n uint256 month,\n uint256 day\n ) internal pure returns (uint256 timestamp) {\n timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY;\n }\n\n /**\n * @notice Gets the Friday of the same week\n * @param timestamp is the given date and time\n * @return the Friday of the same week in unix time\n */\n function getThisWeekFriday(uint256 timestamp)\n internal\n pure\n returns (uint256)\n {\n return timestamp + 5 days - getDayOfWeek(timestamp) * 1 days;\n }\n\n /**\n * @notice Gets the next friday after the given date and time\n * @param timestamp is the given date and time\n * @return the next friday after the given date and time\n */\n function getNextFriday(uint256 timestamp) internal pure returns (uint256) {\n uint256 friday = getThisWeekFriday(timestamp);\n return friday >= timestamp ? friday : friday + 1 weeks;\n }\n\n /**\n * @notice Gets the last day of the month\n * @param timestamp is the given date and time\n * @return the last day of the same month in unix time\n */\n function getLastDayOfMonth(uint256 timestamp)\n internal\n pure\n returns (uint256)\n {\n return\n timestampFromDate(getYear(timestamp), getMonth(timestamp) + 1, 1) -\n 1 days;\n }\n\n /**\n * @notice Gets the last Friday of the month\n * @param timestamp is the given date and time\n * @return the last Friday of the same month in unix time\n */\n function getMonthLastFriday(uint256 timestamp)\n internal\n pure\n returns (uint256)\n {\n uint256 lastDay = getLastDayOfMonth(timestamp);\n uint256 friday = getThisWeekFriday(lastDay);\n\n return friday > lastDay ? friday - 1 weeks : friday;\n }\n\n /**\n * @notice Gets the last Friday of the quarter\n * @param timestamp is the given date and time\n * @return the last Friday of the quarter in unix time\n */\n function getQuarterLastFriday(uint256 timestamp)\n internal\n pure\n returns (uint256)\n {\n uint256 month = getMonth(timestamp);\n uint256 quarterMonth =\n (month <= 3) ? 3 : (month <= 6) ? 6 : (month <= 9) ? 9 : 12;\n\n uint256 quarterDate =\n timestampFromDate(getYear(timestamp), quarterMonth, 1);\n\n return getMonthLastFriday(quarterDate);\n }\n\n /**\n * @notice Gets the last Friday of the half-year\n * @param timestamp is the given date and time\n * @return the last friday of the half-year\n */\n function getBiannualLastFriday(uint256 timestamp)\n internal\n pure\n returns (uint256)\n {\n uint256 month = getMonth(timestamp);\n uint256 biannualMonth = (month <= 6) ? 6 : 12;\n\n uint256 biannualDate =\n timestampFromDate(getYear(timestamp), biannualMonth, 1);\n\n return getMonthLastFriday(biannualDate);\n }\n}\n" + }, + "contracts/utils/Swap.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Source: https://github.com/airswap/airswap-protocols/blob/main/source/swap/contracts/Swap.sol\n\npragma solidity =0.8.4;\n\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"../interfaces/ISwap.sol\";\nimport \"../storage/SwapStorage.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\n\ncontract Swap is\n ISwap,\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n SwapStorage\n{\n using SafeERC20 for IERC20;\n\n uint256 public immutable DOMAIN_CHAIN_ID;\n\n bytes32 public constant DOMAIN_TYPEHASH =\n keccak256(\n abi.encodePacked(\n \"EIP712Domain(\",\n \"string name,\",\n \"string version,\",\n \"uint256 chainId,\",\n \"address verifyingContract\",\n \")\"\n )\n );\n\n bytes32 public constant BID_TYPEHASH =\n keccak256(\n abi.encodePacked(\n \"Bid(\",\n \"uint256 swapId,\",\n \"uint256 nonce,\",\n \"address signerWallet,\",\n \"uint256 sellAmount,\",\n \"uint256 buyAmount,\",\n \"address referrer\",\n \")\"\n )\n );\n\n uint256 internal constant MAX_PERCENTAGE = 10000;\n uint256 internal constant MAX_FEE = 1000;\n uint256 internal constant MAX_ERROR_COUNT = 10;\n uint256 internal constant OTOKEN_DECIMALS = 8;\n\n /************************************************\n * CONSTRUCTOR\n ***********************************************/\n\n constructor() {\n uint256 currentChainId = getChainId();\n DOMAIN_CHAIN_ID = currentChainId;\n }\n\n /************************************************\n * INITIALIZATION\n ***********************************************/\n\n function initialize(\n string memory _domainName,\n string memory _domainVersion,\n address _owner\n ) external initializer {\n require(bytes(_domainName).length > 0, \"!_domainName\");\n require(bytes(_domainVersion).length > 0, \"!_domainVersion\");\n require(_owner != address(0), \"!_owner\");\n\n __ReentrancyGuard_init();\n __Ownable_init();\n transferOwnership(_owner);\n\n DOMAIN_NAME = keccak256(bytes(_domainName));\n DOMAIN_VERSION = keccak256(bytes(_domainVersion));\n DOMAIN_SEPARATOR = keccak256(\n abi.encode(\n DOMAIN_TYPEHASH,\n DOMAIN_NAME,\n DOMAIN_VERSION,\n DOMAIN_CHAIN_ID,\n this\n )\n );\n }\n\n /************************************************\n * SETTER\n ***********************************************/\n\n /**\n * @notice Sets the referral fee for a specific referrer\n * @param referrer is the address of the referrer\n * @param fee is the fee in percent in 2 decimals\n */\n function setFee(address referrer, uint256 fee) external onlyOwner {\n require(referrer != address(0), \"Referrer cannot be the zero address\");\n require(fee < MAX_FEE, \"Fee exceeds maximum\");\n\n referralFees[referrer] = fee;\n\n emit SetFee(referrer, fee);\n }\n\n /************************************************\n * OFFER CREATION AND SETTLEMENT\n ***********************************************/\n\n /**\n * @notice Create a new offer available for swap\n * @param oToken token offered by seller\n * @param biddingToken token asked by seller\n * @param minPrice minimum price of oToken denominated in biddingToken\n * @param minBidSize minimum amount of oToken requested in a single bid\n * @param totalSize amount of oToken offered by seller\n */\n function createOffer(\n address oToken,\n address biddingToken,\n uint96 minPrice,\n uint96 minBidSize,\n uint128 totalSize\n ) external override returns (uint256 swapId) {\n require(oToken != address(0), \"oToken cannot be the zero address\");\n require(\n biddingToken != address(0),\n \"BiddingToken cannot be the zero address\"\n );\n require(minPrice > 0, \"MinPrice must be larger than zero\");\n require(minBidSize > 0, \"MinBidSize must be larger than zero\");\n require(minBidSize <= totalSize, \"MinBidSize exceeds total size\");\n\n offersCounter += 1;\n\n swapId = offersCounter;\n\n swapOffers[swapId].seller = msg.sender;\n swapOffers[swapId].oToken = oToken;\n swapOffers[swapId].biddingToken = biddingToken;\n swapOffers[swapId].minBidSize = minBidSize;\n swapOffers[swapId].minPrice = minPrice;\n swapOffers[swapId].totalSize = totalSize;\n swapOffers[swapId].availableSize = totalSize;\n // We warm the storage slot with 1 wei so we avoid a cold SSTORE\n swapOffers[swapId].totalSales = 1;\n\n emit NewOffer(\n swapId,\n msg.sender,\n oToken,\n biddingToken,\n minPrice,\n minBidSize,\n totalSize\n );\n }\n\n /**\n * @notice Settles the swap offering by iterating through the bids\n * @param swapId unique identifier of the swap offer\n * @param bids bids for swaps\n */\n function settleOffer(uint256 swapId, Bid[] calldata bids)\n external\n override\n nonReentrant\n {\n Offer storage offer = swapOffers[swapId];\n\n address seller = offer.seller;\n require(\n seller == msg.sender,\n \"Only seller can settle or offer doesn't exist\"\n );\n require(offer.availableSize > 0, \"Offer fully settled\");\n\n uint256 totalSales;\n OfferDetails memory offerDetails;\n offerDetails.seller = seller;\n offerDetails.oToken = offer.oToken;\n offerDetails.biddingToken = offer.biddingToken;\n offerDetails.minPrice = offer.minPrice;\n offerDetails.minBidSize = offer.minBidSize;\n\n for (uint256 i = 0; i < bids.length; i++) {\n require(\n swapId == bids[i].swapId,\n \"Offer and bid swapId mismatched\"\n );\n\n _swap(offerDetails, offer, bids[i]);\n totalSales += bids[i].sellAmount;\n }\n\n bool fullySettled = offer.availableSize == 0;\n\n // Deduct the initial 1 wei offset if offer is fully settled\n offer.totalSales += totalSales - (fullySettled ? 1 : 0);\n\n if (fullySettled) {\n offer.seller = address(0);\n offer.oToken = address(0);\n offer.biddingToken = address(0);\n offer.minBidSize = 0;\n offer.minPrice = 0;\n\n emit SettleOffer(swapId);\n }\n }\n\n /**\n * @notice Authorize a signer\n * @param signer address Wallet of the signer to authorize\n * @dev Emits an Authorize event\n */\n function authorize(address signer) external override {\n require(signer != address(0), \"SIGNER_INVALID\");\n authorized[msg.sender] = signer;\n emit Authorize(signer, msg.sender);\n }\n\n /**\n * @notice Revoke the signer\n * @dev Emits a Revoke event\n */\n function revoke() external override {\n address tmp = authorized[msg.sender];\n delete authorized[msg.sender];\n emit Revoke(tmp, msg.sender);\n }\n\n /**\n * @notice Cancel one or more nonces\n * @dev Cancelled nonces are marked as used\n * @dev Emits a Cancel event\n * @dev Out of gas may occur in arrays of length > 400\n * @param nonces uint256[] List of nonces to cancel\n */\n function cancelNonce(uint256[] calldata nonces) external override {\n for (uint256 i = 0; i < nonces.length; i++) {\n uint256 nonce = nonces[i];\n if (_markNonceAsUsed(msg.sender, nonce)) {\n emit Cancel(nonce, msg.sender);\n }\n }\n }\n\n /************************************************\n * PUBLIC VIEW FUNCTIONS\n ***********************************************/\n\n /**\n * @notice Validates Swap bid for any potential errors\n * @param bid Bid struct containing bid details\n * @return tuple of error count and bytes32[] memory array of error messages\n */\n function check(Bid calldata bid)\n external\n view\n override\n returns (uint256, bytes32[] memory)\n {\n Offer memory offer = swapOffers[bid.swapId];\n require(offer.seller != address(0), \"Offer does not exist\");\n\n bytes32[] memory errors = new bytes32[](MAX_ERROR_COUNT);\n\n uint256 errCount;\n\n // Check signature\n address signatory = _getSignatory(bid);\n\n if (signatory == address(0)) {\n errors[errCount] = \"SIGNATURE_INVALID\";\n errCount++;\n }\n\n if (\n bid.signerWallet != signatory &&\n authorized[bid.signerWallet] != signatory\n ) {\n errors[errCount] = \"UNAUTHORIZED\";\n errCount++;\n }\n\n // Check nonce\n if (nonceUsed(signatory, bid.nonce)) {\n errors[errCount] = \"NONCE_ALREADY_USED\";\n errCount++;\n }\n\n // Check bid size\n if (bid.buyAmount < offer.minBidSize) {\n errors[errCount] = \"BID_TOO_SMALL\";\n errCount++;\n }\n if (bid.buyAmount > offer.availableSize) {\n errors[errCount] = \"BID_EXCEED_AVAILABLE_SIZE\";\n errCount++;\n }\n\n // Check bid price\n uint256 bidPrice =\n (bid.sellAmount * 10**OTOKEN_DECIMALS) / bid.buyAmount;\n if (bidPrice < offer.minPrice) {\n errors[errCount] = \"PRICE_TOO_LOW\";\n errCount++;\n }\n\n // Check signer allowance\n uint256 signerAllowance =\n IERC20(offer.biddingToken).allowance(\n bid.signerWallet,\n address(this)\n );\n if (signerAllowance < bid.sellAmount) {\n errors[errCount] = \"SIGNER_ALLOWANCE_LOW\";\n errCount++;\n }\n\n // Check signer balance\n uint256 signerBalance =\n IERC20(offer.biddingToken).balanceOf(bid.signerWallet);\n if (signerBalance < bid.sellAmount) {\n errors[errCount] = \"SIGNER_BALANCE_LOW\";\n errCount++;\n }\n\n // Check seller allowance\n uint256 sellerAllowance =\n IERC20(offer.oToken).allowance(offer.seller, address(this));\n if (sellerAllowance < bid.buyAmount) {\n errors[errCount] = \"SELLER_ALLOWANCE_LOW\";\n errCount++;\n }\n\n // Check seller balance\n uint256 sellerBalance = IERC20(offer.oToken).balanceOf(offer.seller);\n if (sellerBalance < bid.buyAmount) {\n errors[errCount] = \"SELLER_BALANCE_LOW\";\n errCount++;\n }\n\n return (errCount, errors);\n }\n\n /**\n * @notice Returns the average settlement price for a swap offer\n * @param swapId unique identifier of the swap offer\n */\n function averagePriceForOffer(uint256 swapId)\n external\n view\n override\n returns (uint256)\n {\n Offer storage offer = swapOffers[swapId];\n require(offer.totalSize != 0, \"Offer does not exist\");\n\n uint256 availableSize = offer.availableSize;\n\n // Deduct the initial 1 wei offset if offer is not fully settled\n uint256 adjustment = availableSize != 0 ? 1 : 0;\n\n return\n ((offer.totalSales - adjustment) * (10**8)) /\n (offer.totalSize - availableSize);\n }\n\n /**\n * @notice Returns true if the nonce has been used\n * @param signer address Address of the signer\n * @param nonce uint256 Nonce being checked\n */\n function nonceUsed(address signer, uint256 nonce)\n public\n view\n override\n returns (bool)\n {\n uint256 groupKey = nonce / 256;\n uint256 indexInGroup = nonce % 256;\n return (_nonceGroups[signer][groupKey] >> indexInGroup) & 1 == 1;\n }\n\n /************************************************\n * INTERNAL FUNCTIONS\n ***********************************************/\n\n /**\n * @notice Swap Atomic ERC20 Swap\n * @param details Details of offering\n * @param offer Offer struct containing offer details\n * @param bid Bid struct containing bid details\n */\n function _swap(\n OfferDetails memory details,\n Offer storage offer,\n Bid calldata bid\n ) internal {\n require(DOMAIN_CHAIN_ID == getChainId(), \"CHAIN_ID_CHANGED\");\n\n address signatory = _getSignatory(bid);\n\n require(signatory != address(0), \"SIGNATURE_INVALID\");\n\n if (bid.signerWallet != signatory) {\n require(authorized[bid.signerWallet] == signatory, \"UNAUTHORIZED\");\n }\n\n require(_markNonceAsUsed(signatory, bid.nonce), \"NONCE_ALREADY_USED\");\n require(\n bid.buyAmount <= offer.availableSize,\n \"BID_EXCEED_AVAILABLE_SIZE\"\n );\n require(bid.buyAmount >= details.minBidSize, \"BID_TOO_SMALL\");\n\n // Ensure min. price is met\n uint256 bidPrice =\n (bid.sellAmount * 10**OTOKEN_DECIMALS) / bid.buyAmount;\n require(bidPrice >= details.minPrice, \"PRICE_TOO_LOW\");\n\n // don't have to do a uint128 check because we already check\n // that bid.buyAmount <= offer.availableSize\n offer.availableSize -= uint128(bid.buyAmount);\n\n // Transfer token from sender to signer\n IERC20(details.oToken).safeTransferFrom(\n details.seller,\n bid.signerWallet,\n bid.buyAmount\n );\n\n // Transfer to referrer if any\n uint256 feeAmount;\n if (bid.referrer != address(0)) {\n uint256 feePercent = referralFees[bid.referrer];\n\n if (feePercent > 0) {\n feeAmount = (bid.sellAmount * feePercent) / MAX_PERCENTAGE;\n\n IERC20(details.biddingToken).safeTransferFrom(\n bid.signerWallet,\n bid.referrer,\n feeAmount\n );\n }\n }\n\n // Transfer token from signer to recipient\n IERC20(details.biddingToken).safeTransferFrom(\n bid.signerWallet,\n details.seller,\n bid.sellAmount - feeAmount\n );\n\n // Emit a Swap event\n emit Swap(\n bid.swapId,\n bid.nonce,\n bid.signerWallet,\n bid.sellAmount,\n bid.buyAmount,\n bid.referrer,\n feeAmount\n );\n }\n\n /**\n * @notice Marks a nonce as used for the given signer\n * @param signer address Address of the signer for which to mark the nonce as used\n * @param nonce uint256 Nonce to be marked as used\n * @return bool True if the nonce was not marked as used already\n */\n function _markNonceAsUsed(address signer, uint256 nonce)\n internal\n returns (bool)\n {\n uint256 groupKey = nonce / 256;\n uint256 indexInGroup = nonce % 256;\n uint256 group = _nonceGroups[signer][groupKey];\n\n // If it is already used, return false\n if ((group >> indexInGroup) & 1 == 1) {\n return false;\n }\n\n _nonceGroups[signer][groupKey] = group | (uint256(1) << indexInGroup);\n\n return true;\n }\n\n /**\n * @notice Recover the signatory from a signature\n * @param bid Bid struct containing bid details\n */\n function _getSignatory(Bid calldata bid) internal view returns (address) {\n return\n ecrecover(\n keccak256(\n abi.encodePacked(\n \"\\x19\\x01\",\n DOMAIN_SEPARATOR,\n keccak256(\n abi.encode(\n BID_TYPEHASH,\n bid.swapId,\n bid.nonce,\n bid.signerWallet,\n bid.sellAmount,\n bid.buyAmount,\n bid.referrer\n )\n )\n )\n ),\n bid.v,\n bid.r,\n bid.s\n );\n }\n\n /**\n * @notice Returns the current chainId using the chainid opcode\n * @return id uint256 The chain id\n */\n function getChainId() internal view returns (uint256 id) {\n // no-inline-assembly\n assembly {\n id := chainid()\n }\n }\n}\n" + }, + "contracts/storage/SwapStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport \"../interfaces/ISwap.sol\";\n\nabstract contract SwapStorageV1 {\n // Keccak256 of domain name for signature verification\n bytes32 public DOMAIN_NAME;\n // Keccak256 of domain version for signature verification\n bytes32 public DOMAIN_VERSION;\n // Keccak256 of abi-encoded domain parameters for signature verification\n bytes32 public DOMAIN_SEPARATOR;\n\n // Counter to keep track number of offers\n uint256 public offersCounter;\n // Mapping of swap offer details for a given swapId\n mapping(uint256 => ISwap.Offer) public swapOffers;\n // Mapping of referral fees for a given address\n mapping(address => uint256) public referralFees;\n // Mapping of authorized delegate for a given address\n mapping(address => address) public authorized;\n /**\n * @notice Double mapping of signers to nonce groups to nonce states\n * @dev The nonce group is computed as nonce / 256, so each group of 256 sequential nonces uses the same key\n * @dev The nonce states are encoded as 256 bits, for each nonce in the group 0 means available and 1 means used\n */\n mapping(address => mapping(uint256 => uint256)) internal _nonceGroups;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of SwapStorage\n// e.g. SwapStorage, so finally it would look like\n// contract SwapStorage is SwapStorageV1, SwapStorageV2\nabstract contract SwapStorage is SwapStorageV1 {\n\n}\n" + }, + "contracts/vaults/STETHVault/base/RibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\n\nimport {Vault} from \"../../../libraries/Vault.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../../libraries/VaultLifecycleWithSwap.sol\";\nimport {VaultLifecycleSTETH} from \"../../../libraries/VaultLifecycleSTETH.sol\";\nimport {ShareMath} from \"../../../libraries/ShareMath.sol\";\nimport {IWSTETH} from \"../../../interfaces/ISTETH.sol\";\n\ncontract RibbonVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n /// @notice wstETH vault contract\n IWSTETH public immutable collateralToken;\n\n // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure.\n uint256[30] private ____gap;\n\n // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE\n // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaSTETHVaultStorage\n // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice Deprecated: 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n /// @notice 7 day period between each options sale.\n uint256 public constant PERIOD = 7 days;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // SWAP_CONTRACT is a contract for settling bids via signed messages\n // https://github.com/ribbon-finance/ribbon-v2/blob/master/contracts/utils/Swap.sol\n address public immutable SWAP_CONTRACT;\n\n /// @notice STETH contract address\n address public immutable STETH;\n\n /// @notice Lido DAO token 0x5a98fcbea516cf06857215779fd812ca3bef1b32\n address public immutable LDO;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectVaultFees(\n uint256 performanceFee,\n uint256 vaultFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _wsteth is the wstETH contract\n * @param _ldo is the LDO contract\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _wsteth,\n address _ldo,\n address _gammaController,\n address _marginPool,\n address _swapContract\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_swapContract != address(0), \"!_swapContract\");\n require(_wsteth != address(0), \"!_wsteth\");\n require(_ldo != address(0), \"!_ldo\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n\n WETH = _weth;\n USDC = _usdc;\n LDO = _ldo;\n STETH = IWSTETH(_wsteth).stETH();\n\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n SWAP_CONTRACT = _swapContract;\n collateralToken = IWSTETH(_wsteth);\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function baseInitialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) internal initializer {\n VaultLifecycleWithSwap.verifyInitializerParams(\n _owner,\n _keeper,\n _feeRecipient,\n _performanceFee,\n _managementFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_tokenName, _tokenSymbol);\n __Ownable_init();\n transferOwnership(_owner);\n\n keeper = _keeper;\n\n feeRecipient = _feeRecipient;\n performanceFee = _performanceFee;\n managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div(\n WEEKS_PER_YEAR\n );\n vaultParams = _vaultParams;\n\n uint256 assetBalance = totalBalance();\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n\n vaultState.round = 1;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n // We are dividing annualized management fee by num weeks in a year\n uint256 tmpManagementFee =\n newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n\n managementFee = tmpManagementFee;\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Deposits ETH into the contract and mint vault shares.\n */\n function depositETH() external payable nonReentrant {\n require(msg.value > 0, \"!value\");\n\n _depositFor(msg.value, msg.sender, true);\n }\n\n function depositYieldToken(uint256 amount) external {\n _depositYieldTokenFor(amount, msg.sender);\n }\n\n /**\n * @notice Deposits the `collateralAsset` into the contract and mint vault shares.\n * @param amount is the amount of `collateralAsset` to deposit\n * @param creditor is the amount of `collateralAsset` to deposit\n */\n function depositYieldTokenFor(uint256 amount, address creditor) external {\n require(creditor != address(0), \"!creditor\");\n _depositYieldTokenFor(amount, creditor);\n }\n\n function _depositYieldTokenFor(uint256 amount, address creditor)\n internal\n nonReentrant\n {\n require(amount > 0, \"!amount\");\n\n // stETH transfers suffer from an off-by-1 error\n _depositFor(amount.sub(1), creditor, false);\n\n IERC20(STETH).safeTransferFrom(msg.sender, address(this), amount);\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.\n * @notice Used for vault -> vault deposits on the user's behalf\n * @param creditor is the address that can claim/withdraw deposited amount\n */\n function depositFor(address creditor) external payable nonReentrant {\n require(msg.value > 0, \"!value\");\n require(creditor != address(0), \"!creditor\");\n\n _depositFor(msg.value, creditor, true);\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n * @param isETH is whether this is a depositETH call\n */\n function _depositFor(\n uint256 amount,\n address creditor,\n bool isETH\n ) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount =\n isETH ? totalBalance() : totalBalance().add(amount);\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n\n emit Deposit(creditor, amount, currentRound);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function _initiateWithdraw(uint256 numShares) internal {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n if (\n depositReceipts[msg.sender].amount > 0 ||\n depositReceipts[msg.sender].unredeemedShares > 0\n ) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n bool withdrawalIsSameRound = withdrawal.round == currentRound;\n\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n\n uint256 existingShares = uint256(withdrawal.shares);\n\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return amountETHOut the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n IERC20(STETH).safeTransfer(\n msg.sender,\n VaultLifecycleSTETH.withdrawStEth(\n STETH,\n address(collateralToken),\n withdrawAmount\n )\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map.\n * Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k.\n * Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0.\n * @param numRounds is the number of rounds to initialize in the map\n */\n function initRounds(uint256 numRounds) external nonReentrant {\n require(numRounds > 0, \"!numRounds\");\n\n uint256 _round = vaultState.round;\n for (uint256 i = 0; i < numRounds; i++) {\n uint256 index = _round + i;\n require(roundPricePerShare[index] == 0, \"Initialized\"); // AVOID OVERWRITING ACTUAL VALUES\n roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT;\n }\n }\n\n /**\n * @notice Helper function that performs most administrative tasks\n * such as minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @param currentQueuedWithdrawShares is the queued withdraw shares for the current round\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _closeRound(\n uint256 lastQueuedWithdrawAmount,\n uint256 currentQueuedWithdrawShares\n ) internal returns (uint256 lockedBalance, uint256 queuedWithdrawAmount) {\n address recipient = feeRecipient;\n address collateral = address(collateralToken);\n uint256 mintShares;\n uint256 performanceFeeInAsset;\n uint256 totalVaultFee;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n ) = VaultLifecycleWithSwap.closeRound(\n vaultState,\n VaultLifecycleWithSwap.CloseParams(\n vaultParams.decimals,\n totalBalance(),\n totalSupply(),\n lastQueuedWithdrawAmount,\n performanceFee,\n managementFee,\n currentQueuedWithdrawShares\n )\n );\n\n // Finalize the pricePerShare at the end of the round\n uint256 currentRound = vaultState.round;\n roundPricePerShare[currentRound] = newPricePerShare;\n\n // Wrap entire `asset` balance to `collateralToken` balance\n VaultLifecycleSTETH.wrapToYieldToken(WETH, collateral, STETH);\n\n emit CollectVaultFees(\n performanceFeeInAsset,\n totalVaultFee,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (totalVaultFee > 0) {\n VaultLifecycleSTETH.withdrawYieldAndBaseToken(\n collateral,\n WETH,\n recipient,\n totalVaultFee\n );\n }\n\n return (lockedBalance, queuedWithdrawAmount);\n }\n\n /*\n * @notice Transfers LDO rewards to feeRecipient\n */\n function sendLDORewards() external {\n IERC20 ldo = IERC20(LDO);\n ldo.safeTransfer(feeRecipient, ldo.balanceOf(address(this)));\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n external\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n // After calling closeRound, current option is set to none\n // We also commit the lockedAmount but do not deposit into Opyn\n // which results in double counting of asset balance and lockedAmount\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 ethBalance = address(this).balance;\n uint256 stethFromWsteth =\n collateralToken.getStETHByWstETH(\n collateralToken.balanceOf(address(this))\n );\n\n uint256 stEthBalance = IERC20(STETH).balanceOf(address(this));\n uint256 lockedAmount =\n optionState.currentOption != address(0)\n ? uint256(vaultState.lockedAmount)\n : 0;\n\n return\n wethBalance\n .add(lockedAmount)\n .add(ethBalance)\n .add(stethFromWsteth)\n .add(stEthBalance);\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n}\n" + }, + "contracts/libraries/VaultLifecycleSTETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {DSMath} from \"../vendor/DSMath.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {VaultLifecycle} from \"./VaultLifecycle.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {ShareMath} from \"./ShareMath.sol\";\nimport {ISTETH, IWSTETH} from \"../interfaces/ISTETH.sol\";\nimport {IWETH} from \"../interfaces/IWETH.sol\";\nimport {ICRV} from \"../interfaces/ICRV.sol\";\nimport {IStrikeSelection} from \"../interfaces/IRibbon.sol\";\nimport {\n IOtokenFactory,\n IOtoken,\n IController,\n GammaTypes\n} from \"../interfaces/GammaInterface.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\n\nlibrary VaultLifecycleSTETH {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n /**\n * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @param collateralAsset is the address of the collateral asset\n * @return otokenAddress is the address of the new option\n * @return strikePrice is the strike price of the new option\n * @return delta is the delta of the new option\n */\n function commitAndClose(\n VaultLifecycle.CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n Vault.VaultState storage vaultState,\n address collateralAsset\n )\n external\n returns (\n address otokenAddress,\n uint256 strikePrice,\n uint256 delta\n )\n {\n uint256 expiry =\n VaultLifecycle.getNextExpiry(closeParams.currentOption);\n\n IStrikeSelection selection =\n IStrikeSelection(closeParams.strikeSelection);\n\n // calculate strike and delta\n (strikePrice, delta) = closeParams.lastStrikeOverrideRound ==\n vaultState.round\n ? (closeParams.overriddenStrikePrice, selection.delta())\n : selection.getStrikePrice(expiry, false);\n\n require(strikePrice != 0, \"!strikePrice\");\n\n // retrieve address if option already exists, or deploy it\n otokenAddress = VaultLifecycle.getOrDeployOtoken(\n closeParams,\n vaultParams,\n vaultParams.underlying,\n collateralAsset,\n strikePrice,\n expiry,\n false\n );\n\n return (otokenAddress, strikePrice, delta);\n }\n\n /**\n * @notice Calculate the shares to mint, new price per share, and\n amount of funds to re-allocate as collateral for the new round\n * @param currentShareSupply is the total supply of shares\n * @param currentBalance is the total balance of the vault\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @return newLockedAmount is the amount of funds to allocate for the new round\n * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal\n * @return newPricePerShare is the price per share of the new round\n * @return mintShares is the amount of shares to mint from deposits\n */\n function rollover(\n uint256 currentShareSupply,\n uint256 currentBalance,\n Vault.VaultParams calldata vaultParams,\n Vault.VaultState calldata vaultState\n )\n external\n pure\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares\n )\n {\n uint256 pendingAmount = uint256(vaultState.totalPending);\n uint256 _decimals = vaultParams.decimals;\n\n newPricePerShare = ShareMath.pricePerShare(\n currentShareSupply,\n currentBalance,\n pendingAmount,\n _decimals\n );\n\n // After closing the short, if the options expire in-the-money\n // vault pricePerShare would go down because vault's asset balance decreased.\n // This ensures that the newly-minted shares do not take on the loss.\n uint256 _mintShares =\n ShareMath.assetToShares(pendingAmount, newPricePerShare, _decimals);\n\n uint256 newSupply = currentShareSupply.add(_mintShares);\n uint256 queuedAmount =\n newSupply > 0\n ? ShareMath.sharesToAsset(\n vaultState.queuedWithdrawShares,\n newPricePerShare,\n _decimals\n )\n : 0;\n\n return (\n currentBalance.sub(queuedAmount),\n queuedAmount,\n newPricePerShare,\n _mintShares\n );\n }\n\n /**\n * @notice Creates the actual Opyn short position by depositing collateral and minting otokens\n * @param gammaController is the address of the opyn controller contract\n * @param marginPool is the address of the opyn margin contract which holds the collateral\n * @param oTokenAddress is the address of the otoken to mint\n * @param depositAmount is the amount of collateral to deposit\n * @return the otoken mint amount\n */\n function createShort(\n address gammaController,\n address marginPool,\n address oTokenAddress,\n uint256 depositAmount\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n uint256 newVaultID =\n (controller.getAccountVaultCounter(address(this))).add(1);\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IOtoken oToken = IOtoken(oTokenAddress);\n address collateralAsset = oToken.collateralAsset();\n\n uint256 collateralDecimals =\n uint256(IERC20Detailed(collateralAsset).decimals());\n uint256 mintAmount;\n\n mintAmount = depositAmount;\n if (collateralDecimals > 8) {\n uint256 scaleBy = 10**(collateralDecimals.sub(8)); // oTokens have 8 decimals\n if (mintAmount > scaleBy) {\n mintAmount = depositAmount.div(scaleBy); // scale down from 10**18 to 10**8\n }\n }\n\n IERC20 collateralToken = IERC20(collateralAsset);\n collateralToken.safeApprove(marginPool, depositAmount);\n\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](3);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.OpenVault,\n address(this), // owner\n address(this), // receiver\n address(0), // asset, otoken\n newVaultID, // vaultId\n 0, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.DepositCollateral,\n address(this), // owner\n address(this), // address to transfer from\n collateralAsset, // deposited asset\n newVaultID, // vaultId\n depositAmount, // amount\n 0, //index\n \"\" //data\n );\n\n actions[2] = IController.ActionArgs(\n IController.ActionType.MintShortOption,\n address(this), // owner\n address(this), // address to transfer to\n oTokenAddress, // option address\n newVaultID, // vaultId\n mintAmount, // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n return mintAmount;\n }\n\n /**\n * @notice Withdraws stETH + WETH (if necessary) from vault using vault shares\n * @param collateralToken is the address of the collateral token\n * @param weth is the WETH address\n * @param recipient is the recipient\n * @param amount is the withdraw amount in `asset`\n * @return withdrawAmount is the withdraw amount in `collateralToken`\n */\n function withdrawYieldAndBaseToken(\n address collateralToken,\n address weth,\n address recipient,\n uint256 amount\n ) external returns (uint256) {\n IWSTETH collateral = IWSTETH(collateralToken);\n\n uint256 withdrawAmount = collateral.getWstETHByStETH(amount);\n\n uint256 yieldTokenBalance =\n withdrawYieldToken(collateralToken, recipient, withdrawAmount);\n\n // If there is not enough wstETH in the vault, it withdraws as much as possible and\n // transfers the rest in `asset`\n if (withdrawAmount > yieldTokenBalance) {\n withdrawBaseToken(\n collateralToken,\n weth,\n recipient,\n withdrawAmount,\n yieldTokenBalance\n );\n }\n\n return withdrawAmount;\n }\n\n /**\n * @notice Withdraws stETH from vault\n * @param collateralToken is the address of the collateral token\n * @param recipient is the recipient\n * @param withdrawAmount is the withdraw amount in terms of yearn tokens\n * @return yieldTokenBalance is the balance of the yield token\n */\n function withdrawYieldToken(\n address collateralToken,\n address recipient,\n uint256 withdrawAmount\n ) internal returns (uint256) {\n IERC20 collateral = IERC20(collateralToken);\n\n uint256 yieldTokenBalance = collateral.balanceOf(address(this));\n uint256 yieldTokensToWithdraw =\n DSMath.min(yieldTokenBalance, withdrawAmount);\n if (yieldTokensToWithdraw > 0) {\n collateral.safeTransfer(recipient, yieldTokensToWithdraw);\n }\n\n return yieldTokenBalance;\n }\n\n /**\n * @notice Withdraws `asset` from vault\n * @param collateralToken is the address of the collateral token\n * @param weth is the WETH address\n * @param recipient is the recipient\n * @param withdrawAmount is the withdraw amount in terms of yearn tokens\n * @param yieldTokenBalance is the collateral token (stETH) balance of the vault\n */\n function withdrawBaseToken(\n address collateralToken,\n address weth,\n address recipient,\n uint256 withdrawAmount,\n uint256 yieldTokenBalance\n ) internal {\n uint256 underlyingTokensToWithdraw =\n IWSTETH(collateralToken).getStETHByWstETH(\n withdrawAmount.sub(yieldTokenBalance)\n );\n\n IWETH(weth).deposit{value: underlyingTokensToWithdraw}();\n IERC20(weth).safeTransfer(recipient, underlyingTokensToWithdraw);\n }\n\n /**\n * @notice Unwraps the necessary amount of the wstETH token\n * and transfers ETH amount to vault\n * @param amount is the amount of ETH to withdraw\n * @param wstEth is the address of wstETH\n * @param stethToken is the address of stETH\n * @param crvPool is the address of the steth <-> eth pool on curve\n * @param minETHOut is the minimum eth amount to receive from the swap\n * @return amountETHOut is the amount of eth unwrapped\n available for the withdrawal (may incur curve slippage)\n */\n function unwrapYieldToken(\n uint256 amount,\n address wstEth,\n address stethToken,\n address crvPool,\n uint256 minETHOut\n ) external returns (uint256) {\n require(\n amount >= minETHOut,\n \"Amount withdrawn smaller than minETHOut from swap\"\n );\n require(\n minETHOut.mul(10**18).div(amount) >= 0.95 ether,\n \"Slippage on minETHOut too high\"\n );\n\n uint256 ethBalance = address(this).balance;\n IERC20 steth = IERC20(stethToken);\n uint256 stethBalance = steth.balanceOf(address(this));\n\n // 3 different success scenarios\n // Scenario 1. We hold enough ETH to satisfy withdrawal. Send it out directly\n // Scenario 2. We hold enough wstETH to satisy withdrawal. Unwrap then swap\n // Scenario 3. We hold enough ETH + stETH to satisfy withdrawal. Do a swap\n\n // Scenario 1\n if (ethBalance >= amount) {\n return amount;\n }\n\n // Scenario 2\n stethBalance = unwrapWstethForWithdrawal(\n wstEth,\n steth,\n ethBalance,\n stethBalance,\n amount,\n minETHOut\n );\n\n // Scenario 3\n // Now that we satisfied the ETH + stETH sum, we swap the stETH amounts necessary\n // to facilitate a withdrawal\n\n // This won't underflow since we already asserted that ethBalance < amount before this\n uint256 stEthAmountToSwap =\n DSMath.min(amount.sub(ethBalance), stethBalance);\n\n uint256 ethAmountOutFromSwap =\n swapStEthToEth(steth, crvPool, stEthAmountToSwap);\n\n uint256 totalETHOut = ethBalance.add(ethAmountOutFromSwap);\n\n // Since minETHOut is derived from calling the Curve pool's getter,\n // it reverts in the worst case where the user needs to unwrap and sell\n // 100% of their ETH withdrawal amount\n require(\n totalETHOut >= minETHOut,\n \"Output ETH amount smaller than minETHOut\"\n );\n\n return totalETHOut;\n }\n\n /**\n * @notice Unwraps the required amount of wstETH to a target ETH amount\n * @param wstEthAddress is the address for wstETH\n * @param steth is the ERC20 of stETH\n * @param startStEthBalance is the starting stETH balance used to determine how much more to unwrap\n * @param ethAmount is the ETH amount needed for the contract\n * @param minETHOut is the ETH amount but adjusted for slippage\n * @return the new stETH balance\n */\n function unwrapWstethForWithdrawal(\n address wstEthAddress,\n IERC20 steth,\n uint256 ethBalance,\n uint256 startStEthBalance,\n uint256 ethAmount,\n uint256 minETHOut\n ) internal returns (uint256) {\n uint256 ethstEthSum = ethBalance.add(startStEthBalance);\n\n if (ethstEthSum < minETHOut) {\n uint256 stethNeededFromUnwrap = ethAmount.sub(ethstEthSum);\n IWSTETH wstEth = IWSTETH(wstEthAddress);\n uint256 wstAmountToUnwrap =\n wstEth.getWstETHByStETH(stethNeededFromUnwrap);\n\n wstEth.unwrap(wstAmountToUnwrap);\n\n uint256 newStEthBalance = steth.balanceOf(address(this));\n require(\n ethBalance.add(newStEthBalance) >= minETHOut,\n \"Unwrapping wstETH did not return sufficient stETH\"\n );\n return newStEthBalance;\n }\n return startStEthBalance;\n }\n\n /**\n * @notice Swaps from stEth to ETH on the Lido Curve pool\n * @param steth is the address for the Lido staked ether\n * @param crvPool is the Curve pool address to do the swap\n * @param stEthAmount is the stEth amount to be swapped to Ether\n * @return ethAmountOutFromSwap is the returned ETH amount from swap\n */\n function swapStEthToEth(\n IERC20 steth,\n address crvPool,\n uint256 stEthAmount\n ) internal returns (uint256) {\n steth.safeApprove(crvPool, stEthAmount);\n\n // CRV SWAP HERE from steth -> eth\n // 0 = ETH, 1 = STETH\n // We are setting 1, which is the smallest possible value for the _minAmountOut parameter\n // However it is fine because we check that the totalETHOut >= minETHOut at the end\n // which makes sandwich attacks not possible\n uint256 ethAmountOutFromSwap =\n ICRV(crvPool).exchange(1, 0, stEthAmount, 1);\n\n return ethAmountOutFromSwap;\n }\n\n /**\n * @notice Wraps the necessary amount of the base token to the yield-bearing yearn token\n * @param weth is the address of weth\n * @param collateralToken is the address of the collateral token\n */\n function wrapToYieldToken(\n address weth,\n address collateralToken,\n address steth\n ) external {\n // Unwrap all weth premiums transferred to contract\n IWETH wethToken = IWETH(weth);\n uint256 wethBalance = wethToken.balanceOf(address(this));\n\n if (wethBalance > 0) {\n wethToken.withdraw(wethBalance);\n }\n\n uint256 ethBalance = address(this).balance;\n\n IWSTETH collateral = IWSTETH(collateralToken);\n IERC20 stethToken = IERC20(steth);\n\n if (ethBalance > 0) {\n // Send eth to Lido, recieve steth\n ISTETH(steth).submit{value: ethBalance}(address(this));\n }\n\n // Get all steth in contract\n uint256 stethBalance = stethToken.balanceOf(address(this));\n\n if (stethBalance > 0) {\n // approve wrap\n stethToken.safeApprove(collateralToken, stethBalance.add(1));\n // Wrap to wstETH - need to add 1 to steth balance as it is innacurate\n collateral.wrap(stethBalance.add(1));\n }\n }\n\n /**\n * @notice Gets stETH for direct stETH withdrawals, converts wstETH/ETH to stETH if not enough stETH\n * @param steth is the address of steth\n * @param wstEth is the address of wsteth\n * @param amount is the amount to withdraw\n * @return amount of stETH to transfer to the user, this is to account for rounding errors when unwrapping wstETH\n */\n function withdrawStEth(\n address steth,\n address wstEth,\n uint256 amount\n ) external returns (uint256) {\n // 3 different scenarios for withdrawing stETH directly\n // Scenario 1. We hold enough stETH to satisfy withdrawal. Send it out directly\n // Scenario 2. We hold enough stETH + wstETH to satisy withdrawal. Unwrap wstETH then send it\n // Scenario 3. We hold enough stETH + wstETH + ETH satisfy withdrawal. Unwrap wstETH, wrap ETH then send it\n uint256 _amount = amount;\n uint256 stethBalance = IERC20(steth).balanceOf(address(this));\n if (stethBalance >= amount) {\n // Can send out the stETH directly\n return amount; // We return here if we have enough stETH to satisfy the withdrawal\n } else {\n // If amount > stethBalance, send out the entire stethBalance and check wstETH and ETH\n amount = amount.sub(stethBalance);\n }\n uint256 wstethBalance = IWSTETH(wstEth).balanceOf(address(this));\n uint256 totalShares = ISTETH(steth).getTotalShares();\n uint256 totalPooledEther = ISTETH(steth).getTotalPooledEther();\n stethBalance = wstethBalance.mul(totalPooledEther).div(totalShares);\n if (stethBalance >= amount) {\n wstethBalance = amount.mul(totalShares).div(totalPooledEther);\n // Avoids reverting if unwrap amount is 0\n if (wstethBalance > 0) {\n // Unwraps wstETH and sends out the received stETH directly\n IWSTETH(wstEth).unwrap(wstethBalance);\n // Accounts for rounding errors when unwrapping wstETH, this is safe because this function would've\n // returned already if the stETH balance was greater than our withdrawal amount\n return IERC20(steth).balanceOf(address(this)); // We return here if we have enough stETH + wstETH\n }\n } else if (stethBalance > 0) {\n stethBalance = IERC20(steth).balanceOf(address(this));\n IWSTETH(wstEth).unwrap(wstethBalance);\n // Accounts for rounding errors when unwrapping wstETH\n amount = amount.sub(\n IERC20(steth).balanceOf(address(this)).sub(stethBalance)\n );\n }\n // Wrap ETH to stETH if we don't have enough stETH + wstETH\n uint256 ethBalance = address(this).balance;\n if (amount > 0 && ethBalance >= amount) {\n ISTETH(steth).submit{value: amount}(address(this));\n } else if (ethBalance > 0) {\n ISTETH(steth).submit{value: ethBalance}(address(this));\n }\n stethBalance = IERC20(steth).balanceOf(address(this));\n // Accounts for rounding errors by a margin of 3 wei\n require(_amount.add(3) >= stethBalance, \"Unwrapped too much stETH\");\n require(_amount <= stethBalance.add(3), \"Unwrapped insufficient stETH\");\n return stethBalance; // We return here if we have enough stETH + wstETH + ETH\n }\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) public {\n (bool success, ) = payable(recipient).call{value: amount}(\"\");\n require(success, \"!success\");\n }\n\n function getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount,\n address collateralToken\n ) external view returns (uint256) {\n return\n _getOTokenPremium(\n oTokenAddress,\n optionsPremiumPricer,\n premiumDiscount,\n collateralToken\n );\n }\n\n function _getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount,\n address collateralToken\n ) internal view returns (uint256) {\n IOtoken newOToken = IOtoken(oTokenAddress);\n IOptionsPremiumPricer premiumPricer =\n IOptionsPremiumPricer(optionsPremiumPricer);\n\n // Apply black-scholes formula (from rvol library) to option given its features\n // and get price for 100 contracts denominated in the underlying asset for call option\n // and USDC for put option\n uint256 optionPremium =\n premiumPricer.getPremium(\n newOToken.strikePrice(),\n newOToken.expiryTimestamp(),\n newOToken.isPut()\n );\n\n // Apply a discount to incentivize arbitraguers\n optionPremium = optionPremium.mul(premiumDiscount).div(\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER\n );\n\n // get the black scholes premium of the option and adjust premium based on\n // steth <-> eth exchange rate\n uint256 adjustedPremium =\n DSMath.wmul(\n optionPremium,\n IWSTETH(collateralToken).stEthPerToken()\n );\n\n require(\n adjustedPremium <= type(uint96).max,\n \"adjustedPremium > type(uint96) max value!\"\n );\n require(adjustedPremium > 0, \"!adjustedPremium\");\n\n return adjustedPremium;\n }\n}\n" + }, + "contracts/interfaces/ISTETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface IWSTETH {\n function getStETHByWstETH(uint256 _amount) external view returns (uint256);\n\n function getWstETHByStETH(uint256 _amount) external view returns (uint256);\n\n function stEthPerToken() external view returns (uint256);\n\n function tokensPerStEth() external view returns (uint256);\n\n function stETH() external view returns (address);\n\n function wrap(uint256 _amount) external returns (uint256);\n\n function unwrap(uint256 _amount) external returns (uint256);\n\n function approve(address _recipient, uint256 _amount)\n external\n returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function decimals() external view returns (uint256);\n}\n\ninterface ISTETH {\n function getBufferedEther(uint256 _amount) external view returns (uint256);\n\n function getPooledEthByShares(uint256 _amount)\n external\n view\n returns (uint256);\n\n function getSharesByPooledEth(uint256 _amount)\n external\n view\n returns (uint256);\n\n function submit(address _referralAddress)\n external\n payable\n returns (uint256);\n\n function withdraw(uint256 _amount, bytes32 _pubkeyHash)\n external\n returns (uint256);\n\n function approve(address _recipient, uint256 _amount)\n external\n returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function decimals() external view returns (uint256);\n\n function getTotalShares() external view returns (uint256);\n\n function getTotalPooledEther() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICRV.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface ICRV {\n function get_dy(\n int128 _indexIn,\n int128 _indexOut,\n uint256 _amountIn\n ) external view returns (uint256);\n\n // https://github.com/curvefi/curve-contract/blob/\n // b0bbf77f8f93c9c5f4e415bce9cd71f0cdee960e/contracts/pools/steth/StableSwapSTETH.vy#L431\n function exchange(\n int128 _indexIn,\n int128 _indexOut,\n uint256 _amountIn,\n uint256 _minAmountOut\n ) external returns (uint256);\n}\n" + }, + "contracts/vaults/STETHVault/RibbonThetaSTETHVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport {ISwap} from \"../../interfaces/ISwap.sol\";\nimport {\n RibbonThetaSTETHVaultStorage\n} from \"../../storage/RibbonThetaSTETHVaultStorage.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../libraries/VaultLifecycleWithSwap.sol\";\nimport {VaultLifecycleSTETH} from \"../../libraries/VaultLifecycleSTETH.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {ILiquidityGauge} from \"../../interfaces/ILiquidityGauge.sol\";\nimport {RibbonVault} from \"./base/RibbonVault.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\nimport {IWSTETH} from \"../../interfaces/ISTETH.sol\";\n\n/**\n * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe\n * the inheritance chain closely.\n * Any changes/appends in storage variable needs to happen in RibbonThetaSTETHVaultStorage.\n * RibbonThetaSTETHVault should not inherit from any other contract aside from RibbonVault, RibbonThetaSTETHVaultStorage\n */\ncontract RibbonThetaSTETHVault is RibbonVault, RibbonThetaSTETHVaultStorage {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice oTokenFactory is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event NewOffer(\n uint256 swapId,\n address seller,\n address oToken,\n address biddingToken,\n uint256 minPrice,\n uint256 minBidSize,\n uint256 totalSize\n );\n\n /************************************************\n * STRUCTS\n ***********************************************/\n\n /**\n * @notice Initialization parameters for the vault.\n * @param _owner is the owner of the vault with critical permissions\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n */\n struct InitParams {\n address _owner;\n address _keeper;\n address _feeRecipient;\n uint256 _managementFee;\n uint256 _performanceFee;\n string _tokenName;\n string _tokenSymbol;\n address _optionsPremiumPricer;\n address _strikeSelection;\n }\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _wsteth is the LDO contract\n * @param _ldo is the LDO contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _wsteth,\n address _ldo,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _swapContract\n )\n RibbonVault(\n _weth,\n _usdc,\n _wsteth,\n _ldo,\n _gammaController,\n _marginPool,\n _swapContract\n )\n {\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n OTOKEN_FACTORY = _oTokenFactory;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n * @param _initParams is the struct with vault initialization parameters\n * @param _vaultParams is the struct with vault general data\n */\n function initialize(\n InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n baseInitialize(\n _initParams._owner,\n _initParams._keeper,\n _initParams._feeRecipient,\n _initParams._managementFee,\n _initParams._performanceFee,\n _initParams._tokenName,\n _initParams._tokenSymbol,\n _vaultParams\n );\n require(\n _initParams._optionsPremiumPricer != address(0),\n \"!_optionsPremiumPricer\"\n );\n require(\n _initParams._strikeSelection != address(0),\n \"!_strikeSelection\"\n );\n\n optionsPremiumPricer = _initParams._optionsPremiumPricer;\n strikeSelection = _initParams._strikeSelection;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new strike selection or options premium pricer contract\n * @param newContract is the address of the new strike selection or options premium pricer contract\n * @param isStrikeSelection is whether we are setting the strike selection contract\n */\n function setStrikeSelectionOrPricer(\n address newContract,\n bool isStrikeSelection\n ) external onlyOwner {\n require(newContract != address(0), \"!newContract\");\n if (isStrikeSelection) {\n strikeSelection = newContract;\n } else {\n optionsPremiumPricer = newContract;\n }\n }\n\n /**\n * @notice Optionality to set strike price manually\n * Should be called after closeRound if we are setting current week's strike\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice) external onlyOwner {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Sets the new liquidityGauge contract for this vault\n * @param newLiquidityGauge is the address of the new liquidityGauge contract\n */\n function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {\n liquidityGauge = newLiquidityGauge;\n }\n\n /**\n * @notice Sets oToken Premium\n * @param minPrice is the new oToken Premium in the units of 10**18\n */\n function setMinPrice(uint256 minPrice) external onlyKeeper {\n require(minPrice > 0, \"!minPrice\");\n currentOtokenPremium = minPrice;\n }\n\n /**\n * @notice Sets the new Vault Pauser contract for this vault\n * @param newVaultPauser is the address of the new vaultPauser contract\n */\n function setVaultPauser(address newVaultPauser) external onlyOwner {\n vaultPauser = newVaultPauser;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount, uint256) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n IERC20(STETH).safeTransfer(\n msg.sender,\n VaultLifecycleSTETH.withdrawStEth(\n STETH,\n address(collateralToken),\n amount\n )\n );\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n _initiateWithdraw(numShares);\n currentQueuedWithdrawShares = currentQueuedWithdrawShares.add(\n numShares\n );\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /**\n * @notice Stakes a users vault shares\n * @param numShares is the number of shares to stake\n */\n function stake(uint256 numShares) external nonReentrant {\n address _liquidityGauge = liquidityGauge;\n require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit\n require(numShares > 0);\n uint256 heldByAccount = balanceOf(msg.sender);\n if (heldByAccount < numShares) {\n _redeem(numShares.sub(heldByAccount), false);\n }\n _transfer(msg.sender, address(this), numShares);\n _approve(address(this), _liquidityGauge, numShares);\n ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);\n }\n\n /**\n * @notice Closes the existing short and calculate the shares to mint, new price per share &\n amount of funds to re-allocate as collateral for the new round\n * Since we are incrementing the round here, the options are sold in the beginning of a round\n * instead of at the end of the round. For example, at round 1, we don't sell any options. We\n * start selling options at the beginning of round 2.\n */\n function closeRound() external nonReentrant {\n address oldOption = optionState.currentOption;\n require(\n oldOption != address(0) || vaultState.round == 1,\n \"Round closed\"\n );\n _closeShort(oldOption);\n\n uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares;\n (uint256 lockedBalance, uint256 queuedWithdrawAmount) =\n _closeRound(\n uint256(lastQueuedWithdrawAmount),\n currQueuedWithdrawShares\n );\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(\n currQueuedWithdrawShares\n );\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n currentQueuedWithdrawShares = 0;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycleWithSwap.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Sets the next option the vault will be shorting\n */\n function commitNextOption() external onlyKeeper nonReentrant {\n address currentOption = optionState.currentOption;\n require(\n currentOption == address(0) && vaultState.round != 1,\n \"Round not closed\"\n );\n\n VaultLifecycleWithSwap.CommitParams memory commitParams =\n VaultLifecycleWithSwap.CommitParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n collateralAsset: address(collateralToken),\n currentOption: currentOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n strikeSelection: strikeSelection,\n optionsPremiumPricer: optionsPremiumPricer\n });\n\n (address otokenAddress, uint256 strikePrice, uint256 delta) =\n VaultLifecycleWithSwap.commitNextOption(\n commitParams,\n vaultParams,\n vaultState\n );\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n optionState.nextOption = otokenAddress;\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position and create a new offer.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n address newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n // Locked balance denominated in `collateralToken`\n uint256 lockedBalance =\n collateralToken.balanceOf(address(this)).sub(\n collateralToken.getWstETHByStETH(lastQueuedWithdrawAmount)\n );\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n VaultLifecycleWithSwap.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n _createOffer();\n }\n\n function _createOffer() private {\n address currentOtoken = optionState.currentOption;\n uint256 currOtokenPremium = currentOtokenPremium;\n\n optionAuctionID = VaultLifecycleWithSwap.createOffer(\n currentOtoken,\n currOtokenPremium,\n SWAP_CONTRACT,\n vaultParams\n );\n }\n\n /**\n * @notice Settle current offer\n */\n function settleOffer(ISwap.Bid[] calldata bids)\n external\n onlyKeeper\n nonReentrant\n {\n ISwap(SWAP_CONTRACT).settleOffer(optionAuctionID, bids);\n }\n\n /**\n * @notice Burn the remaining oTokens left over\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n uint256 unlockedAssetAmount =\n VaultLifecycleWithSwap.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n\n vaultState.lockedAmount = uint104(\n uint256(vaultState.lockedAmount).sub(\n collateralToken.getStETHByWstETH(unlockedAssetAmount)\n )\n );\n\n // Wrap entire `asset` balance to `collateralToken` balance\n VaultLifecycleSTETH.wrapToYieldToken(\n WETH,\n address(collateralToken),\n STETH\n );\n }\n\n /**\n * @notice pause a user's vault position\n */\n function pausePosition() external {\n address _vaultPauserAddress = vaultPauser;\n require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit\n _redeem(0, true);\n uint256 heldByAccount = balanceOf(msg.sender);\n _approve(msg.sender, _vaultPauserAddress, heldByAccount);\n IVaultPauser(_vaultPauserAddress).pausePosition(\n msg.sender,\n heldByAccount\n );\n }\n}\n" + }, + "contracts/storage/RibbonThetaSTETHVaultStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nabstract contract RibbonThetaSTETHVaultStorageV1 {\n // Logic contract used to price options\n address public optionsPremiumPricer;\n // Logic contract used to select strike prices\n address public strikeSelection;\n // Premium discount on options we are selling (thousandths place: 000 - 999)\n uint256 public premiumDiscount;\n // Current oToken premium\n uint256 public currentOtokenPremium;\n // Last round id at which the strike was manually overridden\n uint16 public lastStrikeOverrideRound;\n // Price last overridden strike set to\n uint256 public overriddenStrikePrice;\n // Auction duration\n uint256 public auctionDuration;\n // Auction id of current option\n uint256 public optionAuctionID;\n}\n\nabstract contract RibbonThetaSTETHVaultStorageV2 {\n // Amount locked for scheduled withdrawals last week;\n uint256 public lastQueuedWithdrawAmount;\n}\n\nabstract contract RibbonThetaSTETHVaultStorageV3 {\n // LiquidityGauge contract for the vault\n address public liquidityGauge;\n}\n\nabstract contract RibbonThetaSTETHVaultStorageV4 {\n // Queued withdraw shares for the current round\n uint256 public currentQueuedWithdrawShares;\n}\n\nabstract contract RibbonThetaSTETHVaultStorageV5 {\n // Vault Pauser Contract for the vault\n address public vaultPauser;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of RibbonThetaVaultStorage\n// e.g. RibbonThetaVaultStorage, so finally it would look like\n// contract RibbonThetaVaultStorage is RibbonThetaVaultStorageV1, RibbonThetaVaultStorageV2\nabstract contract RibbonThetaSTETHVaultStorage is\n RibbonThetaSTETHVaultStorageV1,\n RibbonThetaSTETHVaultStorageV2,\n RibbonThetaSTETHVaultStorageV3,\n RibbonThetaSTETHVaultStorageV4,\n RibbonThetaSTETHVaultStorageV5\n{\n\n}\n" + }, + "contracts/tests/TestVaultLifecycleSTETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {ISTETH, IWSTETH} from \"../interfaces/ISTETH.sol\";\nimport {VaultLifecycleSTETH} from \"../libraries/VaultLifecycleSTETH.sol\";\n\ncontract TestVaultLifecycleSTETH {\n address constant wstETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;\n address constant crvPool = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022;\n\n // To avoid using events to get the output, we just set it so we can read\n // it off the contract\n uint256 public output;\n\n function unwrapYieldToken(uint256 amount, uint256 minETHOut) external {\n uint256 amountETHOut =\n VaultLifecycleSTETH.unwrapYieldToken(\n amount,\n wstETH,\n IWSTETH(wstETH).stETH(),\n crvPool,\n minETHOut\n );\n output = amountETHOut;\n }\n\n function withdrawStEth(uint256 amount) external {\n address steth = IWSTETH(wstETH).stETH();\n uint256 amountETHOut =\n VaultLifecycleSTETH.withdrawStEth(steth, wstETH, amount);\n output = amountETHOut;\n ISTETH(steth).transfer(msg.sender, amountETHOut);\n }\n\n // Enables test to send ETH for testing\n receive() external payable {}\n}\n" + }, + "contracts/utils/SAVAXDepositHelper.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {ISAVAX} from \"../interfaces/ISAVAX.sol\";\nimport {IRibbonVault} from \"../interfaces/IRibbon.sol\";\n\ncontract SAVAXDepositHelper {\n ISAVAX public immutable sAVAX;\n IRibbonVault public immutable sAVAXVault;\n\n constructor(address _sAVAX, address _sAVAXVault) {\n require(_sAVAX != address(0), \"!sAVAX\");\n require(_sAVAXVault != address(0), \"!sAVAXVault\");\n\n sAVAX = ISAVAX(_sAVAX);\n sAVAXVault = IRibbonVault(_sAVAXVault);\n }\n\n function deposit() external payable {\n uint256 sAVAXAmount = sAVAX.submit{value: msg.value}();\n sAVAX.approve(address(sAVAXVault), sAVAXAmount);\n sAVAXVault.depositFor(sAVAXAmount, msg.sender);\n }\n}\n" + }, + "contracts/vendor/CustomSafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure.\n * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n function safeTransfer(IERC20 token, address to, uint256 value) internal {\n require(_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)),\n \"ERC20 transfer failed\");\n }\n\n function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n require(_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)),\n \"ERC20 transferFrom failed\");\n }\n\n function safeApprove(IERC20 token, address spender, uint256 value) internal {\n if (_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value))) {\n return;\n }\n require(_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0))\n && _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)),\n \"ERC20 approve failed\");\n }\n\n function _callOptionalReturn(IERC20 token, bytes memory data) private returns (bool) {\n // solhint-disable-next-line avoid-low-level-calls\n (bool success, bytes memory returndata) = address(token).call(data);\n if (!success) {\n return false;\n }\n\n if (returndata.length >= 32) { // Return data is optional\n return abi.decode(returndata, (bool));\n }\n\n // In a wierd case when return data is 1-31 bytes long - return false.\n return returndata.length == 0;\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/ERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n\n uint256 currentAllowance = _allowances[sender][_msgSender()];\n require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n unchecked {\n _approve(sender, _msgSender(), currentAllowance - amount);\n }\n\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n uint256 currentAllowance = _allowances[_msgSender()][spender];\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n uint256 senderBalance = _balances[sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[sender] = senderBalance - amount;\n }\n _balances[recipient] += amount;\n\n emit Transfer(sender, recipient, amount);\n\n _afterTokenTransfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n _balances[account] += amount;\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n }\n _totalSupply -= amount;\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/utils/PercentStrikeSelection.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {\n IPriceOracle\n} from \"@ribbon-finance/rvol/contracts/interfaces/IPriceOracle.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\nimport {\n IManualVolatilityOracle\n} from \"@ribbon-finance/rvol/contracts/interfaces/IManualVolatilityOracle.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\ncontract PercentStrikeSelection is Ownable {\n using SafeMath for uint256;\n\n /**\n * Immutables\n */\n IOptionsPremiumPricer public immutable optionsPremiumPricer;\n\n // step in absolute terms at which we will increment\n // (ex: 100 * 10 ** assetOracleDecimals means we will move at increments of 100 points)\n uint256 public step;\n\n // multiplier for strike selection\n uint256 public strikeMultiplier;\n\n // multiplier to shift asset prices\n uint256 private immutable assetOracleMultiplier;\n\n // Delta are in 4 decimal places. 1 * 10**4 = 1 delta.\n uint256 private constant DELTA_MULTIPLIER = 10**4;\n\n // ChainLink's USD Price oracles return results in 8 decimal places\n uint256 private constant ORACLE_PRICE_MULTIPLIER = 10**8;\n\n // Strike multiplier has 2 decimal places. For example: 150 = 1.5x spot price\n uint256 private constant STRIKE_MULTIPLIER = 10**2;\n\n event StepSet(uint256 oldStep, uint256 newStep, address indexed owner);\n\n constructor(\n address _optionsPremiumPricer,\n uint256 _strikeMultiplier,\n uint256 _step\n ) {\n require(_optionsPremiumPricer != address(0), \"!_optionsPremiumPricer\");\n require(\n _strikeMultiplier > STRIKE_MULTIPLIER,\n \"Multiplier must be bigger than 1!\"\n );\n require(_step > 0, \"!_step\");\n\n optionsPremiumPricer = IOptionsPremiumPricer(_optionsPremiumPricer);\n\n // ex: delta = 7500 (.75)\n uint256 _assetOracleMultiplier =\n 10 **\n IPriceOracle(\n IOptionsPremiumPricer(_optionsPremiumPricer).priceOracle()\n )\n .decimals();\n\n step = _step;\n\n strikeMultiplier = _strikeMultiplier;\n\n assetOracleMultiplier = _assetOracleMultiplier;\n }\n\n /**\n * @notice Gets the strike price by multiplying the current underlying price\n * with a multiplier\n * @param expiryTimestamp is the unix timestamp of expiration\n * @param isPut is whether option is put or call\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\n * @return newDelta will be set to zero for percent strike selection\n */\n\n function getStrikePrice(uint256 expiryTimestamp, bool isPut)\n external\n view\n returns (uint256 newStrikePrice, uint256 newDelta)\n {\n require(\n expiryTimestamp > block.timestamp,\n \"Expiry must be in the future!\"\n );\n\n // asset price\n uint256 strikePrice =\n optionsPremiumPricer.getUnderlyingPrice().mul(strikeMultiplier).div(\n STRIKE_MULTIPLIER\n );\n\n newStrikePrice = isPut\n ? strikePrice.sub(strikePrice % step)\n : strikePrice.add(step.sub(strikePrice % step));\n\n newDelta = 0;\n }\n\n /**\n * @notice Set the multiplier for setting the strike price\n * @param newStrikeMultiplier is the strike multiplier (decimals = 2)\n */\n function setStrikeMultiplier(uint256 newStrikeMultiplier)\n external\n onlyOwner\n {\n require(\n newStrikeMultiplier > STRIKE_MULTIPLIER,\n \"Multiplier must be bigger than 1!\"\n );\n strikeMultiplier = newStrikeMultiplier;\n }\n\n /**\n * @notice Sets new step value\n * @param newStep is the new step value\n */\n function setStep(uint256 newStep) external onlyOwner {\n require(newStep > 0, \"!newStep\");\n uint256 oldStep = step;\n step = newStep;\n emit StepSet(oldStep, newStep, msg.sender);\n }\n}\n" + }, + "@ribbon-finance/rvol/contracts/interfaces/IPriceOracle.sol": { + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >0.6.0 <0.8.7;\n\ninterface IPriceOracle {\n function decimals() external view returns (uint256 _decimals);\n\n function latestAnswer() external view returns (uint256 price);\n}\n" + }, + "@ribbon-finance/rvol/contracts/interfaces/IManualVolatilityOracle.sol": { + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >0.6.0 <0.8.7;\n\ninterface IManualVolatilityOracle {\n function vol(bytes32 optionId)\n external\n view\n returns (uint256 standardDeviation);\n\n function annualizedVol(bytes32 optionId)\n external\n view\n returns (uint256 annualStdev);\n\n function setAnnualizedVol(\n bytes32[] calldata optionIds,\n uint256[] calldata newAnnualizedVols\n ) external;\n}\n" + }, + "contracts/vaults/RETHVault/RibbonThetaRETHVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport {GnosisAuction} from \"../../libraries/GnosisAuction.sol\";\nimport {\n RibbonThetaVaultStorage\n} from \"../../storage/RibbonThetaVaultStorage.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {VaultLifecycle} from \"../../libraries/VaultLifecycle.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {ILiquidityGauge} from \"../../interfaces/ILiquidityGauge.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\nimport {RibbonVault} from \"./base/RibbonVault.sol\";\n\n/**\n * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe\n * the inheritance chain closely.\n * Any changes/appends in storage variable needs to happen in RibbonThetaVaultStorage.\n * RibbonThetaVault should not inherit from any other contract aside from RibbonVault, RibbonThetaVaultStorage\n */\ncontract RibbonThetaRETHVault is RibbonVault, RibbonThetaVaultStorage {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice oTokenFactory is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event PremiumDiscountSet(\n uint256 premiumDiscount,\n uint256 newPremiumDiscount\n );\n\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event InitiateGnosisAuction(\n address indexed auctioningToken,\n address indexed biddingToken,\n uint256 auctionCounter,\n address indexed manager\n );\n\n /************************************************\n * STRUCTS\n ***********************************************/\n\n /**\n * @notice Initialization parameters for the vault.\n * @param _owner is the owner of the vault with critical permissions\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n * @param _premiumDiscount is the vault's discount applied to the premium\n * @param _auctionDuration is the duration of the gnosis auction\n */\n struct InitParams {\n address _owner;\n address _keeper;\n address _feeRecipient;\n uint256 _managementFee;\n uint256 _performanceFee;\n string _tokenName;\n string _tokenSymbol;\n address _optionsPremiumPricer;\n address _strikeSelection;\n uint32 _premiumDiscount;\n uint256 _auctionDuration;\n }\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions\n */\n constructor(\n address _weth,\n address _usdc,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _gnosisEasyAuction\n )\n RibbonVault(\n _weth,\n _usdc,\n _gammaController,\n _marginPool,\n _gnosisEasyAuction\n )\n {\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n OTOKEN_FACTORY = _oTokenFactory;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n * @param _initParams is the struct with vault initialization parameters\n * @param _vaultParams is the struct with vault general data\n */\n function initialize(\n InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n baseInitialize(\n _initParams._owner,\n _initParams._keeper,\n _initParams._feeRecipient,\n _initParams._managementFee,\n _initParams._performanceFee,\n _initParams._tokenName,\n _initParams._tokenSymbol,\n _vaultParams\n );\n require(\n _initParams._optionsPremiumPricer != address(0),\n \"!_optionsPremiumPricer\"\n );\n require(\n _initParams._strikeSelection != address(0),\n \"!_strikeSelection\"\n );\n require(\n _initParams._premiumDiscount > 0 &&\n _initParams._premiumDiscount <\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"!_premiumDiscount\"\n );\n require(\n _initParams._auctionDuration >= MIN_AUCTION_DURATION,\n \"!_auctionDuration\"\n );\n optionsPremiumPricer = _initParams._optionsPremiumPricer;\n strikeSelection = _initParams._strikeSelection;\n premiumDiscount = _initParams._premiumDiscount;\n auctionDuration = _initParams._auctionDuration;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new discount on premiums for options we are selling\n * @param newPremiumDiscount is the premium discount\n */\n function setPremiumDiscount(uint256 newPremiumDiscount)\n external\n onlyKeeper\n {\n require(\n newPremiumDiscount > 0 &&\n newPremiumDiscount <= 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"Invalid discount\"\n );\n\n emit PremiumDiscountSet(premiumDiscount, newPremiumDiscount);\n\n premiumDiscount = newPremiumDiscount;\n }\n\n /**\n * @notice Sets the new auction duration\n * @param newAuctionDuration is the auction duration\n */\n function setAuctionDuration(uint256 newAuctionDuration) external onlyOwner {\n require(\n newAuctionDuration >= MIN_AUCTION_DURATION,\n \"Invalid auction duration\"\n );\n\n emit AuctionDurationSet(auctionDuration, newAuctionDuration);\n\n auctionDuration = newAuctionDuration;\n }\n\n /**\n * @notice Sets the new strike selection contract\n * @param newStrikeSelection is the address of the new strike selection contract\n */\n function setStrikeSelection(address newStrikeSelection) external onlyOwner {\n require(newStrikeSelection != address(0), \"!newStrikeSelection\");\n strikeSelection = newStrikeSelection;\n }\n\n /**\n * @notice Sets the new options premium pricer contract\n * @param newOptionsPremiumPricer is the address of the new strike selection contract\n */\n function setOptionsPremiumPricer(address newOptionsPremiumPricer)\n external\n onlyOwner\n {\n require(\n newOptionsPremiumPricer != address(0),\n \"!newOptionsPremiumPricer\"\n );\n optionsPremiumPricer = newOptionsPremiumPricer;\n }\n\n /**\n * @notice Optionality to set strike price manually\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice) external onlyOwner {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Sets the new liquidityGauge contract for this vault\n * @param newLiquidityGauge is the address of the new liquidityGauge contract\n */\n function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {\n liquidityGauge = newLiquidityGauge;\n }\n\n /**\n * @notice Sets the new optionsPurchaseQueue contract for this vault\n * @param newOptionsPurchaseQueue is the address of the new optionsPurchaseQueue contract\n */\n function setOptionsPurchaseQueue(address newOptionsPurchaseQueue)\n external\n onlyOwner\n {\n optionsPurchaseQueue = newOptionsPurchaseQueue;\n }\n\n /**\n * @notice Sets oToken Premium\n * @param minPrice is the new oToken Premium in the units of 10**18\n */\n function setMinPrice(uint256 minPrice) external onlyKeeper {\n require(minPrice > 0, \"!minPrice\");\n currentOtokenPremium = minPrice;\n }\n\n /**\n * @notice Sets the new Vault Pauser contract for this vault\n * @param newVaultPauser is the address of the new vaultPauser contract\n */\n function setVaultPauser(address newVaultPauser) external onlyOwner {\n vaultPauser = newVaultPauser;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n transferAsset(msg.sender, amount);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n _initiateWithdraw(numShares);\n currentQueuedWithdrawShares = currentQueuedWithdrawShares.add(\n numShares\n );\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /**\n * @notice Stakes a users vault shares\n * @param numShares is the number of shares to stake\n */\n function stake(uint256 numShares) external nonReentrant {\n address _liquidityGauge = liquidityGauge;\n require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit\n require(numShares > 0);\n uint256 heldByAccount = balanceOf(msg.sender);\n if (heldByAccount < numShares) {\n _redeem(numShares.sub(heldByAccount), false);\n }\n _transfer(msg.sender, address(this), numShares);\n _approve(address(this), _liquidityGauge, numShares);\n ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);\n }\n\n /**\n * @notice Sets the next option the vault will be shorting, and closes the existing short.\n * This allows all the users to withdraw if the next option is malicious.\n */\n function commitAndClose() external nonReentrant {\n address oldOption = optionState.currentOption;\n\n VaultLifecycle.CloseParams memory closeParams =\n VaultLifecycle.CloseParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n currentOption: oldOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n strikeSelection: strikeSelection,\n optionsPremiumPricer: optionsPremiumPricer,\n premiumDiscount: premiumDiscount\n });\n\n (address otokenAddress, uint256 strikePrice, uint256 delta) =\n VaultLifecycle.commitAndClose(closeParams, vaultParams, vaultState);\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n optionState.nextOption = otokenAddress;\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n\n _closeShort(oldOption);\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycle.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares;\n\n (\n address newOption,\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount\n ) =\n _rollToNextOption(\n lastQueuedWithdrawAmount,\n currQueuedWithdrawShares\n );\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(\n currQueuedWithdrawShares\n );\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n currentQueuedWithdrawShares = 0;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n uint256 optionsMintAmount =\n VaultLifecycle.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n VaultLifecycle.allocateOptions(\n optionsPurchaseQueue,\n newOption,\n optionsMintAmount,\n VaultLifecycle.QUEUE_OPTION_ALLOCATION\n );\n\n _startAuction();\n }\n\n /**\n * @notice Initiate the gnosis auction.\n */\n function startAuction() external onlyKeeper nonReentrant {\n _startAuction();\n }\n\n function _startAuction() private {\n GnosisAuction.AuctionDetails memory auctionDetails;\n\n address currentOtoken = optionState.currentOption;\n\n auctionDetails.oTokenAddress = currentOtoken;\n auctionDetails.gnosisEasyAuction = GNOSIS_EASY_AUCTION;\n auctionDetails.asset = vaultParams.asset;\n auctionDetails.assetDecimals = vaultParams.decimals;\n auctionDetails.oTokenPremium = currentOtokenPremium;\n auctionDetails.duration = auctionDuration;\n\n optionAuctionID = VaultLifecycle.startAuction(auctionDetails);\n }\n\n /**\n * @notice Sell the allocated options to the purchase queue post auction settlement\n */\n function sellOptionsToQueue() external onlyKeeper nonReentrant {\n VaultLifecycle.sellOptionsToQueue(\n optionsPurchaseQueue,\n GNOSIS_EASY_AUCTION,\n optionAuctionID\n );\n }\n\n /**\n * @notice Burn the remaining oTokens left over from gnosis auction.\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n uint256 unlockedAssetAmount =\n VaultLifecycle.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n\n vaultState.lockedAmount = uint104(\n uint256(vaultState.lockedAmount).sub(unlockedAssetAmount)\n );\n }\n\n /**\n * @notice Recovery function that returns an ERC20 token to the recipient\n * @param token is the ERC20 token to recover from the vault\n * @param recipient is the recipient of the recovered tokens\n */\n function recoverTokens(address token, address recipient)\n external\n onlyOwner\n {\n require(token != vaultParams.asset, \"Vault asset not recoverable\");\n require(token != address(this), \"Vault share not recoverable\");\n require(recipient != address(this), \"Recipient cannot be vault\");\n\n IERC20(token).safeTransfer(\n recipient,\n IERC20(token).balanceOf(address(this))\n );\n }\n\n /**\n * @notice pause a user's vault position\n */\n function pausePosition() external {\n address _vaultPauserAddress = vaultPauser;\n require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit\n _redeem(0, true);\n uint256 heldByAccount = balanceOf(msg.sender);\n _approve(msg.sender, _vaultPauserAddress, heldByAccount);\n IVaultPauser(_vaultPauserAddress).pausePosition(\n msg.sender,\n heldByAccount\n );\n }\n}\n" + }, + "contracts/storage/RibbonThetaVaultStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nabstract contract RibbonThetaVaultStorageV1 {\n // Logic contract used to price options\n address public optionsPremiumPricer;\n // Logic contract used to select strike prices\n address public strikeSelection;\n // Premium discount on options we are selling (thousandths place: 000 - 999)\n uint256 public premiumDiscount;\n // Current oToken premium\n uint256 public currentOtokenPremium;\n // Last round id at which the strike was manually overridden\n uint16 public lastStrikeOverrideRound;\n // Price last overridden strike set to\n uint256 public overriddenStrikePrice;\n // Auction duration\n uint256 public auctionDuration;\n // Auction id of current option\n uint256 public optionAuctionID;\n}\n\nabstract contract RibbonThetaVaultStorageV2 {\n // Amount locked for scheduled withdrawals last week;\n uint256 public lastQueuedWithdrawAmount;\n}\n\nabstract contract RibbonThetaVaultStorageV3 {\n // DEPRECATED: Auction will be denominated in USDC if true\n bool private _isUsdcAuction;\n // DEPRECATED: Path for swaps\n bytes private _swapPath;\n}\n\nabstract contract RibbonThetaVaultStorageV4 {\n // LiquidityGauge contract for the vault\n address public liquidityGauge;\n}\n\nabstract contract RibbonThetaVaultStorageV5 {\n // OptionsPurchaseQueue contract for selling options\n address public optionsPurchaseQueue;\n}\n\nabstract contract RibbonThetaVaultStorageV6 {\n // Queued withdraw shares for the current round\n uint256 public currentQueuedWithdrawShares;\n}\n\nabstract contract RibbonThetaVaultStorageV7 {\n // Vault Pauser Contract for the vault\n address public vaultPauser;\n}\n\nabstract contract RibbonThetaVaultStorageV8 {\n // DEPRECATED: Executor role for Swap offers\n address private _offerExecutor;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of RibbonThetaVaultStorage\n// e.g. RibbonThetaVaultStorage, so finally it would look like\n// contract RibbonThetaVaultStorage is RibbonThetaVaultStorageV1, RibbonThetaVaultStorageV2\nabstract contract RibbonThetaVaultStorage is\n RibbonThetaVaultStorageV1,\n RibbonThetaVaultStorageV2,\n RibbonThetaVaultStorageV3,\n RibbonThetaVaultStorageV4,\n RibbonThetaVaultStorageV5,\n RibbonThetaVaultStorageV6,\n RibbonThetaVaultStorageV7,\n RibbonThetaVaultStorageV8\n{\n\n}\n" + }, + "contracts/vaults/RETHVault/base/RibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\n\nimport {Vault} from \"../../../libraries/Vault.sol\";\nimport {VaultLifecycle} from \"../../../libraries/VaultLifecycle.sol\";\nimport {ShareMath} from \"../../../libraries/ShareMath.sol\";\nimport {IWETH} from \"../../../interfaces/IWETH.sol\";\nimport {IRETH} from \"../../../interfaces/IRETH.sol\";\nimport {IRETHDepositPool} from \"../../../interfaces/IRETHDepositPool.sol\";\n\ncontract RibbonVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n /// @notice depositETH transactions within 24hr are paused\n uint256 public lastrETHMintCutoff;\n\n // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure.\n uint256[30] private ____gap;\n\n // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE\n // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaVaultStorage\n // or RibbonDeltaVaultStorage instead. Read this documentation to learn more:\n // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice Deprecated: 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n /// @notice 7 day period between each options sale.\n uint256 public constant PERIOD = 7 days;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // GNOSIS_EASY_AUCTION is Gnosis protocol's contract for initiating auctions and placing bids\n // https://github.com/gnosis/ido-contracts/blob/main/contracts/EasyAuction.sol\n address public immutable GNOSIS_EASY_AUCTION;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectVaultFees(\n uint256 performanceFee,\n uint256 vaultFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions\n */\n constructor(\n address _weth,\n address _usdc,\n address _gammaController,\n address _marginPool,\n address _gnosisEasyAuction\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_gnosisEasyAuction != address(0), \"!_gnosisEasyAuction\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n\n WETH = _weth;\n USDC = _usdc;\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n GNOSIS_EASY_AUCTION = _gnosisEasyAuction;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function baseInitialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) internal initializer {\n VaultLifecycle.verifyInitializerParams(\n _owner,\n _keeper,\n _feeRecipient,\n _performanceFee,\n _managementFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_tokenName, _tokenSymbol);\n __Ownable_init();\n transferOwnership(_owner);\n\n keeper = _keeper;\n\n feeRecipient = _feeRecipient;\n performanceFee = _performanceFee;\n managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div(\n WEEKS_PER_YEAR\n );\n vaultParams = _vaultParams;\n\n uint256 assetBalance =\n IERC20(vaultParams.asset).balanceOf(address(this));\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n\n vaultState.round = 1;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n // We are dividing annualized management fee by num weeks in a year\n uint256 tmpManagementFee =\n newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n\n managementFee = tmpManagementFee;\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /**\n * @notice Sets the new rETH mint cutoff\n */\n function updaterETHMintCutoff() external onlyKeeper {\n lastrETHMintCutoff = block.timestamp;\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Deposits ETH into the contract and mint vault shares.\n */\n function depositETH() external payable nonReentrant {\n require(msg.value > 0, \"!value\");\n\n // After minting rETH there is a period of 5760 blocks (~21 hours)\n // where the minting wallet is not able to transfer any of its rETH tokens.\n // To allow rollToNexOption to occur we must pause mints\n require(block.timestamp.sub(lastrETHMintCutoff) >= 1 days, \"!cutoff\");\n\n uint256 rETHAmount = IRETH(vaultParams.asset).getRethValue(msg.value);\n\n _depositFor(rETHAmount, msg.sender);\n\n // Deposit ETH for rETH\n IRETHDepositPool(0x4D05E3d48a938db4b7a9A59A802D5b45011BDe58).deposit{\n value: msg.value\n }();\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender.\n * @param amount is the amount of `asset` to deposit\n */\n function deposit(uint256 amount) external nonReentrant {\n require(amount > 0, \"!amount\");\n\n _depositFor(amount, msg.sender);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.\n * @notice Used for vault -> vault deposits on the user's behalf\n * @param amount is the amount of `asset` to deposit\n * @param creditor is the address that can claim/withdraw deposited amount\n */\n function depositFor(uint256 amount, address creditor)\n external\n nonReentrant\n {\n require(amount > 0, \"!amount\");\n require(creditor != address(0));\n\n _depositFor(amount, creditor);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n */\n function _depositFor(uint256 amount, address creditor) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount = totalBalance().add(amount);\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n\n emit Deposit(creditor, amount, currentRound);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function _initiateWithdraw(uint256 numShares) internal {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n if (\n depositReceipts[msg.sender].amount > 0 ||\n depositReceipts[msg.sender].unredeemedShares > 0\n ) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n bool withdrawalIsSameRound = withdrawal.round == currentRound;\n\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n\n uint256 existingShares = uint256(withdrawal.shares);\n\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return withdrawAmount the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n require(withdrawAmount > 0, \"!withdrawAmount\");\n transferAsset(msg.sender, withdrawAmount);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map.\n * Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k.\n * Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0.\n * @param numRounds is the number of rounds to initialize in the map\n */\n function initRounds(uint256 numRounds) external nonReentrant {\n require(numRounds > 0, \"!numRounds\");\n\n uint256 _round = vaultState.round;\n for (uint256 i = 0; i < numRounds; i++) {\n uint256 index = _round + i;\n require(roundPricePerShare[index] == 0, \"Initialized\"); // AVOID OVERWRITING ACTUAL VALUES\n roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT;\n }\n }\n\n /**\n * @notice Helper function that performs most administrative tasks\n * such as setting next option, minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @param currentQueuedWithdrawShares is the queued withdraw shares for the current round\n * @return newOption is the new option address\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _rollToNextOption(\n uint256 lastQueuedWithdrawAmount,\n uint256 currentQueuedWithdrawShares\n )\n internal\n returns (\n address newOption,\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount\n )\n {\n require(block.timestamp >= optionState.nextOptionReadyAt, \"!ready\");\n\n newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n address recipient = feeRecipient;\n uint256 mintShares;\n uint256 performanceFeeInAsset;\n uint256 totalVaultFee;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n ) = VaultLifecycle.rollover(\n vaultState,\n VaultLifecycle.RolloverParams(\n vaultParams.decimals,\n IERC20(vaultParams.asset).balanceOf(address(this)),\n totalSupply(),\n lastQueuedWithdrawAmount,\n performanceFee,\n managementFee,\n currentQueuedWithdrawShares\n )\n );\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n\n // Finalize the pricePerShare at the end of the round\n uint256 currentRound = vaultState.round;\n roundPricePerShare[currentRound] = newPricePerShare;\n\n emit CollectVaultFees(\n performanceFeeInAsset,\n totalVaultFee,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (totalVaultFee > 0) {\n transferAsset(payable(recipient), totalVaultFee);\n }\n\n return (newOption, lockedBalance, queuedWithdrawAmount);\n }\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) internal {\n address asset = vaultParams.asset;\n if (asset == WETH) {\n IWETH(WETH).withdraw(amount);\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Transfer failed\");\n return;\n }\n IERC20(asset).safeTransfer(recipient, amount);\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n external\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n return\n uint256(vaultState.lockedAmount).add(\n IERC20(vaultParams.asset).balanceOf(address(this))\n );\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n}\n" + }, + "contracts/interfaces/IRETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IRETH is IERC20 {\n function getExchangeRate() external view returns (uint256);\n\n function getETHValue(uint256 rethAmount) external view returns (uint256);\n\n function getRethValue(uint256 ethAmount) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IRETHDepositPool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\ninterface IRETHDepositPool {\n function deposit() external payable;\n}\n" + }, + "contracts/vaults/BaseVaults/RibbonThetaVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport {GnosisAuction} from \"../../libraries/GnosisAuction.sol\";\nimport {\n RibbonThetaVaultStorage\n} from \"../../storage/RibbonThetaVaultStorage.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {VaultLifecycle} from \"../../libraries/VaultLifecycle.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {ILiquidityGauge} from \"../../interfaces/ILiquidityGauge.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\nimport {RibbonVault} from \"./base/RibbonVault.sol\";\n\n/**\n * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe\n * the inheritance chain closely.\n * Any changes/appends in storage variable needs to happen in RibbonThetaVaultStorage.\n * RibbonThetaVault should not inherit from any other contract aside from RibbonVault, RibbonThetaVaultStorage\n */\ncontract RibbonThetaVault is RibbonVault, RibbonThetaVaultStorage {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice oTokenFactory is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event PremiumDiscountSet(\n uint256 premiumDiscount,\n uint256 newPremiumDiscount\n );\n\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event InitiateGnosisAuction(\n address indexed auctioningToken,\n address indexed biddingToken,\n uint256 auctionCounter,\n address indexed manager\n );\n\n /************************************************\n * STRUCTS\n ***********************************************/\n\n /**\n * @notice Initialization parameters for the vault.\n * @param _owner is the owner of the vault with critical permissions\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n * @param _premiumDiscount is the vault's discount applied to the premium\n * @param _auctionDuration is the duration of the gnosis auction\n */\n struct InitParams {\n address _owner;\n address _keeper;\n address _feeRecipient;\n uint256 _managementFee;\n uint256 _performanceFee;\n string _tokenName;\n string _tokenSymbol;\n address _optionsPremiumPricer;\n address _strikeSelection;\n uint32 _premiumDiscount;\n uint256 _auctionDuration;\n }\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions\n */\n constructor(\n address _weth,\n address _usdc,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _gnosisEasyAuction\n )\n RibbonVault(\n _weth,\n _usdc,\n _gammaController,\n _marginPool,\n _gnosisEasyAuction\n )\n {\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n OTOKEN_FACTORY = _oTokenFactory;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n * @param _initParams is the struct with vault initialization parameters\n * @param _vaultParams is the struct with vault general data\n */\n function initialize(\n InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n baseInitialize(\n _initParams._owner,\n _initParams._keeper,\n _initParams._feeRecipient,\n _initParams._managementFee,\n _initParams._performanceFee,\n _initParams._tokenName,\n _initParams._tokenSymbol,\n _vaultParams\n );\n require(\n _initParams._optionsPremiumPricer != address(0),\n \"!_optionsPremiumPricer\"\n );\n require(\n _initParams._strikeSelection != address(0),\n \"!_strikeSelection\"\n );\n require(\n _initParams._premiumDiscount > 0 &&\n _initParams._premiumDiscount <\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"!_premiumDiscount\"\n );\n require(\n _initParams._auctionDuration >= MIN_AUCTION_DURATION,\n \"!_auctionDuration\"\n );\n optionsPremiumPricer = _initParams._optionsPremiumPricer;\n strikeSelection = _initParams._strikeSelection;\n premiumDiscount = _initParams._premiumDiscount;\n auctionDuration = _initParams._auctionDuration;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new discount on premiums for options we are selling\n * @param newPremiumDiscount is the premium discount\n */\n function setPremiumDiscount(uint256 newPremiumDiscount)\n external\n onlyKeeper\n {\n require(\n newPremiumDiscount > 0 &&\n newPremiumDiscount <= 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"Invalid discount\"\n );\n\n emit PremiumDiscountSet(premiumDiscount, newPremiumDiscount);\n\n premiumDiscount = newPremiumDiscount;\n }\n\n /**\n * @notice Sets the new auction duration\n * @param newAuctionDuration is the auction duration\n */\n function setAuctionDuration(uint256 newAuctionDuration) external onlyOwner {\n require(\n newAuctionDuration >= MIN_AUCTION_DURATION,\n \"Invalid auction duration\"\n );\n\n emit AuctionDurationSet(auctionDuration, newAuctionDuration);\n\n auctionDuration = newAuctionDuration;\n }\n\n /**\n * @notice Sets the new strike selection contract\n * @param newStrikeSelection is the address of the new strike selection contract\n */\n function setStrikeSelection(address newStrikeSelection) external onlyOwner {\n require(newStrikeSelection != address(0), \"!newStrikeSelection\");\n strikeSelection = newStrikeSelection;\n }\n\n /**\n * @notice Sets the new options premium pricer contract\n * @param newOptionsPremiumPricer is the address of the new strike selection contract\n */\n function setOptionsPremiumPricer(address newOptionsPremiumPricer)\n external\n onlyOwner\n {\n require(\n newOptionsPremiumPricer != address(0),\n \"!newOptionsPremiumPricer\"\n );\n optionsPremiumPricer = newOptionsPremiumPricer;\n }\n\n /**\n * @notice Optionality to set strike price manually\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice) external onlyOwner {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Sets the new liquidityGauge contract for this vault\n * @param newLiquidityGauge is the address of the new liquidityGauge contract\n */\n function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {\n liquidityGauge = newLiquidityGauge;\n }\n\n /**\n * @notice Sets the new optionsPurchaseQueue contract for this vault\n * @param newOptionsPurchaseQueue is the address of the new optionsPurchaseQueue contract\n */\n function setOptionsPurchaseQueue(address newOptionsPurchaseQueue)\n external\n onlyOwner\n {\n optionsPurchaseQueue = newOptionsPurchaseQueue;\n }\n\n /**\n * @notice Sets oToken Premium\n * @param minPrice is the new oToken Premium in the units of 10**18\n */\n function setMinPrice(uint256 minPrice) external onlyKeeper {\n require(minPrice > 0, \"!minPrice\");\n currentOtokenPremium = minPrice;\n }\n\n /**\n * @notice Sets the new Vault Pauser contract for this vault\n * @param newVaultPauser is the address of the new vaultPauser contract\n */\n function setVaultPauser(address newVaultPauser) external onlyOwner {\n vaultPauser = newVaultPauser;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n transferAsset(msg.sender, amount);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n _initiateWithdraw(numShares);\n currentQueuedWithdrawShares = currentQueuedWithdrawShares.add(\n numShares\n );\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /**\n * @notice Stakes a users vault shares\n * @param numShares is the number of shares to stake\n */\n function stake(uint256 numShares) external nonReentrant {\n address _liquidityGauge = liquidityGauge;\n require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit\n require(numShares > 0);\n uint256 heldByAccount = balanceOf(msg.sender);\n if (heldByAccount < numShares) {\n _redeem(numShares.sub(heldByAccount), false);\n }\n _transfer(msg.sender, address(this), numShares);\n _approve(address(this), _liquidityGauge, numShares);\n ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);\n }\n\n /**\n * @notice Sets the next option the vault will be shorting, and closes the existing short.\n * This allows all the users to withdraw if the next option is malicious.\n */\n function commitAndClose() external nonReentrant {\n address oldOption = optionState.currentOption;\n\n VaultLifecycle.CloseParams memory closeParams =\n VaultLifecycle.CloseParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n currentOption: oldOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n strikeSelection: strikeSelection,\n optionsPremiumPricer: optionsPremiumPricer,\n premiumDiscount: premiumDiscount\n });\n\n (address otokenAddress, uint256 strikePrice, uint256 delta) =\n VaultLifecycle.commitAndClose(closeParams, vaultParams, vaultState);\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n optionState.nextOption = otokenAddress;\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n\n _closeShort(oldOption);\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycle.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares;\n\n (\n address newOption,\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount\n ) =\n _rollToNextOption(\n lastQueuedWithdrawAmount,\n currQueuedWithdrawShares\n );\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(\n currQueuedWithdrawShares\n );\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n currentQueuedWithdrawShares = 0;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n uint256 optionsMintAmount =\n VaultLifecycle.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n VaultLifecycle.allocateOptions(\n optionsPurchaseQueue,\n newOption,\n optionsMintAmount,\n VaultLifecycle.QUEUE_OPTION_ALLOCATION\n );\n\n _startAuction();\n }\n\n /**\n * @notice Initiate the gnosis auction.\n */\n function startAuction() external onlyKeeper nonReentrant {\n _startAuction();\n }\n\n function _startAuction() private {\n GnosisAuction.AuctionDetails memory auctionDetails;\n\n address currentOtoken = optionState.currentOption;\n\n auctionDetails.oTokenAddress = currentOtoken;\n auctionDetails.gnosisEasyAuction = GNOSIS_EASY_AUCTION;\n auctionDetails.asset = vaultParams.asset;\n auctionDetails.assetDecimals = vaultParams.decimals;\n auctionDetails.oTokenPremium = currentOtokenPremium;\n auctionDetails.duration = auctionDuration;\n\n optionAuctionID = VaultLifecycle.startAuction(auctionDetails);\n }\n\n /**\n * @notice Sell the allocated options to the purchase queue post auction settlement\n */\n function sellOptionsToQueue() external onlyKeeper nonReentrant {\n VaultLifecycle.sellOptionsToQueue(\n optionsPurchaseQueue,\n GNOSIS_EASY_AUCTION,\n optionAuctionID\n );\n }\n\n /**\n * @notice Burn the remaining oTokens left over from gnosis auction.\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n uint256 unlockedAssetAmount =\n VaultLifecycle.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n\n vaultState.lockedAmount = uint104(\n uint256(vaultState.lockedAmount).sub(unlockedAssetAmount)\n );\n }\n\n /**\n * @notice Recovery function that returns an ERC20 token to the recipient\n * @param token is the ERC20 token to recover from the vault\n * @param recipient is the recipient of the recovered tokens\n */\n function recoverTokens(address token, address recipient)\n external\n onlyOwner\n {\n require(token != vaultParams.asset, \"Vault asset not recoverable\");\n require(token != address(this), \"Vault share not recoverable\");\n require(recipient != address(this), \"Recipient cannot be vault\");\n\n IERC20(token).safeTransfer(\n recipient,\n IERC20(token).balanceOf(address(this))\n );\n }\n\n /**\n * @notice pause a user's vault position\n */\n function pausePosition() external {\n address _vaultPauserAddress = vaultPauser;\n require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit\n _redeem(0, true);\n uint256 heldByAccount = balanceOf(msg.sender);\n _approve(msg.sender, _vaultPauserAddress, heldByAccount);\n IVaultPauser(_vaultPauserAddress).pausePosition(\n msg.sender,\n heldByAccount\n );\n }\n}\n" + }, + "contracts/vaults/BaseVaults/base/RibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\n\nimport {Vault} from \"../../../libraries/Vault.sol\";\nimport {VaultLifecycle} from \"../../../libraries/VaultLifecycle.sol\";\nimport {ShareMath} from \"../../../libraries/ShareMath.sol\";\nimport {IWETH} from \"../../../interfaces/IWETH.sol\";\n\ncontract RibbonVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure.\n uint256[30] private ____gap;\n\n // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE\n // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaVaultStorage\n // or RibbonDeltaVaultStorage instead. Read this documentation to learn more:\n // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice Deprecated: 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n /// @notice 7 day period between each options sale.\n uint256 public constant PERIOD = 7 days;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // GNOSIS_EASY_AUCTION is Gnosis protocol's contract for initiating auctions and placing bids\n // https://github.com/gnosis/ido-contracts/blob/main/contracts/EasyAuction.sol\n address public immutable GNOSIS_EASY_AUCTION;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectVaultFees(\n uint256 performanceFee,\n uint256 vaultFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions\n */\n constructor(\n address _weth,\n address _usdc,\n address _gammaController,\n address _marginPool,\n address _gnosisEasyAuction\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_gnosisEasyAuction != address(0), \"!_gnosisEasyAuction\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n\n WETH = _weth;\n USDC = _usdc;\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n GNOSIS_EASY_AUCTION = _gnosisEasyAuction;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function baseInitialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) internal initializer {\n VaultLifecycle.verifyInitializerParams(\n _owner,\n _keeper,\n _feeRecipient,\n _performanceFee,\n _managementFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_tokenName, _tokenSymbol);\n __Ownable_init();\n transferOwnership(_owner);\n\n keeper = _keeper;\n\n feeRecipient = _feeRecipient;\n performanceFee = _performanceFee;\n managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div(\n WEEKS_PER_YEAR\n );\n vaultParams = _vaultParams;\n\n uint256 assetBalance =\n IERC20(vaultParams.asset).balanceOf(address(this));\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n\n vaultState.round = 1;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n // We are dividing annualized management fee by num weeks in a year\n uint256 tmpManagementFee =\n newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n\n managementFee = tmpManagementFee;\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Deposits ETH into the contract and mint vault shares. Reverts if the asset is not WETH.\n */\n function depositETH() external payable nonReentrant {\n require(vaultParams.asset == WETH, \"!WETH\");\n require(msg.value > 0, \"!value\");\n\n _depositFor(msg.value, msg.sender);\n\n IWETH(WETH).deposit{value: msg.value}();\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender.\n * @param amount is the amount of `asset` to deposit\n */\n function deposit(uint256 amount) external nonReentrant {\n require(amount > 0, \"!amount\");\n\n _depositFor(amount, msg.sender);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.\n * @notice Used for vault -> vault deposits on the user's behalf\n * @param amount is the amount of `asset` to deposit\n * @param creditor is the address that can claim/withdraw deposited amount\n */\n function depositFor(uint256 amount, address creditor)\n external\n nonReentrant\n {\n require(amount > 0, \"!amount\");\n require(creditor != address(0));\n\n _depositFor(amount, creditor);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n */\n function _depositFor(uint256 amount, address creditor) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount = totalBalance().add(amount);\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n\n emit Deposit(creditor, amount, currentRound);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function _initiateWithdraw(uint256 numShares) internal {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n if (\n depositReceipts[msg.sender].amount > 0 ||\n depositReceipts[msg.sender].unredeemedShares > 0\n ) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n bool withdrawalIsSameRound = withdrawal.round == currentRound;\n\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n\n uint256 existingShares = uint256(withdrawal.shares);\n\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return withdrawAmount the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n require(withdrawAmount > 0, \"!withdrawAmount\");\n transferAsset(msg.sender, withdrawAmount);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map.\n * Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k.\n * Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0.\n * @param numRounds is the number of rounds to initialize in the map\n */\n function initRounds(uint256 numRounds) external nonReentrant {\n require(numRounds > 0, \"!numRounds\");\n\n uint256 _round = vaultState.round;\n for (uint256 i = 0; i < numRounds; i++) {\n uint256 index = _round + i;\n require(roundPricePerShare[index] == 0, \"Initialized\"); // AVOID OVERWRITING ACTUAL VALUES\n roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT;\n }\n }\n\n /**\n * @notice Helper function that performs most administrative tasks\n * such as setting next option, minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @param currentQueuedWithdrawShares is the queued withdraw shares for the current round\n * @return newOption is the new option address\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _rollToNextOption(\n uint256 lastQueuedWithdrawAmount,\n uint256 currentQueuedWithdrawShares\n )\n internal\n returns (\n address newOption,\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount\n )\n {\n require(block.timestamp >= optionState.nextOptionReadyAt, \"!ready\");\n\n newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n address recipient = feeRecipient;\n uint256 mintShares;\n uint256 performanceFeeInAsset;\n uint256 totalVaultFee;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n ) = VaultLifecycle.rollover(\n vaultState,\n VaultLifecycle.RolloverParams(\n vaultParams.decimals,\n IERC20(vaultParams.asset).balanceOf(address(this)),\n totalSupply(),\n lastQueuedWithdrawAmount,\n performanceFee,\n managementFee,\n currentQueuedWithdrawShares\n )\n );\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n\n // Finalize the pricePerShare at the end of the round\n uint256 currentRound = vaultState.round;\n roundPricePerShare[currentRound] = newPricePerShare;\n\n emit CollectVaultFees(\n performanceFeeInAsset,\n totalVaultFee,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (totalVaultFee > 0) {\n transferAsset(payable(recipient), totalVaultFee);\n }\n\n return (newOption, lockedBalance, queuedWithdrawAmount);\n }\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) internal {\n address asset = vaultParams.asset;\n if (asset == WETH) {\n IWETH(WETH).withdraw(amount);\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Transfer failed\");\n return;\n }\n IERC20(asset).safeTransfer(recipient, amount);\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n external\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n return\n uint256(vaultState.lockedAmount).add(\n IERC20(vaultParams.asset).balanceOf(address(this))\n );\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n}\n" + }, + "contracts/vaults/BaseVaultWithSwap/base/RibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\n\nimport {Vault} from \"../../../libraries/Vault.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../../libraries/VaultLifecycleWithSwap.sol\";\nimport {ShareMath} from \"../../../libraries/ShareMath.sol\";\nimport {IWETH} from \"../../../interfaces/IWETH.sol\";\n\ncontract RibbonVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure.\n uint256[30] private ____gap;\n\n // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE\n // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaVaultStorage\n // or RibbonDeltaVaultStorage instead. Read this documentation to learn more:\n // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice Deprecated: 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n /// @notice 7 day period between each options sale.\n uint256 public constant PERIOD = 7 days;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // SWAP_CONTRACT is a contract for settling bids via signed messages\n // https://github.com/ribbon-finance/ribbon-v2/blob/master/contracts/utils/Swap.sol\n address public immutable SWAP_CONTRACT;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectVaultFees(\n uint256 performanceFee,\n uint256 vaultFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _gammaController,\n address _marginPool,\n address _swapContract\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_swapContract != address(0), \"!_swapContract\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n\n WETH = _weth;\n USDC = _usdc;\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n SWAP_CONTRACT = _swapContract;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function baseInitialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) internal initializer {\n VaultLifecycleWithSwap.verifyInitializerParams(\n _owner,\n _keeper,\n _feeRecipient,\n _performanceFee,\n _managementFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_tokenName, _tokenSymbol);\n __Ownable_init();\n transferOwnership(_owner);\n\n keeper = _keeper;\n\n feeRecipient = _feeRecipient;\n performanceFee = _performanceFee;\n managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div(\n WEEKS_PER_YEAR\n );\n vaultParams = _vaultParams;\n\n uint256 assetBalance =\n IERC20(vaultParams.asset).balanceOf(address(this));\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n\n vaultState.round = 1;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n // We are dividing annualized management fee by num weeks in a year\n uint256 tmpManagementFee =\n newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n\n managementFee = tmpManagementFee;\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Deposits ETH into the contract and mint vault shares. Reverts if the asset is not WETH.\n */\n function depositETH() external payable nonReentrant {\n require(vaultParams.asset == WETH, \"!WETH\");\n require(msg.value > 0, \"!value\");\n\n _depositFor(msg.value, msg.sender);\n\n IWETH(WETH).deposit{value: msg.value}();\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender.\n * @param amount is the amount of `asset` to deposit\n */\n function deposit(uint256 amount) external nonReentrant {\n require(amount > 0, \"!amount\");\n\n _depositFor(amount, msg.sender);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.\n * @notice Used for vault -> vault deposits on the user's behalf\n * @param amount is the amount of `asset` to deposit\n * @param creditor is the address that can claim/withdraw deposited amount\n */\n function depositFor(uint256 amount, address creditor)\n external\n nonReentrant\n {\n require(amount > 0, \"!amount\");\n require(creditor != address(0));\n\n _depositFor(amount, creditor);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n */\n function _depositFor(uint256 amount, address creditor) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount = totalBalance().add(amount);\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n\n emit Deposit(creditor, amount, currentRound);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function _initiateWithdraw(uint256 numShares) internal {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n if (\n depositReceipts[msg.sender].amount > 0 ||\n depositReceipts[msg.sender].unredeemedShares > 0\n ) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n bool withdrawalIsSameRound = withdrawal.round == currentRound;\n\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n\n uint256 existingShares = uint256(withdrawal.shares);\n\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return withdrawAmount the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n require(withdrawAmount > 0, \"!withdrawAmount\");\n transferAsset(msg.sender, withdrawAmount);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map.\n * Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k.\n * Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0.\n * @param numRounds is the number of rounds to initialize in the map\n */\n function initRounds(uint256 numRounds) external nonReentrant {\n require(numRounds > 0, \"!numRounds\");\n\n uint256 _round = vaultState.round;\n for (uint256 i = 0; i < numRounds; i++) {\n uint256 index = _round + i;\n require(roundPricePerShare[index] == 0, \"Initialized\"); // AVOID OVERWRITING ACTUAL VALUES\n roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT;\n }\n }\n\n /**\n * @notice Helper function that performs most administrative tasks\n * such as minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @param currentQueuedWithdrawShares is the queued withdraw shares for the current round\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _closeRound(\n uint256 lastQueuedWithdrawAmount,\n uint256 currentQueuedWithdrawShares\n ) internal returns (uint256 lockedBalance, uint256 queuedWithdrawAmount) {\n address recipient = feeRecipient;\n uint256 mintShares;\n uint256 performanceFeeInAsset;\n uint256 totalVaultFee;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n ) = VaultLifecycleWithSwap.closeRound(\n vaultState,\n VaultLifecycleWithSwap.CloseParams(\n vaultParams.decimals,\n IERC20(vaultParams.asset).balanceOf(address(this)),\n totalSupply(),\n lastQueuedWithdrawAmount,\n performanceFee,\n managementFee,\n currentQueuedWithdrawShares\n )\n );\n\n // Finalize the pricePerShare at the end of the round\n uint256 currentRound = vaultState.round;\n roundPricePerShare[currentRound] = newPricePerShare;\n\n emit CollectVaultFees(\n performanceFeeInAsset,\n totalVaultFee,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (totalVaultFee > 0) {\n transferAsset(payable(recipient), totalVaultFee);\n }\n\n return (lockedBalance, queuedWithdrawAmount);\n }\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) internal {\n address asset = vaultParams.asset;\n if (asset == WETH) {\n IWETH(WETH).withdraw(amount);\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Transfer failed\");\n return;\n }\n IERC20(asset).safeTransfer(recipient, amount);\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n external\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n // After calling closeRound, current option is set to none\n // We also commit the lockedAmount but do not deposit into Opyn\n // which results in double counting of asset balance and lockedAmount\n\n return\n optionState.currentOption != address(0)\n ? uint256(vaultState.lockedAmount).add(\n IERC20(vaultParams.asset).balanceOf(address(this))\n )\n : IERC20(vaultParams.asset).balanceOf(address(this));\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n}\n" + }, + "contracts/vaults/BaseVaultWithSwap/RibbonThetaVaultWithSwap.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport {ISwap} from \"../../interfaces/ISwap.sol\";\nimport {\n RibbonThetaVaultStorage\n} from \"../../storage/RibbonThetaVaultStorage.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../libraries/VaultLifecycleWithSwap.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {ILiquidityGauge} from \"../../interfaces/ILiquidityGauge.sol\";\nimport {RibbonVault} from \"./base/RibbonVault.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\n\n/**\n * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe\n * the inheritance chain closely.\n * Any changes/appends in storage variable needs to happen in RibbonThetaVaultStorage.\n * RibbonThetaVault should not inherit from any other contract aside from RibbonVault, RibbonThetaVaultStorage\n */\ncontract RibbonThetaVaultWithSwap is RibbonVault, RibbonThetaVaultStorage {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice oTokenFactory is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event NewOffer(\n uint256 swapId,\n address seller,\n address oToken,\n address biddingToken,\n uint256 minPrice,\n uint256 minBidSize,\n uint256 totalSize\n );\n\n /************************************************\n * STRUCTS\n ***********************************************/\n\n /**\n * @notice Initialization parameters for the vault.\n * @param _owner is the owner of the vault with critical permissions\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n */\n struct InitParams {\n address _owner;\n address _keeper;\n address _feeRecipient;\n uint256 _managementFee;\n uint256 _performanceFee;\n string _tokenName;\n string _tokenSymbol;\n address _optionsPremiumPricer;\n address _strikeSelection;\n }\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _swapContract\n ) RibbonVault(_weth, _usdc, _gammaController, _marginPool, _swapContract) {\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n OTOKEN_FACTORY = _oTokenFactory;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n * @param _initParams is the struct with vault initialization parameters\n * @param _vaultParams is the struct with vault general data\n */\n function initialize(\n InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n baseInitialize(\n _initParams._owner,\n _initParams._keeper,\n _initParams._feeRecipient,\n _initParams._managementFee,\n _initParams._performanceFee,\n _initParams._tokenName,\n _initParams._tokenSymbol,\n _vaultParams\n );\n require(\n _initParams._optionsPremiumPricer != address(0),\n \"!_optionsPremiumPricer\"\n );\n require(\n _initParams._strikeSelection != address(0),\n \"!_strikeSelection\"\n );\n\n optionsPremiumPricer = _initParams._optionsPremiumPricer;\n strikeSelection = _initParams._strikeSelection;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new strike selection contract\n * @param newStrikeSelection is the address of the new strike selection contract\n */\n function setStrikeSelection(address newStrikeSelection) external onlyOwner {\n require(newStrikeSelection != address(0), \"!newStrikeSelection\");\n strikeSelection = newStrikeSelection;\n }\n\n /**\n * @notice Sets the new options premium pricer contract\n * @param newOptionsPremiumPricer is the address of the new strike selection contract\n */\n function setOptionsPremiumPricer(address newOptionsPremiumPricer)\n external\n onlyOwner\n {\n require(\n newOptionsPremiumPricer != address(0),\n \"!newOptionsPremiumPricer\"\n );\n optionsPremiumPricer = newOptionsPremiumPricer;\n }\n\n /**\n * @notice Optionality to set strike price manually\n * Should be called after closeRound if we are setting current week's strike\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice) external onlyOwner {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Sets the new liquidityGauge contract for this vault\n * @param newLiquidityGauge is the address of the new liquidityGauge contract\n */\n function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {\n liquidityGauge = newLiquidityGauge;\n }\n\n /**\n * @notice Sets oToken Premium\n * @param minPrice is the new oToken Premium in the units of 10**18\n */\n function setMinPrice(uint256 minPrice) external onlyKeeper {\n require(minPrice > 0, \"!minPrice\");\n currentOtokenPremium = minPrice;\n }\n\n /**\n * @notice Sets the new Vault Pauser contract for this vault\n * @param newVaultPauser is the address of the new vaultPauser contract\n */\n function setVaultPauser(address newVaultPauser) external onlyOwner {\n vaultPauser = newVaultPauser;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n transferAsset(msg.sender, amount);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n _initiateWithdraw(numShares);\n currentQueuedWithdrawShares = currentQueuedWithdrawShares.add(\n numShares\n );\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /**\n * @notice Stakes a users vault shares\n * @param numShares is the number of shares to stake\n */\n function stake(uint256 numShares) external nonReentrant {\n address _liquidityGauge = liquidityGauge;\n require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit\n require(numShares > 0);\n uint256 heldByAccount = balanceOf(msg.sender);\n if (heldByAccount < numShares) {\n _redeem(numShares.sub(heldByAccount), false);\n }\n _transfer(msg.sender, address(this), numShares);\n _approve(address(this), _liquidityGauge, numShares);\n ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);\n }\n\n /**\n * @notice Closes the existing short and calculate the shares to mint, new price per share &\n amount of funds to re-allocate as collateral for the new round\n * Since we are incrementing the round here, the options are sold in the beginning of a round\n * instead of at the end of the round. For example, at round 1, we don't sell any options. We\n * start selling options at the beginning of round 2.\n */\n function closeRound() external nonReentrant {\n address oldOption = optionState.currentOption;\n require(\n oldOption != address(0) || vaultState.round == 1,\n \"Round closed\"\n );\n _closeShort(oldOption);\n\n uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares;\n (uint256 lockedBalance, uint256 queuedWithdrawAmount) =\n _closeRound(\n uint256(lastQueuedWithdrawAmount),\n currQueuedWithdrawShares\n );\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(\n currQueuedWithdrawShares\n );\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n currentQueuedWithdrawShares = 0;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycleWithSwap.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Sets the next option the vault will be shorting\n */\n function commitNextOption() external onlyKeeper nonReentrant {\n address currentOption = optionState.currentOption;\n require(\n currentOption == address(0) && vaultState.round != 1,\n \"Round not closed\"\n );\n\n VaultLifecycleWithSwap.CommitParams memory commitParams =\n VaultLifecycleWithSwap.CommitParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n collateralAsset: vaultParams.asset,\n currentOption: currentOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n strikeSelection: strikeSelection,\n optionsPremiumPricer: optionsPremiumPricer\n });\n\n (address otokenAddress, uint256 strikePrice, uint256 delta) =\n VaultLifecycleWithSwap.commitNextOption(\n commitParams,\n vaultParams,\n vaultState\n );\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n optionState.nextOption = otokenAddress;\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position and create a new offer.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n address newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n uint256 lockedBalance = vaultState.lockedAmount;\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n VaultLifecycleWithSwap.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n _createOffer();\n }\n\n function _createOffer() private {\n address currentOtoken = optionState.currentOption;\n uint256 currOtokenPremium = currentOtokenPremium;\n\n optionAuctionID = VaultLifecycleWithSwap.createOffer(\n currentOtoken,\n currOtokenPremium,\n SWAP_CONTRACT,\n vaultParams\n );\n }\n\n /**\n * @notice Settle current offer\n */\n function settleOffer(ISwap.Bid[] calldata bids)\n external\n onlyKeeper\n nonReentrant\n {\n ISwap(SWAP_CONTRACT).settleOffer(optionAuctionID, bids);\n }\n\n /**\n * @notice Burn the remaining oTokens left over\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n uint256 unlockedAssetAmount =\n VaultLifecycleWithSwap.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n vaultState.lockedAmount = uint104(\n uint256(vaultState.lockedAmount).sub(unlockedAssetAmount)\n );\n }\n\n /**\n * @notice pause a user's vault position\n */\n function pausePosition() external {\n address _vaultPauserAddress = vaultPauser;\n require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit\n _redeem(0, true);\n uint256 heldByAccount = balanceOf(msg.sender);\n _approve(msg.sender, _vaultPauserAddress, heldByAccount);\n IVaultPauser(_vaultPauserAddress).pausePosition(\n msg.sender,\n heldByAccount\n );\n }\n}\n" + }, + "contracts/utils/STETHDepositHelper.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {ICurveETHSTETHPool} from \"../interfaces/ICurveETHSTETHPool.sol\";\nimport {IRibbonThetaVault} from \"../interfaces/IRibbonThetaVault.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\ncontract STETHDepositHelper {\n using SafeERC20 for IERC20;\n\n ICurveETHSTETHPool public immutable curveETHSTETHPool;\n IRibbonThetaVault public immutable stETHVault;\n IERC20 public immutable stETH;\n\n constructor(\n address _curveETHSTETHPool,\n address _stETHVault,\n address _stETH\n ) {\n require(_curveETHSTETHPool != address(0), \"!curveETHSTETH Pool\");\n require(_stETHVault != address(0), \"!stETHVault\");\n require(_stETH != address(0), \"!_stETH\");\n\n curveETHSTETHPool = ICurveETHSTETHPool(_curveETHSTETHPool);\n stETHVault = IRibbonThetaVault(_stETHVault);\n stETH = IERC20(_stETH);\n }\n\n /**\n * Swaps ETH -> stETH on Curve ETH-stETH pool, and deposits into stETH vault\n */\n function deposit(uint256 minSTETHAmount) external payable {\n curveETHSTETHPool.exchange{value: msg.value}(\n 0,\n 1,\n msg.value,\n minSTETHAmount\n );\n uint256 balance = stETH.balanceOf(address(this));\n stETH.safeApprove(address(stETHVault), balance);\n stETHVault.depositYieldTokenFor(balance, msg.sender);\n }\n}\n" + }, + "contracts/interfaces/ICurveETHSTETHPool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\ninterface ICurveETHSTETHPool {\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 dx,\n uint256 min_dy\n ) external payable returns (uint256);\n}\n" + }, + "contracts/utils/OptionsPurchaseQueue.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {SafeCast} from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport {IOptionsPurchaseQueue} from \"../interfaces/IOptionsPurchaseQueue.sol\";\nimport {IRibbonThetaVault} from \"../interfaces/IRibbonThetaVault.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\ncontract OptionsPurchaseQueue is IOptionsPurchaseQueue, Ownable {\n using SafeERC20 for IERC20;\n\n /************************************************\n * STORAGE\n ***********************************************/\n\n /// @notice Stores the purchase queue for each vault\n mapping(address => Purchase[]) public override purchases;\n\n /// @notice Stores the total options being purchased for each vault\n mapping(address => uint256) public override totalOptionsAmount;\n\n /// @notice Stores the amount of options the vault is allocating towards the purchase queue\n /// @dev When vaultAllocatedOptions != 0, new purchase requests for the vault are blocked since the vault has\n /// already allocated options for this contract\n mapping(address => uint256) public override vaultAllocatedOptions;\n\n /// @notice Stores whether a buyer is whitelisted\n mapping(address => bool) public override whitelistedBuyer;\n\n /// @notice Stores the ceiling price of a vaults options\n /// @dev If the ceilingPrice != 0, then the vault is available for requesting purchases\n mapping(address => uint256) public override ceilingPrice;\n\n /// @notice Minimum amount of options a buyer needs to request from a vault, necessary to prevent the purchase\n /// queue from getting griefed\n /// @dev Buyers on the whitelist are exempted from this requirement\n mapping(address => uint256) public override minPurchaseAmount;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n /**\n * @notice Emitted when a purchase is requested\n * @param buyer The buyer requesting the purchase\n * @param vault The vault the buyer is purchasing from\n * @param optionsAmount Amount of options requested\n * @param premiums Total premiums from the buyers (optionsAmount * ceilingPrice)\n */\n event PurchaseRequested(\n address indexed buyer,\n address indexed vault,\n uint256 optionsAmount,\n uint256 premiums\n );\n\n /**\n * @notice Emitted when a purchase is cancelled\n * @param buyer The buyer cancelling their purchase\n * @param vault The vault the buyer was purchasing from\n * @param optionsAmount Amount of options cancelled\n * @param premiums Total premiums transferred back to the buyer\n */\n event PurchaseCancelled(\n address indexed buyer,\n address indexed vault,\n uint256 optionsAmount,\n uint256 premiums\n );\n\n /**\n * @notice Emitted when the vault allocates options to be sold to the buyers\n * @param vault The vault allocating options\n * @param allocatedOptions Amount of options allocated\n */\n event OptionsAllocated(address indexed vault, uint256 allocatedOptions);\n\n /**\n * @notice Emitted when the vault sells options to the buyers\n * @param vault The vault selling the options\n * @param totalPremiums Total premiums earnt by the vault\n * @param totalOptions Total options transferred to the buyers (allocatedOptions)\n */\n event OptionsSold(\n address indexed vault,\n uint256 totalPremiums,\n uint256 totalOptions\n );\n\n /**\n * @notice Emitted when a buyer is whitelisted for purchasing options\n * @param buyer The whitelisted buyer\n */\n event AddWhitelist(address indexed buyer);\n\n /**\n * @notice Emitted when a buyer is removed from the whitelist for purchasing options\n * @param buyer The blacklisted buyer\n */\n event RemoveWhitelist(address indexed buyer);\n\n /**\n * @notice Emitted when the ceiling price for a vault is updated\n * @param vault The vault\n * @param ceilingPrice The new ceiling price\n */\n event CeilingPriceUpdated(address indexed vault, uint256 ceilingPrice);\n\n /**\n * @notice Emitted when the minimum purchase amount for a vault is updated\n * @param vault The vault\n * @param optionsAmount The new minimum purchase amount\n */\n event MinPurchaseAmountUpdated(\n address indexed vault,\n uint256 optionsAmount\n );\n\n /************************************************\n * BUYER OPERATIONS\n ***********************************************/\n\n /**\n * @notice Create a request to purchase options from a vault at the auction settlement price\n * @dev The buyer must be whitelisted to prevent the purchase queue from getting griefed (since sellToBuyers()\n * iterates through it). This function transfers the premiums for the options from the buyer at the ceiling\n * price (maximum price the buyer has to pay), however when the options are sold the buyer only pays the\n * auction settlement price and the leftover premiums are transferred back to the buyer. This function will\n * revert after the vault calls allocateOptions. New purchases can be made after the vault calls sellToBuyers().\n * The requests on the purchased queue are filled FIFO. Any unfilled/partially filled requests are refunded\n * their premiums, this can occur when the vault allocates less options than there are on the queue.\n * @param vault The vault to purchase options from\n * @param optionsAmount Amount of options requested\n * @return premiums Amount of premiums transferred from the buyer\n */\n function requestPurchase(address vault, uint256 optionsAmount)\n external\n override\n returns (uint256)\n {\n uint256 _ceilingPrice = ceilingPrice[vault];\n require(_ceilingPrice != 0, \"Invalid vault\");\n require(optionsAmount != 0, \"!optionsAmount\");\n // Exempt buyers on the whitelist from the minimum purchase requirement\n require(\n optionsAmount >= minPurchaseAmount[vault] ||\n whitelistedBuyer[msg.sender],\n \"Minimum purchase requirement\"\n );\n // This prevents new purchase requested after the vault has set its allocation\n require(vaultAllocatedOptions[vault] == 0, \"Vault allocated\");\n\n // premiums = optionsAmount * ceilingPrice\n uint256 premiums =\n (optionsAmount * _ceilingPrice) / (10**Vault.OTOKEN_DECIMALS);\n\n // Add purchase to queue\n purchases[vault].push(\n Purchase(\n SafeCast.toUint128(optionsAmount),\n SafeCast.toUint128(premiums),\n msg.sender\n )\n );\n\n totalOptionsAmount[vault] += optionsAmount;\n\n // Transfer premiums from the buyer to this contract\n IERC20(IRibbonThetaVault(vault).vaultParams().asset).safeTransferFrom(\n msg.sender,\n address(this),\n premiums\n );\n\n emit PurchaseRequested(msg.sender, vault, optionsAmount, premiums);\n\n return premiums;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Allocate options to the purchase queue\n * @dev Only callable by the vault selling options. Since we cannot allocate more options than there are on the\n * purchase queue, we cap the allocated options at the totalOptionsAmount. The vault decides how many options\n * of its options it wants to allocate. Allows allocating additional options if already called. Transfers the\n * options from the vault to this contract.\n * @param allocatedOptions Maximum amount of options the vault can allocate to buyers\n * @return allocatedOptions The actual amount of options allocated\n */\n function allocateOptions(uint256 allocatedOptions)\n external\n override\n returns (uint256)\n {\n require(ceilingPrice[msg.sender] != 0, \"Not vault\");\n\n // Prevent the vault from allocating more options than there are requested\n allocatedOptions = getOptionsAllocation(msg.sender, allocatedOptions);\n\n // Blocks new purchase requests until sellToBuyers() is called\n vaultAllocatedOptions[msg.sender] += allocatedOptions;\n\n if (allocatedOptions != 0) {\n // Transfer allocated options from the vault to this contract\n IERC20(IRibbonThetaVault(msg.sender).currentOption())\n .safeTransferFrom(msg.sender, address(this), allocatedOptions);\n }\n\n emit OptionsAllocated(msg.sender, allocatedOptions);\n\n return allocatedOptions;\n }\n\n /**\n * @notice Sells allocated options to the buyers on the purchase queue\n * @dev Only callable by the vault. Lets say the vault starts an auction and it doesn't fully fill and\n * settles at a poor price. If this function were callable by anyone, then they could sell the allocated\n * options to the buyers at the poor price. Hence the vault should call this once its auction has settled at a\n * good price. The vault must allocate options first, otherwise all the buyers are returned their premiums.\n * The buyers receive their options at the auction settlement price and any leftover premiums are refunded.\n * If the auction settles above the ceiling price, the vault receives the premiums at the ceiling price (so it\n * receives premiums at a worse price than the auction) and the buyers are not refunded.\n * @param settlementPrice The vault passes in the settlement price of the options\n * @return totalPremiums The total premiums the vault received from the purchase queue\n */\n function sellToBuyers(uint256 settlementPrice)\n external\n override\n returns (uint256)\n {\n require(ceilingPrice[msg.sender] != 0, \"Not vault\");\n\n uint256 totalPremiums;\n uint256 allocatedOptions = vaultAllocatedOptions[msg.sender];\n uint256 totalOptions = allocatedOptions; // Cache allocatedOptions here for emitting an event later\n IERC20 currentOption =\n IERC20(IRibbonThetaVault(msg.sender).currentOption());\n IERC20 asset =\n IERC20(IRibbonThetaVault(msg.sender).vaultParams().asset);\n Purchase[] memory purchaseQueue = purchases[msg.sender];\n\n for (uint256 i; i < purchaseQueue.length; i++) {\n if (allocatedOptions == 0) {\n // Transfer premiums back to the buyer if no options are left\n asset.safeTransfer(\n purchaseQueue[i].buyer,\n purchaseQueue[i].premiums\n );\n } else {\n // Prevent transferring more options than there are allocated\n // optionsAmount = min(purchase.optionsAmount, allocatedOptions)\n uint256 optionsAmount =\n purchaseQueue[i].optionsAmount < allocatedOptions\n ? purchaseQueue[i].optionsAmount\n : allocatedOptions;\n\n // premiums = optionsAmount * settlementPrice\n uint256 premiums =\n (optionsAmount * settlementPrice) /\n (10**Vault.OTOKEN_DECIMALS);\n\n if (premiums < purchaseQueue[i].premiums) {\n // Transfer leftover premiums back to the buyer\n asset.safeTransfer(\n purchaseQueue[i].buyer,\n purchaseQueue[i].premiums - premiums\n );\n\n totalPremiums += premiums;\n } else {\n // If the settlement price exceed the buyer's price (ceiling price), the vault receives all\n // of the buyer's premiums at a worse price than the auction\n totalPremiums += purchaseQueue[i].premiums;\n }\n\n // Transfer options to the buyer\n currentOption.safeTransfer(\n purchaseQueue[i].buyer,\n optionsAmount\n );\n\n // Deduct transferred options from allocatedOptions\n allocatedOptions -= optionsAmount;\n }\n }\n\n // Transfer premiums to the vault\n if (totalPremiums != 0) asset.safeTransfer(msg.sender, totalPremiums);\n\n // Clear purchase queue\n delete purchases[msg.sender];\n totalOptionsAmount[msg.sender] = 0;\n // Purchase requests are unblocked\n vaultAllocatedOptions[msg.sender] = 0;\n\n emit OptionsSold(msg.sender, totalPremiums, totalOptions);\n\n return totalPremiums;\n }\n\n /************************************************\n * OWNER OPERATIONS\n ***********************************************/\n\n /**\n * @notice Cancels all purchase requests for a delisted vault\n * @dev Only callable by the owner. Will revert if options have already been allocated by the vault.\n * @param vault The vault to cancel all purchases for\n */\n function cancelAllPurchases(address vault) external override onlyOwner {\n // Revert if the vault is still listed\n require(ceilingPrice[vault] == 0, \"Vault listed\");\n // This prevents cancellations after the vault has set its allocation\n require(vaultAllocatedOptions[vault] == 0, \"Vault allocated\");\n\n IERC20 asset = IERC20(IRibbonThetaVault(vault).vaultParams().asset);\n Purchase[] memory purchaseQueue = purchases[vault];\n\n for (uint256 i; i < purchaseQueue.length; i++) {\n // Refund premiums to the buyer\n asset.safeTransfer(\n purchaseQueue[i].buyer,\n purchaseQueue[i].premiums\n );\n\n emit PurchaseCancelled(\n purchaseQueue[i].buyer,\n vault,\n purchaseQueue[i].optionsAmount,\n purchaseQueue[i].premiums\n );\n }\n\n // Clear purchase queue\n delete purchases[vault];\n totalOptionsAmount[vault] = 0;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Adds a buyer to the purchase queue whitelist\n * @dev Only callable by the owner\n * @param buyer The buyer to whitelist\n */\n function addWhitelist(address buyer) external override onlyOwner {\n require(buyer != address(0), \"!buyer\");\n\n whitelistedBuyer[buyer] = true;\n\n emit AddWhitelist(buyer);\n }\n\n /**\n * @notice Removes a buyer from the purchase queue whitelist\n * @dev Only callable by the owner\n * @param buyer The buyer to remove from the whitelist\n */\n function removeWhitelist(address buyer) external override onlyOwner {\n require(buyer != address(0), \"!buyer\");\n\n whitelistedBuyer[buyer] = false;\n\n emit RemoveWhitelist(buyer);\n }\n\n /**\n * @notice Set the ceiling price for a vault\n * @dev Only callable by the owner\n * @param vault The vault to set a ceiling price for\n * @param price The ceiling price\n */\n function setCeilingPrice(address vault, uint256 price)\n external\n override\n onlyOwner\n {\n require(vault != address(0), \"!vault\");\n\n // Setting the ceiling price to 0 is the same as delisting a vault\n ceilingPrice[vault] = price;\n\n emit CeilingPriceUpdated(vault, price);\n }\n\n /**\n * @notice Sets the minimum purchase amount for a vault\n * @dev Only callable by the owner\n * @param vault The vault to set the minimum purchase amount for\n * @param optionsAmount The minimum options purchase amount\n */\n function setMinPurchaseAmount(address vault, uint256 optionsAmount)\n external\n override\n onlyOwner\n {\n require(vault != address(0), \"!vault\");\n\n minPurchaseAmount[vault] = optionsAmount;\n\n emit MinPurchaseAmountUpdated(vault, optionsAmount);\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Gets all the purchase requests for a vault\n * @param vault The vault to get purchase requests for\n * @return purchases The purchase array\n */\n function getPurchases(address vault)\n external\n view\n override\n returns (Purchase[] memory)\n {\n return purchases[vault];\n }\n\n /**\n * @notice Gets the premiums the buyer needs to deposit to request a certain amount of options\n * @param vault The vault to purchase options from\n * @param optionsAmount Amount of options requested\n * @return premiums Premiums required to request a purchase\n */\n function getPremiums(address vault, uint256 optionsAmount)\n external\n view\n override\n returns (uint256)\n {\n // premiums = optionsAmount * ceilingPrice\n return\n (optionsAmount * ceilingPrice[vault]) / (10**Vault.OTOKEN_DECIMALS);\n }\n\n /**\n * @notice Gets the amount of options the vault can allocate to the queue\n * @param vault The vault selling options to the queue\n * @param allocatedOptions Maximum amount of options the vault can allocate to the queue\n * @return allocatedOptions Actual amount of options the vault allocates to the queue\n */\n function getOptionsAllocation(address vault, uint256 allocatedOptions)\n public\n view\n override\n returns (uint256)\n {\n // Prevent the vault from allocating more options than there are requested\n uint256 optionsAmount =\n totalOptionsAmount[vault] - vaultAllocatedOptions[vault];\n // allocatedOptions = min(allocatedOptions, totalOptionsAmount[vault] - vaultAllocatedOptions[vault])\n return\n optionsAmount < allocatedOptions ? optionsAmount : allocatedOptions;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/tests/MockRibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {IOptionsPurchaseQueue} from \"../interfaces/IOptionsPurchaseQueue.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\ncontract MockRibbonVault {\n Vault.VaultParams public vaultParams;\n\n address public currentOption;\n\n function setAsset(address asset) external {\n vaultParams.asset = asset;\n }\n\n function setCurrentOption(address option) external {\n currentOption = option;\n }\n\n function allocateOptions(\n address optionsPurchaseQueue,\n address option,\n uint256 optionsAmount\n ) external {\n IERC20(option).approve(optionsPurchaseQueue, optionsAmount);\n IOptionsPurchaseQueue(optionsPurchaseQueue).allocateOptions(\n optionsAmount\n );\n }\n\n function sellToBuyers(address optionsPurchaseQueue, uint256 settlementPrice)\n external\n {\n IOptionsPurchaseQueue(optionsPurchaseQueue).sellToBuyers(\n settlementPrice\n );\n }\n}\n" + }, + "contracts/storage/RibbonDeltaVaultStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport {IRibbonThetaVault} from \"../interfaces/IRibbonThetaVault.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\nabstract contract RibbonDeltaVaultStorageV1 {\n // Ribbon counterparty theta vault\n IRibbonThetaVault public counterpartyThetaVault;\n // % of funds to be used for weekly option purchase\n uint256 public optionAllocation;\n // Delta vault equivalent of lockedAmount\n uint256 public balanceBeforePremium;\n // User Id of delta vault in latest gnosis auction\n Vault.AuctionSellOrder public auctionSellOrder;\n}\n\nabstract contract RibbonDeltaVaultStorageV2 {\n // Amount locked for scheduled withdrawals last week;\n uint128 public lastQueuedWithdrawAmount;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of RibbonDeltaVaultStorage\n// e.g. RibbonDeltaVaultStorage, so finally it would look like\n// contract RibbonDeltaVaultStorage is RibbonDeltaVaultStorageV1, RibbonDeltaVaultStorageV2\nabstract contract RibbonDeltaVaultStorage is\n RibbonDeltaVaultStorageV1,\n RibbonDeltaVaultStorageV2\n{\n\n}\n" + }, + "contracts/tests/TestVaultLifecycleTreasury.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {VaultLifecycleTreasury} from \"../libraries/VaultLifecycleTreasury.sol\";\n\ncontract TestVaultLifecycleTreasury {\n function getNextExpiry(uint256 currentExpiry, uint256 interval)\n external\n pure\n returns (uint256)\n {\n return VaultLifecycleTreasury.getNextExpiry(currentExpiry, interval);\n }\n}\n" + }, + "contracts/utils/DeltaStrikeSelection.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {\n IPriceOracle\n} from \"@ribbon-finance/rvol/contracts/interfaces/IPriceOracle.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\nimport {\n IManualVolatilityOracle\n} from \"@ribbon-finance/rvol/contracts/interfaces/IManualVolatilityOracle.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\ncontract DeltaStrikeSelection is Ownable {\n using SafeMath for uint256;\n\n /**\n * Immutables\n */\n IOptionsPremiumPricer public immutable optionsPremiumPricer;\n\n IManualVolatilityOracle public immutable volatilityOracle;\n\n // delta for options strike price selection. 1 is 10000 (10**4)\n uint256 public delta;\n\n // step in absolute terms at which we will increment\n // (ex: 100 * 10 ** assetOracleDecimals means we will move at increments of 100 points)\n uint256 public step;\n\n // multiplier to shift asset prices\n uint256 private immutable assetOracleMultiplier;\n\n // Delta are in 4 decimal places. 1 * 10**4 = 1 delta.\n uint256 private constant DELTA_MULTIPLIER = 10**4;\n\n // ChainLink's USD Price oracles return results in 8 decimal places\n uint256 private constant ORACLE_PRICE_MULTIPLIER = 10**8;\n\n event DeltaSet(uint256 oldDelta, uint256 newDelta, address indexed owner);\n event StepSet(uint256 oldStep, uint256 newStep, address indexed owner);\n\n constructor(\n address _optionsPremiumPricer,\n uint256 _delta,\n uint256 _step\n ) {\n require(_optionsPremiumPricer != address(0), \"!_optionsPremiumPricer\");\n require(_delta > 0, \"!_delta\");\n require(_delta <= DELTA_MULTIPLIER, \"newDelta cannot be more than 1\");\n require(_step > 0, \"!_step\");\n\n optionsPremiumPricer = IOptionsPremiumPricer(_optionsPremiumPricer);\n volatilityOracle = IManualVolatilityOracle(\n IOptionsPremiumPricer(_optionsPremiumPricer).volatilityOracle()\n );\n // ex: delta = 7500 (.75)\n delta = _delta;\n uint256 _assetOracleMultiplier =\n 10 **\n IPriceOracle(\n IOptionsPremiumPricer(_optionsPremiumPricer).priceOracle()\n )\n .decimals();\n\n step = _step;\n\n assetOracleMultiplier = _assetOracleMultiplier;\n }\n\n /**\n * @notice Gets the strike price satisfying the delta value\n * given the expiry timestamp and whether option is call or put\n * @param expiryTimestamp is the unix timestamp of expiration\n * @param isPut is whether option is put or call\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\n * @return newDelta is the delta of the option given its parameters\n */\n function getStrikePrice(uint256 expiryTimestamp, bool isPut)\n external\n view\n returns (uint256 newStrikePrice, uint256 newDelta)\n {\n // asset's annualized volatility\n uint256 annualizedVol =\n volatilityOracle.annualizedVol(optionsPremiumPricer.optionId()).mul(\n 10**10\n );\n return _getStrikePrice(expiryTimestamp, isPut, annualizedVol);\n }\n\n /**\n * @notice Gets the strike price satisfying the delta value\n * given the expiry timestamp and whether option is call or put\n * @param expiryTimestamp is the unix timestamp of expiration\n * @param isPut is whether option is put or call\n * @param annualizedVol is IV of the asset at the specified delta\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\n * @return newDelta is the delta of the option given its parameters\n */\n function getStrikePriceWithVol(\n uint256 expiryTimestamp,\n bool isPut,\n uint256 annualizedVol\n ) external view returns (uint256 newStrikePrice, uint256 newDelta) {\n return\n _getStrikePrice(expiryTimestamp, isPut, annualizedVol.mul(10**10));\n }\n\n /**\n * @notice Gets the strike price satisfying the delta value\n * given the expiry timestamp and whether option is call or put\n * @param expiryTimestamp is the unix timestamp of expiration\n * @param isPut is whether option is put or call\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\n * @return newDelta is the delta of the option given its parameters\n */\n\n function _getStrikePrice(\n uint256 expiryTimestamp,\n bool isPut,\n uint256 annualizedVol\n ) internal view returns (uint256 newStrikePrice, uint256 newDelta) {\n require(\n expiryTimestamp > block.timestamp,\n \"Expiry must be in the future!\"\n );\n\n // asset price\n uint256 assetPrice = optionsPremiumPricer.getUnderlyingPrice();\n\n // For each asset prices with step of 'step' (down if put, up if call)\n // if asset's getOptionDelta(currStrikePrice, spotPrice, annualizedVol, t) == (isPut ? 1 - delta:delta)\n // with certain margin of error\n // return strike price\n\n uint256 strike =\n isPut\n ? assetPrice.sub(assetPrice % step).sub(step)\n : assetPrice.add(step - (assetPrice % step)).add(step);\n uint256 targetDelta = isPut ? DELTA_MULTIPLIER.sub(delta) : delta;\n uint256 prevDelta = isPut ? 0 : DELTA_MULTIPLIER;\n\n while (true) {\n uint256 currDelta =\n optionsPremiumPricer.getOptionDelta(\n assetPrice.mul(ORACLE_PRICE_MULTIPLIER).div(\n assetOracleMultiplier\n ),\n strike,\n annualizedVol,\n expiryTimestamp\n );\n // If the current delta is between the previous\n // strike price delta and current strike price delta\n // then we are done\n bool foundTargetStrikePrice =\n isPut\n ? targetDelta >= prevDelta && targetDelta <= currDelta\n : targetDelta <= prevDelta && targetDelta >= currDelta;\n\n if (foundTargetStrikePrice) {\n uint256 finalDelta =\n _getBestDelta(prevDelta, currDelta, targetDelta, isPut);\n uint256 finalStrike =\n _getBestStrike(finalDelta, prevDelta, strike, isPut);\n require(\n isPut\n ? finalStrike <= assetPrice\n : finalStrike >= assetPrice,\n \"Invalid strike price\"\n );\n // make decimals consistent with oToken strike price decimals (10 ** 8)\n return (\n finalStrike.mul(ORACLE_PRICE_MULTIPLIER).div(\n assetOracleMultiplier\n ),\n finalDelta\n );\n }\n\n strike = isPut ? strike.sub(step) : strike.add(step);\n\n prevDelta = currDelta;\n }\n }\n\n /**\n * @notice Rounds to best delta value\n * @param prevDelta is the delta of the previous strike price\n * @param currDelta is delta of the current strike price\n * @param targetDelta is the delta we are targeting\n * @param isPut is whether its a put\n * @return the best delta value\n */\n function _getBestDelta(\n uint256 prevDelta,\n uint256 currDelta,\n uint256 targetDelta,\n bool isPut\n ) private pure returns (uint256) {\n uint256 finalDelta;\n\n // for tie breaks (ex: 0.05 <= 0.1 <= 0.15) round to higher strike price\n // for calls and lower strike price for puts for deltas\n if (isPut) {\n uint256 upperBoundDiff = currDelta.sub(targetDelta);\n uint256 lowerBoundDiff = targetDelta.sub(prevDelta);\n finalDelta = lowerBoundDiff <= upperBoundDiff\n ? prevDelta\n : currDelta;\n } else {\n uint256 upperBoundDiff = prevDelta.sub(targetDelta);\n uint256 lowerBoundDiff = targetDelta.sub(currDelta);\n finalDelta = lowerBoundDiff <= upperBoundDiff\n ? currDelta\n : prevDelta;\n }\n\n return finalDelta;\n }\n\n /**\n * @notice Rounds to best delta value\n * @param finalDelta is the best delta value we found\n * @param prevDelta is delta of the previous strike price\n * @param strike is the strike of the previous iteration\n * @param isPut is whether its a put\n * @return the best strike\n */\n function _getBestStrike(\n uint256 finalDelta,\n uint256 prevDelta,\n uint256 strike,\n bool isPut\n ) private view returns (uint256) {\n if (finalDelta != prevDelta) {\n return strike;\n }\n return isPut ? strike.add(step) : strike.sub(step);\n }\n\n /**\n * @notice Sets new delta value\n * @param newDelta is the new delta value\n */\n function setDelta(uint256 newDelta) external onlyOwner {\n require(newDelta > 0, \"!newDelta\");\n require(newDelta <= DELTA_MULTIPLIER, \"newDelta cannot be more than 1\");\n uint256 oldDelta = delta;\n delta = newDelta;\n emit DeltaSet(oldDelta, newDelta, msg.sender);\n }\n\n /**\n * @notice Sets new step value\n * @param newStep is the new step value\n */\n function setStep(uint256 newStep) external onlyOwner {\n require(newStep > 0, \"!newStep\");\n uint256 oldStep = step;\n step = newStep;\n emit StepSet(oldStep, newStep, msg.sender);\n }\n}\n" + }, + "contracts/libraries/UniswapRouter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {ISwapRouter} from \"../interfaces/ISwapRouter.sol\";\nimport {IUniswapV3Factory} from \"../interfaces/IUniswapV3Factory.sol\";\nimport \"./Path.sol\";\n\nlibrary UniswapRouter {\n using Path for bytes;\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n /**\n * @notice Check if the path set for swap is valid\n * @param swapPath is the swap path e.g. encodePacked(tokenIn, poolFee, tokenOut)\n * @param validTokenIn is the contract address of the correct tokenIn\n * @param validTokenOut is the contract address of the correct tokenOut\n * @param uniswapFactory is the contract address of UniswapV3 factory\n * @return isValidPath is whether the path is valid\n */\n function checkPath(\n bytes memory swapPath,\n address validTokenIn,\n address validTokenOut,\n address uniswapFactory\n ) internal view returns (bool isValidPath) {\n // Function checks if the tokenIn and tokenOut in the swapPath\n // matches the validTokenIn and validTokenOut specified.\n address tokenIn;\n address tokenOut;\n address tempTokenIn;\n uint24 fee;\n IUniswapV3Factory factory = IUniswapV3Factory(uniswapFactory);\n\n // Return early if swapPath is below the bare minimum (43)\n require(swapPath.length >= 43, \"Path too short\");\n // Return early if swapPath is above the max (66)\n // At worst we have 2 hops e.g. USDC > WETH > asset\n require(swapPath.length <= 66, \"Path too long\");\n\n // Decode the first pool in path\n (tokenIn, tokenOut, fee) = swapPath.decodeFirstPool();\n\n // Check to factory if pool exists\n require(\n factory.getPool(tokenIn, tokenOut, fee) != address(0),\n \"Pool does not exist\"\n );\n\n // Check next pool if multiple pools\n while (swapPath.hasMultiplePools()) {\n // Remove the first pool from path\n swapPath = swapPath.skipToken();\n // Check the next pool and update tokenOut\n (tempTokenIn, tokenOut, fee) = swapPath.decodeFirstPool();\n\n require(\n factory.getPool(tokenIn, tokenOut, fee) != address(0),\n \"Pool does not exist\"\n );\n }\n\n return tokenIn == validTokenIn && tokenOut == validTokenOut;\n }\n\n /**\n * @notice Swaps assets by calling UniswapV3 router\n * @param recipient is the address of recipient of the tokenOut\n * @param tokenIn is the address of the token given to the router\n * @param amountIn is the amount of tokenIn given to the router\n * @param minAmountOut is the minimum acceptable amount of tokenOut received from swap\n * @param router is the contract address of UniswapV3 router\n * @param swapPath is the swap path e.g. encodePacked(tokenIn, poolFee, tokenOut)\n * @return amountOut is the amount of tokenOut received from the swap\n */\n function swap(\n address recipient,\n address tokenIn,\n uint256 amountIn,\n uint256 minAmountOut,\n address router,\n bytes calldata swapPath\n ) internal returns (uint256 amountOut) {\n // Approve router to spend tokenIn\n IERC20(tokenIn).safeApprove(router, amountIn);\n\n // Swap assets using UniswapV3 router\n ISwapRouter.ExactInputParams memory swapParams =\n ISwapRouter.ExactInputParams({\n recipient: recipient,\n path: swapPath,\n deadline: block.timestamp.add(10 minutes),\n amountIn: amountIn,\n amountOutMinimum: minAmountOut\n });\n\n amountOut = ISwapRouter(router).exactInput(swapParams);\n\n return amountOut;\n }\n}\n" + }, + "contracts/interfaces/ISwapRouter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity =0.8.4;\npragma abicoder v2;\n\n/// Source: https://github.com/Uniswap/v3-core/blob/main/contracts/interfaces/callback/IUniswapV3SwapCallback.sol\n\n/// @title Callback for IUniswapV3PoolActions#swap\n/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface\ninterface IUniswapV3SwapCallback {\n /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.\n /// @dev In the implementation you must pay the pool tokens owed for the swap.\n /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.\n /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.\n /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.\n /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.\n /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call\n function uniswapV3SwapCallback(\n int256 amount0Delta,\n int256 amount1Delta,\n bytes calldata data\n ) external;\n}\n\n/// Source: https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/ISwapRouter.sol\n\n/// @title Router token swapping functionality\n/// @notice Functions for swapping tokens via Uniswap V3\ninterface ISwapRouter is IUniswapV3SwapCallback {\n struct ExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n uint24 fee;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInputSingle(ExactInputSingleParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n uint24 fee;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutputSingle(ExactOutputSingleParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n\n struct ExactOutputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutput(ExactOutputParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n}\n" + }, + "contracts/interfaces/IUniswapV3Factory.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// Source: https://github.com/Uniswap/v3-core/blob/main/contracts/interfaces/IUniswapV3Factory.sol\npragma solidity =0.8.4;\n\n/// @title The interface for the Uniswap V3 Factory\n/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees\ninterface IUniswapV3Factory {\n /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist\n /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order\n /// @param tokenA The contract address of either token0 or token1\n /// @param tokenB The contract address of the other token\n /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip\n /// @return pool The pool address\n function getPool(\n address tokenA,\n address tokenB,\n uint24 fee\n ) external view returns (address pool);\n}\n" + }, + "contracts/libraries/Path.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// Source: https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/Path.sol\npragma solidity =0.8.4;\n\nimport \"./BytesLib.sol\";\n\n/// @title Functions for manipulating path data for multihop swaps\nlibrary Path {\n using BytesLib for bytes;\n\n /// @dev The length of the bytes encoded address\n uint256 private constant ADDR_SIZE = 20;\n /// @dev The length of the bytes encoded fee\n uint256 private constant FEE_SIZE = 3;\n\n /// @dev The offset of a single token address and pool fee\n uint256 private constant NEXT_OFFSET = ADDR_SIZE + FEE_SIZE;\n /// @dev The offset of an encoded pool key\n uint256 private constant POP_OFFSET = NEXT_OFFSET + ADDR_SIZE;\n /// @dev The minimum length of an encoding that contains 2 or more pools\n uint256 private constant MULTIPLE_POOLS_MIN_LENGTH =\n POP_OFFSET + NEXT_OFFSET;\n\n /// @notice Returns true iff the path contains two or more pools\n /// @param path The encoded swap path\n /// @return True if path contains two or more pools, otherwise false\n function hasMultiplePools(bytes memory path) internal pure returns (bool) {\n return path.length >= MULTIPLE_POOLS_MIN_LENGTH;\n }\n\n /// @notice Returns the number of pools in the path\n /// @param path The encoded swap path\n /// @return The number of pools in the path\n function numPools(bytes memory path) internal pure returns (uint256) {\n // Ignore the first token address. From then on every fee and token offset indicates a pool.\n return ((path.length - ADDR_SIZE) / NEXT_OFFSET);\n }\n\n /// @notice Decodes the first pool in path\n /// @param path The bytes encoded swap path\n /// @return tokenA The first token of the given pool\n /// @return tokenB The second token of the given pool\n /// @return fee The fee level of the pool\n function decodeFirstPool(bytes memory path)\n internal\n pure\n returns (\n address tokenA,\n address tokenB,\n uint24 fee\n )\n {\n tokenA = path.toAddress(0);\n fee = path.toUint24(ADDR_SIZE);\n tokenB = path.toAddress(NEXT_OFFSET);\n }\n\n /// @notice Gets the segment corresponding to the first pool in the path\n /// @param path The bytes encoded swap path\n /// @return The segment containing all data necessary to target the first pool in the path\n function getFirstPool(bytes memory path)\n internal\n pure\n returns (bytes memory)\n {\n return path.slice(0, POP_OFFSET);\n }\n\n /// @notice Skips a token + fee element from the buffer and returns the remainder\n /// @param path The swap path\n /// @return The remaining token + fee elements in the path\n function skipToken(bytes memory path) internal pure returns (bytes memory) {\n return path.slice(NEXT_OFFSET, path.length - NEXT_OFFSET);\n }\n}\n" + }, + "contracts/libraries/BytesLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Source: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol\n/*\n * @title Solidity Bytes Arrays Utils\n * @author Gonçalo Sá \n *\n * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.\n * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.\n */\npragma solidity =0.8.4;\n\nlibrary BytesLib {\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n ) internal pure returns (bytes memory) {\n require(_length + 31 >= _length, \"slice_overflow\");\n require(_bytes.length >= _start + _length, \"slice_outOfBounds\");\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(\n add(tempBytes, lengthmod),\n mul(0x20, iszero(lengthmod))\n )\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(\n add(\n add(_bytes, lengthmod),\n mul(0x20, iszero(lengthmod))\n ),\n _start\n )\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n\n function toAddress(bytes memory _bytes, uint256 _start)\n internal\n pure\n returns (address)\n {\n require(_bytes.length >= _start + 20, \"toAddress_outOfBounds\");\n address tempAddress;\n\n assembly {\n tempAddress := div(\n mload(add(add(_bytes, 0x20), _start)),\n 0x1000000000000000000000000\n )\n }\n\n return tempAddress;\n }\n\n function toUint24(bytes memory _bytes, uint256 _start)\n internal\n pure\n returns (uint24)\n {\n require(_bytes.length >= _start + 3, \"toUint24_outOfBounds\");\n uint24 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x3), _start))\n }\n\n return tempUint;\n }\n}\n" + }, + "contracts/tests/MockLiquidityGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\ncontract MockLiquidityGauge {\n using SafeERC20 for IERC20;\n\n address public lp_token;\n uint256 public totalSupply;\n mapping(address => uint256) public balanceOf;\n\n constructor(address _lp_token) {\n lp_token = _lp_token;\n }\n\n function deposit(\n uint256 _value,\n address _addr,\n bool\n ) external {\n if (_value != 0) {\n totalSupply += _value;\n balanceOf[_addr] += _value;\n\n IERC20(lp_token).safeTransferFrom(\n msg.sender,\n address(this),\n _value\n );\n }\n }\n}\n" + }, + "contracts/vendor/upgrades/UpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport './Proxy.sol';\nimport '@openzeppelin/contracts/utils/Address.sol';\n\n/**\n * @title UpgradeabilityProxy\n * @dev This contract implements a proxy that allows to change the\n * implementation address to which it will delegate.\n * Such a change is called an implementation upgrade.\n */\ncontract UpgradeabilityProxy is Proxy {\n /**\n * @dev Contract constructor.\n * @param _logic Address of the initial implementation.\n * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\n */\n constructor(address _logic, bytes memory _data) payable {\n assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));\n _setImplementation(_logic);\n if(_data.length > 0) {\n (bool success,) = _logic.delegatecall(_data);\n require(success);\n }\n }\n\n /**\n * @dev Emitted when the implementation is upgraded.\n * @param implementation Address of the new implementation.\n */\n event Upgraded(address indexed implementation);\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Returns the current implementation.\n * @return impl Address of the current implementation\n */\n function _implementation() internal override view returns (address impl) {\n bytes32 slot = IMPLEMENTATION_SLOT;\n assembly {\n impl := sload(slot)\n }\n }\n\n /**\n * @dev Upgrades the proxy to a new implementation.\n * @param newImplementation Address of the new implementation.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Sets the implementation address of the proxy.\n * @param newImplementation Address of the new implementation.\n */\n function _setImplementation(address newImplementation) internal {\n require(Address.isContract(newImplementation), \"Cannot set a proxy implementation to a non-contract address\");\n\n bytes32 slot = IMPLEMENTATION_SLOT;\n\n assembly {\n sstore(slot, newImplementation)\n }\n }\n}\n" + }, + "contracts/vendor/upgrades/Proxy.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\n/**\n * @title Proxy\n * @dev Implements delegation of calls to other contracts, with proper\n * forwarding of return values and bubbling of failures.\n * It defines a fallback function that delegates all calls to the address\n * returned by the abstract _implementation() internal function.\n */\nabstract contract Proxy {\n /**\n * @dev Fallback function.\n * Implemented entirely in `_fallback`.\n */\n fallback () payable external {\n _fallback();\n }\n\n /**\n * @dev Receive function.\n * Implemented entirely in `_fallback`.\n */\n receive () payable external {\n // _fallback();\n }\n\n /**\n * @return The Address of the implementation.\n */\n function _implementation() internal virtual view returns (address);\n\n /**\n * @dev Delegates execution to an implementation contract.\n * This is a low level function that doesn't return to its internal call site.\n * It will return to the external caller whatever the implementation returns.\n * @param implementation Address to delegate.\n */\n function _delegate(address implementation) internal {\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 { revert(0, returndatasize()) }\n default { return(0, returndatasize()) }\n }\n }\n\n /**\n * @dev Function that is run as the first thing in the fallback function.\n * Can be redefined in derived contracts to add functionality.\n * Redefinitions must call super._willFallback().\n */\n function _willFallback() internal virtual {\n }\n\n /**\n * @dev fallback implementation.\n * Extracted to enable manual triggering.\n */\n function _fallback() internal {\n _willFallback();\n _delegate(_implementation());\n }\n}\n" + }, + "contracts/vendor/upgrades/AdminUpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.8;\n\nimport './UpgradeabilityProxy.sol';\n\n/**\n * @title AdminUpgradeabilityProxy\n * @dev This contract combines an upgradeability proxy with an authorization\n * mechanism for administrative tasks.\n * All external functions in this contract must be guarded by the\n * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity\n * feature proposal that would enable this to be done automatically.\n */\ncontract AdminUpgradeabilityProxy is UpgradeabilityProxy {\n /**\n * Contract constructor.\n * @param _logic address of the initial implementation.\n * @param admin_ Address of the proxy administrator.\n * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\n */\n constructor(address _logic, address admin_, bytes memory _data) UpgradeabilityProxy(_logic, _data) payable {\n assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));\n _setAdmin(admin_);\n }\n\n /**\n * @dev Emitted when the administration has been transferred.\n * @param previousAdmin Address of the previous admin.\n * @param newAdmin Address of the new admin.\n */\n event AdminChanged(address previousAdmin, address newAdmin);\n\n /**\n * @dev Storage slot with the admin of the contract.\n * This is the keccak-256 hash of \"eip1967.proxy.admin\" subtracted by 1, and is\n * validated in the constructor.\n */\n\n bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n /**\n * @dev Modifier to check whether the `msg.sender` is the admin.\n * If it is, it will run the function. Otherwise, it will delegate the call\n * to the implementation.\n */\n modifier ifAdmin() {\n if (msg.sender == _admin()) {\n _;\n } else {\n _fallback();\n }\n }\n\n /**\n * @return adminAddress The address of the proxy admin.\n */\n function admin() external ifAdmin returns (address adminAddress) {\n return _admin();\n }\n\n /**\n * @return implementationAddress The address of the implementation.\n */\n function implementation() external ifAdmin returns (address implementationAddress) {\n return _implementation();\n }\n\n /**\n * @dev Changes the admin of the proxy.\n * Only the current admin can call this function.\n * @param newAdmin Address to transfer proxy administration to.\n */\n function changeAdmin(address newAdmin) external ifAdmin {\n require(newAdmin != address(0), \"Cannot change the admin of a proxy to the zero address\");\n emit AdminChanged(_admin(), newAdmin);\n _setAdmin(newAdmin);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy.\n * Only the admin can call this function.\n * @param newImplementation Address of the new implementation.\n */\n function upgradeTo(address newImplementation) external ifAdmin {\n _upgradeTo(newImplementation);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy and call a function\n * on the new implementation.\n * This is useful to initialize the proxied contract.\n * @param newImplementation Address of the new implementation.\n * @param data Data to send as msg.data in the low level call.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {\n _upgradeTo(newImplementation);\n (bool success,) = newImplementation.delegatecall(data);\n require(success);\n }\n\n /**\n * @return adm The admin slot.\n */\n function _admin() internal view returns (address adm) {\n bytes32 slot = ADMIN_SLOT;\n assembly {\n adm := sload(slot)\n }\n }\n\n /**\n * @dev Sets the address of the proxy admin.\n * @param newAdmin Address of the new proxy admin.\n */\n function _setAdmin(address newAdmin) internal {\n bytes32 slot = ADMIN_SLOT;\n\n assembly {\n sstore(slot, newAdmin)\n }\n }\n\n /**\n * @dev Only fall back when the sender is not the admin.\n */\n function _willFallback() internal override virtual {\n require(msg.sender != _admin(), \"Cannot call fallback function from the proxy admin\");\n super._willFallback();\n }\n}\n" + }, + "contracts/tests/TestVaultLifecycle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {VaultLifecycle} from \"../libraries/VaultLifecycle.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\ncontract TestVaultLifecycle {\n Vault.VaultState public vaultState;\n\n function getNextFriday(uint256 currentExpiry)\n external\n pure\n returns (uint256 nextFriday)\n {\n return VaultLifecycle.getNextFriday(currentExpiry);\n }\n\n function getNextExpiry(address currentOption)\n external\n view\n returns (uint256 nextExpiry)\n {\n return VaultLifecycle.getNextExpiry(currentOption);\n }\n\n function balanceOf(address account) public view returns (uint256) {\n if (account == address(this)) {\n return 1 ether;\n }\n return 0;\n }\n\n function setVaultState(Vault.VaultState calldata newVaultState) public {\n vaultState.totalPending = newVaultState.totalPending;\n vaultState.queuedWithdrawShares = newVaultState.queuedWithdrawShares;\n }\n\n function rollover(VaultLifecycle.RolloverParams calldata params)\n external\n view\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares,\n uint256 performanceFeeInAsset,\n uint256 totalVaultFee\n )\n {\n return VaultLifecycle.rollover(vaultState, params);\n }\n\n function getAuctionSettlementPrice(\n address gnosisEasyAuction,\n uint256 optionAuctionID\n ) external view returns (uint256) {\n return\n VaultLifecycle.getAuctionSettlementPrice(\n gnosisEasyAuction,\n optionAuctionID\n );\n }\n}\n" + }, + "contracts/utils/ManualStrikeSelection.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract ManualStrikeSelection is Ownable {\n /// @dev Selected strike price\n uint256 public strikePrice;\n\n /// @dev Delta for options strike price selection. 1 is 10000 (10**4)\n uint256 public constant delta = 1000;\n\n /**\n * @notice Sets the strike price, only callable by the owner\n * @param _strikePrice is the strike price of the option\n */\n function setStrikePrice(uint256 _strikePrice) external onlyOwner {\n strikePrice = _strikePrice;\n }\n\n /**\n * @notice Gets the strike price satisfying the delta value\n * given the expiry timestamp and whether option is call or put\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\n * @return newDelta is the delta of the option given its parameters\n */\n function getStrikePrice(uint256, bool)\n external\n view\n returns (uint256, uint256)\n {\n return (strikePrice, delta);\n }\n}\n" + }, + "contracts/tests/MockERC20.sol": { + "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockERC20 is ERC20 {\n constructor(string memory _name, string memory _symbol)\n ERC20(_name, _symbol)\n {}\n\n function mint(address to, uint256 value) external {\n _mint(to, value);\n }\n\n function burn(address from, uint256 value) external {\n _burn(from, value);\n }\n}\n" + }, + "contracts/tests/TestShareMath.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {ShareMath} from \"../libraries/ShareMath.sol\";\n\ncontract TestShareMath {\n function assetToShares(\n uint256 assetAmount,\n uint256 pps,\n uint256 decimals\n ) external pure returns (uint256) {\n return ShareMath.assetToShares(assetAmount, pps, decimals);\n }\n\n function sharesToAsset(\n uint256 shares,\n uint256 pps,\n uint256 decimals\n ) external pure returns (uint256) {\n return ShareMath.sharesToAsset(shares, pps, decimals);\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/deployments/mainnet/solcInputs/b8ecb5d79e85c723002fd505e92835b0.json b/deployments/mainnet/solcInputs/b8ecb5d79e85c723002fd505e92835b0.json new file mode 100644 index 000000000..abe118170 --- /dev/null +++ b/deployments/mainnet/solcInputs/b8ecb5d79e85c723002fd505e92835b0.json @@ -0,0 +1,305 @@ +{ + "language": "Solidity", + "sources": { + "contracts/interfaces/GammaInterface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nlibrary GammaTypes {\n // vault is a struct of 6 arrays that describe a position a user has, a user can have multiple vaults.\n struct Vault {\n // addresses of oTokens a user has shorted (i.e. written) against this vault\n address[] shortOtokens;\n // addresses of oTokens a user has bought and deposited in this vault\n // user can be long oTokens without opening a vault (e.g. by buying on a DEX)\n // generally, long oTokens will be 'deposited' in vaults to act as collateral\n // in order to write oTokens against (i.e. in spreads)\n address[] longOtokens;\n // addresses of other ERC-20s a user has deposited as collateral in this vault\n address[] collateralAssets;\n // quantity of oTokens minted/written for each oToken address in shortOtokens\n uint256[] shortAmounts;\n // quantity of oTokens owned and held in the vault for each oToken address in longOtokens\n uint256[] longAmounts;\n // quantity of ERC-20 deposited as collateral in the vault for each ERC-20 address in collateralAssets\n uint256[] collateralAmounts;\n }\n}\n\ninterface IOtoken {\n function underlyingAsset() external view returns (address);\n\n function strikeAsset() external view returns (address);\n\n function collateralAsset() external view returns (address);\n\n function strikePrice() external view returns (uint256);\n\n function expiryTimestamp() external view returns (uint256);\n\n function isPut() external view returns (bool);\n}\n\ninterface IOtokenFactory {\n function getOtoken(\n address _underlyingAsset,\n address _strikeAsset,\n address _collateralAsset,\n uint256 _strikePrice,\n uint256 _expiry,\n bool _isPut\n ) external view returns (address);\n\n function createOtoken(\n address _underlyingAsset,\n address _strikeAsset,\n address _collateralAsset,\n uint256 _strikePrice,\n uint256 _expiry,\n bool _isPut\n ) external returns (address);\n\n function getTargetOtokenAddress(\n address _underlyingAsset,\n address _strikeAsset,\n address _collateralAsset,\n uint256 _strikePrice,\n uint256 _expiry,\n bool _isPut\n ) external view returns (address);\n\n event OtokenCreated(\n address tokenAddress,\n address creator,\n address indexed underlying,\n address indexed strike,\n address indexed collateral,\n uint256 strikePrice,\n uint256 expiry,\n bool isPut\n );\n}\n\ninterface IController {\n // possible actions that can be performed\n enum ActionType {\n OpenVault,\n MintShortOption,\n BurnShortOption,\n DepositLongOption,\n WithdrawLongOption,\n DepositCollateral,\n WithdrawCollateral,\n SettleVault,\n Redeem,\n Call,\n Liquidate\n }\n\n struct ActionArgs {\n // type of action that is being performed on the system\n ActionType actionType;\n // address of the account owner\n address owner;\n // address which we move assets from or to (depending on the action type)\n address secondAddress;\n // asset that is to be transfered\n address asset;\n // index of the vault that is to be modified (if any)\n uint256 vaultId;\n // amount of asset that is to be transfered\n uint256 amount;\n // each vault can hold multiple short / long / collateral assets\n // but we are restricting the scope to only 1 of each in this version\n // in future versions this would be the index of the short / long / collateral asset that needs to be modified\n uint256 index;\n // any other data that needs to be passed in for arbitrary function calls\n bytes data;\n }\n\n struct RedeemArgs {\n // address to which we pay out the oToken proceeds\n address receiver;\n // oToken that is to be redeemed\n address otoken;\n // amount of oTokens that is to be redeemed\n uint256 amount;\n }\n\n function getPayout(address _otoken, uint256 _amount)\n external\n view\n returns (uint256);\n\n function operate(ActionArgs[] calldata _actions) external;\n\n function getAccountVaultCounter(address owner)\n external\n view\n returns (uint256);\n\n function oracle() external view returns (address);\n\n function getVault(address _owner, uint256 _vaultId)\n external\n view\n returns (GammaTypes.Vault memory);\n\n function getProceed(address _owner, uint256 _vaultId)\n external\n view\n returns (uint256);\n\n function isSettlementAllowed(\n address _underlying,\n address _strike,\n address _collateral,\n uint256 _expiry\n ) external view returns (bool);\n}\n\ninterface IOracle {\n function setAssetPricer(address _asset, address _pricer) external;\n\n function updateAssetPricer(address _asset, address _pricer) external;\n\n function getPrice(address _asset) external view returns (uint256);\n}\n" + }, + "contracts/oracles/OpynOracle.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\nimport {IOracle} from \"../interfaces/GammaInterface.sol\";\nimport {IPriceOracle} from \"../interfaces/IPriceOracle.sol\";\n\ncontract OpynOracle is IPriceOracle {\n /// @dev base decimals\n uint256 public constant override decimals = 8;\n\n /// @notice Gamma Protocol oracle\n IOracle public immutable oracle;\n\n /// @notice Asset to get the price of\n address public immutable asset;\n\n constructor(address _oracle, address _asset) {\n require(_oracle != address(0), \"!oracle\");\n require(_asset != address(0), \"!asset\");\n\n oracle = IOracle(_oracle);\n asset = _asset;\n }\n\n function latestAnswer() external view override returns (uint256) {\n return oracle.getPrice(asset);\n }\n}\n" + }, + "contracts/interfaces/IPriceOracle.sol": { + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.4;\n\ninterface IPriceOracle {\n function decimals() external view returns (uint256 _decimals);\n\n function latestAnswer() external view returns (uint256 price);\n}\n" + }, + "contracts/oracles/SAvaxOracle.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\nimport \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IPriceOracle} from \"../interfaces/IPriceOracle.sol\";\nimport {ISAVAX} from \"../interfaces/ISAVAX.sol\";\nimport {DSMath} from \"../vendor/DSMath.sol\";\n\ncontract SAvaxOracle is IPriceOracle {\n using SafeMath for uint256;\n\n AggregatorV3Interface public immutable WAVAXOracle;\n ISAVAX public immutable sAVAX;\n\n constructor(address _sAVAX, address _WAVAXOracle) {\n require(_sAVAX != address(0), \"!sAVAX\");\n require(_WAVAXOracle != address(0), \"!WAVAXOracle\");\n\n sAVAX = ISAVAX(_sAVAX);\n WAVAXOracle = AggregatorV3Interface(_WAVAXOracle);\n }\n\n function _underlyingPriceToSAvaxPrice(uint256 underlyingPrice)\n private\n view\n returns (uint256)\n {\n // Passing 1e18 to getPooledAvaxByShares() gives us the number of AVAX per sAVAX.\n uint256 sAvaxPerAvax = sAVAX.getPooledAvaxByShares(1e18);\n return sAvaxPerAvax.mul(underlyingPrice).div(1e18);\n }\n\n function latestAnswer() external view override returns (uint256) {\n (\n uint80 roundID,\n int256 price,\n ,\n uint256 timeStamp,\n uint80 answeredInRound\n ) = WAVAXOracle.latestRoundData();\n\n require(answeredInRound >= roundID, \"Stale oracle price\");\n require(timeStamp != 0, \"!timeStamp\");\n uint256 underlyingPrice = uint256(DSMath.imax(price, 0));\n return _underlyingPriceToSAvaxPrice(underlyingPrice);\n }\n\n function decimals() external view override returns (uint256) {\n return WAVAXOracle.decimals();\n }\n}\n" + }, + "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n// CAUTION\n// This version of SafeMath should only be used with Solidity 0.8 or later,\n// because it relies on the compiler's built in overflow checks.\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations.\n *\n * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler\n * now has built in overflow checking.\n */\nlibrary SafeMath {\n /**\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n uint256 c = a + b;\n if (c < a) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b > a) return (false, 0);\n return (true, a - b);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) return (true, 0);\n uint256 c = a * b;\n if (c / a != b) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a / b);\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a % b);\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n return a + b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n return a - b;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n return a * b;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator.\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n return a % b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n * overflow (when the result is negative).\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {trySub}.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b <= a, errorMessage);\n return a - b;\n }\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a / b;\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting with custom message when dividing by zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryMod}.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a % b;\n }\n }\n}\n" + }, + "contracts/interfaces/ISAVAX.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface ISAVAX is IERC20 {\n function getSharesByPooledAvax(uint256 avaxAmount)\n external\n view\n returns (uint256);\n\n function getPooledAvaxByShares(uint256 shareAmount)\n external\n view\n returns (uint256);\n\n function submit() external payable returns (uint256);\n}\n" + }, + "contracts/vendor/DSMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n\n/// math.sol -- mixin for inline numerical wizardry\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >0.4.13;\n\nlibrary DSMath {\n function add(uint256 x, uint256 y) internal pure returns (uint256 z) {\n require((z = x + y) >= x, \"ds-math-add-overflow\");\n }\n\n function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {\n require((z = x - y) <= x, \"ds-math-sub-underflow\");\n }\n\n function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {\n require(y == 0 || (z = x * y) / y == x, \"ds-math-mul-overflow\");\n }\n\n function min(uint256 x, uint256 y) internal pure returns (uint256 z) {\n return x <= y ? x : y;\n }\n\n function max(uint256 x, uint256 y) internal pure returns (uint256 z) {\n return x >= y ? x : y;\n }\n\n function imin(int256 x, int256 y) internal pure returns (int256 z) {\n return x <= y ? x : y;\n }\n\n function imax(int256 x, int256 y) internal pure returns (int256 z) {\n return x >= y ? x : y;\n }\n\n uint256 constant WAD = 10**18;\n uint256 constant RAY = 10**27;\n\n //rounds to zero if x*y < WAD / 2\n function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {\n z = add(mul(x, y), WAD / 2) / WAD;\n }\n\n //rounds to zero if x*y < WAD / 2\n function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {\n z = add(mul(x, y), RAY / 2) / RAY;\n }\n\n //rounds to zero if x*y < WAD / 2\n function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {\n z = add(mul(x, WAD), y / 2) / y;\n }\n\n //rounds to zero if x*y < RAY / 2\n function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {\n z = add(mul(x, RAY), y / 2) / y;\n }\n\n // This famous algorithm is called \"exponentiation by squaring\"\n // and calculates x^n with x as fixed-point and n as regular unsigned.\n //\n // It's O(log n), instead of O(n) for naive repeated multiplication.\n //\n // These facts are why it works:\n //\n // If n is even, then x^n = (x^2)^(n/2).\n // If n is odd, then x^n = x * x^(n-1),\n // and applying the equation for even x gives\n // x^n = x * (x^2)^((n-1) / 2).\n //\n // Also, EVM division is flooring and\n // floor[(n-1) / 2] = floor[n / 2].\n //\n function rpow(uint256 x, uint256 n) internal pure returns (uint256 z) {\n z = n % 2 != 0 ? x : RAY;\n\n for (n /= 2; n != 0; n /= 2) {\n x = rmul(x, x);\n\n if (n % 2 != 0) {\n z = rmul(z, x);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "contracts/vaults/YearnVaults/RibbonThetaYearnVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {DSMath} from \"../../vendor/DSMath.sol\";\nimport {GnosisAuction} from \"../../libraries/GnosisAuction.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {VaultLifecycle} from \"../../libraries/VaultLifecycle.sol\";\nimport {VaultLifecycleYearn} from \"../../libraries/VaultLifecycleYearn.sol\";\nimport {ILiquidityGauge} from \"../../interfaces/ILiquidityGauge.sol\";\nimport {RibbonVault} from \"./base/RibbonVault.sol\";\nimport {\n RibbonThetaYearnVaultStorage\n} from \"../../storage/RibbonThetaYearnVaultStorage.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\n/**\n * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe\n * the inheritance chain closely.\n * Any changes/appends in storage variable needs to happen in RibbonThetaYearnVaultStorage.\n * RibbonThetaYearnVault should not inherit from any other contract aside from RibbonVault, RibbonThetaYearnVaultStorage\n */\ncontract RibbonThetaYearnVault is RibbonVault, RibbonThetaYearnVaultStorage {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice oTokenFactory is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event PremiumDiscountSet(\n uint256 premiumDiscount,\n uint256 newPremiumDiscount\n );\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event InitiateGnosisAuction(\n address indexed auctioningToken,\n address indexed biddingToken,\n uint256 auctionCounter,\n address indexed manager\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions\n * @param _yearnRegistry is the address of the yearn registry from token to vault token\n */\n constructor(\n address _weth,\n address _usdc,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _gnosisEasyAuction,\n address _yearnRegistry\n )\n RibbonVault(\n _weth,\n _usdc,\n _gammaController,\n _marginPool,\n _gnosisEasyAuction,\n _yearnRegistry\n )\n {\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n OTOKEN_FACTORY = _oTokenFactory;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n * @param _owner is the owner of the vault with critical permissions\n * @param _keeper is the keeper of the vault with medium permissions (weekly actions)\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n * @param _premiumDiscount is the vault's discount applied to the premium\n * @param _auctionDuration is the duration of the gnosis auction\n * @param _vaultParams is the struct with vault general data\n */\n function initialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n address _optionsPremiumPricer,\n address _strikeSelection,\n uint32 _premiumDiscount,\n uint256 _auctionDuration,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n baseInitialize(\n _owner,\n _keeper,\n _feeRecipient,\n _managementFee,\n _performanceFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n require(_optionsPremiumPricer != address(0), \"!_optionsPremiumPricer\");\n require(_strikeSelection != address(0), \"!_strikeSelection\");\n require(\n _premiumDiscount > 0 &&\n _premiumDiscount < 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"!_premiumDiscount\"\n );\n require(_auctionDuration >= MIN_AUCTION_DURATION, \"!_auctionDuration\");\n optionsPremiumPricer = _optionsPremiumPricer;\n strikeSelection = _strikeSelection;\n premiumDiscount = _premiumDiscount;\n auctionDuration = _auctionDuration;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new auction duration\n * @param newAuctionDuration is the auction duration\n */\n function setAuctionDuration(uint256 newAuctionDuration) external onlyOwner {\n require(\n newAuctionDuration >= MIN_AUCTION_DURATION,\n \"Invalid auction duration\"\n );\n emit AuctionDurationSet(auctionDuration, newAuctionDuration);\n\n auctionDuration = newAuctionDuration;\n }\n\n /**\n * @notice Sets the new strike selection contract\n * @param newStrikeSelection is the address of the new strike selection contract\n */\n function setStrikeSelection(address newStrikeSelection) external onlyOwner {\n require(newStrikeSelection != address(0), \"!newStrikeSelection\");\n strikeSelection = newStrikeSelection;\n }\n\n /**\n * @notice Sets the new options premium pricer contract\n * @param newOptionsPremiumPricer is the address of the new strike selection contract\n */\n function setOptionsPremiumPricer(address newOptionsPremiumPricer)\n external\n onlyOwner\n {\n require(\n newOptionsPremiumPricer != address(0),\n \"!newOptionsPremiumPricer\"\n );\n optionsPremiumPricer = newOptionsPremiumPricer;\n }\n\n /**\n * @notice Optionality to set strike price manually\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice) external onlyOwner {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Sets the new liquidityGauge contract for this vault\n * @param newLiquidityGauge is the address of the new liquidityGauge contract\n */\n function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {\n liquidityGauge = newLiquidityGauge;\n }\n\n /**\n * @notice Sets the new optionsPurchaseQueue contract for this vault\n * @param newOptionsPurchaseQueue is the address of the new optionsPurchaseQueue contract\n */\n function setOptionsPurchaseQueue(address newOptionsPurchaseQueue)\n external\n onlyOwner\n {\n optionsPurchaseQueue = newOptionsPurchaseQueue;\n }\n\n /**\n * @notice Sets oToken Premium\n * @param minPrice is the new oToken Premium in the units of 10**18\n */\n function setMinPrice(uint256 minPrice) external onlyKeeper {\n require(minPrice > 0, \"!minPrice\");\n currentOtokenPremium = minPrice;\n }\n\n /**\n * @notice Sets the new Vault Pauser contract for this vault\n * @param newVaultPauser is the address of the new vaultPauser contract\n */\n function setVaultPauser(address newVaultPauser) external onlyOwner {\n vaultPauser = newVaultPauser;\n }\n\n /**\n * @notice Sets whether vault is using yearn\n * @param isPaused is whether vault is using yearn\n */\n function setYearnPaused(bool isPaused) external onlyOwner {\n isYearnPaused = isPaused;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n VaultLifecycleYearn.transferAsset(\n WETH,\n vaultParams.asset,\n msg.sender,\n amount\n );\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n _initiateWithdraw(numShares);\n currentQueuedWithdrawShares = currentQueuedWithdrawShares.add(\n numShares\n );\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /**\n * @notice Stakes a users vault shares\n * @param numShares is the number of shares to stake\n */\n function stake(uint256 numShares) external nonReentrant {\n address _liquidityGauge = liquidityGauge;\n require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit\n require(numShares > 0);\n uint256 heldByAccount = balanceOf(msg.sender);\n if (heldByAccount < numShares) {\n _redeem(numShares.sub(heldByAccount), false);\n }\n _transfer(msg.sender, address(this), numShares);\n _approve(address(this), _liquidityGauge, numShares);\n ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);\n }\n\n /**\n * @notice Sets the next option the vault will be shorting, and closes the existing short.\n * This allows all the users to withdraw if the next option is malicious.\n */\n function commitAndClose() external nonReentrant {\n address oldOption = optionState.currentOption;\n\n VaultLifecycle.CloseParams memory closeParams =\n VaultLifecycle.CloseParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n currentOption: oldOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n strikeSelection: strikeSelection,\n optionsPremiumPricer: optionsPremiumPricer,\n premiumDiscount: premiumDiscount\n });\n\n (address otokenAddress, uint256 strikePrice, uint256 delta) =\n VaultLifecycleYearn.commitAndClose(\n closeParams,\n vaultParams,\n vaultState,\n address(collateralToken),\n isYearnPaused\n );\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n optionState.nextOption = otokenAddress;\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n\n _closeShort(oldOption);\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycle.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares;\n\n (address newOption, uint256 queuedWithdrawAmount) =\n _rollToNextOption(\n rollToNextOptionParams(\n lastQueuedWithdrawAmount,\n currQueuedWithdrawShares,\n isYearnPaused\n )\n );\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(\n currQueuedWithdrawShares\n );\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n currentQueuedWithdrawShares = 0;\n\n // Locked balance denominated in `collateralToken`\n // there is a slight imprecision with regards to calculating back from yearn token -> underlying\n // that stems from miscoordination between ytoken .deposit() amount wrapped and pricePerShare\n // at that point in time.\n // ex: if I have 1 eth, deposit 1 eth into yearn vault and calculate value of yearn token balance\n // denominated in eth (via balance(yearn token) * pricePerShare) we will get 1 eth - 1 wei.\n\n // We are subtracting `collateralAsset` balance by queuedWithdrawAmount denominated in `collateralAsset` plus\n // a buffer for withdrawals taking into account slippage from yearn vault\n\n uint256 lockedBalance =\n isYearnPaused\n ? IERC20(vaultParams.asset).balanceOf(address(this)).sub(\n queuedWithdrawAmount\n )\n : collateralToken.balanceOf(address(this)).sub(\n DSMath.wdiv(\n queuedWithdrawAmount.add(\n queuedWithdrawAmount\n .mul(YEARN_WITHDRAWAL_BUFFER)\n .div(10000)\n ),\n collateralToken.pricePerShare().mul(\n VaultLifecycleYearn.decimalShift(\n address(collateralToken)\n )\n )\n )\n );\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n uint256 optionsMintAmount =\n VaultLifecycle.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n VaultLifecycle.allocateOptions(\n optionsPurchaseQueue,\n newOption,\n optionsMintAmount,\n VaultLifecycle.QUEUE_OPTION_ALLOCATION\n );\n\n _startAuction();\n }\n\n /**\n * @notice Initiate the gnosis auction.\n */\n function startAuction() external onlyKeeper nonReentrant {\n _startAuction();\n }\n\n function _startAuction() private {\n GnosisAuction.AuctionDetails memory auctionDetails;\n\n address currentOtoken = optionState.currentOption;\n\n auctionDetails.oTokenAddress = currentOtoken;\n auctionDetails.gnosisEasyAuction = GNOSIS_EASY_AUCTION;\n auctionDetails.asset = vaultParams.asset;\n auctionDetails.assetDecimals = vaultParams.decimals;\n auctionDetails.oTokenPremium = currentOtokenPremium;\n auctionDetails.duration = auctionDuration;\n\n optionAuctionID = VaultLifecycle.startAuction(auctionDetails);\n }\n\n /**\n * @notice Burn the remaining oTokens left over from gnosis auction.\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n uint256 unlockedAssetAmount =\n VaultLifecycle.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n\n vaultState.lockedAmount = uint104(\n uint256(vaultState.lockedAmount).sub(unlockedAssetAmount)\n );\n }\n\n /**\n * @notice pause a user's vault position\n */\n function pausePosition() external {\n address _vaultPauserAddress = vaultPauser;\n require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit\n _redeem(0, true);\n uint256 heldByAccount = balanceOf(msg.sender);\n _approve(msg.sender, _vaultPauserAddress, heldByAccount);\n IVaultPauser(_vaultPauserAddress).pausePosition(\n msg.sender,\n heldByAccount\n );\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "contracts/libraries/GnosisAuction.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {DSMath} from \"../vendor/DSMath.sol\";\nimport {IGnosisAuction} from \"../interfaces/IGnosisAuction.sol\";\nimport {IOtoken} from \"../interfaces/GammaInterface.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {IRibbonThetaVault} from \"../interfaces/IRibbonThetaVault.sol\";\n\nlibrary GnosisAuction {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n event InitiateGnosisAuction(\n address indexed auctioningToken,\n address indexed biddingToken,\n uint256 auctionCounter,\n address indexed manager\n );\n\n event PlaceAuctionBid(\n uint256 auctionId,\n address indexed auctioningToken,\n uint256 sellAmount,\n uint256 buyAmount,\n address indexed bidder\n );\n\n struct AuctionDetails {\n address oTokenAddress;\n address gnosisEasyAuction;\n address asset;\n uint256 assetDecimals;\n uint256 oTokenPremium;\n uint256 duration;\n }\n\n struct BidDetails {\n address oTokenAddress;\n address gnosisEasyAuction;\n address asset;\n uint256 assetDecimals;\n uint256 auctionId;\n uint256 lockedBalance;\n uint256 optionAllocation;\n uint256 optionPremium;\n address bidder;\n }\n\n function startAuction(AuctionDetails calldata auctionDetails)\n internal\n returns (uint256 auctionID)\n {\n uint256 oTokenSellAmount =\n getOTokenSellAmount(auctionDetails.oTokenAddress);\n require(oTokenSellAmount > 0, \"No otokens to sell\");\n\n IERC20(auctionDetails.oTokenAddress).safeApprove(\n auctionDetails.gnosisEasyAuction,\n IERC20(auctionDetails.oTokenAddress).balanceOf(address(this))\n );\n\n // minBidAmount is total oTokens to sell * premium per oToken\n // shift decimals to correspond to decimals of USDC for puts\n // and underlying for calls\n uint256 minBidAmount =\n DSMath.wmul(\n oTokenSellAmount.mul(10**10),\n auctionDetails.oTokenPremium\n );\n\n minBidAmount = auctionDetails.assetDecimals > 18\n ? minBidAmount.mul(10**(auctionDetails.assetDecimals.sub(18)))\n : minBidAmount.div(\n 10**(uint256(18).sub(auctionDetails.assetDecimals))\n );\n\n require(\n minBidAmount <= type(uint96).max,\n \"optionPremium * oTokenSellAmount > type(uint96) max value!\"\n );\n\n uint256 auctionEnd = block.timestamp.add(auctionDetails.duration);\n\n auctionID = IGnosisAuction(auctionDetails.gnosisEasyAuction)\n .initiateAuction(\n // address of oToken we minted and are selling\n auctionDetails.oTokenAddress,\n // address of asset we want in exchange for oTokens. Should match vault `asset`\n auctionDetails.asset,\n // orders can be cancelled at any time during the auction\n auctionEnd,\n // order will last for `duration`\n auctionEnd,\n // we are selling all of the otokens minus a fee taken by gnosis\n uint96(oTokenSellAmount),\n // the minimum we are willing to sell all the oTokens for. A discount is applied on black-scholes price\n uint96(minBidAmount),\n // the minimum bidding amount must be 1 * 10 ** -assetDecimals\n 1,\n // the min funding threshold\n 0,\n // no atomic closure\n false,\n // access manager contract\n address(0),\n // bytes for storing info like a whitelist for who can bid\n bytes(\"\")\n );\n\n emit InitiateGnosisAuction(\n auctionDetails.oTokenAddress,\n auctionDetails.asset,\n auctionID,\n msg.sender\n );\n }\n\n function placeBid(BidDetails calldata bidDetails)\n internal\n returns (\n uint256 sellAmount,\n uint256 buyAmount,\n uint64 userId\n )\n {\n // calculate how much to allocate\n sellAmount = bidDetails\n .lockedBalance\n .mul(bidDetails.optionAllocation)\n .div(100 * Vault.OPTION_ALLOCATION_MULTIPLIER);\n\n // divide the `asset` sellAmount by the target premium per oToken to\n // get the number of oTokens to buy (8 decimals)\n buyAmount = sellAmount\n .mul(10**(bidDetails.assetDecimals.add(Vault.OTOKEN_DECIMALS)))\n .div(bidDetails.optionPremium)\n .div(10**bidDetails.assetDecimals);\n\n require(\n sellAmount <= type(uint96).max,\n \"sellAmount > type(uint96) max value!\"\n );\n require(\n buyAmount <= type(uint96).max,\n \"buyAmount > type(uint96) max value!\"\n );\n\n // approve that amount\n IERC20(bidDetails.asset).safeApprove(\n bidDetails.gnosisEasyAuction,\n sellAmount\n );\n\n uint96[] memory _minBuyAmounts = new uint96[](1);\n uint96[] memory _sellAmounts = new uint96[](1);\n bytes32[] memory _prevSellOrders = new bytes32[](1);\n _minBuyAmounts[0] = uint96(buyAmount);\n _sellAmounts[0] = uint96(sellAmount);\n _prevSellOrders[\n 0\n ] = 0x0000000000000000000000000000000000000000000000000000000000000001;\n\n // place sell order with that amount\n userId = IGnosisAuction(bidDetails.gnosisEasyAuction).placeSellOrders(\n bidDetails.auctionId,\n _minBuyAmounts,\n _sellAmounts,\n _prevSellOrders,\n \"0x\"\n );\n\n emit PlaceAuctionBid(\n bidDetails.auctionId,\n bidDetails.oTokenAddress,\n sellAmount,\n buyAmount,\n bidDetails.bidder\n );\n\n return (sellAmount, buyAmount, userId);\n }\n\n function claimAuctionOtokens(\n Vault.AuctionSellOrder calldata auctionSellOrder,\n address gnosisEasyAuction,\n address counterpartyThetaVault\n ) internal {\n bytes32 order =\n encodeOrder(\n auctionSellOrder.userId,\n auctionSellOrder.buyAmount,\n auctionSellOrder.sellAmount\n );\n bytes32[] memory orders = new bytes32[](1);\n orders[0] = order;\n IGnosisAuction(gnosisEasyAuction).claimFromParticipantOrder(\n IRibbonThetaVault(counterpartyThetaVault).optionAuctionID(),\n orders\n );\n }\n\n function getOTokenSellAmount(address oTokenAddress)\n internal\n view\n returns (uint256)\n {\n // We take our current oToken balance. That will be our sell amount\n // but otokens will be transferred to gnosis.\n uint256 oTokenSellAmount =\n IERC20(oTokenAddress).balanceOf(address(this));\n\n require(\n oTokenSellAmount <= type(uint96).max,\n \"oTokenSellAmount > type(uint96) max value!\"\n );\n\n return oTokenSellAmount;\n }\n\n function getOTokenPremiumInStables(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount\n ) internal view returns (uint256) {\n IOtoken newOToken = IOtoken(oTokenAddress);\n IOptionsPremiumPricer premiumPricer =\n IOptionsPremiumPricer(optionsPremiumPricer);\n\n // Apply black-scholes formula (from rvol library) to option given its features\n // and get price for 100 contracts denominated USDC for both call and put options\n uint256 optionPremium =\n premiumPricer.getPremiumInStables(\n newOToken.strikePrice(),\n newOToken.expiryTimestamp(),\n newOToken.isPut()\n );\n\n // Apply a discount to incentivize arbitraguers\n optionPremium = optionPremium.mul(premiumDiscount).div(\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER\n );\n\n require(\n optionPremium <= type(uint96).max,\n \"optionPremium > type(uint96) max value!\"\n );\n\n return optionPremium;\n }\n\n function encodeOrder(\n uint64 userId,\n uint96 buyAmount,\n uint96 sellAmount\n ) internal pure returns (bytes32) {\n return\n bytes32(\n (uint256(userId) << 192) +\n (uint256(buyAmount) << 96) +\n uint256(sellAmount)\n );\n }\n}\n" + }, + "contracts/libraries/Vault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nlibrary Vault {\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n // Fees are 6-decimal places. For example: 20 * 10**6 = 20%\n uint256 internal constant FEE_MULTIPLIER = 10**6;\n\n // Premium discount has 1-decimal place. For example: 80 * 10**1 = 80%. Which represents a 20% discount.\n uint256 internal constant PREMIUM_DISCOUNT_MULTIPLIER = 10;\n\n // Otokens have 8 decimal places.\n uint256 internal constant OTOKEN_DECIMALS = 8;\n\n // Percentage of funds allocated to options is 2 decimal places. 10 * 10**2 = 10%\n uint256 internal constant OPTION_ALLOCATION_MULTIPLIER = 10**2;\n\n // Placeholder uint value to prevent cold writes\n uint256 internal constant PLACEHOLDER_UINT = 1;\n\n struct VaultParams {\n // Option type the vault is selling\n bool isPut;\n // Token decimals for vault shares\n uint8 decimals;\n // Asset used in Theta / Delta Vault\n address asset;\n // Underlying asset of the options sold by vault\n address underlying;\n // Minimum supply of the vault shares issued, for ETH it's 10**10\n uint56 minimumSupply;\n // Vault cap\n uint104 cap;\n }\n\n struct OptionState {\n // Option that the vault is shorting / longing in the next cycle\n address nextOption;\n // Option that the vault is currently shorting / longing\n address currentOption;\n // The timestamp when the `nextOption` can be used by the vault\n uint32 nextOptionReadyAt;\n }\n\n struct VaultState {\n // 32 byte slot 1\n // Current round number. `round` represents the number of `period`s elapsed.\n uint16 round;\n // Amount that is currently locked for selling options\n uint104 lockedAmount;\n // Amount that was locked for selling options in the previous round\n // used for calculating performance fee deduction\n uint104 lastLockedAmount;\n // 32 byte slot 2\n // Stores the total tally of how much of `asset` there is\n // to be used to mint rTHETA tokens\n uint128 totalPending;\n // Total amount of queued withdrawal shares from previous rounds (doesn't include the current round)\n uint128 queuedWithdrawShares;\n }\n\n struct DepositReceipt {\n // Maximum of 65535 rounds. Assuming 1 round is 7 days, maximum is 1256 years.\n uint16 round;\n // Deposit amount, max 20,282,409,603,651 or 20 trillion ETH deposit\n uint104 amount;\n // Unredeemed shares balance\n uint128 unredeemedShares;\n }\n\n struct Withdrawal {\n // Maximum of 65535 rounds. Assuming 1 round is 7 days, maximum is 1256 years.\n uint16 round;\n // Number of shares withdrawn\n uint128 shares;\n }\n\n struct AuctionSellOrder {\n // Amount of `asset` token offered in auction\n uint96 sellAmount;\n // Amount of oToken requested in auction\n uint96 buyAmount;\n // User Id of delta vault in latest gnosis auction\n uint64 userId;\n }\n}\n" + }, + "contracts/libraries/ShareMath.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {Vault} from \"./Vault.sol\";\n\nlibrary ShareMath {\n using SafeMath for uint256;\n\n uint256 internal constant PLACEHOLDER_UINT = 1;\n\n function assetToShares(\n uint256 assetAmount,\n uint256 assetPerShare,\n uint256 decimals\n ) internal pure returns (uint256) {\n // If this throws, it means that vault's roundPricePerShare[currentRound] has not been set yet\n // which should never happen.\n // Has to be larger than 1 because `1` is used in `initRoundPricePerShares` to prevent cold writes.\n require(assetPerShare > PLACEHOLDER_UINT, \"Invalid assetPerShare\");\n\n return assetAmount.mul(10**decimals).div(assetPerShare);\n }\n\n function sharesToAsset(\n uint256 shares,\n uint256 assetPerShare,\n uint256 decimals\n ) internal pure returns (uint256) {\n // If this throws, it means that vault's roundPricePerShare[currentRound] has not been set yet\n // which should never happen.\n // Has to be larger than 1 because `1` is used in `initRoundPricePerShares` to prevent cold writes.\n require(assetPerShare > PLACEHOLDER_UINT, \"Invalid assetPerShare\");\n\n return shares.mul(assetPerShare).div(10**decimals);\n }\n\n /**\n * @notice Returns the shares unredeemed by the user given their DepositReceipt\n * @param depositReceipt is the user's deposit receipt\n * @param currentRound is the `round` stored on the vault\n * @param assetPerShare is the price in asset per share\n * @param decimals is the number of decimals the asset/shares use\n * @return unredeemedShares is the user's virtual balance of shares that are owed\n */\n function getSharesFromReceipt(\n Vault.DepositReceipt memory depositReceipt,\n uint256 currentRound,\n uint256 assetPerShare,\n uint256 decimals\n ) internal pure returns (uint256 unredeemedShares) {\n if (depositReceipt.round > 0 && depositReceipt.round < currentRound) {\n uint256 sharesFromRound =\n assetToShares(depositReceipt.amount, assetPerShare, decimals);\n\n return\n uint256(depositReceipt.unredeemedShares).add(sharesFromRound);\n }\n return depositReceipt.unredeemedShares;\n }\n\n function pricePerShare(\n uint256 totalSupply,\n uint256 totalBalance,\n uint256 pendingAmount,\n uint256 decimals\n ) internal pure returns (uint256) {\n uint256 singleShare = 10**decimals;\n return\n totalSupply > 0\n ? singleShare.mul(totalBalance.sub(pendingAmount)).div(\n totalSupply\n )\n : singleShare;\n }\n\n /************************************************\n * HELPERS\n ***********************************************/\n\n function assertUint104(uint256 num) internal pure {\n require(num <= type(uint104).max, \"Overflow uint104\");\n }\n\n function assertUint128(uint256 num) internal pure {\n require(num <= type(uint128).max, \"Overflow uint128\");\n }\n}\n" + }, + "contracts/libraries/VaultLifecycle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {ShareMath} from \"./ShareMath.sol\";\nimport {IStrikeSelection} from \"../interfaces/IRibbon.sol\";\nimport {GnosisAuction} from \"./GnosisAuction.sol\";\nimport {\n IOtokenFactory,\n IOtoken,\n IController,\n GammaTypes\n} from \"../interfaces/GammaInterface.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\nimport {IGnosisAuction} from \"../interfaces/IGnosisAuction.sol\";\nimport {IOptionsPurchaseQueue} from \"../interfaces/IOptionsPurchaseQueue.sol\";\nimport {SupportsNonCompliantERC20} from \"./SupportsNonCompliantERC20.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\n\nlibrary VaultLifecycle {\n using SafeMath for uint256;\n using SupportsNonCompliantERC20 for IERC20;\n\n struct CloseParams {\n address OTOKEN_FACTORY;\n address USDC;\n address currentOption;\n uint256 delay;\n uint16 lastStrikeOverrideRound;\n uint256 overriddenStrikePrice;\n address strikeSelection;\n address optionsPremiumPricer;\n uint256 premiumDiscount;\n }\n\n /// @notice Default maximum option allocation for the queue (50%)\n uint256 internal constant QUEUE_OPTION_ALLOCATION = 5000;\n\n /**\n * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @return otokenAddress is the address of the new option\n * @return strikePrice is the strike price of the new option\n * @return delta is the delta of the new option\n */\n function commitAndClose(\n CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n Vault.VaultState storage vaultState\n )\n external\n returns (\n address otokenAddress,\n uint256 strikePrice,\n uint256 delta\n )\n {\n uint256 expiry = getNextExpiry(closeParams.currentOption);\n\n IStrikeSelection selection =\n IStrikeSelection(closeParams.strikeSelection);\n\n bool isPut = vaultParams.isPut;\n address underlying = vaultParams.underlying;\n address asset = vaultParams.asset;\n\n (strikePrice, delta) = closeParams.lastStrikeOverrideRound ==\n vaultState.round\n ? (closeParams.overriddenStrikePrice, selection.delta())\n : selection.getStrikePrice(expiry, isPut);\n\n require(strikePrice != 0, \"!strikePrice\");\n\n // retrieve address if option already exists, or deploy it\n otokenAddress = getOrDeployOtoken(\n closeParams,\n vaultParams,\n underlying,\n asset,\n strikePrice,\n expiry,\n isPut\n );\n\n return (otokenAddress, strikePrice, delta);\n }\n\n /**\n * @notice Verify the otoken has the correct parameters to prevent vulnerability to opyn contract changes\n * @param otokenAddress is the address of the otoken\n * @param vaultParams is the struct with vault general data\n * @param collateralAsset is the address of the collateral asset\n * @param USDC is the address of usdc\n * @param delay is the delay between commitAndClose and rollToNextOption\n */\n function verifyOtoken(\n address otokenAddress,\n Vault.VaultParams storage vaultParams,\n address collateralAsset,\n address USDC,\n uint256 delay\n ) private view {\n require(otokenAddress != address(0), \"!otokenAddress\");\n\n IOtoken otoken = IOtoken(otokenAddress);\n require(otoken.isPut() == vaultParams.isPut, \"Type mismatch\");\n require(\n otoken.underlyingAsset() == vaultParams.underlying,\n \"Wrong underlyingAsset\"\n );\n require(\n otoken.collateralAsset() == collateralAsset,\n \"Wrong collateralAsset\"\n );\n\n // we just assume all options use USDC as the strike\n require(otoken.strikeAsset() == USDC, \"strikeAsset != USDC\");\n\n uint256 readyAt = block.timestamp.add(delay);\n require(otoken.expiryTimestamp() >= readyAt, \"Expiry before delay\");\n }\n\n /**\n * @param decimals is the decimals of the asset\n * @param totalBalance is the vaults total balance of the asset\n * @param currentShareSupply is the supply of the shares invoked with totalSupply()\n * @param lastQueuedWithdrawAmount is the total amount queued for withdrawals\n * @param performanceFee is the perf fee percent to charge on premiums\n * @param managementFee is the management fee percent to charge on the AUM\n * @param currentQueuedWithdrawShares is amount of queued withdrawals from the current round\n */\n struct RolloverParams {\n uint256 decimals;\n uint256 totalBalance;\n uint256 currentShareSupply;\n uint256 lastQueuedWithdrawAmount;\n uint256 performanceFee;\n uint256 managementFee;\n uint256 currentQueuedWithdrawShares;\n }\n\n /**\n * @notice Calculate the shares to mint, new price per share, and\n amount of funds to re-allocate as collateral for the new round\n * @param vaultState is the storage variable vaultState passed from RibbonVault\n * @param params is the rollover parameters passed to compute the next state\n * @return newLockedAmount is the amount of funds to allocate for the new round\n * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal\n * @return newPricePerShare is the price per share of the new round\n * @return mintShares is the amount of shares to mint from deposits\n * @return performanceFeeInAsset is the performance fee charged by vault\n * @return totalVaultFee is the total amount of fee charged by vault\n */\n function rollover(\n Vault.VaultState storage vaultState,\n RolloverParams calldata params\n )\n external\n view\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares,\n uint256 performanceFeeInAsset,\n uint256 totalVaultFee\n )\n {\n uint256 currentBalance = params.totalBalance;\n uint256 pendingAmount = vaultState.totalPending;\n // Total amount of queued withdrawal shares from previous rounds (doesn't include the current round)\n uint256 lastQueuedWithdrawShares = vaultState.queuedWithdrawShares;\n\n // Deduct older queued withdraws so we don't charge fees on them\n uint256 balanceForVaultFees =\n currentBalance.sub(params.lastQueuedWithdrawAmount);\n\n {\n (performanceFeeInAsset, , totalVaultFee) = VaultLifecycle\n .getVaultFees(\n balanceForVaultFees,\n vaultState.lastLockedAmount,\n vaultState.totalPending,\n params.performanceFee,\n params.managementFee\n );\n }\n\n // Take into account the fee\n // so we can calculate the newPricePerShare\n currentBalance = currentBalance.sub(totalVaultFee);\n\n {\n newPricePerShare = ShareMath.pricePerShare(\n params.currentShareSupply.sub(lastQueuedWithdrawShares),\n currentBalance.sub(params.lastQueuedWithdrawAmount),\n pendingAmount,\n params.decimals\n );\n\n queuedWithdrawAmount = params.lastQueuedWithdrawAmount.add(\n ShareMath.sharesToAsset(\n params.currentQueuedWithdrawShares,\n newPricePerShare,\n params.decimals\n )\n );\n\n // After closing the short, if the options expire in-the-money\n // vault pricePerShare would go down because vault's asset balance decreased.\n // This ensures that the newly-minted shares do not take on the loss.\n mintShares = ShareMath.assetToShares(\n pendingAmount,\n newPricePerShare,\n params.decimals\n );\n }\n\n return (\n currentBalance.sub(queuedWithdrawAmount), // new locked balance subtracts the queued withdrawals\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n );\n }\n\n /**\n * @notice Creates the actual Opyn short position by depositing collateral and minting otokens\n * @param gammaController is the address of the opyn controller contract\n * @param marginPool is the address of the opyn margin contract which holds the collateral\n * @param oTokenAddress is the address of the otoken to mint\n * @param depositAmount is the amount of collateral to deposit\n * @return the otoken mint amount\n */\n function createShort(\n address gammaController,\n address marginPool,\n address oTokenAddress,\n uint256 depositAmount\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n uint256 newVaultID =\n (controller.getAccountVaultCounter(address(this))).add(1);\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IOtoken oToken = IOtoken(oTokenAddress);\n address collateralAsset = oToken.collateralAsset();\n\n uint256 collateralDecimals =\n uint256(IERC20Detailed(collateralAsset).decimals());\n uint256 mintAmount;\n\n if (oToken.isPut()) {\n // For minting puts, there will be instances where the full depositAmount will not be used for minting.\n // This is because of an issue with precision.\n //\n // For ETH put options, we are calculating the mintAmount (10**8 decimals) using\n // the depositAmount (10**18 decimals), which will result in truncation of decimals when scaling down.\n // As a result, there will be tiny amounts of dust left behind in the Opyn vault when minting put otokens.\n //\n // For simplicity's sake, we do not refund the dust back to the address(this) on minting otokens.\n // We retain the dust in the vault so the calling contract can withdraw the\n // actual locked amount + dust at settlement.\n //\n // To test this behavior, we can console.log\n // MarginCalculatorInterface(0x7A48d10f372b3D7c60f6c9770B91398e4ccfd3C7).getExcessCollateral(vault)\n // to see how much dust (or excess collateral) is left behind.\n mintAmount = depositAmount\n .mul(10**Vault.OTOKEN_DECIMALS)\n .mul(10**18) // we use 10**18 to give extra precision\n .div(oToken.strikePrice().mul(10**(10 + collateralDecimals)));\n } else {\n mintAmount = depositAmount;\n\n if (collateralDecimals > 8) {\n uint256 scaleBy = 10**(collateralDecimals.sub(8)); // oTokens have 8 decimals\n if (mintAmount > scaleBy) {\n mintAmount = depositAmount.div(scaleBy); // scale down from 10**18 to 10**8\n }\n }\n }\n\n // double approve to fix non-compliant ERC20s\n IERC20 collateralToken = IERC20(collateralAsset);\n collateralToken.safeApproveNonCompliant(marginPool, depositAmount);\n\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](3);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.OpenVault,\n address(this), // owner\n address(this), // receiver\n address(0), // asset, otoken\n newVaultID, // vaultId\n 0, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.DepositCollateral,\n address(this), // owner\n address(this), // address to transfer from\n collateralAsset, // deposited asset\n newVaultID, // vaultId\n depositAmount, // amount\n 0, //index\n \"\" //data\n );\n\n actions[2] = IController.ActionArgs(\n IController.ActionType.MintShortOption,\n address(this), // owner\n address(this), // address to transfer to\n oTokenAddress, // option address\n newVaultID, // vaultId\n mintAmount, // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n return mintAmount;\n }\n\n /**\n * @notice Close the existing short otoken position. Currently this implementation is simple.\n * It closes the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time. Since calling `_closeShort` deletes vaults by\n calling SettleVault action, this assumption should hold.\n * @param gammaController is the address of the opyn controller contract\n * @return amount of collateral redeemed from the vault\n */\n function settleShort(address gammaController) external returns (uint256) {\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n // The short position has been previously closed, or all the otokens have been burned.\n // So we return early.\n if (address(collateralToken) == address(0)) {\n return 0;\n }\n\n // This is equivalent to doing IERC20(vault.asset).balanceOf(address(this))\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // If it is after expiry, we need to settle the short position using the normal way\n // Delete the vault and withdraw all remaining collateral from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.SettleVault,\n address(this), // owner\n address(this), // address to transfer to\n address(0), // not used\n vaultID, // vaultId\n 0, // not used\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Exercises the ITM option using existing long otoken position. Currently this implementation is simple.\n * It calls the `Redeem` action to claim the payout.\n * @param gammaController is the address of the opyn controller contract\n * @param oldOption is the address of the old option\n * @param asset is the address of the vault's asset\n * @return amount of asset received by exercising the option\n */\n function settleLong(\n address gammaController,\n address oldOption,\n address asset\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n\n uint256 oldOptionBalance = IERC20(oldOption).balanceOf(address(this));\n\n if (controller.getPayout(oldOption, oldOptionBalance) == 0) {\n return 0;\n }\n\n uint256 startAssetBalance = IERC20(asset).balanceOf(address(this));\n\n // If it is after expiry, we need to redeem the profits\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.Redeem,\n address(0), // not used\n address(this), // address to send profits to\n oldOption, // address of otoken\n 0, // not used\n oldOptionBalance, // otoken balance\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endAssetBalance = IERC20(asset).balanceOf(address(this));\n\n return endAssetBalance.sub(startAssetBalance);\n }\n\n /**\n * @notice Burn the remaining oTokens left over from auction. Currently this implementation is simple.\n * It burns oTokens from the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time.\n * @param gammaController is the address of the opyn controller contract\n * @param currentOption is the address of the current option\n * @return amount of collateral redeemed by burning otokens\n */\n function burnOtokens(address gammaController, address currentOption)\n external\n returns (uint256)\n {\n uint256 numOTokensToBurn =\n IERC20(currentOption).balanceOf(address(this));\n\n require(numOTokensToBurn > 0, \"No oTokens to burn\");\n\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // Burning `amount` of oTokens from the ribbon vault,\n // then withdrawing the corresponding collateral amount from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](2);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.BurnShortOption,\n address(this), // owner\n address(this), // address to transfer from\n address(vault.shortOtokens[0]), // otoken address\n vaultID, // vaultId\n numOTokensToBurn, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.WithdrawCollateral,\n address(this), // owner\n address(this), // address to transfer to\n address(collateralToken), // withdrawn asset\n vaultID, // vaultId\n vault.collateralAmounts[0].mul(numOTokensToBurn).div(\n vault.shortAmounts[0]\n ), // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Calculates the performance and management fee for this week's round\n * @param currentBalance is the balance of funds held on the vault after closing short\n * @param lastLockedAmount is the amount of funds locked from the previous round\n * @param pendingAmount is the pending deposit amount\n * @param performanceFeePercent is the performance fee pct.\n * @param managementFeePercent is the management fee pct.\n * @return performanceFeeInAsset is the performance fee\n * @return managementFeeInAsset is the management fee\n * @return vaultFee is the total fees\n */\n function getVaultFees(\n uint256 currentBalance,\n uint256 lastLockedAmount,\n uint256 pendingAmount,\n uint256 performanceFeePercent,\n uint256 managementFeePercent\n )\n internal\n pure\n returns (\n uint256 performanceFeeInAsset,\n uint256 managementFeeInAsset,\n uint256 vaultFee\n )\n {\n // At the first round, currentBalance=0, pendingAmount>0\n // so we just do not charge anything on the first round\n uint256 lockedBalanceSansPending =\n currentBalance > pendingAmount\n ? currentBalance.sub(pendingAmount)\n : 0;\n\n uint256 _performanceFeeInAsset;\n uint256 _managementFeeInAsset;\n uint256 _vaultFee;\n\n // Take performance fee and management fee ONLY if difference between\n // last week and this week's vault deposits, taking into account pending\n // deposits and withdrawals, is positive. If it is negative, last week's\n // option expired ITM past breakeven, and the vault took a loss so we\n // do not collect performance fee for last week\n if (lockedBalanceSansPending > lastLockedAmount) {\n _performanceFeeInAsset = performanceFeePercent > 0\n ? lockedBalanceSansPending\n .sub(lastLockedAmount)\n .mul(performanceFeePercent)\n .div(100 * Vault.FEE_MULTIPLIER)\n : 0;\n _managementFeeInAsset = managementFeePercent > 0\n ? lockedBalanceSansPending.mul(managementFeePercent).div(\n 100 * Vault.FEE_MULTIPLIER\n )\n : 0;\n\n _vaultFee = _performanceFeeInAsset.add(_managementFeeInAsset);\n }\n\n return (_performanceFeeInAsset, _managementFeeInAsset, _vaultFee);\n }\n\n /**\n * @notice Either retrieves the option token if it already exists, or deploy it\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param underlying is the address of the underlying asset of the option\n * @param collateralAsset is the address of the collateral asset of the option\n * @param strikePrice is the strike price of the option\n * @param expiry is the expiry timestamp of the option\n * @param isPut is whether the option is a put\n * @return the address of the option\n */\n function getOrDeployOtoken(\n CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n address underlying,\n address collateralAsset,\n uint256 strikePrice,\n uint256 expiry,\n bool isPut\n ) internal returns (address) {\n IOtokenFactory factory = IOtokenFactory(closeParams.OTOKEN_FACTORY);\n\n address otokenFromFactory =\n factory.getOtoken(\n underlying,\n closeParams.USDC,\n collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n if (otokenFromFactory != address(0)) {\n return otokenFromFactory;\n }\n\n address otoken =\n factory.createOtoken(\n underlying,\n closeParams.USDC,\n collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n verifyOtoken(\n otoken,\n vaultParams,\n collateralAsset,\n closeParams.USDC,\n closeParams.delay\n );\n\n return otoken;\n }\n\n function getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount\n ) external view returns (uint256) {\n return\n _getOTokenPremium(\n oTokenAddress,\n optionsPremiumPricer,\n premiumDiscount\n );\n }\n\n function _getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount\n ) internal view returns (uint256) {\n IOtoken newOToken = IOtoken(oTokenAddress);\n IOptionsPremiumPricer premiumPricer =\n IOptionsPremiumPricer(optionsPremiumPricer);\n\n // Apply black-scholes formula (from rvol library) to option given its features\n // and get price for 100 contracts denominated in the underlying asset for call option\n // and USDC for put option\n uint256 optionPremium =\n premiumPricer.getPremium(\n newOToken.strikePrice(),\n newOToken.expiryTimestamp(),\n newOToken.isPut()\n );\n\n // Apply a discount to incentivize arbitraguers\n optionPremium = optionPremium.mul(premiumDiscount).div(\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER\n );\n\n require(\n optionPremium <= type(uint96).max,\n \"optionPremium > type(uint96) max value!\"\n );\n require(optionPremium > 0, \"!optionPremium\");\n\n return optionPremium;\n }\n\n /**\n * @notice Starts the gnosis auction\n * @param auctionDetails is the struct with all the custom parameters of the auction\n * @return the auction id of the newly created auction\n */\n function startAuction(GnosisAuction.AuctionDetails calldata auctionDetails)\n external\n returns (uint256)\n {\n return GnosisAuction.startAuction(auctionDetails);\n }\n\n /**\n * @notice Settles the gnosis auction\n * @param gnosisEasyAuction is the contract address of Gnosis easy auction protocol\n * @param auctionID is the auction ID of the gnosis easy auction\n */\n function settleAuction(address gnosisEasyAuction, uint256 auctionID)\n internal\n {\n IGnosisAuction(gnosisEasyAuction).settleAuction(auctionID);\n }\n\n /**\n * @notice Places a bid in an auction\n * @param bidDetails is the struct with all the details of the\n bid including the auction's id and how much to bid\n */\n function placeBid(GnosisAuction.BidDetails calldata bidDetails)\n external\n returns (\n uint256 sellAmount,\n uint256 buyAmount,\n uint64 userId\n )\n {\n return GnosisAuction.placeBid(bidDetails);\n }\n\n /**\n * @notice Claims the oTokens belonging to the vault\n * @param auctionSellOrder is the sell order of the bid\n * @param gnosisEasyAuction is the address of the gnosis auction contract\n holding custody to the funds\n * @param counterpartyThetaVault is the address of the counterparty theta\n vault of this delta vault\n */\n function claimAuctionOtokens(\n Vault.AuctionSellOrder calldata auctionSellOrder,\n address gnosisEasyAuction,\n address counterpartyThetaVault\n ) external {\n GnosisAuction.claimAuctionOtokens(\n auctionSellOrder,\n gnosisEasyAuction,\n counterpartyThetaVault\n );\n }\n\n /**\n * @notice Allocates the vault's minted options to the OptionsPurchaseQueue contract\n * @dev Skipped if the optionsPurchaseQueue doesn't exist\n * @param optionsPurchaseQueue is the OptionsPurchaseQueue contract\n * @param option is the minted option\n * @param optionsAmount is the amount of options minted\n * @param optionAllocation is the maximum % of options to allocate towards the purchase queue (will only allocate\n * up to the amount that is on the queue)\n * @return allocatedOptions is the amount of options that ended up getting allocated to the OptionsPurchaseQueue\n */\n function allocateOptions(\n address optionsPurchaseQueue,\n address option,\n uint256 optionsAmount,\n uint256 optionAllocation\n ) external returns (uint256 allocatedOptions) {\n // Skip if optionsPurchaseQueue is address(0)\n if (optionsPurchaseQueue != address(0)) {\n allocatedOptions = optionsAmount.mul(optionAllocation).div(\n 100 * Vault.OPTION_ALLOCATION_MULTIPLIER\n );\n allocatedOptions = IOptionsPurchaseQueue(optionsPurchaseQueue)\n .getOptionsAllocation(address(this), allocatedOptions);\n\n if (allocatedOptions != 0) {\n IERC20(option).approve(optionsPurchaseQueue, allocatedOptions);\n IOptionsPurchaseQueue(optionsPurchaseQueue).allocateOptions(\n allocatedOptions\n );\n }\n }\n\n return allocatedOptions;\n }\n\n /**\n * @notice Sell the allocated options to the purchase queue post auction settlement\n * @dev Reverts if the auction hasn't settled yet\n * @param optionsPurchaseQueue is the OptionsPurchaseQueue contract\n * @param gnosisEasyAuction The address of the Gnosis Easy Auction contract\n * @return totalPremiums Total premiums earnt by the vault\n */\n function sellOptionsToQueue(\n address optionsPurchaseQueue,\n address gnosisEasyAuction,\n uint256 optionAuctionID\n ) external returns (uint256) {\n uint256 settlementPrice =\n getAuctionSettlementPrice(gnosisEasyAuction, optionAuctionID);\n require(settlementPrice != 0, \"!settlementPrice\");\n\n return\n IOptionsPurchaseQueue(optionsPurchaseQueue).sellToBuyers(\n settlementPrice\n );\n }\n\n /**\n * @notice Gets the settlement price of a settled auction\n * @param gnosisEasyAuction The address of the Gnosis Easy Auction contract\n * @return settlementPrice Auction settlement price\n */\n function getAuctionSettlementPrice(\n address gnosisEasyAuction,\n uint256 optionAuctionID\n ) public view returns (uint256) {\n bytes32 clearingPriceOrder =\n IGnosisAuction(gnosisEasyAuction)\n .auctionData(optionAuctionID)\n .clearingPriceOrder;\n\n if (clearingPriceOrder == bytes32(0)) {\n // Current auction hasn't settled yet\n return 0;\n } else {\n // We decode the clearingPriceOrder to find the auction settlement price\n // settlementPrice = clearingPriceOrder.sellAmount / clearingPriceOrder.buyAmount\n return\n (10**Vault.OTOKEN_DECIMALS)\n .mul(\n uint96(uint256(clearingPriceOrder)) // sellAmount\n )\n .div(\n uint96(uint256(clearingPriceOrder) >> 96) // buyAmount\n );\n }\n }\n\n /**\n * @notice Verify the constructor params satisfy requirements\n * @param owner is the owner of the vault with critical permissions\n * @param feeRecipient is the address to recieve vault performance and management fees\n * @param performanceFee is the perfomance fee pct.\n * @param tokenName is the name of the token\n * @param tokenSymbol is the symbol of the token\n * @param _vaultParams is the struct with vault general data\n */\n function verifyInitializerParams(\n address owner,\n address keeper,\n address feeRecipient,\n uint256 performanceFee,\n uint256 managementFee,\n string calldata tokenName,\n string calldata tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) external pure {\n require(owner != address(0), \"!owner\");\n require(keeper != address(0), \"!keeper\");\n require(feeRecipient != address(0), \"!feeRecipient\");\n require(\n performanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"performanceFee >= 100%\"\n );\n require(\n managementFee < 100 * Vault.FEE_MULTIPLIER,\n \"managementFee >= 100%\"\n );\n require(bytes(tokenName).length > 0, \"!tokenName\");\n require(bytes(tokenSymbol).length > 0, \"!tokenSymbol\");\n\n require(_vaultParams.asset != address(0), \"!asset\");\n require(_vaultParams.underlying != address(0), \"!underlying\");\n require(_vaultParams.minimumSupply > 0, \"!minimumSupply\");\n require(_vaultParams.cap > 0, \"!cap\");\n require(\n _vaultParams.cap > _vaultParams.minimumSupply,\n \"cap has to be higher than minimumSupply\"\n );\n }\n\n /**\n * @notice Gets the next option expiry timestamp\n * @param currentOption is the otoken address that the vault is currently writing\n */\n function getNextExpiry(address currentOption)\n internal\n view\n returns (uint256)\n {\n // uninitialized state\n if (currentOption == address(0)) {\n return getNextFriday(block.timestamp);\n }\n uint256 currentExpiry = IOtoken(currentOption).expiryTimestamp();\n\n // After options expiry if no options are written for >1 week\n // We need to give the ability continue writing options\n if (block.timestamp > currentExpiry + 7 days) {\n return getNextFriday(block.timestamp);\n }\n return getNextFriday(currentExpiry);\n }\n\n /**\n * @notice Gets the next options expiry timestamp\n * @param timestamp is the expiry timestamp of the current option\n * Reference: https://codereview.stackexchange.com/a/33532\n * Examples:\n * getNextFriday(week 1 thursday) -> week 1 friday\n * getNextFriday(week 1 friday) -> week 2 friday\n * getNextFriday(week 1 saturday) -> week 2 friday\n */\n function getNextFriday(uint256 timestamp) internal pure returns (uint256) {\n // dayOfWeek = 0 (sunday) - 6 (saturday)\n uint256 dayOfWeek = ((timestamp / 1 days) + 4) % 7;\n uint256 nextFriday = timestamp + ((7 + 5 - dayOfWeek) % 7) * 1 days;\n uint256 friday8am = nextFriday - (nextFriday % (24 hours)) + (8 hours);\n\n // If the passed timestamp is day=Friday hour>8am, we simply increment it by a week to next Friday\n if (timestamp >= friday8am) {\n friday8am += 7 days;\n }\n return friday8am;\n }\n}\n" + }, + "contracts/libraries/VaultLifecycleYearn.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {DSMath} from \"../vendor/DSMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {VaultLifecycle} from \"./VaultLifecycle.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {ShareMath} from \"./ShareMath.sol\";\nimport {IYearnVault} from \"../interfaces/IYearn.sol\";\nimport {IWETH} from \"../interfaces/IWETH.sol\";\nimport {IStrikeSelection} from \"../interfaces/IRibbon.sol\";\nimport {\n IOtokenFactory,\n IOtoken,\n IController,\n GammaTypes\n} from \"../interfaces/GammaInterface.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\nimport {SupportsNonCompliantERC20} from \"./SupportsNonCompliantERC20.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\n\nlibrary VaultLifecycleYearn {\n using SafeMath for uint256;\n using SupportsNonCompliantERC20 for IERC20;\n using SafeERC20 for IERC20;\n\n /**\n * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @param collateralAsset is the address of the collateral asset\n * @param isYearnPaused is whether yearn is enabled in contract\n * @return otokenAddress is the address of the new option\n * @return strikePrice is the strike price of the new option\n * @return delta is the delta of the new option\n */\n function commitAndClose(\n VaultLifecycle.CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n Vault.VaultState storage vaultState,\n address collateralAsset,\n bool isYearnPaused\n )\n external\n returns (\n address otokenAddress,\n uint256 strikePrice,\n uint256 delta\n )\n {\n uint256 expiry =\n VaultLifecycle.getNextExpiry(closeParams.currentOption);\n\n bool isPut = vaultParams.isPut;\n\n IStrikeSelection selection =\n IStrikeSelection(closeParams.strikeSelection);\n\n // calculate strike and delta\n (strikePrice, delta) = closeParams.lastStrikeOverrideRound ==\n vaultState.round\n ? (closeParams.overriddenStrikePrice, selection.delta())\n : selection.getStrikePrice(expiry, isPut);\n\n require(strikePrice != 0, \"!strikePrice\");\n\n // retrieve address if option already exists, or deploy it\n otokenAddress = VaultLifecycle.getOrDeployOtoken(\n closeParams,\n vaultParams,\n vaultParams.underlying,\n isYearnPaused ? vaultParams.asset : collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n return (otokenAddress, strikePrice, delta);\n }\n\n /**\n * @notice Calculate the shares to mint, new price per share, and\n amount of funds to re-allocate as collateral for the new round\n * @param currentShareSupply is the total supply of shares\n * @param currentBalance is the total balance of the vault\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @return newLockedAmount is the amount of funds to allocate for the new round\n * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal\n * @return newPricePerShare is the price per share of the new round\n * @return mintShares is the amount of shares to mint from deposits\n */\n function rollover(\n uint256 currentShareSupply,\n uint256 currentBalance,\n Vault.VaultParams calldata vaultParams,\n Vault.VaultState calldata vaultState\n )\n external\n pure\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares\n )\n {\n uint256 pendingAmount = uint256(vaultState.totalPending);\n uint256 _decimals = vaultParams.decimals;\n\n newPricePerShare = ShareMath.pricePerShare(\n currentShareSupply,\n currentBalance,\n pendingAmount,\n _decimals\n );\n\n // After closing the short, if the options expire in-the-money\n // vault pricePerShare would go down because vault's asset balance decreased.\n // This ensures that the newly-minted shares do not take on the loss.\n uint256 _mintShares =\n ShareMath.assetToShares(pendingAmount, newPricePerShare, _decimals);\n\n uint256 newSupply = currentShareSupply.add(_mintShares);\n\n uint256 queuedAmount =\n newSupply > 0\n ? ShareMath.sharesToAsset(\n vaultState.queuedWithdrawShares,\n newPricePerShare,\n _decimals\n )\n : 0;\n\n return (\n currentBalance.sub(queuedAmount),\n queuedAmount,\n newPricePerShare,\n _mintShares\n );\n }\n\n /**\n * @notice Withdraws yvWETH + WETH (if necessary) from vault using vault shares\n * @param weth is the weth address\n * @param asset is the vault asset address\n * @param collateralToken is the address of the collateral token\n * @param recipient is the recipient\n * @param amount is the withdraw amount in `asset`\n * @return withdrawAmount is the withdraw amount in `collateralToken`\n */\n function withdrawYieldAndBaseToken(\n address weth,\n address asset,\n address collateralToken,\n address recipient,\n uint256 amount\n ) external returns (uint256) {\n uint256 pricePerYearnShare =\n IYearnVault(collateralToken).pricePerShare();\n uint256 withdrawAmount =\n DSMath.wdiv(\n amount,\n pricePerYearnShare.mul(decimalShift(collateralToken))\n );\n uint256 yieldTokenBalance =\n withdrawYieldToken(collateralToken, recipient, withdrawAmount);\n\n // If there is not enough yvWETH in the vault, it withdraws as much as possible and\n // transfers the rest in `asset`\n if (withdrawAmount > yieldTokenBalance) {\n withdrawBaseToken(\n weth,\n asset,\n collateralToken,\n recipient,\n withdrawAmount,\n yieldTokenBalance,\n pricePerYearnShare\n );\n }\n\n return withdrawAmount;\n }\n\n /**\n * @notice Withdraws yvWETH from vault\n * @param collateralToken is the address of the collateral token\n * @param recipient is the recipient\n * @param withdrawAmount is the withdraw amount in terms of yearn tokens\n * @return yieldTokenBalance is the balance of the yield token\n */\n function withdrawYieldToken(\n address collateralToken,\n address recipient,\n uint256 withdrawAmount\n ) internal returns (uint256) {\n IERC20 collateral = IERC20(collateralToken);\n\n uint256 yieldTokenBalance = collateral.balanceOf(address(this));\n uint256 yieldTokensToWithdraw =\n DSMath.min(yieldTokenBalance, withdrawAmount);\n if (yieldTokensToWithdraw > 0) {\n collateral.safeTransfer(recipient, yieldTokensToWithdraw);\n }\n\n return yieldTokenBalance;\n }\n\n /**\n * @notice Withdraws `asset` from vault\n * @param weth is the weth address\n * @param asset is the vault asset address\n * @param collateralToken is the address of the collateral token\n * @param recipient is the recipient\n * @param withdrawAmount is the withdraw amount in terms of yearn tokens\n * @param yieldTokenBalance is the collateral token (yvWETH) balance of the vault\n * @param pricePerYearnShare is the yvWETH<->WETH price ratio\n */\n function withdrawBaseToken(\n address weth,\n address asset,\n address collateralToken,\n address recipient,\n uint256 withdrawAmount,\n uint256 yieldTokenBalance,\n uint256 pricePerYearnShare\n ) internal {\n uint256 underlyingTokensToWithdraw =\n DSMath.wmul(\n withdrawAmount.sub(yieldTokenBalance),\n pricePerYearnShare.mul(decimalShift(collateralToken))\n );\n transferAsset(\n weth,\n asset,\n payable(recipient),\n underlyingTokensToWithdraw\n );\n }\n\n /**\n * @notice Unwraps the necessary amount of the yield-bearing yearn token\n * and transfers amount to vault\n * @param amount is the amount of `asset` to withdraw\n * @param asset is the vault asset address\n * @param collateralToken is the address of the collateral token\n * @param yearnWithdrawalBuffer is the buffer for withdrawals from yearn vault\n * @param yearnWithdrawalSlippage is the slippage for withdrawals from yearn vault\n */\n function unwrapYieldToken(\n uint256 amount,\n address asset,\n address collateralToken,\n uint256 yearnWithdrawalBuffer,\n uint256 yearnWithdrawalSlippage\n ) external {\n uint256 assetBalance = IERC20(asset).balanceOf(address(this));\n IYearnVault collateral = IYearnVault(collateralToken);\n\n uint256 amountToUnwrap =\n DSMath.wdiv(\n DSMath.max(assetBalance, amount).sub(assetBalance),\n collateral.pricePerShare().mul(decimalShift(collateralToken))\n );\n\n if (amountToUnwrap > 0) {\n amountToUnwrap = amountToUnwrap\n .add(amountToUnwrap.mul(yearnWithdrawalBuffer).div(10000))\n .sub(1);\n\n collateral.withdraw(\n amountToUnwrap,\n address(this),\n yearnWithdrawalSlippage\n );\n }\n }\n\n /**\n * @notice Wraps the necessary amount of the base token to the yield-bearing yearn token\n * @param asset is the vault asset address\n * @param collateralToken is the address of the collateral token\n */\n function wrapToYieldToken(address asset, address collateralToken) external {\n uint256 amountToWrap = IERC20(asset).balanceOf(address(this));\n\n if (amountToWrap > 0) {\n IERC20(asset).safeApprove(collateralToken, amountToWrap);\n\n // there is a slight imprecision with regards to calculating back from yearn token -> underlying\n // that stems from miscoordination between ytoken .deposit() amount wrapped and pricePerShare\n // at that point in time.\n // ex: if I have 1 eth, deposit 1 eth into yearn vault and calculate value of yearn token balance\n // denominated in eth (via balance(yearn token) * pricePerShare) we will get 1 eth - 1 wei.\n IYearnVault(collateralToken).deposit(amountToWrap, address(this));\n }\n }\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param weth is the weth address\n * @param asset is the vault asset address\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(\n address weth,\n address asset,\n address recipient,\n uint256 amount\n ) public {\n if (asset == weth) {\n IWETH(weth).withdraw(amount);\n (bool success, ) = payable(recipient).call{value: amount}(\"\");\n require(success, \"!success\");\n return;\n }\n IERC20(asset).safeTransfer(recipient, amount);\n }\n\n /**\n * @notice Returns the decimal shift between 18 decimals and asset tokens\n * @param collateralToken is the address of the collateral token\n */\n function decimalShift(address collateralToken)\n public\n view\n returns (uint256)\n {\n return\n 10**(uint256(18).sub(IERC20Detailed(collateralToken).decimals()));\n }\n\n function getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount,\n address collateralAsset,\n bool isYearnPaused\n ) external view returns (uint256) {\n return\n _getOTokenPremium(\n oTokenAddress,\n optionsPremiumPricer,\n premiumDiscount,\n collateralAsset,\n isYearnPaused\n );\n }\n\n function _getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount,\n address collateralAsset,\n bool isYearnPaused\n ) internal view returns (uint256) {\n IOtoken newOToken = IOtoken(oTokenAddress);\n IOptionsPremiumPricer premiumPricer =\n IOptionsPremiumPricer(optionsPremiumPricer);\n\n // Apply black-scholes formula (from rvol library) to option given its features\n // and get price for 100 contracts denominated in the underlying asset for call option\n // and USDC for put option\n uint256 optionPremium =\n premiumPricer.getPremium(\n newOToken.strikePrice(),\n newOToken.expiryTimestamp(),\n newOToken.isPut()\n );\n\n // Apply a discount to incentivize arbitraguers\n optionPremium = optionPremium.mul(premiumDiscount).div(\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER\n );\n\n // get the black scholes premium of the option and adjust premium based on\n // collateral asset <-> asset exchange rate\n uint256 adjustedPremium =\n isYearnPaused\n ? optionPremium\n : DSMath.wmul(\n optionPremium,\n IYearnVault(collateralAsset).pricePerShare().mul(\n decimalShift(collateralAsset)\n )\n );\n\n require(\n adjustedPremium <= type(uint96).max,\n \"adjustedPremium > type(uint96) max value!\"\n );\n\n require(adjustedPremium > 0, \"!adjustedPremium\");\n\n return adjustedPremium;\n }\n}\n" + }, + "contracts/interfaces/ILiquidityGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface ILiquidityGauge {\n function balanceOf(address) external view returns (uint256);\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function withdraw(uint256 _value) external;\n}\n" + }, + "contracts/vaults/YearnVaults/base/RibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\n\nimport {DSMath} from \"../../../vendor/DSMath.sol\";\nimport {IYearnRegistry, IYearnVault} from \"../../../interfaces/IYearn.sol\";\nimport {Vault} from \"../../../libraries/Vault.sol\";\nimport {VaultLifecycle} from \"../../../libraries/VaultLifecycle.sol\";\nimport {VaultLifecycleYearn} from \"../../../libraries/VaultLifecycleYearn.sol\";\nimport {ShareMath} from \"../../../libraries/ShareMath.sol\";\nimport {IWETH} from \"../../../interfaces/IWETH.sol\";\n\ncontract RibbonVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n /// @notice Yearn vault contract\n IYearnVault public collateralToken;\n\n // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure.\n uint256[30] private ____gap;\n\n // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE\n // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaYearnVaultStorage\n // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n /// @notice Withdrawal buffer for yearn vault\n uint256 public constant YEARN_WITHDRAWAL_BUFFER = 5; // 0.05%\n\n /// @notice Slippage incurred during withdrawal\n uint256 public constant YEARN_WITHDRAWAL_SLIPPAGE = 5; // 0.05%\n\n /// @notice 7 day period between each options sale.\n uint256 public constant PERIOD = 7 days;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // GNOSIS_EASY_AUCTION is Gnosis protocol's contract for initiating auctions and placing bids\n // https://github.com/gnosis/ido-contracts/blob/main/contracts/EasyAuction.sol\n address public immutable GNOSIS_EASY_AUCTION;\n\n // Yearn registry contract\n address public immutable YEARN_REGISTRY;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectVaultFees(\n uint256 performanceFee,\n uint256 vaultFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions\n * @param _yearnRegistry is the address of the yearn registry from token to vault token\n */\n constructor(\n address _weth,\n address _usdc,\n address _gammaController,\n address _marginPool,\n address _gnosisEasyAuction,\n address _yearnRegistry\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_gnosisEasyAuction != address(0), \"!_gnosisEasyAuction\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n require(_yearnRegistry != address(0), \"!_yearnRegistry\");\n\n WETH = _weth;\n USDC = _usdc;\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n GNOSIS_EASY_AUCTION = _gnosisEasyAuction;\n YEARN_REGISTRY = _yearnRegistry;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function baseInitialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) internal initializer {\n VaultLifecycle.verifyInitializerParams(\n _owner,\n _keeper,\n _feeRecipient,\n _performanceFee,\n _managementFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_tokenName, _tokenSymbol);\n __Ownable_init();\n transferOwnership(_owner);\n\n keeper = _keeper;\n\n feeRecipient = _feeRecipient;\n performanceFee = _performanceFee;\n managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div(\n WEEKS_PER_YEAR\n );\n vaultParams = _vaultParams;\n\n _upgradeYearnVault();\n\n uint256 assetBalance = totalBalance();\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n\n vaultState.round = 1;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n // We are dividing annualized management fee by num weeks in a year\n uint256 tmpManagementFee =\n newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n\n managementFee = tmpManagementFee;\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Deposits the `asset` from msg.sender.\n * @param amount is the amount of `asset` to deposit\n */\n function deposit(uint256 amount) external nonReentrant {\n require(amount > 0, \"!amount\");\n\n _depositFor(amount, msg.sender);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.\n * @notice Used for vault -> vault deposits on the user's behalf\n * @param amount is the amount of `asset` to deposit\n * @param creditor is the address that can claim/withdraw deposited amount\n */\n function depositFor(uint256 amount, address creditor)\n external\n nonReentrant\n {\n require(amount > 0, \"!amount\");\n require(creditor != address(0), \"!creditor\");\n\n _depositFor(amount, creditor);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n */\n function _depositFor(uint256 amount, address creditor) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount = totalBalance().add(amount);\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n\n emit Deposit(creditor, amount, currentRound);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function _initiateWithdraw(uint256 numShares) internal {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n if (\n depositReceipts[msg.sender].amount > 0 ||\n depositReceipts[msg.sender].unredeemedShares > 0\n ) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n bool withdrawalIsSameRound = withdrawal.round == currentRound;\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n uint256 existingShares = uint256(withdrawal.shares);\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return withdrawAmount the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n require(withdrawAmount > 0, \"!withdrawAmount\");\n\n VaultLifecycleYearn.transferAsset(\n WETH,\n vaultParams.asset,\n msg.sender,\n withdrawAmount\n );\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n struct rollToNextOptionParams {\n uint256 lastQueuedWithdrawAmount;\n uint256 currentQueuedWithdrawShares;\n bool isYearnPaused;\n }\n\n /**\n * @notice Helper function that performs most administrative tasks\n * such as setting next option, minting new shares, getting vault fees, etc.\n * @param params is a struct of lastQueuedWithdrawAmount, currentQueuedWithdrawShares, isYearnPaused\n * @return newOption is the new option address\n * @return queuedWithdrawAmount is the queued amount for withdrawal\n */\n function _rollToNextOption(rollToNextOptionParams memory params)\n internal\n returns (address, uint256)\n {\n require(block.timestamp >= optionState.nextOptionReadyAt, \"!ready\");\n\n address newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n (\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares,\n uint256 performanceFeeInAsset,\n uint256 totalVaultFee\n ) =\n VaultLifecycle.rollover(\n vaultState,\n VaultLifecycle.RolloverParams(\n vaultParams.decimals,\n totalBalance(),\n totalSupply(),\n params.lastQueuedWithdrawAmount,\n performanceFee,\n managementFee,\n params.currentQueuedWithdrawShares\n )\n );\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n\n // Finalize the pricePerShare at the end of the round\n uint256 currentRound = vaultState.round;\n roundPricePerShare[currentRound] = newPricePerShare;\n\n address recipient = feeRecipient;\n\n emit CollectVaultFees(\n performanceFeeInAsset,\n totalVaultFee,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n _mint(address(this), mintShares);\n\n address collateral = address(collateralToken);\n\n if (params.isYearnPaused) {\n // note: withdrawing large amounts of yvUSDC will result in a slippage of ~ 0.001%\n // withdraw reverts if balance is 0\n if (collateralToken.balanceOf(address(this)) > 0) {\n collateralToken.withdraw(\n collateralToken.balanceOf(address(this)),\n address(this),\n 0\n );\n }\n } else {\n VaultLifecycleYearn.wrapToYieldToken(vaultParams.asset, collateral);\n }\n\n if (totalVaultFee > 0) {\n IERC20(vaultParams.asset).safeTransfer(recipient, totalVaultFee);\n }\n\n return (newOption, queuedWithdrawAmount);\n }\n\n /*\n Upgrades the vault to point to the latest yearn vault for the asset token\n */\n function upgradeYearnVault() external onlyOwner {\n // Unwrap old yvUSDC\n IYearnVault collateral = IYearnVault(collateralToken);\n collateral.withdraw(\n collateral.balanceOf(address(this)),\n address(this),\n YEARN_WITHDRAWAL_SLIPPAGE\n );\n\n _upgradeYearnVault();\n }\n\n function _upgradeYearnVault() internal {\n address collateralAddr =\n IYearnRegistry(YEARN_REGISTRY).latestVault(vaultParams.asset);\n require(collateralAddr != address(0), \"!collateralToken\");\n collateralToken = IYearnVault(collateralAddr);\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n external\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n return\n uint256(vaultState.lockedAmount)\n .add(IERC20(vaultParams.asset).balanceOf(address(this)))\n .add(\n DSMath.wmul(\n collateralToken.balanceOf(address(this)),\n collateralToken.pricePerShare().mul(\n VaultLifecycleYearn.decimalShift(\n address(collateralToken)\n )\n )\n )\n );\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n\n /************************************************\n * HELPERS\n ***********************************************/\n}\n" + }, + "contracts/storage/RibbonThetaYearnVaultStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nabstract contract RibbonThetaYearnVaultStorageV1 {\n // Logic contract used to price options\n address public optionsPremiumPricer;\n // Logic contract used to select strike prices\n address public strikeSelection;\n // Premium discount on options we are selling (thousandths place: 000 - 999)\n uint256 public premiumDiscount;\n // Current oToken premium\n uint256 public currentOtokenPremium;\n // Last round id at which the strike was manually overridden\n uint16 public lastStrikeOverrideRound;\n // Price last overridden strike set to\n uint256 public overriddenStrikePrice;\n // Auction duration\n uint256 public auctionDuration;\n // Auction id of current option\n uint256 public optionAuctionID;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV2 {\n // Amount locked for scheduled withdrawals last week;\n uint256 public lastQueuedWithdrawAmount;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV3 {\n // LiquidityGauge contract for the vault\n address public liquidityGauge;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV4 {\n // OptionsPurchaseQueue contract for selling options\n address public optionsPurchaseQueue;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV5 {\n // Queued withdraw shares for the current round\n uint256 public currentQueuedWithdrawShares;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV6 {\n // Vault Pauser Contract for the vault\n address public vaultPauser;\n}\n\nabstract contract RibbonThetaYearnVaultStorageV7 {\n // Check if yearn is used in vault\n bool public isYearnPaused;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of RibbonThetaVaultStorage\n// e.g. RibbonThetaVaultStorage, so finally it would look like\n// contract RibbonThetaVaultStorage is RibbonThetaVaultStorageV1, RibbonThetaVaultStorageV2\nabstract contract RibbonThetaYearnVaultStorage is\n RibbonThetaYearnVaultStorageV1,\n RibbonThetaYearnVaultStorageV2,\n RibbonThetaYearnVaultStorageV3,\n RibbonThetaYearnVaultStorageV4,\n RibbonThetaYearnVaultStorageV5,\n RibbonThetaYearnVaultStorageV6,\n RibbonThetaYearnVaultStorageV7\n{\n\n}\n" + }, + "contracts/interfaces/IVaultPauser.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface IVaultPauser {\n /// @notice pause vault position of an account with max amount\n /// @param _account the address of user\n /// @param _amount amount of shares\n function pausePosition(address _account, uint256 _amount) external;\n\n /// @notice resume vault position of an account with max amount\n /// @param _vaultAddress the address of vault\n function resumePosition(address _vaultAddress) external;\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "contracts/interfaces/IGnosisAuction.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nlibrary AuctionType {\n struct AuctionData {\n IERC20 auctioningToken;\n IERC20 biddingToken;\n uint256 orderCancellationEndDate;\n uint256 auctionEndDate;\n bytes32 initialAuctionOrder;\n uint256 minimumBiddingAmountPerOrder;\n uint256 interimSumBidAmount;\n bytes32 interimOrder;\n bytes32 clearingPriceOrder;\n uint96 volumeClearingPriceOrder;\n bool minFundingThresholdNotReached;\n bool isAtomicClosureAllowed;\n uint256 feeNumerator;\n uint256 minFundingThreshold;\n }\n}\n\ninterface IGnosisAuction {\n function initiateAuction(\n address _auctioningToken,\n address _biddingToken,\n uint256 orderCancellationEndDate,\n uint256 auctionEndDate,\n uint96 _auctionedSellAmount,\n uint96 _minBuyAmount,\n uint256 minimumBiddingAmountPerOrder,\n uint256 minFundingThreshold,\n bool isAtomicClosureAllowed,\n address accessManagerContract,\n bytes memory accessManagerContractData\n ) external returns (uint256);\n\n function auctionCounter() external view returns (uint256);\n\n function auctionData(uint256 auctionId)\n external\n view\n returns (AuctionType.AuctionData memory);\n\n function auctionAccessManager(uint256 auctionId)\n external\n view\n returns (address);\n\n function auctionAccessData(uint256 auctionId)\n external\n view\n returns (bytes memory);\n\n function FEE_DENOMINATOR() external view returns (uint256);\n\n function feeNumerator() external view returns (uint256);\n\n function settleAuction(uint256 auctionId) external returns (bytes32);\n\n function placeSellOrders(\n uint256 auctionId,\n uint96[] memory _minBuyAmounts,\n uint96[] memory _sellAmounts,\n bytes32[] memory _prevSellOrders,\n bytes calldata allowListCallData\n ) external returns (uint64);\n\n function claimFromParticipantOrder(\n uint256 auctionId,\n bytes32[] memory orders\n ) external returns (uint256, uint256);\n}\n" + }, + "contracts/interfaces/IRibbon.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport {Vault} from \"../libraries/Vault.sol\";\n\ninterface IRibbonVault {\n function deposit(uint256 amount) external;\n\n function depositETH() external payable;\n\n function cap() external view returns (uint256);\n\n function depositFor(uint256 amount, address creditor) external;\n\n function vaultParams() external view returns (Vault.VaultParams memory);\n}\n\ninterface IStrikeSelection {\n function getStrikePrice(uint256 expiryTimestamp, bool isPut)\n external\n view\n returns (uint256, uint256);\n\n function delta() external view returns (uint256);\n}\n\ninterface IOptionsPremiumPricer {\n function getPremium(\n uint256 strikePrice,\n uint256 timeToExpiry,\n bool isPut\n ) external view returns (uint256);\n\n function getPremiumInStables(\n uint256 strikePrice,\n uint256 timeToExpiry,\n bool isPut\n ) external view returns (uint256);\n\n function getOptionDelta(\n uint256 spotPrice,\n uint256 strikePrice,\n uint256 volatility,\n uint256 expiryTimestamp\n ) external view returns (uint256 delta);\n\n function getUnderlyingPrice() external view returns (uint256);\n\n function priceOracle() external view returns (address);\n\n function volatilityOracle() external view returns (address);\n\n function optionId() external view returns (bytes32);\n}\n" + }, + "contracts/interfaces/IRibbonThetaVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {Vault} from \"../libraries/Vault.sol\";\n\ninterface IRibbonThetaVault {\n function currentOption() external view returns (address);\n\n function nextOption() external view returns (address);\n\n function vaultParams() external view returns (Vault.VaultParams memory);\n\n function vaultState() external view returns (Vault.VaultState memory);\n\n function optionState() external view returns (Vault.OptionState memory);\n\n function optionAuctionID() external view returns (uint256);\n\n function pricePerShare() external view returns (uint256);\n\n function roundPricePerShare(uint256) external view returns (uint256);\n\n function depositFor(uint256 amount, address creditor) external;\n\n function initiateWithdraw(uint256 numShares) external;\n\n function completeWithdraw() external;\n\n function maxRedeem() external;\n\n function depositYieldTokenFor(uint256 amount, address creditor) external;\n\n function symbol() external view returns (string calldata);\n}\n" + }, + "contracts/interfaces/IERC20Detailed.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IERC20Detailed is IERC20 {\n function decimals() external view returns (uint8);\n\n function symbol() external view returns (string calldata);\n\n function name() external view returns (string calldata);\n}\n" + }, + "contracts/interfaces/IOptionsPurchaseQueue.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\ninterface IOptionsPurchaseQueue {\n /**\n * @dev Contains purchase request info\n * @param optionsAmount Amount of options to purchase\n * @param premiums Total premiums the buyer is spending to purchase the options (optionsAmount * ceilingPrice)\n * We need to track the premiums here since the ceilingPrice could change between the time the purchase was\n * requested and when the options are sold\n * @param buyer The buyer requesting this purchase\n */\n struct Purchase {\n uint128 optionsAmount; // Slot 0\n uint128 premiums;\n address buyer; // Slot 1\n }\n\n function purchases(address, uint256)\n external\n view\n returns (\n uint128,\n uint128,\n address\n );\n\n function totalOptionsAmount(address) external view returns (uint256);\n\n function vaultAllocatedOptions(address) external view returns (uint256);\n\n function whitelistedBuyer(address) external view returns (bool);\n\n function minPurchaseAmount(address) external view returns (uint256);\n\n function ceilingPrice(address) external view returns (uint256);\n\n function getPurchases(address vault)\n external\n view\n returns (Purchase[] memory);\n\n function getPremiums(address vault, uint256 optionsAmount)\n external\n view\n returns (uint256);\n\n function getOptionsAllocation(address vault, uint256 allocatedOptions)\n external\n view\n returns (uint256);\n\n function requestPurchase(address vault, uint256 optionsAmount)\n external\n returns (uint256);\n\n function allocateOptions(uint256 allocatedOptions)\n external\n returns (uint256);\n\n function sellToBuyers(uint256 settlementPrice) external returns (uint256);\n\n function cancelAllPurchases(address vault) external;\n\n function addWhitelist(address buyer) external;\n\n function removeWhitelist(address buyer) external;\n\n function setCeilingPrice(address vault, uint256 price) external;\n\n function setMinPurchaseAmount(address vault, uint256 amount) external;\n}\n" + }, + "contracts/libraries/SupportsNonCompliantERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\n/**\n * This library supports ERC20s that have quirks in their behavior.\n * One such ERC20 is USDT, which requires allowance to be 0 before calling approve.\n * We plan to update this library with ERC20s that display such idiosyncratic behavior.\n */\nlibrary SupportsNonCompliantERC20 {\n address private constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;\n\n function safeApproveNonCompliant(\n IERC20 token,\n address spender,\n uint256 amount\n ) internal {\n if (address(token) == USDT) {\n SafeERC20.safeApprove(token, spender, 0);\n }\n SafeERC20.safeApprove(token, spender, amount);\n }\n}\n" + }, + "contracts/interfaces/IYearn.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface IYearnVault {\n function pricePerShare() external view returns (uint256);\n\n function deposit(uint256 _amount, address _recipient)\n external\n returns (uint256);\n\n function withdraw(\n uint256 _maxShares,\n address _recipient,\n uint256 _maxLoss\n ) external returns (uint256);\n\n function approve(address _recipient, uint256 _amount)\n external\n returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n}\n\ninterface IYearnRegistry {\n function latestVault(address token) external returns (address);\n}\n\ninterface IYearnPricer {\n function setExpiryPriceInOracle(uint256 _expiryTimestamp) external;\n\n function getPrice() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IWETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface IWETH {\n function deposit() external payable;\n\n function withdraw(uint256) external;\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function decimals() external view returns (uint256);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuardUpgradeable is Initializable {\n // Booleans are more expensive than uint256 or any type that takes up a full\n // word because each write operation emits an extra SLOAD to first read the\n // slot's contents, replace the bits taken up by the boolean, and then write\n // back. This is the compiler's defense against contract upgrades and\n // pointer aliasing, and it cannot be disabled.\n\n // The values being non-zero value makes deployment a bit more expensive,\n // but in exchange the refund on every call to nonReentrant will be lower in\n // amount. Since refunds are capped to a percentage of the total\n // transaction's gas, it is best to keep them low in cases like this one, to\n // increase the likelihood of the full refund coming into effect.\n uint256 private constant _NOT_ENTERED = 1;\n uint256 private constant _ENTERED = 2;\n\n uint256 private _status;\n\n function __ReentrancyGuard_init() internal initializer {\n __ReentrancyGuard_init_unchained();\n }\n\n function __ReentrancyGuard_init_unchained() internal initializer {\n _status = _NOT_ENTERED;\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n // On the first call to nonReentrant, _notEntered will be true\n require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n _status = _ENTERED;\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = _NOT_ENTERED;\n }\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../utils/ContextUpgradeable.sol\";\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n function __Ownable_init() internal initializer {\n __Context_init_unchained();\n __Ownable_init_unchained();\n }\n\n function __Ownable_init_unchained() internal initializer {\n _setOwner(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _setOwner(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _setOwner(newOwner);\n }\n\n function _setOwner(address newOwner) private {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20Upgradeable.sol\";\nimport \"./extensions/IERC20MetadataUpgradeable.sol\";\nimport \"../../utils/ContextUpgradeable.sol\";\nimport \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n function __ERC20_init(string memory name_, string memory symbol_) internal initializer {\n __Context_init_unchained();\n __ERC20_init_unchained(name_, symbol_);\n }\n\n function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n\n uint256 currentAllowance = _allowances[sender][_msgSender()];\n require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n unchecked {\n _approve(sender, _msgSender(), currentAllowance - amount);\n }\n\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n uint256 currentAllowance = _allowances[_msgSender()][spender];\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n uint256 senderBalance = _balances[sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[sender] = senderBalance - amount;\n }\n _balances[recipient] += amount;\n\n emit Transfer(sender, recipient, amount);\n\n _afterTokenTransfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n _balances[account] += amount;\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n }\n _totalSupply -= amount;\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n uint256[45] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private _initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private _initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(_initializing || !_initialized, \"Initializable: contract is already initialized\");\n\n bool isTopLevelCall = !_initializing;\n if (isTopLevelCall) {\n _initializing = true;\n _initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n _initializing = false;\n }\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\nimport \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n function __Context_init() internal initializer {\n __Context_init_unchained();\n }\n\n function __Context_init_unchained() internal initializer {\n }\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n uint256[50] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20Upgradeable {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20Upgradeable.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20MetadataUpgradeable is IERC20Upgradeable {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/vaults/VaultPauser/RibbonVaultPauser.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {IRibbonThetaVault} from \"../../interfaces/IRibbonThetaVault.sol\";\nimport {IWETH} from \"../../interfaces/IWETH.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\n\ncontract RibbonVaultPauser is Ownable, IVaultPauser {\n using SafeERC20 for IERC20;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores all the vault's paused positions\n struct PauseReceipt {\n uint16 round;\n uint128 shares;\n }\n\n mapping(address => mapping(address => PauseReceipt)) public pausedPositions;\n mapping(address => bool) private registeredVaults;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n address public immutable STETH;\n address public immutable STETH_VAULT;\n\n address public keeper;\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Pause(\n address indexed account,\n address indexed vaultAddress,\n uint256 share,\n uint256 round\n );\n\n event Resume(\n address indexed account,\n address indexed vaultAddress,\n uint256 withdrawAmount\n );\n\n event ProcessWithdrawal(address indexed vaultAddress, uint256 round);\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n */\n constructor(\n address _keeper,\n address _weth,\n address _steth,\n address _steth_vault\n ) {\n require(_keeper != address(0), \"!_keeper\");\n require(_weth != address(0), \"!_weth\");\n require(_steth != address(0), \"!_steth\");\n require(_steth_vault != address(0), \"!_steth_vault\");\n\n keeper = _keeper;\n WETH = _weth;\n STETH = _steth;\n STETH_VAULT = _steth_vault;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n function getPausePosition(address _vaultAddress, address _userAddress)\n external\n view\n returns (PauseReceipt memory)\n {\n return pausedPositions[_vaultAddress][_userAddress];\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param _newKeeper is the address of the new keeper\n */\n function setNewKeeper(address _newKeeper) external onlyOwner {\n require(_newKeeper != address(0), \"!newKeeper\");\n keeper = _newKeeper;\n }\n\n /**\n * @notice add vault into registered vaults\n * @param _vaultAddress is the address of the new vault to be registered\n */\n function addVault(address _vaultAddress) external onlyOwner {\n registeredVaults[_vaultAddress] = true;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice pause position from vault by redeem all the shares from vault to Pauser\n * @param _account user's address\n * @param _amount the amount of shares\n */\n function pausePosition(address _account, uint256 _amount)\n external\n override\n {\n address currentVaultAddress = msg.sender;\n IRibbonThetaVault currentVault = IRibbonThetaVault(currentVaultAddress);\n\n // check if vault is registered\n require(\n registeredVaults[currentVaultAddress],\n \"Vault is not registered\"\n );\n\n PauseReceipt storage pausedPosition =\n pausedPositions[currentVaultAddress][_account];\n\n // check if position is paused\n require(\n pausedPosition.shares == 0 && pausedPosition.round == 0,\n \"Position is paused\"\n );\n\n uint16 round = currentVault.vaultState().round;\n\n require(_amount < type(uint128).max, \"_amount overflow\");\n\n pausedPositions[currentVaultAddress][_account] = PauseReceipt({\n round: round,\n shares: uint128(_amount)\n });\n\n emit Pause(_account, currentVaultAddress, _amount, round);\n\n // transfer from user to pauser\n IERC20(currentVaultAddress).safeTransferFrom(\n _account,\n address(this),\n _amount\n );\n\n currentVault.initiateWithdraw(_amount);\n }\n\n /**\n * @notice resume user's position into vault by making a deposit\n * @param _vaultAddress vault's address\n */\n function resumePosition(address _vaultAddress) external override {\n IRibbonThetaVault currentVault = IRibbonThetaVault(_vaultAddress);\n\n // check if vault is registered\n require(registeredVaults[_vaultAddress], \"Vault is not registered\");\n\n // get params and round\n Vault.VaultParams memory currentParams = currentVault.vaultParams();\n uint256 round = currentVault.vaultState().round;\n\n PauseReceipt storage pauseReceipt =\n pausedPositions[_vaultAddress][msg.sender];\n uint256 pauseReceiptRound = pauseReceipt.round;\n\n // check if roun is closed before resuming position\n require(pauseReceiptRound < round, \"Round not closed yet\");\n uint256 totalWithdrawAmount =\n ShareMath.sharesToAsset(\n pauseReceipt.shares,\n currentVault.roundPricePerShare(pauseReceiptRound),\n currentParams.decimals\n );\n\n // delete position once transfer (revert to zero)\n delete pausedPositions[_vaultAddress][msg.sender];\n\n // stETH transfers suffer from an off-by-1 error\n // since we received STETH , we shall deposit using STETH instead of ETH\n if (_vaultAddress == STETH_VAULT) {\n totalWithdrawAmount = totalWithdrawAmount - 3;\n\n emit Resume(msg.sender, _vaultAddress, totalWithdrawAmount - 1);\n IERC20(STETH).safeApprove(_vaultAddress, totalWithdrawAmount);\n currentVault.depositYieldTokenFor(totalWithdrawAmount, msg.sender);\n } else {\n emit Resume(msg.sender, _vaultAddress, totalWithdrawAmount);\n\n // if asset is ETH, we will convert it into WETH before depositing\n if (currentParams.asset == WETH) {\n IWETH(WETH).deposit{value: totalWithdrawAmount}();\n }\n IERC20(currentParams.asset).safeApprove(\n _vaultAddress,\n totalWithdrawAmount\n );\n\n currentVault.depositFor(totalWithdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice process withdrawals by completing in a batch\n * @param _vaultAddress vault's address to be processed\n */\n function processWithdrawal(address _vaultAddress) external onlyKeeper {\n IRibbonThetaVault currentVault = IRibbonThetaVault(_vaultAddress);\n // we can only process withdrawal after closing the previous round\n // hence round should be - 1\n emit ProcessWithdrawal(\n _vaultAddress,\n currentVault.vaultState().round - 1\n );\n currentVault.completeWithdraw();\n }\n\n fallback() external payable {}\n\n receive() external payable {}\n}\n" + }, + "@openzeppelin/contracts/access/Ownable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _setOwner(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _setOwner(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _setOwner(newOwner);\n }\n\n function _setOwner(address newOwner) private {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "contracts/vaults/TreasuryVault/RibbonTreasuryVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\n\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {\n VaultLifecycleTreasury\n} from \"../../libraries/VaultLifecycleTreasury.sol\";\nimport {\n RibbonTreasuryVaultStorage\n} from \"../../storage/RibbonTreasuryVaultStorage.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {IWETH} from \"../../interfaces/IWETH.sol\";\nimport {GnosisAuction} from \"../../libraries/GnosisAuction.sol\";\nimport {IERC20Detailed} from \"../../interfaces/IERC20Detailed.sol\";\n\ncontract RibbonTreasuryVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable,\n RibbonTreasuryVaultStorage\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // GNOSIS_EASY_AUCTION is Gnosis protocol's contract for initiating auctions and placing bids\n // https://github.com/gnosis/ido-contracts/blob/main/contracts/EasyAuction.sol\n address public immutable GNOSIS_EASY_AUCTION;\n\n /// OTOKEN_FACTORY is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n // The minimum amount above which premium distribution will occur during commitAndClose\n uint256 private constant MIN_DUST_AMOUNT = 10000000;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectManagementFee(\n uint256 managementFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n event CollectPerformanceFee(\n uint256 performanceFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n event DistributePremium(\n uint256 amount,\n uint256[] amounts,\n address[] recipients,\n uint256 round\n );\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event PremiumDiscountSet(\n uint256 premiumDiscount,\n uint256 newPremiumDiscount\n );\n\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event InitiateGnosisAuction(\n address indexed auctioningToken,\n address indexed biddingToken,\n uint256 auctionCounter,\n address indexed manager\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions\n */\n constructor(\n address _weth,\n address _usdc,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _gnosisEasyAuction\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n require(_gnosisEasyAuction != address(0), \"!_gnosisEasyAuction\");\n\n WETH = _weth;\n USDC = _usdc;\n OTOKEN_FACTORY = _oTokenFactory;\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n GNOSIS_EASY_AUCTION = _gnosisEasyAuction;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function initialize(\n VaultLifecycleTreasury.InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n VaultLifecycleTreasury.verifyInitializerParams(\n _initParams,\n _vaultParams,\n MIN_AUCTION_DURATION\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_initParams._tokenName, _initParams._tokenSymbol);\n __Ownable_init();\n transferOwnership(_initParams._owner);\n\n keeper = _initParams._keeper;\n period = _initParams._period;\n optionsPremiumPricer = _initParams._optionsPremiumPricer;\n strikeSelection = _initParams._strikeSelection;\n premiumDiscount = _initParams._premiumDiscount;\n auctionDuration = _initParams._auctionDuration;\n feeRecipient = _initParams._feeRecipient;\n performanceFee = _initParams._performanceFee;\n managementFee = _perRoundManagementFee(_initParams._managementFee);\n maxDepositors = _initParams._maxDepositors;\n minDeposit = _initParams._minDeposit;\n\n vaultParams = _vaultParams;\n vaultState.round = 1;\n\n uint256 assetBalance =\n IERC20(vaultParams.asset).balanceOf(address(this));\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n managementFee = _perRoundManagementFee(newManagementFee);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n }\n\n /**\n * @notice Internal function to set the management fee for the vault\n * @param managementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2\n * @return perRoundManagementFee is the management divided by the number of rounds per year\n */\n function _perRoundManagementFee(uint256 managementFee)\n internal\n view\n returns (uint256)\n {\n uint256 _period = period;\n uint256 feeDivider =\n _period % 30 == 0\n ? Vault.FEE_MULTIPLIER * (12 / (_period / 30))\n : WEEKS_PER_YEAR / (_period / 7);\n\n // We are dividing annualized management fee by num weeks in a year\n return managementFee.mul(Vault.FEE_MULTIPLIER).div(feeDivider);\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /**\n * @notice Sets the new discount on premiums for options we are selling\n * @param newPremiumDiscount is the premium discount\n */\n function setPremiumDiscount(uint256 newPremiumDiscount) external onlyOwner {\n require(\n newPremiumDiscount > 0 &&\n newPremiumDiscount < 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"Invalid discount\"\n );\n\n emit PremiumDiscountSet(premiumDiscount, newPremiumDiscount);\n\n premiumDiscount = newPremiumDiscount;\n }\n\n /**\n * @notice Sets the new auction duration\n * @param newAuctionDuration is the auction duration\n */\n function setAuctionDuration(uint256 newAuctionDuration) external onlyOwner {\n require(\n newAuctionDuration >= MIN_AUCTION_DURATION,\n \"Invalid auction duration\"\n );\n\n emit AuctionDurationSet(auctionDuration, newAuctionDuration);\n\n auctionDuration = newAuctionDuration;\n }\n\n /**\n * @notice Sets the new strike selection contract\n * @param newStrikeSelection is the address of the new strike selection contract\n */\n function setStrikeSelection(address newStrikeSelection) external onlyOwner {\n require(newStrikeSelection != address(0), \"!newStrikeSelection\");\n strikeSelection = newStrikeSelection;\n }\n\n /**\n * @notice Sets the new options premium pricer contract\n * @param newOptionsPremiumPricer is the address of the new strike selection contract\n */\n function setOptionsPremiumPricer(address newOptionsPremiumPricer)\n external\n onlyOwner\n {\n require(\n newOptionsPremiumPricer != address(0),\n \"!newOptionsPremiumPricer\"\n );\n optionsPremiumPricer = newOptionsPremiumPricer;\n }\n\n /**\n * @notice Optionality to set strike price manually\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice)\n external\n onlyOwner\n nonReentrant\n {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Set the maximum number of depositors\n * @param newMaxDepositors is the new cap for number of depositors\n */\n function setMaxDepositors(uint256 newMaxDepositors)\n external\n onlyOwner\n nonReentrant\n {\n require(newMaxDepositors > 0, \"!newMaxDepositors\");\n maxDepositors = newMaxDepositors;\n }\n\n /**\n * @notice Set the minimum deposit amount\n * @param newMinDeposit is the new minimum amount for deposit\n */\n function setMinDeposit(uint256 newMinDeposit)\n external\n onlyOwner\n nonReentrant\n {\n require(newMinDeposit > 0, \"!newMinDeposit\");\n minDeposit = newMinDeposit;\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Internal function to add new depositor address\n * @param newDepositor is the address to include in the depositors list\n */\n function _addDepositor(address newDepositor) internal {\n if (!depositorsMap[newDepositor]) {\n require(newDepositor != address(0), \"Depositor address null\");\n require(\n (depositorsArray.length + 1) <= maxDepositors,\n \"Number of depositors exceeds limit\"\n );\n\n depositorsMap[newDepositor] = true;\n depositorsArray.push(newDepositor);\n }\n }\n\n /**\n * @notice Remove addresses from depositors list\n * @param excludeDepositor is the address to exclude from the depositors list\n */\n function _removeDepositor(address excludeDepositor) internal {\n address[] storage array = depositorsArray;\n uint256 arrayLength = array.length;\n\n require(depositorsMap[excludeDepositor], \"Depositor does not exist\");\n\n depositorsMap[excludeDepositor] = false;\n\n for (uint256 i = 0; i < arrayLength - 1; i++) {\n if (excludeDepositor == array[i]) {\n (array[i], array[arrayLength - 1]) = (\n array[arrayLength - 1],\n array[i]\n );\n }\n }\n array.pop();\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender.\n * @param amount is the amount of `asset` to deposit\n */\n function deposit(uint256 amount) external nonReentrant {\n require(amount > 0, \"!amount\");\n\n _addDepositor(msg.sender);\n\n _depositFor(amount, msg.sender);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n */\n function _depositFor(uint256 amount, address creditor) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount = totalBalance().add(amount);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n uint256 totalUserDeposit =\n accountVaultBalance(msg.sender).add(depositReceipt.amount).add(\n amount\n );\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n require(totalUserDeposit >= minDeposit, \"Minimum deposit not reached\");\n\n emit Deposit(creditor, amount, currentRound);\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n if (depositReceipt.amount > 0 || depositReceipt.unredeemedShares > 0) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n uint256 withdrawalRound = withdrawal.round;\n\n bool withdrawalIsSameRound = withdrawalRound == currentRound;\n\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n\n uint256 existingShares = uint256(withdrawal.shares);\n\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n\n // Ensure withdrawal does not reduce user deposit below the minimum amount\n uint256 vaultDecimals = vaultParams.decimals;\n uint256 userBalance = accountVaultBalance(msg.sender);\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n numShares,\n currentRound != 1\n ? roundPricePerShare[currentRound - 1]\n : 10**vaultDecimals,\n vaultDecimals\n );\n\n if (userBalance > withdrawAmount) {\n uint256 totalDeposit = userBalance.sub(withdrawAmount);\n require(totalDeposit >= minDeposit, \"Minimum deposit not reached\");\n }\n\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(numShares);\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n if (depositReceipt.amount == 0 && balanceOf(msg.sender) == numShares) {\n _removeDepositor(msg.sender);\n }\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return withdrawAmount the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n require(withdrawAmount > 0, \"!withdrawAmount\");\n transferAsset(msg.sender, withdrawAmount);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n uint256 userBalance =\n accountVaultBalance(msg.sender).add(receiptAmount);\n\n if (userBalance > amount) {\n uint256 totalUserDeposit = userBalance.sub(amount);\n require(\n totalUserDeposit >= minDeposit,\n \"Minimum deposit not reached\"\n );\n }\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n if (depositReceipt.amount == 0 && shares(msg.sender) == 0) {\n _removeDepositor(msg.sender);\n }\n\n transferAsset(msg.sender, amount);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /*\n * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map.\n * Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k.\n * Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0.\n * @param numRounds is the number of rounds to initialize in the map\n */\n function initRounds(uint256 numRounds) external nonReentrant {\n require(numRounds > 0, \"!numRounds\");\n\n uint256 _round = vaultState.round;\n for (uint256 i = 0; i < numRounds; i++) {\n uint256 index = _round + i;\n require(roundPricePerShare[index] == 0, \"Initialized\"); // AVOID OVERWRITING ACTUAL VALUES\n roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT;\n }\n }\n\n /*\n * @notice Helper function that performs most administrative tasks\n * such as setting next option, minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @return newOption is the new option address\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _rollToNextOption(uint256 lastQueuedWithdrawAmount)\n internal\n returns (\n address newOption,\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount\n )\n {\n require(block.timestamp >= optionState.nextOptionReadyAt, \"!ready\");\n\n newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n uint256 currentRound = vaultState.round;\n address recipient = feeRecipient;\n uint256 mintShares;\n uint256 managementFeeInAsset;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n managementFeeInAsset\n ) = VaultLifecycleTreasury.rollover(\n vaultState,\n VaultLifecycleTreasury.RolloverParams(\n vaultParams.decimals,\n IERC20(vaultParams.asset).balanceOf(address(this)),\n totalSupply(),\n lastQueuedWithdrawAmount,\n currentRound != 1 ? managementFee : 0\n )\n );\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n\n // Finalize the pricePerShare at the end of the round\n\n roundPricePerShare[currentRound] = newPricePerShare;\n\n emit CollectManagementFee(\n managementFeeInAsset,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (managementFeeInAsset > 0) {\n transferAsset(payable(recipient), managementFeeInAsset);\n }\n\n return (newOption, lockedBalance, queuedWithdrawAmount);\n }\n\n /**\n * @notice Helper function to make an ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) internal {\n address asset = vaultParams.asset;\n IERC20(asset).safeTransfer(recipient, amount);\n }\n\n /**\n * @notice Sets the next option the vault will be shorting, and closes the existing short.\n * This allows all the users to withdraw if the next option is malicious.\n */\n function commitAndClose() external nonReentrant {\n address oldOption = optionState.currentOption;\n\n VaultLifecycleTreasury.CloseParams memory closeParams =\n VaultLifecycleTreasury.CloseParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n currentOption: oldOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n period: period\n });\n\n (\n address otokenAddress,\n uint256 premium,\n uint256 strikePrice,\n uint256 delta\n ) =\n VaultLifecycleTreasury.commitAndClose(\n strikeSelection,\n optionsPremiumPricer,\n premiumDiscount,\n closeParams,\n vaultParams,\n vaultState\n );\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n ShareMath.assertUint104(premium);\n currentOtokenPremium = uint104(premium);\n optionState.nextOption = otokenAddress;\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n\n _closeShort(oldOption);\n\n // In case chargeAndDistribute was not called last round, call\n // the function to conclude last round's performance fee and distribution\n if (IERC20(USDC).balanceOf(address(this)) > MIN_DUST_AMOUNT) {\n _chargeAndDistribute();\n }\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycleTreasury.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n (\n address newOption,\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount\n ) = _rollToNextOption(uint256(lastQueuedWithdrawAmount));\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n VaultLifecycleTreasury.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n _startAuction();\n }\n\n /**\n * @notice Initiate the gnosis auction.\n */\n function startAuction() external onlyKeeper nonReentrant {\n _startAuction();\n }\n\n function _startAuction() private {\n GnosisAuction.AuctionDetails memory auctionDetails;\n\n uint256 currOtokenPremium = currentOtokenPremium;\n\n require(currOtokenPremium > 0, \"!currentOtokenPremium\");\n\n uint256 stableDecimals = IERC20Detailed(USDC).decimals();\n\n auctionDetails.oTokenAddress = optionState.currentOption;\n auctionDetails.gnosisEasyAuction = GNOSIS_EASY_AUCTION;\n auctionDetails.asset = USDC;\n auctionDetails.assetDecimals = stableDecimals;\n auctionDetails.oTokenPremium = currOtokenPremium;\n auctionDetails.duration = auctionDuration;\n\n optionAuctionID = VaultLifecycleTreasury.startAuction(auctionDetails);\n }\n\n /**\n * @notice Burn the remaining oTokens left over from gnosis auction.\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n uint256 unlockedAssetAmount =\n VaultLifecycleTreasury.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n\n vaultState.lockedAmount = uint104(\n uint256(vaultState.lockedAmount).sub(unlockedAssetAmount)\n );\n }\n\n /**\n * @notice Settles the round's Gnosis auction and distribute the premiums earned\n */\n function concludeOptionsSale() external onlyKeeper nonReentrant {\n VaultLifecycleTreasury.settleAuction(\n GNOSIS_EASY_AUCTION,\n optionAuctionID\n );\n\n if (IERC20(USDC).balanceOf(address(this)) > MIN_DUST_AMOUNT) {\n _chargeAndDistribute();\n }\n }\n\n /**\n * @notice Charge performance fee and distribute remaining to depositors addresses\n */\n function chargeAndDistribute() external onlyKeeper nonReentrant {\n _chargeAndDistribute();\n }\n\n /**\n * @notice Calculate performance fee and transfer to fee recipient\n */\n function _chargeAndDistribute() internal {\n IERC20 stableAsset = IERC20(USDC);\n uint256 stableBalance = stableAsset.balanceOf(address(this));\n\n require(stableBalance > 0, \"no premium to distribute\");\n\n _chargePerformanceFee(stableAsset, stableBalance);\n\n _distributePremium(\n stableAsset,\n stableAsset.balanceOf(address(this)) // Get the new balance\n );\n }\n\n /**\n * @notice Charge performance fee\n */\n function _chargePerformanceFee(IERC20 token, uint256 amount) internal {\n address recipient = feeRecipient;\n uint256 transferAmount =\n amount.mul(performanceFee).div(100 * Vault.FEE_MULTIPLIER);\n\n token.safeTransfer(recipient, transferAmount);\n\n // Performance fee for the round is charged after rollover\n // hence we need to adjust the round to the previous\n emit CollectPerformanceFee(\n transferAmount,\n vaultState.round - 1,\n recipient\n );\n }\n\n /**\n * @notice Distribute the premium to depositor addresses\n */\n function _distributePremium(IERC20 token, uint256 amount) internal {\n // Distribute to depositor address\n address[] storage _depositors = depositorsArray;\n uint256[] memory _amounts = new uint256[](_depositors.length);\n uint256 totalSupply = totalSupply() - lastQueuedWithdrawAmount;\n\n for (uint256 i = 0; i < _depositors.length; i++) {\n // Distribute to depositors proportional to the amount of\n // shares they own\n address depositorAddress = _depositors[i];\n _amounts[i] = shares(depositorAddress).mul(amount).div(totalSupply);\n\n token.safeTransfer(depositorAddress, _amounts[i]);\n }\n\n emit DistributePremium(\n amount,\n _amounts,\n _depositors,\n vaultState.round - 1\n );\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n public\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n return\n uint256(vaultState.lockedAmount).add(\n IERC20(vaultParams.asset).balanceOf(address(this))\n );\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n /**\n * @notice Returns the maximum capacity of the vault in terms of the vault's asset\n */\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n /**\n * @notice Returns the date and time for the next options sale\n */\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n /**\n * @notice Returns the options specification for the current round\n */\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n /**\n * @notice Returns the options specification for the next round\n */\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n /**\n * @notice Returns total pending deposit for the current round\n */\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n\n /**\n * @notice ERC20 _transfer override function\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal override {\n require(\n recipient == address(this) || sender == address(this),\n \"Treasury rToken is not transferrable\"\n );\n return ERC20Upgradeable._transfer(sender, recipient, amount);\n }\n}\n" + }, + "contracts/libraries/VaultLifecycleTreasury.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {ShareMath} from \"./ShareMath.sol\";\nimport {IStrikeSelection} from \"../interfaces/IRibbon.sol\";\nimport {GnosisAuction} from \"./GnosisAuction.sol\";\nimport {DateTime} from \"./DateTime.sol\";\nimport {\n IOtokenFactory,\n IOtoken,\n IController,\n GammaTypes\n} from \"../interfaces/GammaInterface.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\nimport {IGnosisAuction} from \"../interfaces/IGnosisAuction.sol\";\nimport {SupportsNonCompliantERC20} from \"./SupportsNonCompliantERC20.sol\";\n\nlibrary VaultLifecycleTreasury {\n using SafeMath for uint256;\n using SupportsNonCompliantERC20 for IERC20;\n\n struct CloseParams {\n address OTOKEN_FACTORY;\n address USDC;\n address currentOption;\n uint256 delay;\n uint16 lastStrikeOverrideRound;\n uint256 overriddenStrikePrice;\n uint256 period;\n }\n\n /**\n * @notice Initialization parameters for the vault.\n * @param _owner is the owner of the vault with critical permissions\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n * @param _premiumDiscount is the vault's discount applied to the premium\n * @param _auctionDuration is the duration of the gnosis auction\n * @param _period is the period between each option sales\n */\n struct InitParams {\n address _owner;\n address _keeper;\n address _feeRecipient;\n uint256 _managementFee;\n uint256 _performanceFee;\n string _tokenName;\n string _tokenSymbol;\n address _optionsPremiumPricer;\n address _strikeSelection;\n uint32 _premiumDiscount;\n uint256 _auctionDuration;\n uint256 _period;\n uint256 _maxDepositors;\n uint256 _minDeposit;\n }\n\n /**\n * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction\n * @param strikeSelection is the address of the contract with strike selection logic\n * @param optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param premiumDiscount is the vault's discount applied to the premium\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @return otokenAddress is the address of the new option\n * @return premium is the premium of the new option\n * @return strikePrice is the strike price of the new option\n * @return delta is the delta of the new option\n */\n function commitAndClose(\n address strikeSelection,\n address optionsPremiumPricer,\n uint256 premiumDiscount,\n CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n Vault.VaultState storage vaultState\n )\n external\n returns (\n address otokenAddress,\n uint256 premium,\n uint256 strikePrice,\n uint256 delta\n )\n {\n uint256 expiry;\n\n // uninitialized state\n if (closeParams.currentOption == address(0)) {\n expiry = getNextExpiry(block.timestamp, closeParams.period);\n } else {\n expiry = getNextExpiry(\n IOtoken(closeParams.currentOption).expiryTimestamp(),\n closeParams.period\n );\n }\n\n IStrikeSelection selection = IStrikeSelection(strikeSelection);\n\n bool isPut = vaultParams.isPut;\n address underlying = vaultParams.underlying;\n address asset = vaultParams.asset;\n\n (strikePrice, delta) = closeParams.lastStrikeOverrideRound ==\n vaultState.round\n ? (closeParams.overriddenStrikePrice, 0)\n : selection.getStrikePrice(expiry, isPut);\n\n require(strikePrice != 0, \"!strikePrice\");\n\n // retrieve address if option already exists, or deploy it\n otokenAddress = getOrDeployOtoken(\n closeParams,\n vaultParams,\n underlying,\n asset,\n strikePrice,\n expiry,\n isPut\n );\n\n // get the black scholes premium of the option\n premium = GnosisAuction.getOTokenPremiumInStables(\n otokenAddress,\n optionsPremiumPricer,\n premiumDiscount\n );\n\n require(premium > 0, \"!premium\");\n\n return (otokenAddress, premium, strikePrice, delta);\n }\n\n /**\n * @notice Verify the otoken has the correct parameters to prevent vulnerability to opyn contract changes\n * @param otokenAddress is the address of the otoken\n * @param vaultParams is the struct with vault general data\n * @param collateralAsset is the address of the collateral asset\n * @param USDC is the address of usdc\n * @param delay is the delay between commitAndClose and rollToNextOption\n */\n function verifyOtoken(\n address otokenAddress,\n Vault.VaultParams storage vaultParams,\n address collateralAsset,\n address USDC,\n uint256 delay\n ) private view {\n require(otokenAddress != address(0), \"!otokenAddress\");\n\n IOtoken otoken = IOtoken(otokenAddress);\n require(otoken.isPut() == vaultParams.isPut, \"Type mismatch\");\n require(\n otoken.underlyingAsset() == vaultParams.underlying,\n \"Wrong underlyingAsset\"\n );\n require(\n otoken.collateralAsset() == collateralAsset,\n \"Wrong collateralAsset\"\n );\n\n // we just assume all options use USDC as the strike\n require(otoken.strikeAsset() == USDC, \"strikeAsset != USDC\");\n\n uint256 readyAt = block.timestamp.add(delay);\n require(otoken.expiryTimestamp() >= readyAt, \"Expiry before delay\");\n }\n\n /**\n * @param currentShareSupply is the supply of the shares invoked with totalSupply()\n * @param asset is the address of the vault's asset\n * @param decimals is the decimals of the asset\n * @param lastQueuedWithdrawAmount is the amount queued for withdrawals from last round\n * @param managementFee is the management fee percent to charge on the AUM\n */\n struct RolloverParams {\n uint256 decimals;\n uint256 totalBalance;\n uint256 currentShareSupply;\n uint256 lastQueuedWithdrawAmount;\n uint256 managementFee;\n }\n\n /**\n * @notice Calculate the shares to mint, new price per share, and\n amount of funds to re-allocate as collateral for the new round\n * @param vaultState is the storage variable vaultState passed from RibbonVault\n * @param params is the rollover parameters passed to compute the next state\n * @return newLockedAmount is the amount of funds to allocate for the new round\n * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal\n * @return newPricePerShare is the price per share of the new round\n * @return mintShares is the amount of shares to mint from deposits\n * @return managementFeeInAsset is the amount of management fee charged by vault\n */\n function rollover(\n Vault.VaultState storage vaultState,\n RolloverParams calldata params\n )\n external\n view\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares,\n uint256 managementFeeInAsset\n )\n {\n uint256 currentBalance = params.totalBalance;\n uint256 pendingAmount = vaultState.totalPending;\n uint256 queuedWithdrawShares = vaultState.queuedWithdrawShares;\n\n uint256 balanceForVaultFees;\n {\n uint256 pricePerShareBeforeFee =\n ShareMath.pricePerShare(\n params.currentShareSupply,\n currentBalance,\n pendingAmount,\n params.decimals\n );\n\n uint256 queuedWithdrawBeforeFee =\n params.currentShareSupply > 0\n ? ShareMath.sharesToAsset(\n queuedWithdrawShares,\n pricePerShareBeforeFee,\n params.decimals\n )\n : 0;\n\n // Deduct the difference between the newly scheduled withdrawals\n // and the older withdrawals\n // so we can charge them fees before they leave\n uint256 withdrawAmountDiff =\n queuedWithdrawBeforeFee > params.lastQueuedWithdrawAmount\n ? queuedWithdrawBeforeFee.sub(\n params.lastQueuedWithdrawAmount\n )\n : 0;\n\n balanceForVaultFees = currentBalance\n .sub(queuedWithdrawBeforeFee)\n .add(withdrawAmountDiff);\n }\n\n managementFeeInAsset = getManagementFee(\n balanceForVaultFees,\n vaultState.totalPending,\n params.managementFee\n );\n\n // Take into account the fee\n // so we can calculate the newPricePerShare\n currentBalance = currentBalance.sub(managementFeeInAsset);\n\n {\n newPricePerShare = ShareMath.pricePerShare(\n params.currentShareSupply,\n currentBalance,\n pendingAmount,\n params.decimals\n );\n\n // After closing the short, if the options expire in-the-money\n // vault pricePerShare would go down because vault's asset balance decreased.\n // This ensures that the newly-minted shares do not take on the loss.\n mintShares = ShareMath.assetToShares(\n pendingAmount,\n newPricePerShare,\n params.decimals\n );\n\n uint256 newSupply = params.currentShareSupply.add(mintShares);\n\n queuedWithdrawAmount = newSupply > 0\n ? ShareMath.sharesToAsset(\n queuedWithdrawShares,\n newPricePerShare,\n params.decimals\n )\n : 0;\n }\n\n return (\n currentBalance.sub(queuedWithdrawAmount), // new locked balance subtracts the queued withdrawals\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n managementFeeInAsset\n );\n }\n\n /**\n * @notice Creates the actual Opyn short position by depositing collateral and minting otokens\n * @param gammaController is the address of the opyn controller contract\n * @param marginPool is the address of the opyn margin contract which holds the collateral\n * @param oTokenAddress is the address of the otoken to mint\n * @param depositAmount is the amount of collateral to deposit\n * @return the otoken mint amount\n */\n function createShort(\n address gammaController,\n address marginPool,\n address oTokenAddress,\n uint256 depositAmount\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n uint256 newVaultID =\n (controller.getAccountVaultCounter(address(this))).add(1);\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IOtoken oToken = IOtoken(oTokenAddress);\n address collateralAsset = oToken.collateralAsset();\n\n uint256 collateralDecimals =\n uint256(IERC20Detailed(collateralAsset).decimals());\n uint256 mintAmount;\n\n if (oToken.isPut()) {\n // For minting puts, there will be instances where the full depositAmount will not be used for minting.\n // This is because of an issue with precision.\n //\n // For ETH put options, we are calculating the mintAmount (10**8 decimals) using\n // the depositAmount (10**18 decimals), which will result in truncation of decimals when scaling down.\n // As a result, there will be tiny amounts of dust left behind in the Opyn vault when minting put otokens.\n //\n // For simplicity's sake, we do not refund the dust back to the address(this) on minting otokens.\n // We retain the dust in the vault so the calling contract can withdraw the\n // actual locked amount + dust at settlement.\n //\n // To test this behavior, we can console.log\n // MarginCalculatorInterface(0x7A48d10f372b3D7c60f6c9770B91398e4ccfd3C7).getExcessCollateral(vault)\n // to see how much dust (or excess collateral) is left behind.\n mintAmount = depositAmount\n .mul(10**Vault.OTOKEN_DECIMALS)\n .mul(10**18) // we use 10**18 to give extra precision\n .div(oToken.strikePrice().mul(10**(10 + collateralDecimals)));\n } else {\n mintAmount = depositAmount;\n\n if (collateralDecimals > 8) {\n uint256 scaleBy = 10**(collateralDecimals.sub(8)); // oTokens have 8 decimals\n if (mintAmount > scaleBy) {\n mintAmount = depositAmount.div(scaleBy); // scale down from 10**18 to 10**8\n }\n }\n }\n\n // double approve to fix non-compliant ERC20s\n IERC20 collateralToken = IERC20(collateralAsset);\n collateralToken.safeApproveNonCompliant(marginPool, depositAmount);\n\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](3);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.OpenVault,\n address(this), // owner\n address(this), // receiver\n address(0), // asset, otoken\n newVaultID, // vaultId\n 0, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.DepositCollateral,\n address(this), // owner\n address(this), // address to transfer from\n collateralAsset, // deposited asset\n newVaultID, // vaultId\n depositAmount, // amount\n 0, //index\n \"\" //data\n );\n\n actions[2] = IController.ActionArgs(\n IController.ActionType.MintShortOption,\n address(this), // owner\n address(this), // address to transfer to\n oTokenAddress, // option address\n newVaultID, // vaultId\n mintAmount, // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n return mintAmount;\n }\n\n /**\n * @notice Close the existing short otoken position. Currently this implementation is simple.\n * It closes the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time. Since calling `_closeShort` deletes vaults by\n calling SettleVault action, this assumption should hold.\n * @param gammaController is the address of the opyn controller contract\n * @return amount of collateral redeemed from the vault\n */\n function settleShort(address gammaController) external returns (uint256) {\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n // The short position has been previously closed, or all the otokens have been burned.\n // So we return early.\n if (address(collateralToken) == address(0)) {\n return 0;\n }\n\n // This is equivalent to doing IERC20(vault.asset).balanceOf(address(this))\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // If it is after expiry, we need to settle the short position using the normal way\n // Delete the vault and withdraw all remaining collateral from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.SettleVault,\n address(this), // owner\n address(this), // address to transfer to\n address(0), // not used\n vaultID, // vaultId\n 0, // not used\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Exercises the ITM option using existing long otoken position. Currently this implementation is simple.\n * It calls the `Redeem` action to claim the payout.\n * @param gammaController is the address of the opyn controller contract\n * @param oldOption is the address of the old option\n * @param asset is the address of the vault's asset\n * @return amount of asset received by exercising the option\n */\n function settleLong(\n address gammaController,\n address oldOption,\n address asset\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n\n uint256 oldOptionBalance = IERC20(oldOption).balanceOf(address(this));\n\n if (controller.getPayout(oldOption, oldOptionBalance) == 0) {\n return 0;\n }\n\n uint256 startAssetBalance = IERC20(asset).balanceOf(address(this));\n\n // If it is after expiry, we need to redeem the profits\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.Redeem,\n address(0), // not used\n address(this), // address to send profits to\n oldOption, // address of otoken\n 0, // not used\n oldOptionBalance, // otoken balance\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endAssetBalance = IERC20(asset).balanceOf(address(this));\n\n return endAssetBalance.sub(startAssetBalance);\n }\n\n /**\n * @notice Burn the remaining oTokens left over from auction. Currently this implementation is simple.\n * It burns oTokens from the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time.\n * @param gammaController is the address of the opyn controller contract\n * @param currentOption is the address of the current option\n * @return amount of collateral redeemed by burning otokens\n */\n function burnOtokens(address gammaController, address currentOption)\n external\n returns (uint256)\n {\n uint256 numOTokensToBurn =\n IERC20(currentOption).balanceOf(address(this));\n\n require(numOTokensToBurn > 0, \"No oTokens to burn\");\n\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // Burning `amount` of oTokens from the ribbon vault,\n // then withdrawing the corresponding collateral amount from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](2);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.BurnShortOption,\n address(this), // owner\n address(this), // address to transfer from\n address(vault.shortOtokens[0]), // otoken address\n vaultID, // vaultId\n numOTokensToBurn, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.WithdrawCollateral,\n address(this), // owner\n address(this), // address to transfer to\n address(collateralToken), // withdrawn asset\n vaultID, // vaultId\n vault.collateralAmounts[0].mul(numOTokensToBurn).div(\n vault.shortAmounts[0]\n ), // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Calculates the management fee for this week's round\n * @param currentBalance is the balance of funds held on the vault after closing short\n * @param pendingAmount is the pending deposit amount\n * @param managementFeePercent is the management fee pct.\n * @return managementFeeInAsset is the management fee\n */\n function getManagementFee(\n uint256 currentBalance,\n uint256 pendingAmount,\n uint256 managementFeePercent\n ) internal pure returns (uint256 managementFeeInAsset) {\n // At the first round, currentBalance=0, pendingAmount>0\n // so we just do not charge anything on the first round\n uint256 lockedBalanceSansPending =\n currentBalance > pendingAmount\n ? currentBalance.sub(pendingAmount)\n : 0;\n\n uint256 _managementFeeInAsset;\n\n // Always charge management fee regardless of whether the vault is\n // making a profit from the previous options sale\n _managementFeeInAsset = managementFeePercent > 0\n ? lockedBalanceSansPending.mul(managementFeePercent).div(\n 100 * Vault.FEE_MULTIPLIER\n )\n : 0;\n\n return _managementFeeInAsset;\n }\n\n /**\n * @notice Either retrieves the option token if it already exists, or deploy it\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param underlying is the address of the underlying asset of the option\n * @param collateralAsset is the address of the collateral asset of the option\n * @param strikePrice is the strike price of the option\n * @param expiry is the expiry timestamp of the option\n * @param isPut is whether the option is a put\n * @return the address of the option\n */\n function getOrDeployOtoken(\n CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n address underlying,\n address collateralAsset,\n uint256 strikePrice,\n uint256 expiry,\n bool isPut\n ) internal returns (address) {\n IOtokenFactory factory = IOtokenFactory(closeParams.OTOKEN_FACTORY);\n\n address otokenFromFactory =\n factory.getOtoken(\n underlying,\n closeParams.USDC,\n collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n if (otokenFromFactory != address(0)) {\n return otokenFromFactory;\n }\n\n address otoken =\n factory.createOtoken(\n underlying,\n closeParams.USDC,\n collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n verifyOtoken(\n otoken,\n vaultParams,\n collateralAsset,\n closeParams.USDC,\n closeParams.delay\n );\n\n return otoken;\n }\n\n /**\n * @notice Starts the gnosis auction\n * @param auctionDetails is the struct with all the custom parameters of the auction\n * @return the auction id of the newly created auction\n */\n function startAuction(GnosisAuction.AuctionDetails calldata auctionDetails)\n external\n returns (uint256)\n {\n return GnosisAuction.startAuction(auctionDetails);\n }\n\n /**\n * @notice Settles the gnosis auction\n * @param gnosisEasyAuction is the contract address of Gnosis easy auction protocol\n * @param auctionID is the auction ID of the gnosis easy auction\n */\n function settleAuction(address gnosisEasyAuction, uint256 auctionID)\n internal\n {\n IGnosisAuction(gnosisEasyAuction).settleAuction(auctionID);\n }\n\n /**\n * @notice Places a bid in an auction\n * @param bidDetails is the struct with all the details of the\n bid including the auction's id and how much to bid\n */\n function placeBid(GnosisAuction.BidDetails calldata bidDetails)\n external\n returns (\n uint256 sellAmount,\n uint256 buyAmount,\n uint64 userId\n )\n {\n return GnosisAuction.placeBid(bidDetails);\n }\n\n /**\n * @notice Claims the oTokens belonging to the vault\n * @param auctionSellOrder is the sell order of the bid\n * @param gnosisEasyAuction is the address of the gnosis auction contract\n holding custody to the funds\n * @param counterpartyThetaVault is the address of the counterparty theta\n vault of this delta vault\n */\n function claimAuctionOtokens(\n Vault.AuctionSellOrder calldata auctionSellOrder,\n address gnosisEasyAuction,\n address counterpartyThetaVault\n ) external {\n GnosisAuction.claimAuctionOtokens(\n auctionSellOrder,\n gnosisEasyAuction,\n counterpartyThetaVault\n );\n }\n\n /**\n * @notice Verify the constructor params satisfy requirements\n * @param _initParams is the initialization parameter including owner, keeper, etc.\n * @param _vaultParams is the struct with vault general data\n */\n function verifyInitializerParams(\n InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams,\n uint256 _min_auction_duration\n ) external pure {\n require(_initParams._owner != address(0), \"!_owner\");\n require(_initParams._keeper != address(0), \"!_keeper\");\n require(_initParams._feeRecipient != address(0), \"!_feeRecipient\");\n require(\n _initParams._performanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"performanceFee >= 100%\"\n );\n require(\n _initParams._managementFee < 100 * Vault.FEE_MULTIPLIER,\n \"managementFee >= 100%\"\n );\n require(bytes(_initParams._tokenName).length > 0, \"!_tokenName\");\n require(bytes(_initParams._tokenSymbol).length > 0, \"!_tokenSymbol\");\n require(\n (_initParams._period == 7) ||\n (_initParams._period == 14) ||\n (_initParams._period == 30) ||\n (_initParams._period == 90) ||\n (_initParams._period == 180),\n \"!_period\"\n );\n require(\n _initParams._optionsPremiumPricer != address(0),\n \"!_optionsPremiumPricer\"\n );\n require(\n _initParams._strikeSelection != address(0),\n \"!_strikeSelection\"\n );\n require(\n _initParams._premiumDiscount > 0 &&\n _initParams._premiumDiscount <\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"!_premiumDiscount\"\n );\n require(\n _initParams._auctionDuration >= _min_auction_duration,\n \"!_auctionDuration\"\n );\n require(_initParams._maxDepositors > 0, \"!_maxDepositors\");\n require(_initParams._minDeposit > 0, \"!_minDeposit\");\n\n require(_vaultParams.asset != address(0), \"!asset\");\n require(_vaultParams.underlying != address(0), \"!underlying\");\n require(_vaultParams.minimumSupply > 0, \"!minimumSupply\");\n require(_vaultParams.cap > 0, \"!cap\");\n require(\n _vaultParams.cap > _vaultParams.minimumSupply,\n \"cap has to be higher than minimumSupply\"\n );\n }\n\n /**\n * @notice Gets the next options expiry timestamp, this function should be called\n when there is sufficient guard to ensure valid period\n * @param timestamp is the expiry timestamp of the current option\n * @param period is no. of days in between option sales. Available periods are: \n * 7(1w), 14(2w), 30(1m), 90(3m), 180(6m)\n */\n function getNextExpiry(uint256 timestamp, uint256 period)\n internal\n pure\n returns (uint256 nextExpiry)\n {\n if (period == 7) {\n nextExpiry = DateTime.getNextFriday(timestamp);\n nextExpiry = nextExpiry <= timestamp\n ? nextExpiry + 1 weeks\n : nextExpiry;\n } else if (period == 14) {\n nextExpiry = DateTime.getNextFriday(timestamp);\n nextExpiry = nextExpiry <= timestamp\n ? nextExpiry + 2 weeks\n : nextExpiry;\n } else if (period == 30) {\n nextExpiry = DateTime.getMonthLastFriday(timestamp);\n nextExpiry = nextExpiry <= timestamp\n ? DateTime.getMonthLastFriday(nextExpiry + 1 weeks)\n : nextExpiry;\n } else if (period == 90) {\n nextExpiry = DateTime.getQuarterLastFriday(timestamp);\n nextExpiry = nextExpiry <= timestamp\n ? DateTime.getQuarterLastFriday(nextExpiry + 1 weeks)\n : nextExpiry;\n } else if (period == 180) {\n nextExpiry = DateTime.getBiannualLastFriday(timestamp);\n nextExpiry = nextExpiry <= timestamp\n ? DateTime.getBiannualLastFriday(nextExpiry + 1 weeks)\n : nextExpiry;\n }\n\n nextExpiry = nextExpiry - (nextExpiry % (24 hours)) + (8 hours);\n }\n}\n" + }, + "contracts/storage/RibbonTreasuryVaultStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {Vault} from \"../libraries/Vault.sol\";\n\nabstract contract RibbonTreasuryVaultStorageV1 {\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Mapping of depositors in the vault\n mapping(address => bool) public depositorsMap;\n\n /// @notice Array of depositors in the vault\n address[] public depositorsArray;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Logic contract used to price options\n address public optionsPremiumPricer;\n\n /// @notice Logic contract used to select strike prices\n address public strikeSelection;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n /// @notice Premium discount on options we are selling (thousandths place: 000 - 999)\n uint256 public premiumDiscount;\n\n /// @notice Current oToken premium\n uint256 public currentOtokenPremium;\n\n /// @notice Price last overridden strike set to\n uint256 public overriddenStrikePrice;\n\n /// @notice Auction duration\n uint256 public auctionDuration;\n\n /// @notice Auction id of current option\n uint256 public optionAuctionID;\n\n /// @notice Amount locked for scheduled withdrawals last week;\n uint256 public lastQueuedWithdrawAmount;\n\n /// @notice Period between each options sale.\n /// Available options 7 (weekly), 14 (biweekly), 30 (monthly), 90 (quarterly), 180 (biannually)\n uint256 public period;\n\n /// @notice Maximum number of depositors\n uint256 public maxDepositors;\n\n /// @notice Minimum amount to deposit\n uint256 public minDeposit;\n\n /// @notice Last round id at which the strike was manually overridden\n uint16 public lastStrikeOverrideRound;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of RibbonTreasuryVaultStorage\n// e.g. RibbonTreasuryVaultStorage, so finally it would look like\n// contract RibbonTreasuryVaultStorage is RibbonTreasuryVaultStorageV1, RibbonTreasuryVaultStorageV2\nabstract contract RibbonTreasuryVaultStorage is RibbonTreasuryVaultStorageV1 {\n\n}\n" + }, + "contracts/libraries/DateTime.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Source: https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary\n// ----------------------------------------------------------------------------\n// BokkyPooBah's DateTime Library v1.01\n// ----------------------------------------------------------------------------\n\npragma solidity =0.8.4;\n\nlibrary DateTime {\n uint256 constant SECONDS_PER_DAY = 24 * 60 * 60;\n uint256 constant SECONDS_PER_HOUR = 60 * 60;\n uint256 constant SECONDS_PER_MINUTE = 60;\n int256 constant OFFSET19700101 = 2440588;\n\n uint256 constant DOW_MON = 1;\n uint256 constant DOW_TUE = 2;\n uint256 constant DOW_WED = 3;\n uint256 constant DOW_THU = 4;\n uint256 constant DOW_FRI = 5;\n uint256 constant DOW_SAT = 6;\n uint256 constant DOW_SUN = 7;\n\n // ------------------------------------------------------------------------\n // Calculate the number of days from 1970/01/01 to year/month/day using\n // the date conversion algorithm from\n // http://aa.usno.navy.mil/faq/docs/JD_Formula.php\n // and subtracting the offset 2440588 so that 1970/01/01 is day 0\n //\n // days = day\n // - 32075\n // + 1461 * (year + 4800 + (month - 14) / 12) / 4\n // + 367 * (month - 2 - (month - 14) / 12 * 12) / 12\n // - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4\n // - offset\n // ------------------------------------------------------------------------\n function _daysFromDate(\n uint256 year,\n uint256 month,\n uint256 day\n ) internal pure returns (uint256 _days) {\n require(year >= 1970);\n int256 _year = int256(year);\n int256 _month = int256(month);\n int256 _day = int256(day);\n\n int256 __days =\n _day -\n 32075 +\n (1461 * (_year + 4800 + (_month - 14) / 12)) /\n 4 +\n (367 * (_month - 2 - ((_month - 14) / 12) * 12)) /\n 12 -\n (3 * ((_year + 4900 + (_month - 14) / 12) / 100)) /\n 4 -\n OFFSET19700101;\n\n _days = uint256(__days);\n }\n\n // ------------------------------------------------------------------------\n // Calculate year/month/day from the number of days since 1970/01/01 using\n // the date conversion algorithm from\n // http://aa.usno.navy.mil/faq/docs/JD_Formula.php\n // and adding the offset 2440588 so that 1970/01/01 is day 0\n //\n // int L = days + 68569 + offset\n // int N = 4 * L / 146097\n // L = L - (146097 * N + 3) / 4\n // year = 4000 * (L + 1) / 1461001\n // L = L - 1461 * year / 4 + 31\n // month = 80 * L / 2447\n // dd = L - 2447 * month / 80\n // L = month / 11\n // month = month + 2 - 12 * L\n // year = 100 * (N - 49) + year + L\n // ------------------------------------------------------------------------\n function _daysToDate(uint256 _days)\n internal\n pure\n returns (\n uint256 year,\n uint256 month,\n uint256 day\n )\n {\n int256 __days = int256(_days);\n\n int256 L = __days + 68569 + OFFSET19700101;\n int256 N = (4 * L) / 146097;\n L = L - (146097 * N + 3) / 4;\n int256 _year = (4000 * (L + 1)) / 1461001;\n L = L - (1461 * _year) / 4 + 31;\n int256 _month = (80 * L) / 2447;\n int256 _day = L - (2447 * _month) / 80;\n L = _month / 11;\n _month = _month + 2 - 12 * L;\n _year = 100 * (N - 49) + _year + L;\n\n year = uint256(_year);\n month = uint256(_month);\n day = uint256(_day);\n }\n\n function isLeapYear(uint256 timestamp)\n internal\n pure\n returns (bool leapYear)\n {\n (uint256 year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);\n leapYear = _isLeapYear(year);\n }\n\n function _isLeapYear(uint256 year) internal pure returns (bool leapYear) {\n leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);\n }\n\n function getDaysInMonth(uint256 timestamp)\n internal\n pure\n returns (uint256 daysInMonth)\n {\n (uint256 year, uint256 month, ) =\n _daysToDate(timestamp / SECONDS_PER_DAY);\n daysInMonth = _getDaysInMonth(year, month);\n }\n\n function _getDaysInMonth(uint256 year, uint256 month)\n internal\n pure\n returns (uint256 daysInMonth)\n {\n if (\n month == 1 ||\n month == 3 ||\n month == 5 ||\n month == 7 ||\n month == 8 ||\n month == 10 ||\n month == 12\n ) {\n daysInMonth = 31;\n } else if (month != 2) {\n daysInMonth = 30;\n } else {\n daysInMonth = _isLeapYear(year) ? 29 : 28;\n }\n }\n\n // 1 = Monday, 7 = Sunday\n function getDayOfWeek(uint256 timestamp)\n internal\n pure\n returns (uint256 dayOfWeek)\n {\n uint256 _days = timestamp / SECONDS_PER_DAY;\n dayOfWeek = ((_days + 3) % 7) + 1;\n }\n\n function getYear(uint256 timestamp) internal pure returns (uint256 year) {\n (year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);\n }\n\n function getMonth(uint256 timestamp) internal pure returns (uint256 month) {\n (, month, ) = _daysToDate(timestamp / SECONDS_PER_DAY);\n }\n\n function getDay(uint256 timestamp) internal pure returns (uint256 day) {\n (, , day) = _daysToDate(timestamp / SECONDS_PER_DAY);\n }\n\n function timestampFromDate(\n uint256 year,\n uint256 month,\n uint256 day\n ) internal pure returns (uint256 timestamp) {\n timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY;\n }\n\n /**\n * @notice Gets the Friday of the same week\n * @param timestamp is the given date and time\n * @return the Friday of the same week in unix time\n */\n function getThisWeekFriday(uint256 timestamp)\n internal\n pure\n returns (uint256)\n {\n return timestamp + 5 days - getDayOfWeek(timestamp) * 1 days;\n }\n\n /**\n * @notice Gets the next friday after the given date and time\n * @param timestamp is the given date and time\n * @return the next friday after the given date and time\n */\n function getNextFriday(uint256 timestamp) internal pure returns (uint256) {\n uint256 friday = getThisWeekFriday(timestamp);\n return friday >= timestamp ? friday : friday + 1 weeks;\n }\n\n /**\n * @notice Gets the last day of the month\n * @param timestamp is the given date and time\n * @return the last day of the same month in unix time\n */\n function getLastDayOfMonth(uint256 timestamp)\n internal\n pure\n returns (uint256)\n {\n return\n timestampFromDate(getYear(timestamp), getMonth(timestamp) + 1, 1) -\n 1 days;\n }\n\n /**\n * @notice Gets the last Friday of the month\n * @param timestamp is the given date and time\n * @return the last Friday of the same month in unix time\n */\n function getMonthLastFriday(uint256 timestamp)\n internal\n pure\n returns (uint256)\n {\n uint256 lastDay = getLastDayOfMonth(timestamp);\n uint256 friday = getThisWeekFriday(lastDay);\n\n return friday > lastDay ? friday - 1 weeks : friday;\n }\n\n /**\n * @notice Gets the last Friday of the quarter\n * @param timestamp is the given date and time\n * @return the last Friday of the quarter in unix time\n */\n function getQuarterLastFriday(uint256 timestamp)\n internal\n pure\n returns (uint256)\n {\n uint256 month = getMonth(timestamp);\n uint256 quarterMonth =\n (month <= 3) ? 3 : (month <= 6) ? 6 : (month <= 9) ? 9 : 12;\n\n uint256 quarterDate =\n timestampFromDate(getYear(timestamp), quarterMonth, 1);\n\n return getMonthLastFriday(quarterDate);\n }\n\n /**\n * @notice Gets the last Friday of the half-year\n * @param timestamp is the given date and time\n * @return the last friday of the half-year\n */\n function getBiannualLastFriday(uint256 timestamp)\n internal\n pure\n returns (uint256)\n {\n uint256 month = getMonth(timestamp);\n uint256 biannualMonth = (month <= 6) ? 6 : 12;\n\n uint256 biannualDate =\n timestampFromDate(getYear(timestamp), biannualMonth, 1);\n\n return getMonthLastFriday(biannualDate);\n }\n}\n" + }, + "contracts/utils/Swap.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Source: https://github.com/airswap/airswap-protocols/blob/main/source/swap/contracts/Swap.sol\n\npragma solidity =0.8.4;\n\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"../interfaces/ISwap.sol\";\nimport \"../storage/SwapStorage.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\n\ncontract Swap is\n ISwap,\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n SwapStorage\n{\n using SafeERC20 for IERC20;\n\n uint256 public immutable DOMAIN_CHAIN_ID;\n\n bytes32 public constant DOMAIN_TYPEHASH =\n keccak256(\n abi.encodePacked(\n \"EIP712Domain(\",\n \"string name,\",\n \"string version,\",\n \"uint256 chainId,\",\n \"address verifyingContract\",\n \")\"\n )\n );\n\n bytes32 public constant BID_TYPEHASH =\n keccak256(\n abi.encodePacked(\n \"Bid(\",\n \"uint256 swapId,\",\n \"uint256 nonce,\",\n \"address signerWallet,\",\n \"uint256 sellAmount,\",\n \"uint256 buyAmount,\",\n \"address referrer\",\n \")\"\n )\n );\n\n uint256 internal constant MAX_PERCENTAGE = 10000;\n uint256 internal constant MAX_FEE = 1000;\n uint256 internal constant MAX_ERROR_COUNT = 10;\n uint256 internal constant OTOKEN_DECIMALS = 8;\n\n /************************************************\n * CONSTRUCTOR\n ***********************************************/\n\n constructor() {\n uint256 currentChainId = getChainId();\n DOMAIN_CHAIN_ID = currentChainId;\n }\n\n /************************************************\n * INITIALIZATION\n ***********************************************/\n\n function initialize(\n string memory _domainName,\n string memory _domainVersion,\n address _owner\n ) external initializer {\n require(bytes(_domainName).length > 0, \"!_domainName\");\n require(bytes(_domainVersion).length > 0, \"!_domainVersion\");\n require(_owner != address(0), \"!_owner\");\n\n __ReentrancyGuard_init();\n __Ownable_init();\n transferOwnership(_owner);\n\n DOMAIN_NAME = keccak256(bytes(_domainName));\n DOMAIN_VERSION = keccak256(bytes(_domainVersion));\n DOMAIN_SEPARATOR = keccak256(\n abi.encode(\n DOMAIN_TYPEHASH,\n DOMAIN_NAME,\n DOMAIN_VERSION,\n DOMAIN_CHAIN_ID,\n this\n )\n );\n }\n\n /************************************************\n * SETTER\n ***********************************************/\n\n /**\n * @notice Sets the referral fee for a specific referrer\n * @param referrer is the address of the referrer\n * @param fee is the fee in percent in 2 decimals\n */\n function setFee(address referrer, uint256 fee) external onlyOwner {\n require(referrer != address(0), \"Referrer cannot be the zero address\");\n require(fee < MAX_FEE, \"Fee exceeds maximum\");\n\n referralFees[referrer] = fee;\n\n emit SetFee(referrer, fee);\n }\n\n /************************************************\n * OFFER CREATION AND SETTLEMENT\n ***********************************************/\n\n /**\n * @notice Create a new offer available for swap\n * @param oToken token offered by seller\n * @param biddingToken token asked by seller\n * @param minPrice minimum price of oToken denominated in biddingToken\n * @param minBidSize minimum amount of oToken requested in a single bid\n * @param totalSize amount of oToken offered by seller\n */\n function createOffer(\n address oToken,\n address biddingToken,\n uint96 minPrice,\n uint96 minBidSize,\n uint128 totalSize\n ) external override returns (uint256 swapId) {\n require(oToken != address(0), \"oToken cannot be the zero address\");\n require(\n biddingToken != address(0),\n \"BiddingToken cannot be the zero address\"\n );\n require(minPrice > 0, \"MinPrice must be larger than zero\");\n require(minBidSize > 0, \"MinBidSize must be larger than zero\");\n require(minBidSize <= totalSize, \"MinBidSize exceeds total size\");\n\n offersCounter += 1;\n\n swapId = offersCounter;\n\n swapOffers[swapId].seller = msg.sender;\n swapOffers[swapId].oToken = oToken;\n swapOffers[swapId].biddingToken = biddingToken;\n swapOffers[swapId].minBidSize = minBidSize;\n swapOffers[swapId].minPrice = minPrice;\n swapOffers[swapId].totalSize = totalSize;\n swapOffers[swapId].availableSize = totalSize;\n // We warm the storage slot with 1 wei so we avoid a cold SSTORE\n swapOffers[swapId].totalSales = 1;\n\n emit NewOffer(\n swapId,\n msg.sender,\n oToken,\n biddingToken,\n minPrice,\n minBidSize,\n totalSize\n );\n }\n\n /**\n * @notice Settles the swap offering by iterating through the bids\n * @param swapId unique identifier of the swap offer\n * @param bids bids for swaps\n */\n function settleOffer(uint256 swapId, Bid[] calldata bids)\n external\n override\n nonReentrant\n {\n Offer storage offer = swapOffers[swapId];\n\n address seller = offer.seller;\n require(\n seller == msg.sender,\n \"Only seller can settle or offer doesn't exist\"\n );\n require(offer.availableSize > 0, \"Offer fully settled\");\n\n uint256 totalSales;\n OfferDetails memory offerDetails;\n offerDetails.seller = seller;\n offerDetails.oToken = offer.oToken;\n offerDetails.biddingToken = offer.biddingToken;\n offerDetails.minPrice = offer.minPrice;\n offerDetails.minBidSize = offer.minBidSize;\n\n for (uint256 i = 0; i < bids.length; i++) {\n require(\n swapId == bids[i].swapId,\n \"Offer and bid swapId mismatched\"\n );\n\n _swap(offerDetails, offer, bids[i]);\n totalSales += bids[i].sellAmount;\n }\n\n bool fullySettled = offer.availableSize == 0;\n\n // Deduct the initial 1 wei offset if offer is fully settled\n offer.totalSales += totalSales - (fullySettled ? 1 : 0);\n\n if (fullySettled) {\n offer.seller = address(0);\n offer.oToken = address(0);\n offer.biddingToken = address(0);\n offer.minBidSize = 0;\n offer.minPrice = 0;\n\n emit SettleOffer(swapId);\n }\n }\n\n /**\n * @notice Authorize a signer\n * @param signer address Wallet of the signer to authorize\n * @dev Emits an Authorize event\n */\n function authorize(address signer) external override {\n require(signer != address(0), \"SIGNER_INVALID\");\n authorized[msg.sender] = signer;\n emit Authorize(signer, msg.sender);\n }\n\n /**\n * @notice Revoke the signer\n * @dev Emits a Revoke event\n */\n function revoke() external override {\n address tmp = authorized[msg.sender];\n delete authorized[msg.sender];\n emit Revoke(tmp, msg.sender);\n }\n\n /**\n * @notice Cancel one or more nonces\n * @dev Cancelled nonces are marked as used\n * @dev Emits a Cancel event\n * @dev Out of gas may occur in arrays of length > 400\n * @param nonces uint256[] List of nonces to cancel\n */\n function cancelNonce(uint256[] calldata nonces) external override {\n for (uint256 i = 0; i < nonces.length; i++) {\n uint256 nonce = nonces[i];\n if (_markNonceAsUsed(msg.sender, nonce)) {\n emit Cancel(nonce, msg.sender);\n }\n }\n }\n\n /************************************************\n * PUBLIC VIEW FUNCTIONS\n ***********************************************/\n\n /**\n * @notice Validates Swap bid for any potential errors\n * @param bid Bid struct containing bid details\n * @return tuple of error count and bytes32[] memory array of error messages\n */\n function check(Bid calldata bid)\n external\n view\n override\n returns (uint256, bytes32[] memory)\n {\n Offer memory offer = swapOffers[bid.swapId];\n require(offer.seller != address(0), \"Offer does not exist\");\n\n bytes32[] memory errors = new bytes32[](MAX_ERROR_COUNT);\n\n uint256 errCount;\n\n // Check signature\n address signatory = _getSignatory(bid);\n\n if (signatory == address(0)) {\n errors[errCount] = \"SIGNATURE_INVALID\";\n errCount++;\n }\n\n if (\n bid.signerWallet != signatory &&\n authorized[bid.signerWallet] != signatory\n ) {\n errors[errCount] = \"UNAUTHORIZED\";\n errCount++;\n }\n\n // Check nonce\n if (nonceUsed(signatory, bid.nonce)) {\n errors[errCount] = \"NONCE_ALREADY_USED\";\n errCount++;\n }\n\n // Check bid size\n if (bid.buyAmount < offer.minBidSize) {\n errors[errCount] = \"BID_TOO_SMALL\";\n errCount++;\n }\n if (bid.buyAmount > offer.availableSize) {\n errors[errCount] = \"BID_EXCEED_AVAILABLE_SIZE\";\n errCount++;\n }\n\n // Check bid price\n uint256 bidPrice =\n (bid.sellAmount * 10**OTOKEN_DECIMALS) / bid.buyAmount;\n if (bidPrice < offer.minPrice) {\n errors[errCount] = \"PRICE_TOO_LOW\";\n errCount++;\n }\n\n // Check signer allowance\n uint256 signerAllowance =\n IERC20(offer.biddingToken).allowance(\n bid.signerWallet,\n address(this)\n );\n if (signerAllowance < bid.sellAmount) {\n errors[errCount] = \"SIGNER_ALLOWANCE_LOW\";\n errCount++;\n }\n\n // Check signer balance\n uint256 signerBalance =\n IERC20(offer.biddingToken).balanceOf(bid.signerWallet);\n if (signerBalance < bid.sellAmount) {\n errors[errCount] = \"SIGNER_BALANCE_LOW\";\n errCount++;\n }\n\n // Check seller allowance\n uint256 sellerAllowance =\n IERC20(offer.oToken).allowance(offer.seller, address(this));\n if (sellerAllowance < bid.buyAmount) {\n errors[errCount] = \"SELLER_ALLOWANCE_LOW\";\n errCount++;\n }\n\n // Check seller balance\n uint256 sellerBalance = IERC20(offer.oToken).balanceOf(offer.seller);\n if (sellerBalance < bid.buyAmount) {\n errors[errCount] = \"SELLER_BALANCE_LOW\";\n errCount++;\n }\n\n return (errCount, errors);\n }\n\n /**\n * @notice Returns the average settlement price for a swap offer\n * @param swapId unique identifier of the swap offer\n */\n function averagePriceForOffer(uint256 swapId)\n external\n view\n override\n returns (uint256)\n {\n Offer storage offer = swapOffers[swapId];\n require(offer.totalSize != 0, \"Offer does not exist\");\n\n uint256 availableSize = offer.availableSize;\n\n // Deduct the initial 1 wei offset if offer is not fully settled\n uint256 adjustment = availableSize != 0 ? 1 : 0;\n\n return\n ((offer.totalSales - adjustment) * (10**8)) /\n (offer.totalSize - availableSize);\n }\n\n /**\n * @notice Returns true if the nonce has been used\n * @param signer address Address of the signer\n * @param nonce uint256 Nonce being checked\n */\n function nonceUsed(address signer, uint256 nonce)\n public\n view\n override\n returns (bool)\n {\n uint256 groupKey = nonce / 256;\n uint256 indexInGroup = nonce % 256;\n return (_nonceGroups[signer][groupKey] >> indexInGroup) & 1 == 1;\n }\n\n /************************************************\n * INTERNAL FUNCTIONS\n ***********************************************/\n\n /**\n * @notice Swap Atomic ERC20 Swap\n * @param details Details of offering\n * @param offer Offer struct containing offer details\n * @param bid Bid struct containing bid details\n */\n function _swap(\n OfferDetails memory details,\n Offer storage offer,\n Bid calldata bid\n ) internal {\n require(DOMAIN_CHAIN_ID == getChainId(), \"CHAIN_ID_CHANGED\");\n\n address signatory = _getSignatory(bid);\n\n require(signatory != address(0), \"SIGNATURE_INVALID\");\n\n if (bid.signerWallet != signatory) {\n require(authorized[bid.signerWallet] == signatory, \"UNAUTHORIZED\");\n }\n\n require(_markNonceAsUsed(signatory, bid.nonce), \"NONCE_ALREADY_USED\");\n require(\n bid.buyAmount <= offer.availableSize,\n \"BID_EXCEED_AVAILABLE_SIZE\"\n );\n require(bid.buyAmount >= details.minBidSize, \"BID_TOO_SMALL\");\n\n // Ensure min. price is met\n uint256 bidPrice =\n (bid.sellAmount * 10**OTOKEN_DECIMALS) / bid.buyAmount;\n require(bidPrice >= details.minPrice, \"PRICE_TOO_LOW\");\n\n // don't have to do a uint128 check because we already check\n // that bid.buyAmount <= offer.availableSize\n offer.availableSize -= uint128(bid.buyAmount);\n\n // Transfer token from sender to signer\n IERC20(details.oToken).safeTransferFrom(\n details.seller,\n bid.signerWallet,\n bid.buyAmount\n );\n\n // Transfer to referrer if any\n uint256 feeAmount;\n if (bid.referrer != address(0)) {\n uint256 feePercent = referralFees[bid.referrer];\n\n if (feePercent > 0) {\n feeAmount = (bid.sellAmount * feePercent) / MAX_PERCENTAGE;\n\n IERC20(details.biddingToken).safeTransferFrom(\n bid.signerWallet,\n bid.referrer,\n feeAmount\n );\n }\n }\n\n // Transfer token from signer to recipient\n IERC20(details.biddingToken).safeTransferFrom(\n bid.signerWallet,\n details.seller,\n bid.sellAmount - feeAmount\n );\n\n // Emit a Swap event\n emit Swap(\n bid.swapId,\n bid.nonce,\n bid.signerWallet,\n bid.sellAmount,\n bid.buyAmount,\n bid.referrer,\n feeAmount\n );\n }\n\n /**\n * @notice Marks a nonce as used for the given signer\n * @param signer address Address of the signer for which to mark the nonce as used\n * @param nonce uint256 Nonce to be marked as used\n * @return bool True if the nonce was not marked as used already\n */\n function _markNonceAsUsed(address signer, uint256 nonce)\n internal\n returns (bool)\n {\n uint256 groupKey = nonce / 256;\n uint256 indexInGroup = nonce % 256;\n uint256 group = _nonceGroups[signer][groupKey];\n\n // If it is already used, return false\n if ((group >> indexInGroup) & 1 == 1) {\n return false;\n }\n\n _nonceGroups[signer][groupKey] = group | (uint256(1) << indexInGroup);\n\n return true;\n }\n\n /**\n * @notice Recover the signatory from a signature\n * @param bid Bid struct containing bid details\n */\n function _getSignatory(Bid calldata bid) internal view returns (address) {\n return\n ecrecover(\n keccak256(\n abi.encodePacked(\n \"\\x19\\x01\",\n DOMAIN_SEPARATOR,\n keccak256(\n abi.encode(\n BID_TYPEHASH,\n bid.swapId,\n bid.nonce,\n bid.signerWallet,\n bid.sellAmount,\n bid.buyAmount,\n bid.referrer\n )\n )\n )\n ),\n bid.v,\n bid.r,\n bid.s\n );\n }\n\n /**\n * @notice Returns the current chainId using the chainid opcode\n * @return id uint256 The chain id\n */\n function getChainId() internal view returns (uint256 id) {\n // no-inline-assembly\n assembly {\n id := chainid()\n }\n }\n}\n" + }, + "contracts/interfaces/ISwap.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity =0.8.4;\n\ninterface ISwap {\n struct Offer {\n // 32 byte slot 1, partial fill\n // Seller wallet address\n address seller;\n // 32 byte slot 2\n // Addess of oToken\n address oToken;\n // Price per oToken denominated in biddingToken\n uint96 minPrice;\n // 32 byte slot 3\n // ERC20 Token to bid for oToken\n address biddingToken;\n // Minimum oToken amount acceptable for a single bid\n uint96 minBidSize;\n // 32 byte slot 4\n // Total available oToken amount\n uint128 totalSize;\n // Remaining available oToken amount\n // This figure is updated after each successfull swap\n uint128 availableSize;\n // 32 byte slot 5\n // Amount of biddingToken received\n // This figure is updated after each successfull swap\n uint256 totalSales;\n }\n\n struct Bid {\n // ID assigned to offers\n uint256 swapId;\n // Number only used once for each wallet\n uint256 nonce;\n // Signer wallet address\n address signerWallet;\n // Amount of biddingToken offered by signer\n uint256 sellAmount;\n // Amount of oToken requested by signer\n uint256 buyAmount;\n // Referrer wallet address\n address referrer;\n // Signature recovery id\n uint8 v;\n // r portion of the ECSDA signature\n bytes32 r;\n // s portion of the ECSDA signature\n bytes32 s;\n }\n\n struct OfferDetails {\n // Seller wallet address\n address seller;\n // Addess of oToken\n address oToken;\n // Price per oToken denominated in biddingToken\n uint256 minPrice;\n // ERC20 Token to bid for oToken\n address biddingToken;\n // Minimum oToken amount acceptable for a single bid\n uint256 minBidSize;\n }\n\n event Swap(\n uint256 indexed swapId,\n uint256 nonce,\n address indexed signerWallet,\n uint256 signerAmount,\n uint256 sellerAmount,\n address referrer,\n uint256 feeAmount\n );\n\n event NewOffer(\n uint256 swapId,\n address seller,\n address oToken,\n address biddingToken,\n uint256 minPrice,\n uint256 minBidSize,\n uint256 totalSize\n );\n\n event SetFee(address referrer, uint256 fee);\n\n event SettleOffer(uint256 swapId);\n\n event Cancel(uint256 indexed nonce, address indexed signerWallet);\n\n event Authorize(address indexed signer, address indexed signerWallet);\n\n event Revoke(address indexed signer, address indexed signerWallet);\n\n function createOffer(\n address oToken,\n address biddingToken,\n uint96 minPrice,\n uint96 minBidSize,\n uint128 totalSize\n ) external returns (uint256 swapId);\n\n function settleOffer(uint256 swapId, Bid[] calldata bids) external;\n\n function cancelNonce(uint256[] calldata nonces) external;\n\n function check(Bid calldata bid)\n external\n view\n returns (uint256, bytes32[] memory);\n\n function averagePriceForOffer(uint256 swapId)\n external\n view\n returns (uint256);\n\n function authorize(address sender) external;\n\n function revoke() external;\n\n function nonceUsed(address, uint256) external view returns (bool);\n}\n" + }, + "contracts/storage/SwapStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport \"../interfaces/ISwap.sol\";\n\nabstract contract SwapStorageV1 {\n // Keccak256 of domain name for signature verification\n bytes32 public DOMAIN_NAME;\n // Keccak256 of domain version for signature verification\n bytes32 public DOMAIN_VERSION;\n // Keccak256 of abi-encoded domain parameters for signature verification\n bytes32 public DOMAIN_SEPARATOR;\n\n // Counter to keep track number of offers\n uint256 public offersCounter;\n // Mapping of swap offer details for a given swapId\n mapping(uint256 => ISwap.Offer) public swapOffers;\n // Mapping of referral fees for a given address\n mapping(address => uint256) public referralFees;\n // Mapping of authorized delegate for a given address\n mapping(address => address) public authorized;\n /**\n * @notice Double mapping of signers to nonce groups to nonce states\n * @dev The nonce group is computed as nonce / 256, so each group of 256 sequential nonces uses the same key\n * @dev The nonce states are encoded as 256 bits, for each nonce in the group 0 means available and 1 means used\n */\n mapping(address => mapping(uint256 => uint256)) internal _nonceGroups;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of SwapStorage\n// e.g. SwapStorage, so finally it would look like\n// contract SwapStorage is SwapStorageV1, SwapStorageV2\nabstract contract SwapStorage is SwapStorageV1 {\n\n}\n" + }, + "contracts/vaults/STETHVault/base/RibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\n\nimport {Vault} from \"../../../libraries/Vault.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../../libraries/VaultLifecycleWithSwap.sol\";\nimport {VaultLifecycleSTETH} from \"../../../libraries/VaultLifecycleSTETH.sol\";\nimport {ShareMath} from \"../../../libraries/ShareMath.sol\";\nimport {IWSTETH} from \"../../../interfaces/ISTETH.sol\";\n\ncontract RibbonVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n /// @notice wstETH vault contract\n IWSTETH public immutable collateralToken;\n\n // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure.\n uint256[30] private ____gap;\n\n // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE\n // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaSTETHVaultStorage\n // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice Deprecated: 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n /// @notice 7 day period between each options sale.\n uint256 public constant PERIOD = 7 days;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // SWAP_CONTRACT is a contract for settling bids via signed messages\n // https://github.com/ribbon-finance/ribbon-v2/blob/master/contracts/utils/Swap.sol\n address public immutable SWAP_CONTRACT;\n\n /// @notice STETH contract address\n address public immutable STETH;\n\n /// @notice Lido DAO token 0x5a98fcbea516cf06857215779fd812ca3bef1b32\n address public immutable LDO;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectVaultFees(\n uint256 performanceFee,\n uint256 vaultFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _wsteth is the wstETH contract\n * @param _ldo is the LDO contract\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _wsteth,\n address _ldo,\n address _gammaController,\n address _marginPool,\n address _swapContract\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_swapContract != address(0), \"!_swapContract\");\n require(_wsteth != address(0), \"!_wsteth\");\n require(_ldo != address(0), \"!_ldo\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n\n WETH = _weth;\n USDC = _usdc;\n LDO = _ldo;\n STETH = IWSTETH(_wsteth).stETH();\n\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n SWAP_CONTRACT = _swapContract;\n collateralToken = IWSTETH(_wsteth);\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function baseInitialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) internal initializer {\n VaultLifecycleWithSwap.verifyInitializerParams(\n _owner,\n _keeper,\n _feeRecipient,\n _performanceFee,\n _managementFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_tokenName, _tokenSymbol);\n __Ownable_init();\n transferOwnership(_owner);\n\n keeper = _keeper;\n\n feeRecipient = _feeRecipient;\n performanceFee = _performanceFee;\n managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div(\n WEEKS_PER_YEAR\n );\n vaultParams = _vaultParams;\n\n uint256 assetBalance = totalBalance();\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n\n vaultState.round = 1;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n // We are dividing annualized management fee by num weeks in a year\n uint256 tmpManagementFee =\n newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n\n managementFee = tmpManagementFee;\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Deposits ETH into the contract and mint vault shares.\n */\n function depositETH() external payable nonReentrant {\n require(msg.value > 0, \"!value\");\n\n _depositFor(msg.value, msg.sender, true);\n }\n\n function depositYieldToken(uint256 amount) external {\n _depositYieldTokenFor(amount, msg.sender);\n }\n\n /**\n * @notice Deposits the `collateralAsset` into the contract and mint vault shares.\n * @param amount is the amount of `collateralAsset` to deposit\n * @param creditor is the amount of `collateralAsset` to deposit\n */\n function depositYieldTokenFor(uint256 amount, address creditor) external {\n require(creditor != address(0), \"!creditor\");\n _depositYieldTokenFor(amount, creditor);\n }\n\n function _depositYieldTokenFor(uint256 amount, address creditor)\n internal\n nonReentrant\n {\n require(amount > 0, \"!amount\");\n\n // stETH transfers suffer from an off-by-1 error\n _depositFor(amount.sub(1), creditor, false);\n\n IERC20(STETH).safeTransferFrom(msg.sender, address(this), amount);\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.\n * @notice Used for vault -> vault deposits on the user's behalf\n * @param creditor is the address that can claim/withdraw deposited amount\n */\n function depositFor(address creditor) external payable nonReentrant {\n require(msg.value > 0, \"!value\");\n require(creditor != address(0), \"!creditor\");\n\n _depositFor(msg.value, creditor, true);\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n * @param isETH is whether this is a depositETH call\n */\n function _depositFor(\n uint256 amount,\n address creditor,\n bool isETH\n ) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount =\n isETH ? totalBalance() : totalBalance().add(amount);\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n\n emit Deposit(creditor, amount, currentRound);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function _initiateWithdraw(uint256 numShares) internal {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n if (\n depositReceipts[msg.sender].amount > 0 ||\n depositReceipts[msg.sender].unredeemedShares > 0\n ) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n bool withdrawalIsSameRound = withdrawal.round == currentRound;\n\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n\n uint256 existingShares = uint256(withdrawal.shares);\n\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return amountETHOut the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n IERC20(STETH).safeTransfer(\n msg.sender,\n VaultLifecycleSTETH.withdrawStEth(\n STETH,\n address(collateralToken),\n withdrawAmount\n )\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map.\n * Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k.\n * Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0.\n * @param numRounds is the number of rounds to initialize in the map\n */\n function initRounds(uint256 numRounds) external nonReentrant {\n require(numRounds > 0, \"!numRounds\");\n\n uint256 _round = vaultState.round;\n for (uint256 i = 0; i < numRounds; i++) {\n uint256 index = _round + i;\n require(roundPricePerShare[index] == 0, \"Initialized\"); // AVOID OVERWRITING ACTUAL VALUES\n roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT;\n }\n }\n\n /**\n * @notice Helper function that performs most administrative tasks\n * such as minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @param currentQueuedWithdrawShares is the queued withdraw shares for the current round\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _closeRound(\n uint256 lastQueuedWithdrawAmount,\n uint256 currentQueuedWithdrawShares\n ) internal returns (uint256 lockedBalance, uint256 queuedWithdrawAmount) {\n address recipient = feeRecipient;\n address collateral = address(collateralToken);\n uint256 mintShares;\n uint256 performanceFeeInAsset;\n uint256 totalVaultFee;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n ) = VaultLifecycleWithSwap.closeRound(\n vaultState,\n VaultLifecycleWithSwap.CloseParams(\n vaultParams.decimals,\n totalBalance(),\n totalSupply(),\n lastQueuedWithdrawAmount,\n performanceFee,\n managementFee,\n currentQueuedWithdrawShares\n )\n );\n\n // Finalize the pricePerShare at the end of the round\n uint256 currentRound = vaultState.round;\n roundPricePerShare[currentRound] = newPricePerShare;\n\n // Wrap entire `asset` balance to `collateralToken` balance\n VaultLifecycleSTETH.wrapToYieldToken(WETH, collateral, STETH);\n\n emit CollectVaultFees(\n performanceFeeInAsset,\n totalVaultFee,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (totalVaultFee > 0) {\n VaultLifecycleSTETH.withdrawYieldAndBaseToken(\n collateral,\n WETH,\n recipient,\n totalVaultFee\n );\n }\n\n return (lockedBalance, queuedWithdrawAmount);\n }\n\n /*\n * @notice Transfers LDO rewards to feeRecipient\n */\n function sendLDORewards() external {\n IERC20 ldo = IERC20(LDO);\n ldo.safeTransfer(feeRecipient, ldo.balanceOf(address(this)));\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n external\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n // After calling closeRound, current option is set to none\n // We also commit the lockedAmount but do not deposit into Opyn\n // which results in double counting of asset balance and lockedAmount\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 ethBalance = address(this).balance;\n uint256 stethFromWsteth =\n collateralToken.getStETHByWstETH(\n collateralToken.balanceOf(address(this))\n );\n\n uint256 stEthBalance = IERC20(STETH).balanceOf(address(this));\n uint256 lockedAmount =\n optionState.currentOption != address(0)\n ? uint256(vaultState.lockedAmount)\n : 0;\n\n return\n wethBalance\n .add(lockedAmount)\n .add(ethBalance)\n .add(stethFromWsteth)\n .add(stEthBalance);\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n}\n" + }, + "contracts/libraries/VaultLifecycleWithSwap.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {ShareMath} from \"./ShareMath.sol\";\nimport {IStrikeSelection} from \"../interfaces/IRibbon.sol\";\nimport {GnosisAuction} from \"./GnosisAuction.sol\";\nimport {\n IOtokenFactory,\n IOtoken,\n IController,\n GammaTypes\n} from \"../interfaces/GammaInterface.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\nimport {ISwap} from \"../interfaces/ISwap.sol\";\nimport {IOptionsPurchaseQueue} from \"../interfaces/IOptionsPurchaseQueue.sol\";\nimport {SupportsNonCompliantERC20} from \"./SupportsNonCompliantERC20.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\n\nlibrary VaultLifecycleWithSwap {\n using SafeMath for uint256;\n using SupportsNonCompliantERC20 for IERC20;\n using SafeERC20 for IERC20;\n\n struct CommitParams {\n address OTOKEN_FACTORY;\n address USDC;\n address collateralAsset;\n address currentOption;\n uint256 delay;\n uint16 lastStrikeOverrideRound;\n uint256 overriddenStrikePrice;\n address strikeSelection;\n address optionsPremiumPricer;\n }\n\n /**\n * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction\n * @param commitParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @return otokenAddress is the address of the new option\n * @return strikePrice is the strike price of the new option\n * @return delta is the delta of the new option\n */\n function commitNextOption(\n CommitParams calldata commitParams,\n Vault.VaultParams storage vaultParams,\n Vault.VaultState storage vaultState\n )\n external\n returns (\n address otokenAddress,\n uint256 strikePrice,\n uint256 delta\n )\n {\n uint256 expiry = getNextExpiry(commitParams.currentOption);\n\n IStrikeSelection selection =\n IStrikeSelection(commitParams.strikeSelection);\n\n bool isPut = vaultParams.isPut;\n address underlying = vaultParams.underlying;\n\n (strikePrice, delta) = commitParams.lastStrikeOverrideRound ==\n vaultState.round\n ? (commitParams.overriddenStrikePrice, selection.delta())\n : selection.getStrikePrice(expiry, isPut);\n\n require(strikePrice != 0, \"!strikePrice\");\n\n // retrieve address if option already exists, or deploy it\n otokenAddress = getOrDeployOtoken(\n commitParams,\n vaultParams,\n underlying,\n strikePrice,\n expiry,\n isPut\n );\n\n return (otokenAddress, strikePrice, delta);\n }\n\n /**\n * @notice Verify the otoken has the correct parameters to prevent vulnerability to opyn contract changes\n * @param otokenAddress is the address of the otoken\n * @param vaultParams is the struct with vault general data\n * @param collateralAsset is the address of the collateral asset\n * @param USDC is the address of usdc\n * @param delay is the delay between commitAndClose and rollToNextOption\n */\n function verifyOtoken(\n address otokenAddress,\n Vault.VaultParams storage vaultParams,\n address collateralAsset,\n address USDC,\n uint256 delay\n ) private view {\n require(otokenAddress != address(0), \"!otokenAddress\");\n\n IOtoken otoken = IOtoken(otokenAddress);\n require(otoken.isPut() == vaultParams.isPut, \"Type mismatch\");\n require(\n otoken.underlyingAsset() == vaultParams.underlying,\n \"Wrong underlyingAsset\"\n );\n require(\n otoken.collateralAsset() == collateralAsset,\n \"Wrong collateralAsset\"\n );\n\n // we just assume all options use USDC as the strike\n require(otoken.strikeAsset() == USDC, \"strikeAsset != USDC\");\n\n uint256 readyAt = block.timestamp.add(delay);\n require(otoken.expiryTimestamp() >= readyAt, \"Expiry before delay\");\n }\n\n /**\n * @param decimals is the decimals of the asset\n * @param totalBalance is the vault's total asset balance\n * @param currentShareSupply is the supply of the shares invoked with totalSupply()\n * @param lastQueuedWithdrawAmount is the amount queued for withdrawals from last round\n * @param performanceFee is the perf fee percent to charge on premiums\n * @param managementFee is the management fee percent to charge on the AUM\n */\n struct CloseParams {\n uint256 decimals;\n uint256 totalBalance;\n uint256 currentShareSupply;\n uint256 lastQueuedWithdrawAmount;\n uint256 performanceFee;\n uint256 managementFee;\n uint256 currentQueuedWithdrawShares;\n }\n\n /**\n * @notice Calculate the shares to mint, new price per share, and\n amount of funds to re-allocate as collateral for the new round\n * @param vaultState is the storage variable vaultState passed from RibbonVault\n * @param params is the rollover parameters passed to compute the next state\n * @return newLockedAmount is the amount of funds to allocate for the new round\n * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal\n * @return newPricePerShare is the price per share of the new round\n * @return mintShares is the amount of shares to mint from deposits\n * @return performanceFeeInAsset is the performance fee charged by vault\n * @return totalVaultFee is the total amount of fee charged by vault\n */\n function closeRound(\n Vault.VaultState storage vaultState,\n CloseParams calldata params\n )\n external\n view\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares,\n uint256 performanceFeeInAsset,\n uint256 totalVaultFee\n )\n {\n uint256 currentBalance = params.totalBalance;\n uint256 pendingAmount = vaultState.totalPending;\n // Total amount of queued withdrawal shares from previous rounds (doesn't include the current round)\n uint256 lastQueuedWithdrawShares = vaultState.queuedWithdrawShares;\n\n // Deduct older queued withdraws so we don't charge fees on them\n uint256 balanceForVaultFees =\n currentBalance.sub(params.lastQueuedWithdrawAmount);\n\n {\n (performanceFeeInAsset, , totalVaultFee) = getVaultFees(\n balanceForVaultFees,\n vaultState.lastLockedAmount,\n vaultState.totalPending,\n params.performanceFee,\n params.managementFee\n );\n }\n\n // Take into account the fee\n // so we can calculate the newPricePerShare\n currentBalance = currentBalance.sub(totalVaultFee);\n\n {\n newPricePerShare = ShareMath.pricePerShare(\n params.currentShareSupply.sub(lastQueuedWithdrawShares),\n currentBalance.sub(params.lastQueuedWithdrawAmount),\n pendingAmount,\n params.decimals\n );\n\n queuedWithdrawAmount = params.lastQueuedWithdrawAmount.add(\n ShareMath.sharesToAsset(\n params.currentQueuedWithdrawShares,\n newPricePerShare,\n params.decimals\n )\n );\n\n // After closing the short, if the options expire in-the-money\n // vault pricePerShare would go down because vault's asset balance decreased.\n // This ensures that the newly-minted shares do not take on the loss.\n mintShares = ShareMath.assetToShares(\n pendingAmount,\n newPricePerShare,\n params.decimals\n );\n }\n\n return (\n currentBalance.sub(queuedWithdrawAmount), // new locked balance subtracts the queued withdrawals\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n );\n }\n\n /**\n * @notice Creates the actual Opyn short position by depositing collateral and minting otokens\n * @param gammaController is the address of the opyn controller contract\n * @param marginPool is the address of the opyn margin contract which holds the collateral\n * @param oTokenAddress is the address of the otoken to mint\n * @param depositAmount is the amount of collateral to deposit\n * @return the otoken mint amount\n */\n function createShort(\n address gammaController,\n address marginPool,\n address oTokenAddress,\n uint256 depositAmount\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n uint256 newVaultID =\n (controller.getAccountVaultCounter(address(this))).add(1);\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IOtoken oToken = IOtoken(oTokenAddress);\n address collateralAsset = oToken.collateralAsset();\n\n uint256 collateralDecimals =\n uint256(IERC20Detailed(collateralAsset).decimals());\n uint256 mintAmount;\n\n if (oToken.isPut()) {\n // For minting puts, there will be instances where the full depositAmount will not be used for minting.\n // This is because of an issue with precision.\n //\n // For ETH put options, we are calculating the mintAmount (10**8 decimals) using\n // the depositAmount (10**18 decimals), which will result in truncation of decimals when scaling down.\n // As a result, there will be tiny amounts of dust left behind in the Opyn vault when minting put otokens.\n //\n // For simplicity's sake, we do not refund the dust back to the address(this) on minting otokens.\n // We retain the dust in the vault so the calling contract can withdraw the\n // actual locked amount + dust at settlement.\n //\n // To test this behavior, we can console.log\n // MarginCalculatorInterface(0x7A48d10f372b3D7c60f6c9770B91398e4ccfd3C7).getExcessCollateral(vault)\n // to see how much dust (or excess collateral) is left behind.\n mintAmount = depositAmount\n .mul(10**Vault.OTOKEN_DECIMALS)\n .mul(10**18) // we use 10**18 to give extra precision\n .div(oToken.strikePrice().mul(10**(10 + collateralDecimals)));\n } else {\n mintAmount = depositAmount;\n\n if (collateralDecimals > 8) {\n uint256 scaleBy = 10**(collateralDecimals.sub(8)); // oTokens have 8 decimals\n if (mintAmount > scaleBy) {\n mintAmount = depositAmount.div(scaleBy); // scale down from 10**18 to 10**8\n }\n }\n }\n\n // double approve to fix non-compliant ERC20s\n IERC20 collateralToken = IERC20(collateralAsset);\n collateralToken.safeApproveNonCompliant(marginPool, depositAmount);\n\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](3);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.OpenVault,\n address(this), // owner\n address(this), // receiver\n address(0), // asset, otoken\n newVaultID, // vaultId\n 0, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.DepositCollateral,\n address(this), // owner\n address(this), // address to transfer from\n collateralAsset, // deposited asset\n newVaultID, // vaultId\n depositAmount, // amount\n 0, //index\n \"\" //data\n );\n\n actions[2] = IController.ActionArgs(\n IController.ActionType.MintShortOption,\n address(this), // owner\n address(this), // address to transfer to\n oTokenAddress, // option address\n newVaultID, // vaultId\n mintAmount, // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n return mintAmount;\n }\n\n /**\n * @notice Close the existing short otoken position. Currently this implementation is simple.\n * It closes the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time. Since calling `_closeShort` deletes vaults by\n calling SettleVault action, this assumption should hold.\n * @param gammaController is the address of the opyn controller contract\n * @return amount of collateral redeemed from the vault\n */\n function settleShort(address gammaController) external returns (uint256) {\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n // The short position has been previously closed, or all the otokens have been burned.\n // So we return early.\n if (address(collateralToken) == address(0)) {\n return 0;\n }\n\n // This is equivalent to doing IERC20(vault.asset).balanceOf(address(this))\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // If it is after expiry, we need to settle the short position using the normal way\n // Delete the vault and withdraw all remaining collateral from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.SettleVault,\n address(this), // owner\n address(this), // address to transfer to\n address(0), // not used\n vaultID, // vaultId\n 0, // not used\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Exercises the ITM option using existing long otoken position. Currently this implementation is simple.\n * It calls the `Redeem` action to claim the payout.\n * @param gammaController is the address of the opyn controller contract\n * @param oldOption is the address of the old option\n * @param asset is the address of the vault's asset\n * @return amount of asset received by exercising the option\n */\n function settleLong(\n address gammaController,\n address oldOption,\n address asset\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n\n uint256 oldOptionBalance = IERC20(oldOption).balanceOf(address(this));\n\n if (controller.getPayout(oldOption, oldOptionBalance) == 0) {\n return 0;\n }\n\n uint256 startAssetBalance = IERC20(asset).balanceOf(address(this));\n\n // If it is after expiry, we need to redeem the profits\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](1);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.Redeem,\n address(0), // not used\n address(this), // address to send profits to\n oldOption, // address of otoken\n 0, // not used\n oldOptionBalance, // otoken balance\n 0, // not used\n \"\" // not used\n );\n\n controller.operate(actions);\n\n uint256 endAssetBalance = IERC20(asset).balanceOf(address(this));\n\n return endAssetBalance.sub(startAssetBalance);\n }\n\n /**\n * @notice Burn the remaining oTokens left over from auction. Currently this implementation is simple.\n * It burns oTokens from the most recent vault opened by the contract. This assumes that the contract will\n * only have a single vault open at any given time.\n * @param gammaController is the address of the opyn controller contract\n * @param currentOption is the address of the current option\n * @return amount of collateral redeemed by burning otokens\n */\n function burnOtokens(address gammaController, address currentOption)\n external\n returns (uint256)\n {\n uint256 numOTokensToBurn =\n IERC20(currentOption).balanceOf(address(this));\n\n require(numOTokensToBurn > 0, \"No oTokens to burn\");\n\n IController controller = IController(gammaController);\n\n // gets the currently active vault ID\n uint256 vaultID = controller.getAccountVaultCounter(address(this));\n\n GammaTypes.Vault memory vault =\n controller.getVault(address(this), vaultID);\n\n require(vault.shortOtokens.length > 0, \"No short\");\n\n IERC20 collateralToken = IERC20(vault.collateralAssets[0]);\n\n uint256 startCollateralBalance =\n collateralToken.balanceOf(address(this));\n\n // Burning `amount` of oTokens from the ribbon vault,\n // then withdrawing the corresponding collateral amount from the vault\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](2);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.BurnShortOption,\n address(this), // owner\n address(this), // address to transfer from\n address(vault.shortOtokens[0]), // otoken address\n vaultID, // vaultId\n numOTokensToBurn, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.WithdrawCollateral,\n address(this), // owner\n address(this), // address to transfer to\n address(collateralToken), // withdrawn asset\n vaultID, // vaultId\n vault.collateralAmounts[0].mul(numOTokensToBurn).div(\n vault.shortAmounts[0]\n ), // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n uint256 endCollateralBalance = collateralToken.balanceOf(address(this));\n\n return endCollateralBalance.sub(startCollateralBalance);\n }\n\n /**\n * @notice Calculates the performance and management fee for this week's round\n * @param currentBalance is the balance of funds held on the vault after closing short\n * @param lastLockedAmount is the amount of funds locked from the previous round\n * @param pendingAmount is the pending deposit amount\n * @param performanceFeePercent is the performance fee pct.\n * @param managementFeePercent is the management fee pct.\n * @return performanceFeeInAsset is the performance fee\n * @return managementFeeInAsset is the management fee\n * @return vaultFee is the total fees\n */\n function getVaultFees(\n uint256 currentBalance,\n uint256 lastLockedAmount,\n uint256 pendingAmount,\n uint256 performanceFeePercent,\n uint256 managementFeePercent\n )\n internal\n pure\n returns (\n uint256 performanceFeeInAsset,\n uint256 managementFeeInAsset,\n uint256 vaultFee\n )\n {\n // At the first round, currentBalance=0, pendingAmount>0\n // so we just do not charge anything on the first round\n uint256 lockedBalanceSansPending =\n currentBalance > pendingAmount\n ? currentBalance.sub(pendingAmount)\n : 0;\n\n uint256 _performanceFeeInAsset;\n uint256 _managementFeeInAsset;\n uint256 _vaultFee;\n\n // Take performance fee and management fee ONLY if difference between\n // last week and this week's vault deposits, taking into account pending\n // deposits and withdrawals, is positive. If it is negative, last week's\n // option expired ITM past breakeven, and the vault took a loss so we\n // do not collect performance fee for last week\n if (lockedBalanceSansPending > lastLockedAmount) {\n _performanceFeeInAsset = performanceFeePercent > 0\n ? lockedBalanceSansPending\n .sub(lastLockedAmount)\n .mul(performanceFeePercent)\n .div(100 * Vault.FEE_MULTIPLIER)\n : 0;\n _managementFeeInAsset = managementFeePercent > 0\n ? lockedBalanceSansPending.mul(managementFeePercent).div(\n 100 * Vault.FEE_MULTIPLIER\n )\n : 0;\n\n _vaultFee = _performanceFeeInAsset.add(_managementFeeInAsset);\n }\n\n return (_performanceFeeInAsset, _managementFeeInAsset, _vaultFee);\n }\n\n /**\n * @notice Either retrieves the option token if it already exists, or deploy it\n * @param commitParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param underlying is the address of the underlying asset of the option\n * @param strikePrice is the strike price of the option\n * @param expiry is the expiry timestamp of the option\n * @param isPut is whether the option is a put\n * @return the address of the option\n */\n function getOrDeployOtoken(\n CommitParams calldata commitParams,\n Vault.VaultParams storage vaultParams,\n address underlying,\n uint256 strikePrice,\n uint256 expiry,\n bool isPut\n ) internal returns (address) {\n IOtokenFactory factory = IOtokenFactory(commitParams.OTOKEN_FACTORY);\n\n address otokenFromFactory =\n factory.getOtoken(\n underlying,\n commitParams.USDC,\n commitParams.collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n if (otokenFromFactory != address(0)) {\n return otokenFromFactory;\n }\n\n address otoken =\n factory.createOtoken(\n underlying,\n commitParams.USDC,\n commitParams.collateralAsset,\n strikePrice,\n expiry,\n isPut\n );\n\n verifyOtoken(\n otoken,\n vaultParams,\n commitParams.collateralAsset,\n commitParams.USDC,\n commitParams.delay\n );\n\n return otoken;\n }\n\n /**\n * @notice Creates an offer in the Swap Contract\n * @param currentOtoken is the current otoken address\n * @param currOtokenPremium is premium for each otoken\n * @param swapContract the address of the swap contract\n * @param vaultParams is the struct with vault general data\n * @return optionAuctionID auction id of the newly created offer\n */\n function createOffer(\n address currentOtoken,\n uint256 currOtokenPremium,\n address swapContract,\n Vault.VaultParams storage vaultParams\n ) external returns (uint256 optionAuctionID) {\n require(\n currOtokenPremium <= type(uint96).max,\n \"currentOtokenPremium > type(uint96) max value!\"\n );\n require(currOtokenPremium > 0, \"!currentOtokenPremium\");\n\n uint256 oTokenBalance = IERC20(currentOtoken).balanceOf(address(this));\n require(\n oTokenBalance <= type(uint128).max,\n \"oTokenBalance > type(uint128) max value!\"\n );\n\n // Use safeIncrease instead of safeApproval because safeApproval is only used for initial\n // approval and cannot be called again. Using safeIncrease allow us to call _createOffer\n // even when we are approving the same oTokens we have used before. This might happen if\n // we accidentally burn the oTokens before settlement.\n uint256 allowance =\n IERC20(currentOtoken).allowance(address(this), swapContract);\n\n if (allowance < oTokenBalance) {\n IERC20(currentOtoken).safeIncreaseAllowance(\n swapContract,\n oTokenBalance.sub(allowance)\n );\n }\n\n uint256 decimals = vaultParams.decimals;\n\n // If total size is larger than 1, set minimum bid as 1\n // Otherwise, set minimum bid to one tenth the total size\n uint256 minBidSize =\n oTokenBalance > 10**decimals ? 10**decimals : oTokenBalance.div(10);\n\n require(\n minBidSize <= type(uint96).max,\n \"minBidSize > type(uint96) max value!\"\n );\n\n currOtokenPremium = decimals > 18\n ? currOtokenPremium.mul(10**(decimals.sub(18)))\n : currOtokenPremium.div(10**(uint256(18).sub(decimals)));\n\n optionAuctionID = ISwap(swapContract).createOffer(\n currentOtoken,\n vaultParams.asset,\n uint96(currOtokenPremium),\n uint96(minBidSize),\n uint128(oTokenBalance)\n );\n }\n\n /**\n * @notice Allocates the vault's minted options to the OptionsPurchaseQueue contract\n * @dev Skipped if the optionsPurchaseQueue doesn't exist\n * @param optionsPurchaseQueue is the OptionsPurchaseQueue contract\n * @param option is the minted option\n * @param optionsAmount is the amount of options minted\n * @param optionAllocation is the maximum % of options to allocate towards the purchase queue (will only allocate\n * up to the amount that is on the queue)\n * @return allocatedOptions is the amount of options that ended up getting allocated to the OptionsPurchaseQueue\n */\n function allocateOptions(\n address optionsPurchaseQueue,\n address option,\n uint256 optionsAmount,\n uint256 optionAllocation\n ) external returns (uint256 allocatedOptions) {\n // Skip if optionsPurchaseQueue is address(0)\n if (optionsPurchaseQueue != address(0)) {\n allocatedOptions = optionsAmount.mul(optionAllocation).div(\n 100 * Vault.OPTION_ALLOCATION_MULTIPLIER\n );\n allocatedOptions = IOptionsPurchaseQueue(optionsPurchaseQueue)\n .getOptionsAllocation(address(this), allocatedOptions);\n\n if (allocatedOptions != 0) {\n IERC20(option).approve(optionsPurchaseQueue, allocatedOptions);\n IOptionsPurchaseQueue(optionsPurchaseQueue).allocateOptions(\n allocatedOptions\n );\n }\n }\n\n return allocatedOptions;\n }\n\n /**\n * @notice Sell the allocated options to the purchase queue post auction settlement\n * @dev Reverts if the auction hasn't settled yet\n * @param optionsPurchaseQueue is the OptionsPurchaseQueue contract\n * @param swapContract The address of the swap settlement contract\n * @return totalPremiums Total premiums earnt by the vault\n */\n function sellOptionsToQueue(\n address optionsPurchaseQueue,\n address swapContract,\n uint256 optionAuctionID\n ) external returns (uint256) {\n uint256 settlementPrice =\n getAuctionSettlementPrice(swapContract, optionAuctionID);\n require(settlementPrice != 0, \"!settlementPrice\");\n\n return\n IOptionsPurchaseQueue(optionsPurchaseQueue).sellToBuyers(\n settlementPrice\n );\n }\n\n /**\n * @notice Gets the settlement price of a settled auction\n * @param swapContract The address of the swap settlement contract\n * @param optionAuctionID is the offer ID\n * @return settlementPrice Auction settlement price\n */\n function getAuctionSettlementPrice(\n address swapContract,\n uint256 optionAuctionID\n ) public view returns (uint256) {\n return ISwap(swapContract).averagePriceForOffer(optionAuctionID);\n }\n\n /**\n * @notice Verify the constructor params satisfy requirements\n * @param owner is the owner of the vault with critical permissions\n * @param feeRecipient is the address to recieve vault performance and management fees\n * @param performanceFee is the perfomance fee pct.\n * @param tokenName is the name of the token\n * @param tokenSymbol is the symbol of the token\n * @param _vaultParams is the struct with vault general data\n */\n function verifyInitializerParams(\n address owner,\n address keeper,\n address feeRecipient,\n uint256 performanceFee,\n uint256 managementFee,\n string calldata tokenName,\n string calldata tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) external pure {\n require(owner != address(0), \"!owner\");\n require(keeper != address(0), \"!keeper\");\n require(feeRecipient != address(0), \"!feeRecipient\");\n require(\n performanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"performanceFee >= 100%\"\n );\n require(\n managementFee < 100 * Vault.FEE_MULTIPLIER,\n \"managementFee >= 100%\"\n );\n require(bytes(tokenName).length > 0, \"!tokenName\");\n require(bytes(tokenSymbol).length > 0, \"!tokenSymbol\");\n\n require(_vaultParams.asset != address(0), \"!asset\");\n require(_vaultParams.underlying != address(0), \"!underlying\");\n require(_vaultParams.minimumSupply > 0, \"!minimumSupply\");\n require(_vaultParams.cap > 0, \"!cap\");\n require(\n _vaultParams.cap > _vaultParams.minimumSupply,\n \"cap has to be higher than minimumSupply\"\n );\n }\n\n /**\n * @notice Gets the next option expiry timestamp\n * @param currentOption is the otoken address that the vault is currently writing\n */\n function getNextExpiry(address currentOption)\n internal\n view\n returns (uint256)\n {\n // uninitialized state\n if (currentOption == address(0)) {\n return getNextFriday(block.timestamp);\n }\n uint256 currentExpiry = IOtoken(currentOption).expiryTimestamp();\n\n // After options expiry if no options are written for >1 week\n // We need to give the ability continue writing options\n if (block.timestamp > currentExpiry + 7 days) {\n return getNextFriday(block.timestamp);\n }\n return getNextFriday(currentExpiry);\n }\n\n /**\n * @notice Gets the next options expiry timestamp\n * @param timestamp is the expiry timestamp of the current option\n * Reference: https://codereview.stackexchange.com/a/33532\n * Examples:\n * getNextFriday(week 1 thursday) -> week 1 friday\n * getNextFriday(week 1 friday) -> week 2 friday\n * getNextFriday(week 1 saturday) -> week 2 friday\n */\n function getNextFriday(uint256 timestamp) internal pure returns (uint256) {\n // dayOfWeek = 0 (sunday) - 6 (saturday)\n uint256 dayOfWeek = ((timestamp / 1 days) + 4) % 7;\n uint256 nextFriday = timestamp + ((7 + 5 - dayOfWeek) % 7) * 1 days;\n uint256 friday8am = nextFriday - (nextFriday % (24 hours)) + (8 hours);\n\n // If the passed timestamp is day=Friday hour>8am, we simply increment it by a week to next Friday\n if (timestamp >= friday8am) {\n friday8am += 7 days;\n }\n return friday8am;\n }\n}\n" + }, + "contracts/libraries/VaultLifecycleSTETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {DSMath} from \"../vendor/DSMath.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {VaultLifecycle} from \"./VaultLifecycle.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {Vault} from \"./Vault.sol\";\nimport {ShareMath} from \"./ShareMath.sol\";\nimport {ISTETH, IWSTETH} from \"../interfaces/ISTETH.sol\";\nimport {IWETH} from \"../interfaces/IWETH.sol\";\nimport {ICRV} from \"../interfaces/ICRV.sol\";\nimport {IStrikeSelection} from \"../interfaces/IRibbon.sol\";\nimport {\n IOtokenFactory,\n IOtoken,\n IController,\n GammaTypes\n} from \"../interfaces/GammaInterface.sol\";\nimport {IERC20Detailed} from \"../interfaces/IERC20Detailed.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\n\nlibrary VaultLifecycleSTETH {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n /**\n * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction\n * @param closeParams is the struct with details on previous option and strike selection details\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @param collateralAsset is the address of the collateral asset\n * @return otokenAddress is the address of the new option\n * @return strikePrice is the strike price of the new option\n * @return delta is the delta of the new option\n */\n function commitAndClose(\n VaultLifecycle.CloseParams calldata closeParams,\n Vault.VaultParams storage vaultParams,\n Vault.VaultState storage vaultState,\n address collateralAsset\n )\n external\n returns (\n address otokenAddress,\n uint256 strikePrice,\n uint256 delta\n )\n {\n uint256 expiry =\n VaultLifecycle.getNextExpiry(closeParams.currentOption);\n\n IStrikeSelection selection =\n IStrikeSelection(closeParams.strikeSelection);\n\n // calculate strike and delta\n (strikePrice, delta) = closeParams.lastStrikeOverrideRound ==\n vaultState.round\n ? (closeParams.overriddenStrikePrice, selection.delta())\n : selection.getStrikePrice(expiry, false);\n\n require(strikePrice != 0, \"!strikePrice\");\n\n // retrieve address if option already exists, or deploy it\n otokenAddress = VaultLifecycle.getOrDeployOtoken(\n closeParams,\n vaultParams,\n vaultParams.underlying,\n collateralAsset,\n strikePrice,\n expiry,\n false\n );\n\n return (otokenAddress, strikePrice, delta);\n }\n\n /**\n * @notice Calculate the shares to mint, new price per share, and\n amount of funds to re-allocate as collateral for the new round\n * @param currentShareSupply is the total supply of shares\n * @param currentBalance is the total balance of the vault\n * @param vaultParams is the struct with vault general data\n * @param vaultState is the struct with vault accounting state\n * @return newLockedAmount is the amount of funds to allocate for the new round\n * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal\n * @return newPricePerShare is the price per share of the new round\n * @return mintShares is the amount of shares to mint from deposits\n */\n function rollover(\n uint256 currentShareSupply,\n uint256 currentBalance,\n Vault.VaultParams calldata vaultParams,\n Vault.VaultState calldata vaultState\n )\n external\n pure\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares\n )\n {\n uint256 pendingAmount = uint256(vaultState.totalPending);\n uint256 _decimals = vaultParams.decimals;\n\n newPricePerShare = ShareMath.pricePerShare(\n currentShareSupply,\n currentBalance,\n pendingAmount,\n _decimals\n );\n\n // After closing the short, if the options expire in-the-money\n // vault pricePerShare would go down because vault's asset balance decreased.\n // This ensures that the newly-minted shares do not take on the loss.\n uint256 _mintShares =\n ShareMath.assetToShares(pendingAmount, newPricePerShare, _decimals);\n\n uint256 newSupply = currentShareSupply.add(_mintShares);\n uint256 queuedAmount =\n newSupply > 0\n ? ShareMath.sharesToAsset(\n vaultState.queuedWithdrawShares,\n newPricePerShare,\n _decimals\n )\n : 0;\n\n return (\n currentBalance.sub(queuedAmount),\n queuedAmount,\n newPricePerShare,\n _mintShares\n );\n }\n\n /**\n * @notice Creates the actual Opyn short position by depositing collateral and minting otokens\n * @param gammaController is the address of the opyn controller contract\n * @param marginPool is the address of the opyn margin contract which holds the collateral\n * @param oTokenAddress is the address of the otoken to mint\n * @param depositAmount is the amount of collateral to deposit\n * @return the otoken mint amount\n */\n function createShort(\n address gammaController,\n address marginPool,\n address oTokenAddress,\n uint256 depositAmount\n ) external returns (uint256) {\n IController controller = IController(gammaController);\n uint256 newVaultID =\n (controller.getAccountVaultCounter(address(this))).add(1);\n\n // An otoken's collateralAsset is the vault's `asset`\n // So in the context of performing Opyn short operations we call them collateralAsset\n IOtoken oToken = IOtoken(oTokenAddress);\n address collateralAsset = oToken.collateralAsset();\n\n uint256 collateralDecimals =\n uint256(IERC20Detailed(collateralAsset).decimals());\n uint256 mintAmount;\n\n mintAmount = depositAmount;\n if (collateralDecimals > 8) {\n uint256 scaleBy = 10**(collateralDecimals.sub(8)); // oTokens have 8 decimals\n if (mintAmount > scaleBy) {\n mintAmount = depositAmount.div(scaleBy); // scale down from 10**18 to 10**8\n }\n }\n\n IERC20 collateralToken = IERC20(collateralAsset);\n collateralToken.safeApprove(marginPool, depositAmount);\n\n IController.ActionArgs[] memory actions =\n new IController.ActionArgs[](3);\n\n actions[0] = IController.ActionArgs(\n IController.ActionType.OpenVault,\n address(this), // owner\n address(this), // receiver\n address(0), // asset, otoken\n newVaultID, // vaultId\n 0, // amount\n 0, //index\n \"\" //data\n );\n\n actions[1] = IController.ActionArgs(\n IController.ActionType.DepositCollateral,\n address(this), // owner\n address(this), // address to transfer from\n collateralAsset, // deposited asset\n newVaultID, // vaultId\n depositAmount, // amount\n 0, //index\n \"\" //data\n );\n\n actions[2] = IController.ActionArgs(\n IController.ActionType.MintShortOption,\n address(this), // owner\n address(this), // address to transfer to\n oTokenAddress, // option address\n newVaultID, // vaultId\n mintAmount, // amount\n 0, //index\n \"\" //data\n );\n\n controller.operate(actions);\n\n return mintAmount;\n }\n\n /**\n * @notice Withdraws stETH + WETH (if necessary) from vault using vault shares\n * @param collateralToken is the address of the collateral token\n * @param weth is the WETH address\n * @param recipient is the recipient\n * @param amount is the withdraw amount in `asset`\n * @return withdrawAmount is the withdraw amount in `collateralToken`\n */\n function withdrawYieldAndBaseToken(\n address collateralToken,\n address weth,\n address recipient,\n uint256 amount\n ) external returns (uint256) {\n IWSTETH collateral = IWSTETH(collateralToken);\n\n uint256 withdrawAmount = collateral.getWstETHByStETH(amount);\n\n uint256 yieldTokenBalance =\n withdrawYieldToken(collateralToken, recipient, withdrawAmount);\n\n // If there is not enough wstETH in the vault, it withdraws as much as possible and\n // transfers the rest in `asset`\n if (withdrawAmount > yieldTokenBalance) {\n withdrawBaseToken(\n collateralToken,\n weth,\n recipient,\n withdrawAmount,\n yieldTokenBalance\n );\n }\n\n return withdrawAmount;\n }\n\n /**\n * @notice Withdraws stETH from vault\n * @param collateralToken is the address of the collateral token\n * @param recipient is the recipient\n * @param withdrawAmount is the withdraw amount in terms of yearn tokens\n * @return yieldTokenBalance is the balance of the yield token\n */\n function withdrawYieldToken(\n address collateralToken,\n address recipient,\n uint256 withdrawAmount\n ) internal returns (uint256) {\n IERC20 collateral = IERC20(collateralToken);\n\n uint256 yieldTokenBalance = collateral.balanceOf(address(this));\n uint256 yieldTokensToWithdraw =\n DSMath.min(yieldTokenBalance, withdrawAmount);\n if (yieldTokensToWithdraw > 0) {\n collateral.safeTransfer(recipient, yieldTokensToWithdraw);\n }\n\n return yieldTokenBalance;\n }\n\n /**\n * @notice Withdraws `asset` from vault\n * @param collateralToken is the address of the collateral token\n * @param weth is the WETH address\n * @param recipient is the recipient\n * @param withdrawAmount is the withdraw amount in terms of yearn tokens\n * @param yieldTokenBalance is the collateral token (stETH) balance of the vault\n */\n function withdrawBaseToken(\n address collateralToken,\n address weth,\n address recipient,\n uint256 withdrawAmount,\n uint256 yieldTokenBalance\n ) internal {\n uint256 underlyingTokensToWithdraw =\n IWSTETH(collateralToken).getStETHByWstETH(\n withdrawAmount.sub(yieldTokenBalance)\n );\n\n IWETH(weth).deposit{value: underlyingTokensToWithdraw}();\n IERC20(weth).safeTransfer(recipient, underlyingTokensToWithdraw);\n }\n\n /**\n * @notice Unwraps the necessary amount of the wstETH token\n * and transfers ETH amount to vault\n * @param amount is the amount of ETH to withdraw\n * @param wstEth is the address of wstETH\n * @param stethToken is the address of stETH\n * @param crvPool is the address of the steth <-> eth pool on curve\n * @param minETHOut is the minimum eth amount to receive from the swap\n * @return amountETHOut is the amount of eth unwrapped\n available for the withdrawal (may incur curve slippage)\n */\n function unwrapYieldToken(\n uint256 amount,\n address wstEth,\n address stethToken,\n address crvPool,\n uint256 minETHOut\n ) external returns (uint256) {\n require(\n amount >= minETHOut,\n \"Amount withdrawn smaller than minETHOut from swap\"\n );\n require(\n minETHOut.mul(10**18).div(amount) >= 0.95 ether,\n \"Slippage on minETHOut too high\"\n );\n\n uint256 ethBalance = address(this).balance;\n IERC20 steth = IERC20(stethToken);\n uint256 stethBalance = steth.balanceOf(address(this));\n\n // 3 different success scenarios\n // Scenario 1. We hold enough ETH to satisfy withdrawal. Send it out directly\n // Scenario 2. We hold enough wstETH to satisy withdrawal. Unwrap then swap\n // Scenario 3. We hold enough ETH + stETH to satisfy withdrawal. Do a swap\n\n // Scenario 1\n if (ethBalance >= amount) {\n return amount;\n }\n\n // Scenario 2\n stethBalance = unwrapWstethForWithdrawal(\n wstEth,\n steth,\n ethBalance,\n stethBalance,\n amount,\n minETHOut\n );\n\n // Scenario 3\n // Now that we satisfied the ETH + stETH sum, we swap the stETH amounts necessary\n // to facilitate a withdrawal\n\n // This won't underflow since we already asserted that ethBalance < amount before this\n uint256 stEthAmountToSwap =\n DSMath.min(amount.sub(ethBalance), stethBalance);\n\n uint256 ethAmountOutFromSwap =\n swapStEthToEth(steth, crvPool, stEthAmountToSwap);\n\n uint256 totalETHOut = ethBalance.add(ethAmountOutFromSwap);\n\n // Since minETHOut is derived from calling the Curve pool's getter,\n // it reverts in the worst case where the user needs to unwrap and sell\n // 100% of their ETH withdrawal amount\n require(\n totalETHOut >= minETHOut,\n \"Output ETH amount smaller than minETHOut\"\n );\n\n return totalETHOut;\n }\n\n /**\n * @notice Unwraps the required amount of wstETH to a target ETH amount\n * @param wstEthAddress is the address for wstETH\n * @param steth is the ERC20 of stETH\n * @param startStEthBalance is the starting stETH balance used to determine how much more to unwrap\n * @param ethAmount is the ETH amount needed for the contract\n * @param minETHOut is the ETH amount but adjusted for slippage\n * @return the new stETH balance\n */\n function unwrapWstethForWithdrawal(\n address wstEthAddress,\n IERC20 steth,\n uint256 ethBalance,\n uint256 startStEthBalance,\n uint256 ethAmount,\n uint256 minETHOut\n ) internal returns (uint256) {\n uint256 ethstEthSum = ethBalance.add(startStEthBalance);\n\n if (ethstEthSum < minETHOut) {\n uint256 stethNeededFromUnwrap = ethAmount.sub(ethstEthSum);\n IWSTETH wstEth = IWSTETH(wstEthAddress);\n uint256 wstAmountToUnwrap =\n wstEth.getWstETHByStETH(stethNeededFromUnwrap);\n\n wstEth.unwrap(wstAmountToUnwrap);\n\n uint256 newStEthBalance = steth.balanceOf(address(this));\n require(\n ethBalance.add(newStEthBalance) >= minETHOut,\n \"Unwrapping wstETH did not return sufficient stETH\"\n );\n return newStEthBalance;\n }\n return startStEthBalance;\n }\n\n /**\n * @notice Swaps from stEth to ETH on the Lido Curve pool\n * @param steth is the address for the Lido staked ether\n * @param crvPool is the Curve pool address to do the swap\n * @param stEthAmount is the stEth amount to be swapped to Ether\n * @return ethAmountOutFromSwap is the returned ETH amount from swap\n */\n function swapStEthToEth(\n IERC20 steth,\n address crvPool,\n uint256 stEthAmount\n ) internal returns (uint256) {\n steth.safeApprove(crvPool, stEthAmount);\n\n // CRV SWAP HERE from steth -> eth\n // 0 = ETH, 1 = STETH\n // We are setting 1, which is the smallest possible value for the _minAmountOut parameter\n // However it is fine because we check that the totalETHOut >= minETHOut at the end\n // which makes sandwich attacks not possible\n uint256 ethAmountOutFromSwap =\n ICRV(crvPool).exchange(1, 0, stEthAmount, 1);\n\n return ethAmountOutFromSwap;\n }\n\n /**\n * @notice Wraps the necessary amount of the base token to the yield-bearing yearn token\n * @param weth is the address of weth\n * @param collateralToken is the address of the collateral token\n */\n function wrapToYieldToken(\n address weth,\n address collateralToken,\n address steth\n ) external {\n // Unwrap all weth premiums transferred to contract\n IWETH wethToken = IWETH(weth);\n uint256 wethBalance = wethToken.balanceOf(address(this));\n\n if (wethBalance > 0) {\n wethToken.withdraw(wethBalance);\n }\n\n uint256 ethBalance = address(this).balance;\n\n IWSTETH collateral = IWSTETH(collateralToken);\n IERC20 stethToken = IERC20(steth);\n\n if (ethBalance > 0) {\n // Send eth to Lido, recieve steth\n ISTETH(steth).submit{value: ethBalance}(address(this));\n }\n\n // Get all steth in contract\n uint256 stethBalance = stethToken.balanceOf(address(this));\n\n if (stethBalance > 0) {\n // approve wrap\n stethToken.safeApprove(collateralToken, stethBalance.add(1));\n // Wrap to wstETH - need to add 1 to steth balance as it is innacurate\n collateral.wrap(stethBalance.add(1));\n }\n }\n\n /**\n * @notice Gets stETH for direct stETH withdrawals, converts wstETH/ETH to stETH if not enough stETH\n * @param steth is the address of steth\n * @param wstEth is the address of wsteth\n * @param amount is the amount to withdraw\n * @return amount of stETH to transfer to the user, this is to account for rounding errors when unwrapping wstETH\n */\n function withdrawStEth(\n address steth,\n address wstEth,\n uint256 amount\n ) external returns (uint256) {\n // 3 different scenarios for withdrawing stETH directly\n // Scenario 1. We hold enough stETH to satisfy withdrawal. Send it out directly\n // Scenario 2. We hold enough stETH + wstETH to satisy withdrawal. Unwrap wstETH then send it\n // Scenario 3. We hold enough stETH + wstETH + ETH satisfy withdrawal. Unwrap wstETH, wrap ETH then send it\n uint256 _amount = amount;\n uint256 stethBalance = IERC20(steth).balanceOf(address(this));\n if (stethBalance >= amount) {\n // Can send out the stETH directly\n return amount; // We return here if we have enough stETH to satisfy the withdrawal\n } else {\n // If amount > stethBalance, send out the entire stethBalance and check wstETH and ETH\n amount = amount.sub(stethBalance);\n }\n uint256 wstethBalance = IWSTETH(wstEth).balanceOf(address(this));\n uint256 totalShares = ISTETH(steth).getTotalShares();\n uint256 totalPooledEther = ISTETH(steth).getTotalPooledEther();\n stethBalance = wstethBalance.mul(totalPooledEther).div(totalShares);\n if (stethBalance >= amount) {\n wstethBalance = amount.mul(totalShares).div(totalPooledEther);\n // Avoids reverting if unwrap amount is 0\n if (wstethBalance > 0) {\n // Unwraps wstETH and sends out the received stETH directly\n IWSTETH(wstEth).unwrap(wstethBalance);\n // Accounts for rounding errors when unwrapping wstETH, this is safe because this function would've\n // returned already if the stETH balance was greater than our withdrawal amount\n return IERC20(steth).balanceOf(address(this)); // We return here if we have enough stETH + wstETH\n }\n } else if (stethBalance > 0) {\n stethBalance = IERC20(steth).balanceOf(address(this));\n IWSTETH(wstEth).unwrap(wstethBalance);\n // Accounts for rounding errors when unwrapping wstETH\n amount = amount.sub(\n IERC20(steth).balanceOf(address(this)).sub(stethBalance)\n );\n }\n // Wrap ETH to stETH if we don't have enough stETH + wstETH\n uint256 ethBalance = address(this).balance;\n if (amount > 0 && ethBalance >= amount) {\n ISTETH(steth).submit{value: amount}(address(this));\n } else if (ethBalance > 0) {\n ISTETH(steth).submit{value: ethBalance}(address(this));\n }\n stethBalance = IERC20(steth).balanceOf(address(this));\n // Accounts for rounding errors by a margin of 3 wei\n require(_amount.add(3) >= stethBalance, \"Unwrapped too much stETH\");\n require(_amount <= stethBalance.add(3), \"Unwrapped insufficient stETH\");\n return stethBalance; // We return here if we have enough stETH + wstETH + ETH\n }\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) public {\n (bool success, ) = payable(recipient).call{value: amount}(\"\");\n require(success, \"!success\");\n }\n\n function getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount,\n address collateralToken\n ) external view returns (uint256) {\n return\n _getOTokenPremium(\n oTokenAddress,\n optionsPremiumPricer,\n premiumDiscount,\n collateralToken\n );\n }\n\n function _getOTokenPremium(\n address oTokenAddress,\n address optionsPremiumPricer,\n uint256 premiumDiscount,\n address collateralToken\n ) internal view returns (uint256) {\n IOtoken newOToken = IOtoken(oTokenAddress);\n IOptionsPremiumPricer premiumPricer =\n IOptionsPremiumPricer(optionsPremiumPricer);\n\n // Apply black-scholes formula (from rvol library) to option given its features\n // and get price for 100 contracts denominated in the underlying asset for call option\n // and USDC for put option\n uint256 optionPremium =\n premiumPricer.getPremium(\n newOToken.strikePrice(),\n newOToken.expiryTimestamp(),\n newOToken.isPut()\n );\n\n // Apply a discount to incentivize arbitraguers\n optionPremium = optionPremium.mul(premiumDiscount).div(\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER\n );\n\n // get the black scholes premium of the option and adjust premium based on\n // steth <-> eth exchange rate\n uint256 adjustedPremium =\n DSMath.wmul(\n optionPremium,\n IWSTETH(collateralToken).stEthPerToken()\n );\n\n require(\n adjustedPremium <= type(uint96).max,\n \"adjustedPremium > type(uint96) max value!\"\n );\n require(adjustedPremium > 0, \"!adjustedPremium\");\n\n return adjustedPremium;\n }\n}\n" + }, + "contracts/interfaces/ISTETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface IWSTETH {\n function getStETHByWstETH(uint256 _amount) external view returns (uint256);\n\n function getWstETHByStETH(uint256 _amount) external view returns (uint256);\n\n function stEthPerToken() external view returns (uint256);\n\n function tokensPerStEth() external view returns (uint256);\n\n function stETH() external view returns (address);\n\n function wrap(uint256 _amount) external returns (uint256);\n\n function unwrap(uint256 _amount) external returns (uint256);\n\n function approve(address _recipient, uint256 _amount)\n external\n returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function decimals() external view returns (uint256);\n}\n\ninterface ISTETH {\n function getBufferedEther(uint256 _amount) external view returns (uint256);\n\n function getPooledEthByShares(uint256 _amount)\n external\n view\n returns (uint256);\n\n function getSharesByPooledEth(uint256 _amount)\n external\n view\n returns (uint256);\n\n function submit(address _referralAddress)\n external\n payable\n returns (uint256);\n\n function withdraw(uint256 _amount, bytes32 _pubkeyHash)\n external\n returns (uint256);\n\n function approve(address _recipient, uint256 _amount)\n external\n returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function decimals() external view returns (uint256);\n\n function getTotalShares() external view returns (uint256);\n\n function getTotalPooledEther() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICRV.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\ninterface ICRV {\n function get_dy(\n int128 _indexIn,\n int128 _indexOut,\n uint256 _amountIn\n ) external view returns (uint256);\n\n // https://github.com/curvefi/curve-contract/blob/\n // b0bbf77f8f93c9c5f4e415bce9cd71f0cdee960e/contracts/pools/steth/StableSwapSTETH.vy#L431\n function exchange(\n int128 _indexIn,\n int128 _indexOut,\n uint256 _amountIn,\n uint256 _minAmountOut\n ) external returns (uint256);\n}\n" + }, + "contracts/vaults/STETHVault/RibbonThetaSTETHVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport {ISwap} from \"../../interfaces/ISwap.sol\";\nimport {\n RibbonThetaSTETHVaultStorage\n} from \"../../storage/RibbonThetaSTETHVaultStorage.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../libraries/VaultLifecycleWithSwap.sol\";\nimport {VaultLifecycleSTETH} from \"../../libraries/VaultLifecycleSTETH.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {ILiquidityGauge} from \"../../interfaces/ILiquidityGauge.sol\";\nimport {RibbonVault} from \"./base/RibbonVault.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\nimport {IWSTETH} from \"../../interfaces/ISTETH.sol\";\n\n/**\n * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe\n * the inheritance chain closely.\n * Any changes/appends in storage variable needs to happen in RibbonThetaSTETHVaultStorage.\n * RibbonThetaSTETHVault should not inherit from any other contract aside from RibbonVault, RibbonThetaSTETHVaultStorage\n */\ncontract RibbonThetaSTETHVault is RibbonVault, RibbonThetaSTETHVaultStorage {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice oTokenFactory is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event NewOffer(\n uint256 swapId,\n address seller,\n address oToken,\n address biddingToken,\n uint256 minPrice,\n uint256 minBidSize,\n uint256 totalSize\n );\n\n /************************************************\n * STRUCTS\n ***********************************************/\n\n /**\n * @notice Initialization parameters for the vault.\n * @param _owner is the owner of the vault with critical permissions\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n */\n struct InitParams {\n address _owner;\n address _keeper;\n address _feeRecipient;\n uint256 _managementFee;\n uint256 _performanceFee;\n string _tokenName;\n string _tokenSymbol;\n address _optionsPremiumPricer;\n address _strikeSelection;\n }\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _wsteth is the LDO contract\n * @param _ldo is the LDO contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _wsteth,\n address _ldo,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _swapContract\n )\n RibbonVault(\n _weth,\n _usdc,\n _wsteth,\n _ldo,\n _gammaController,\n _marginPool,\n _swapContract\n )\n {\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n OTOKEN_FACTORY = _oTokenFactory;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n * @param _initParams is the struct with vault initialization parameters\n * @param _vaultParams is the struct with vault general data\n */\n function initialize(\n InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n baseInitialize(\n _initParams._owner,\n _initParams._keeper,\n _initParams._feeRecipient,\n _initParams._managementFee,\n _initParams._performanceFee,\n _initParams._tokenName,\n _initParams._tokenSymbol,\n _vaultParams\n );\n require(\n _initParams._optionsPremiumPricer != address(0),\n \"!_optionsPremiumPricer\"\n );\n require(\n _initParams._strikeSelection != address(0),\n \"!_strikeSelection\"\n );\n\n optionsPremiumPricer = _initParams._optionsPremiumPricer;\n strikeSelection = _initParams._strikeSelection;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new strike selection or options premium pricer contract\n * @param newContract is the address of the new strike selection or options premium pricer contract\n * @param isStrikeSelection is whether we are setting the strike selection contract\n */\n function setStrikeSelectionOrPricer(\n address newContract,\n bool isStrikeSelection\n ) external onlyOwner {\n require(newContract != address(0), \"!newContract\");\n if (isStrikeSelection) {\n strikeSelection = newContract;\n } else {\n optionsPremiumPricer = newContract;\n }\n }\n\n /**\n * @notice Optionality to set strike price manually\n * Should be called after closeRound if we are setting current week's strike\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice) external onlyOwner {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Sets the new liquidityGauge contract for this vault\n * @param newLiquidityGauge is the address of the new liquidityGauge contract\n */\n function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {\n liquidityGauge = newLiquidityGauge;\n }\n\n /**\n * @notice Sets oToken Premium\n * @param minPrice is the new oToken Premium in the units of 10**18\n */\n function setMinPrice(uint256 minPrice) external onlyKeeper {\n require(minPrice > 0, \"!minPrice\");\n currentOtokenPremium = minPrice;\n }\n\n /**\n * @notice Sets the new Vault Pauser contract for this vault\n * @param newVaultPauser is the address of the new vaultPauser contract\n */\n function setVaultPauser(address newVaultPauser) external onlyOwner {\n vaultPauser = newVaultPauser;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount, uint256) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n IERC20(STETH).safeTransfer(\n msg.sender,\n VaultLifecycleSTETH.withdrawStEth(\n STETH,\n address(collateralToken),\n amount\n )\n );\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n _initiateWithdraw(numShares);\n currentQueuedWithdrawShares = currentQueuedWithdrawShares.add(\n numShares\n );\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /**\n * @notice Stakes a users vault shares\n * @param numShares is the number of shares to stake\n */\n function stake(uint256 numShares) external nonReentrant {\n address _liquidityGauge = liquidityGauge;\n require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit\n require(numShares > 0);\n uint256 heldByAccount = balanceOf(msg.sender);\n if (heldByAccount < numShares) {\n _redeem(numShares.sub(heldByAccount), false);\n }\n _transfer(msg.sender, address(this), numShares);\n _approve(address(this), _liquidityGauge, numShares);\n ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);\n }\n\n /**\n * @notice Closes the existing short and calculate the shares to mint, new price per share &\n amount of funds to re-allocate as collateral for the new round\n * Since we are incrementing the round here, the options are sold in the beginning of a round\n * instead of at the end of the round. For example, at round 1, we don't sell any options. We\n * start selling options at the beginning of round 2.\n */\n function closeRound() external nonReentrant {\n address oldOption = optionState.currentOption;\n require(\n oldOption != address(0) || vaultState.round == 1,\n \"Round closed\"\n );\n _closeShort(oldOption);\n\n uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares;\n (uint256 lockedBalance, uint256 queuedWithdrawAmount) =\n _closeRound(\n uint256(lastQueuedWithdrawAmount),\n currQueuedWithdrawShares\n );\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(\n currQueuedWithdrawShares\n );\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n currentQueuedWithdrawShares = 0;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycleWithSwap.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Sets the next option the vault will be shorting\n */\n function commitNextOption() external onlyKeeper nonReentrant {\n address currentOption = optionState.currentOption;\n require(\n currentOption == address(0) && vaultState.round != 1,\n \"Round not closed\"\n );\n\n VaultLifecycleWithSwap.CommitParams memory commitParams =\n VaultLifecycleWithSwap.CommitParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n collateralAsset: address(collateralToken),\n currentOption: currentOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n strikeSelection: strikeSelection,\n optionsPremiumPricer: optionsPremiumPricer\n });\n\n (address otokenAddress, uint256 strikePrice, uint256 delta) =\n VaultLifecycleWithSwap.commitNextOption(\n commitParams,\n vaultParams,\n vaultState\n );\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n optionState.nextOption = otokenAddress;\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position and create a new offer.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n address newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n // Locked balance denominated in `collateralToken`\n uint256 lockedBalance =\n collateralToken.balanceOf(address(this)).sub(\n collateralToken.getWstETHByStETH(lastQueuedWithdrawAmount)\n );\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n VaultLifecycleWithSwap.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n _createOffer();\n }\n\n function _createOffer() private {\n address currentOtoken = optionState.currentOption;\n uint256 currOtokenPremium = currentOtokenPremium;\n\n optionAuctionID = VaultLifecycleWithSwap.createOffer(\n currentOtoken,\n currOtokenPremium,\n SWAP_CONTRACT,\n vaultParams\n );\n }\n\n /**\n * @notice Settle current offer\n */\n function settleOffer(ISwap.Bid[] calldata bids)\n external\n onlyKeeper\n nonReentrant\n {\n ISwap(SWAP_CONTRACT).settleOffer(optionAuctionID, bids);\n }\n\n /**\n * @notice Burn the remaining oTokens left over\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n uint256 unlockedAssetAmount =\n VaultLifecycleWithSwap.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n\n vaultState.lockedAmount = uint104(\n uint256(vaultState.lockedAmount).sub(\n collateralToken.getStETHByWstETH(unlockedAssetAmount)\n )\n );\n\n // Wrap entire `asset` balance to `collateralToken` balance\n VaultLifecycleSTETH.wrapToYieldToken(\n WETH,\n address(collateralToken),\n STETH\n );\n }\n\n /**\n * @notice pause a user's vault position\n */\n function pausePosition() external {\n address _vaultPauserAddress = vaultPauser;\n require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit\n _redeem(0, true);\n uint256 heldByAccount = balanceOf(msg.sender);\n _approve(msg.sender, _vaultPauserAddress, heldByAccount);\n IVaultPauser(_vaultPauserAddress).pausePosition(\n msg.sender,\n heldByAccount\n );\n }\n}\n" + }, + "contracts/storage/RibbonThetaSTETHVaultStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nabstract contract RibbonThetaSTETHVaultStorageV1 {\n // Logic contract used to price options\n address public optionsPremiumPricer;\n // Logic contract used to select strike prices\n address public strikeSelection;\n // Premium discount on options we are selling (thousandths place: 000 - 999)\n uint256 public premiumDiscount;\n // Current oToken premium\n uint256 public currentOtokenPremium;\n // Last round id at which the strike was manually overridden\n uint16 public lastStrikeOverrideRound;\n // Price last overridden strike set to\n uint256 public overriddenStrikePrice;\n // Auction duration\n uint256 public auctionDuration;\n // Auction id of current option\n uint256 public optionAuctionID;\n}\n\nabstract contract RibbonThetaSTETHVaultStorageV2 {\n // Amount locked for scheduled withdrawals last week;\n uint256 public lastQueuedWithdrawAmount;\n}\n\nabstract contract RibbonThetaSTETHVaultStorageV3 {\n // LiquidityGauge contract for the vault\n address public liquidityGauge;\n}\n\nabstract contract RibbonThetaSTETHVaultStorageV4 {\n // Queued withdraw shares for the current round\n uint256 public currentQueuedWithdrawShares;\n}\n\nabstract contract RibbonThetaSTETHVaultStorageV5 {\n // Vault Pauser Contract for the vault\n address public vaultPauser;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of RibbonThetaVaultStorage\n// e.g. RibbonThetaVaultStorage, so finally it would look like\n// contract RibbonThetaVaultStorage is RibbonThetaVaultStorageV1, RibbonThetaVaultStorageV2\nabstract contract RibbonThetaSTETHVaultStorage is\n RibbonThetaSTETHVaultStorageV1,\n RibbonThetaSTETHVaultStorageV2,\n RibbonThetaSTETHVaultStorageV3,\n RibbonThetaSTETHVaultStorageV4,\n RibbonThetaSTETHVaultStorageV5\n{\n\n}\n" + }, + "contracts/tests/TestVaultLifecycleSTETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {ISTETH, IWSTETH} from \"../interfaces/ISTETH.sol\";\nimport {VaultLifecycleSTETH} from \"../libraries/VaultLifecycleSTETH.sol\";\n\ncontract TestVaultLifecycleSTETH {\n address constant wstETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;\n address constant crvPool = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022;\n\n // To avoid using events to get the output, we just set it so we can read\n // it off the contract\n uint256 public output;\n\n function unwrapYieldToken(uint256 amount, uint256 minETHOut) external {\n uint256 amountETHOut =\n VaultLifecycleSTETH.unwrapYieldToken(\n amount,\n wstETH,\n IWSTETH(wstETH).stETH(),\n crvPool,\n minETHOut\n );\n output = amountETHOut;\n }\n\n function withdrawStEth(uint256 amount) external {\n address steth = IWSTETH(wstETH).stETH();\n uint256 amountETHOut =\n VaultLifecycleSTETH.withdrawStEth(steth, wstETH, amount);\n output = amountETHOut;\n ISTETH(steth).transfer(msg.sender, amountETHOut);\n }\n\n // Enables test to send ETH for testing\n receive() external payable {}\n}\n" + }, + "contracts/utils/SAVAXDepositHelper.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {ISAVAX} from \"../interfaces/ISAVAX.sol\";\nimport {IRibbonVault} from \"../interfaces/IRibbon.sol\";\n\ncontract SAVAXDepositHelper {\n ISAVAX public immutable sAVAX;\n IRibbonVault public immutable sAVAXVault;\n\n constructor(address _sAVAX, address _sAVAXVault) {\n require(_sAVAX != address(0), \"!sAVAX\");\n require(_sAVAXVault != address(0), \"!sAVAXVault\");\n\n sAVAX = ISAVAX(_sAVAX);\n sAVAXVault = IRibbonVault(_sAVAXVault);\n }\n\n function deposit() external payable {\n uint256 sAVAXAmount = sAVAX.submit{value: msg.value}();\n sAVAX.approve(address(sAVAXVault), sAVAXAmount);\n sAVAXVault.depositFor(sAVAXAmount, msg.sender);\n }\n}\n" + }, + "contracts/vendor/CustomSafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure.\n * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n function safeTransfer(IERC20 token, address to, uint256 value) internal {\n require(_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)),\n \"ERC20 transfer failed\");\n }\n\n function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n require(_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)),\n \"ERC20 transferFrom failed\");\n }\n\n function safeApprove(IERC20 token, address spender, uint256 value) internal {\n if (_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value))) {\n return;\n }\n require(_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0))\n && _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)),\n \"ERC20 approve failed\");\n }\n\n function _callOptionalReturn(IERC20 token, bytes memory data) private returns (bool) {\n // solhint-disable-next-line avoid-low-level-calls\n (bool success, bytes memory returndata) = address(token).call(data);\n if (!success) {\n return false;\n }\n\n if (returndata.length >= 32) { // Return data is optional\n return abi.decode(returndata, (bool));\n }\n\n // In a wierd case when return data is 1-31 bytes long - return false.\n return returndata.length == 0;\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/ERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n\n uint256 currentAllowance = _allowances[sender][_msgSender()];\n require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n unchecked {\n _approve(sender, _msgSender(), currentAllowance - amount);\n }\n\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n uint256 currentAllowance = _allowances[_msgSender()][spender];\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n uint256 senderBalance = _balances[sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[sender] = senderBalance - amount;\n }\n _balances[recipient] += amount;\n\n emit Transfer(sender, recipient, amount);\n\n _afterTokenTransfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n _balances[account] += amount;\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n }\n _totalSupply -= amount;\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/utils/PercentStrikeSelection.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {\n IPriceOracle\n} from \"@ribbon-finance/rvol/contracts/interfaces/IPriceOracle.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\nimport {\n IManualVolatilityOracle\n} from \"@ribbon-finance/rvol/contracts/interfaces/IManualVolatilityOracle.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\ncontract PercentStrikeSelection is Ownable {\n using SafeMath for uint256;\n\n /**\n * Immutables\n */\n IOptionsPremiumPricer public immutable optionsPremiumPricer;\n\n // step in absolute terms at which we will increment\n // (ex: 100 * 10 ** assetOracleDecimals means we will move at increments of 100 points)\n uint256 public step;\n\n // multiplier for strike selection\n uint256 public strikeMultiplier;\n\n // multiplier to shift asset prices\n uint256 private immutable assetOracleMultiplier;\n\n // Delta are in 4 decimal places. 1 * 10**4 = 1 delta.\n uint256 private constant DELTA_MULTIPLIER = 10**4;\n\n // ChainLink's USD Price oracles return results in 8 decimal places\n uint256 private constant ORACLE_PRICE_MULTIPLIER = 10**8;\n\n // Strike multiplier has 2 decimal places. For example: 150 = 1.5x spot price\n uint256 private constant STRIKE_MULTIPLIER = 10**2;\n\n event StepSet(uint256 oldStep, uint256 newStep, address indexed owner);\n\n constructor(\n address _optionsPremiumPricer,\n uint256 _strikeMultiplier,\n uint256 _step\n ) {\n require(_optionsPremiumPricer != address(0), \"!_optionsPremiumPricer\");\n require(\n _strikeMultiplier > STRIKE_MULTIPLIER,\n \"Multiplier must be bigger than 1!\"\n );\n require(_step > 0, \"!_step\");\n\n optionsPremiumPricer = IOptionsPremiumPricer(_optionsPremiumPricer);\n\n // ex: delta = 7500 (.75)\n uint256 _assetOracleMultiplier =\n 10 **\n IPriceOracle(\n IOptionsPremiumPricer(_optionsPremiumPricer).priceOracle()\n )\n .decimals();\n\n step = _step;\n\n strikeMultiplier = _strikeMultiplier;\n\n assetOracleMultiplier = _assetOracleMultiplier;\n }\n\n /**\n * @notice Gets the strike price by multiplying the current underlying price\n * with a multiplier\n * @param expiryTimestamp is the unix timestamp of expiration\n * @param isPut is whether option is put or call\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\n * @return newDelta will be set to zero for percent strike selection\n */\n\n function getStrikePrice(uint256 expiryTimestamp, bool isPut)\n external\n view\n returns (uint256 newStrikePrice, uint256 newDelta)\n {\n require(\n expiryTimestamp > block.timestamp,\n \"Expiry must be in the future!\"\n );\n\n // asset price\n uint256 strikePrice =\n optionsPremiumPricer.getUnderlyingPrice().mul(strikeMultiplier).div(\n STRIKE_MULTIPLIER\n );\n\n newStrikePrice = isPut\n ? strikePrice.sub(strikePrice % step)\n : strikePrice.add(step.sub(strikePrice % step));\n\n newDelta = 0;\n }\n\n /**\n * @notice Set the multiplier for setting the strike price\n * @param newStrikeMultiplier is the strike multiplier (decimals = 2)\n */\n function setStrikeMultiplier(uint256 newStrikeMultiplier)\n external\n onlyOwner\n {\n require(\n newStrikeMultiplier > STRIKE_MULTIPLIER,\n \"Multiplier must be bigger than 1!\"\n );\n strikeMultiplier = newStrikeMultiplier;\n }\n\n /**\n * @notice Sets new step value\n * @param newStep is the new step value\n */\n function setStep(uint256 newStep) external onlyOwner {\n require(newStep > 0, \"!newStep\");\n uint256 oldStep = step;\n step = newStep;\n emit StepSet(oldStep, newStep, msg.sender);\n }\n}\n" + }, + "@ribbon-finance/rvol/contracts/interfaces/IPriceOracle.sol": { + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >0.6.0 <0.8.7;\n\ninterface IPriceOracle {\n function decimals() external view returns (uint256 _decimals);\n\n function latestAnswer() external view returns (uint256 price);\n}\n" + }, + "@ribbon-finance/rvol/contracts/interfaces/IManualVolatilityOracle.sol": { + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >0.6.0 <0.8.7;\n\ninterface IManualVolatilityOracle {\n function vol(bytes32 optionId)\n external\n view\n returns (uint256 standardDeviation);\n\n function annualizedVol(bytes32 optionId)\n external\n view\n returns (uint256 annualStdev);\n\n function setAnnualizedVol(\n bytes32[] calldata optionIds,\n uint256[] calldata newAnnualizedVols\n ) external;\n}\n" + }, + "contracts/vaults/RETHVault/RibbonThetaRETHVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport {GnosisAuction} from \"../../libraries/GnosisAuction.sol\";\nimport {\n RibbonThetaVaultStorage\n} from \"../../storage/RibbonThetaVaultStorage.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {VaultLifecycle} from \"../../libraries/VaultLifecycle.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {ILiquidityGauge} from \"../../interfaces/ILiquidityGauge.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\nimport {RibbonVault} from \"./base/RibbonVault.sol\";\n\n/**\n * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe\n * the inheritance chain closely.\n * Any changes/appends in storage variable needs to happen in RibbonThetaVaultStorage.\n * RibbonThetaVault should not inherit from any other contract aside from RibbonVault, RibbonThetaVaultStorage\n */\ncontract RibbonThetaRETHVault is RibbonVault, RibbonThetaVaultStorage {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice oTokenFactory is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event PremiumDiscountSet(\n uint256 premiumDiscount,\n uint256 newPremiumDiscount\n );\n\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event InitiateGnosisAuction(\n address indexed auctioningToken,\n address indexed biddingToken,\n uint256 auctionCounter,\n address indexed manager\n );\n\n /************************************************\n * STRUCTS\n ***********************************************/\n\n /**\n * @notice Initialization parameters for the vault.\n * @param _owner is the owner of the vault with critical permissions\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n * @param _premiumDiscount is the vault's discount applied to the premium\n * @param _auctionDuration is the duration of the gnosis auction\n */\n struct InitParams {\n address _owner;\n address _keeper;\n address _feeRecipient;\n uint256 _managementFee;\n uint256 _performanceFee;\n string _tokenName;\n string _tokenSymbol;\n address _optionsPremiumPricer;\n address _strikeSelection;\n uint32 _premiumDiscount;\n uint256 _auctionDuration;\n }\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions\n */\n constructor(\n address _weth,\n address _usdc,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _gnosisEasyAuction\n )\n RibbonVault(\n _weth,\n _usdc,\n _gammaController,\n _marginPool,\n _gnosisEasyAuction\n )\n {\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n OTOKEN_FACTORY = _oTokenFactory;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n * @param _initParams is the struct with vault initialization parameters\n * @param _vaultParams is the struct with vault general data\n */\n function initialize(\n InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n baseInitialize(\n _initParams._owner,\n _initParams._keeper,\n _initParams._feeRecipient,\n _initParams._managementFee,\n _initParams._performanceFee,\n _initParams._tokenName,\n _initParams._tokenSymbol,\n _vaultParams\n );\n require(\n _initParams._optionsPremiumPricer != address(0),\n \"!_optionsPremiumPricer\"\n );\n require(\n _initParams._strikeSelection != address(0),\n \"!_strikeSelection\"\n );\n require(\n _initParams._premiumDiscount > 0 &&\n _initParams._premiumDiscount <\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"!_premiumDiscount\"\n );\n require(\n _initParams._auctionDuration >= MIN_AUCTION_DURATION,\n \"!_auctionDuration\"\n );\n optionsPremiumPricer = _initParams._optionsPremiumPricer;\n strikeSelection = _initParams._strikeSelection;\n premiumDiscount = _initParams._premiumDiscount;\n auctionDuration = _initParams._auctionDuration;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new discount on premiums for options we are selling\n * @param newPremiumDiscount is the premium discount\n */\n function setPremiumDiscount(uint256 newPremiumDiscount)\n external\n onlyKeeper\n {\n require(\n newPremiumDiscount > 0 &&\n newPremiumDiscount <= 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"Invalid discount\"\n );\n\n emit PremiumDiscountSet(premiumDiscount, newPremiumDiscount);\n\n premiumDiscount = newPremiumDiscount;\n }\n\n /**\n * @notice Sets the new auction duration\n * @param newAuctionDuration is the auction duration\n */\n function setAuctionDuration(uint256 newAuctionDuration) external onlyOwner {\n require(\n newAuctionDuration >= MIN_AUCTION_DURATION,\n \"Invalid auction duration\"\n );\n\n emit AuctionDurationSet(auctionDuration, newAuctionDuration);\n\n auctionDuration = newAuctionDuration;\n }\n\n /**\n * @notice Sets the new strike selection contract\n * @param newStrikeSelection is the address of the new strike selection contract\n */\n function setStrikeSelection(address newStrikeSelection) external onlyOwner {\n require(newStrikeSelection != address(0), \"!newStrikeSelection\");\n strikeSelection = newStrikeSelection;\n }\n\n /**\n * @notice Sets the new options premium pricer contract\n * @param newOptionsPremiumPricer is the address of the new strike selection contract\n */\n function setOptionsPremiumPricer(address newOptionsPremiumPricer)\n external\n onlyOwner\n {\n require(\n newOptionsPremiumPricer != address(0),\n \"!newOptionsPremiumPricer\"\n );\n optionsPremiumPricer = newOptionsPremiumPricer;\n }\n\n /**\n * @notice Optionality to set strike price manually\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice) external onlyOwner {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Sets the new liquidityGauge contract for this vault\n * @param newLiquidityGauge is the address of the new liquidityGauge contract\n */\n function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {\n liquidityGauge = newLiquidityGauge;\n }\n\n /**\n * @notice Sets the new optionsPurchaseQueue contract for this vault\n * @param newOptionsPurchaseQueue is the address of the new optionsPurchaseQueue contract\n */\n function setOptionsPurchaseQueue(address newOptionsPurchaseQueue)\n external\n onlyOwner\n {\n optionsPurchaseQueue = newOptionsPurchaseQueue;\n }\n\n /**\n * @notice Sets oToken Premium\n * @param minPrice is the new oToken Premium in the units of 10**18\n */\n function setMinPrice(uint256 minPrice) external onlyKeeper {\n require(minPrice > 0, \"!minPrice\");\n currentOtokenPremium = minPrice;\n }\n\n /**\n * @notice Sets the new Vault Pauser contract for this vault\n * @param newVaultPauser is the address of the new vaultPauser contract\n */\n function setVaultPauser(address newVaultPauser) external onlyOwner {\n vaultPauser = newVaultPauser;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n transferAsset(msg.sender, amount);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n _initiateWithdraw(numShares);\n currentQueuedWithdrawShares = currentQueuedWithdrawShares.add(\n numShares\n );\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /**\n * @notice Stakes a users vault shares\n * @param numShares is the number of shares to stake\n */\n function stake(uint256 numShares) external nonReentrant {\n address _liquidityGauge = liquidityGauge;\n require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit\n require(numShares > 0);\n uint256 heldByAccount = balanceOf(msg.sender);\n if (heldByAccount < numShares) {\n _redeem(numShares.sub(heldByAccount), false);\n }\n _transfer(msg.sender, address(this), numShares);\n _approve(address(this), _liquidityGauge, numShares);\n ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);\n }\n\n /**\n * @notice Sets the next option the vault will be shorting, and closes the existing short.\n * This allows all the users to withdraw if the next option is malicious.\n */\n function commitAndClose() external nonReentrant {\n address oldOption = optionState.currentOption;\n\n VaultLifecycle.CloseParams memory closeParams =\n VaultLifecycle.CloseParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n currentOption: oldOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n strikeSelection: strikeSelection,\n optionsPremiumPricer: optionsPremiumPricer,\n premiumDiscount: premiumDiscount\n });\n\n (address otokenAddress, uint256 strikePrice, uint256 delta) =\n VaultLifecycle.commitAndClose(closeParams, vaultParams, vaultState);\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n optionState.nextOption = otokenAddress;\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n\n _closeShort(oldOption);\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycle.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares;\n\n (\n address newOption,\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount\n ) =\n _rollToNextOption(\n lastQueuedWithdrawAmount,\n currQueuedWithdrawShares\n );\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(\n currQueuedWithdrawShares\n );\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n currentQueuedWithdrawShares = 0;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n uint256 optionsMintAmount =\n VaultLifecycle.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n VaultLifecycle.allocateOptions(\n optionsPurchaseQueue,\n newOption,\n optionsMintAmount,\n VaultLifecycle.QUEUE_OPTION_ALLOCATION\n );\n\n _startAuction();\n }\n\n /**\n * @notice Initiate the gnosis auction.\n */\n function startAuction() external onlyKeeper nonReentrant {\n _startAuction();\n }\n\n function _startAuction() private {\n GnosisAuction.AuctionDetails memory auctionDetails;\n\n address currentOtoken = optionState.currentOption;\n\n auctionDetails.oTokenAddress = currentOtoken;\n auctionDetails.gnosisEasyAuction = GNOSIS_EASY_AUCTION;\n auctionDetails.asset = vaultParams.asset;\n auctionDetails.assetDecimals = vaultParams.decimals;\n auctionDetails.oTokenPremium = currentOtokenPremium;\n auctionDetails.duration = auctionDuration;\n\n optionAuctionID = VaultLifecycle.startAuction(auctionDetails);\n }\n\n /**\n * @notice Sell the allocated options to the purchase queue post auction settlement\n */\n function sellOptionsToQueue() external onlyKeeper nonReentrant {\n VaultLifecycle.sellOptionsToQueue(\n optionsPurchaseQueue,\n GNOSIS_EASY_AUCTION,\n optionAuctionID\n );\n }\n\n /**\n * @notice Burn the remaining oTokens left over from gnosis auction.\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n uint256 unlockedAssetAmount =\n VaultLifecycle.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n\n vaultState.lockedAmount = uint104(\n uint256(vaultState.lockedAmount).sub(unlockedAssetAmount)\n );\n }\n\n /**\n * @notice Recovery function that returns an ERC20 token to the recipient\n * @param token is the ERC20 token to recover from the vault\n * @param recipient is the recipient of the recovered tokens\n */\n function recoverTokens(address token, address recipient)\n external\n onlyOwner\n {\n require(token != vaultParams.asset, \"Vault asset not recoverable\");\n require(token != address(this), \"Vault share not recoverable\");\n require(recipient != address(this), \"Recipient cannot be vault\");\n\n IERC20(token).safeTransfer(\n recipient,\n IERC20(token).balanceOf(address(this))\n );\n }\n\n /**\n * @notice pause a user's vault position\n */\n function pausePosition() external {\n address _vaultPauserAddress = vaultPauser;\n require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit\n _redeem(0, true);\n uint256 heldByAccount = balanceOf(msg.sender);\n _approve(msg.sender, _vaultPauserAddress, heldByAccount);\n IVaultPauser(_vaultPauserAddress).pausePosition(\n msg.sender,\n heldByAccount\n );\n }\n}\n" + }, + "contracts/storage/RibbonThetaVaultStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nabstract contract RibbonThetaVaultStorageV1 {\n // Logic contract used to price options\n address public optionsPremiumPricer;\n // Logic contract used to select strike prices\n address public strikeSelection;\n // Premium discount on options we are selling (thousandths place: 000 - 999)\n uint256 public premiumDiscount;\n // Current oToken premium\n uint256 public currentOtokenPremium;\n // Last round id at which the strike was manually overridden\n uint16 public lastStrikeOverrideRound;\n // Price last overridden strike set to\n uint256 public overriddenStrikePrice;\n // Auction duration\n uint256 public auctionDuration;\n // Auction id of current option\n uint256 public optionAuctionID;\n}\n\nabstract contract RibbonThetaVaultStorageV2 {\n // Amount locked for scheduled withdrawals last week;\n uint256 public lastQueuedWithdrawAmount;\n}\n\nabstract contract RibbonThetaVaultStorageV3 {\n // DEPRECATED: Auction will be denominated in USDC if true\n bool private _isUsdcAuction;\n // DEPRECATED: Path for swaps\n bytes private _swapPath;\n}\n\nabstract contract RibbonThetaVaultStorageV4 {\n // LiquidityGauge contract for the vault\n address public liquidityGauge;\n}\n\nabstract contract RibbonThetaVaultStorageV5 {\n // OptionsPurchaseQueue contract for selling options\n address public optionsPurchaseQueue;\n}\n\nabstract contract RibbonThetaVaultStorageV6 {\n // Queued withdraw shares for the current round\n uint256 public currentQueuedWithdrawShares;\n}\n\nabstract contract RibbonThetaVaultStorageV7 {\n // Vault Pauser Contract for the vault\n address public vaultPauser;\n}\n\nabstract contract RibbonThetaVaultStorageV8 {\n // DEPRECATED: Executor role for Swap offers\n address private _offerExecutor;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of RibbonThetaVaultStorage\n// e.g. RibbonThetaVaultStorage, so finally it would look like\n// contract RibbonThetaVaultStorage is RibbonThetaVaultStorageV1, RibbonThetaVaultStorageV2\nabstract contract RibbonThetaVaultStorage is\n RibbonThetaVaultStorageV1,\n RibbonThetaVaultStorageV2,\n RibbonThetaVaultStorageV3,\n RibbonThetaVaultStorageV4,\n RibbonThetaVaultStorageV5,\n RibbonThetaVaultStorageV6,\n RibbonThetaVaultStorageV7,\n RibbonThetaVaultStorageV8\n{\n\n}\n" + }, + "contracts/vaults/RETHVault/base/RibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\n\nimport {Vault} from \"../../../libraries/Vault.sol\";\nimport {VaultLifecycle} from \"../../../libraries/VaultLifecycle.sol\";\nimport {ShareMath} from \"../../../libraries/ShareMath.sol\";\nimport {IWETH} from \"../../../interfaces/IWETH.sol\";\nimport {IRETH} from \"../../../interfaces/IRETH.sol\";\nimport {IRETHDepositPool} from \"../../../interfaces/IRETHDepositPool.sol\";\n\ncontract RibbonVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n /// @notice depositETH transactions within 24hr are paused\n uint256 public lastrETHMintCutoff;\n\n // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure.\n uint256[30] private ____gap;\n\n // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE\n // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaVaultStorage\n // or RibbonDeltaVaultStorage instead. Read this documentation to learn more:\n // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice Deprecated: 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n /// @notice 7 day period between each options sale.\n uint256 public constant PERIOD = 7 days;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // GNOSIS_EASY_AUCTION is Gnosis protocol's contract for initiating auctions and placing bids\n // https://github.com/gnosis/ido-contracts/blob/main/contracts/EasyAuction.sol\n address public immutable GNOSIS_EASY_AUCTION;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectVaultFees(\n uint256 performanceFee,\n uint256 vaultFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions\n */\n constructor(\n address _weth,\n address _usdc,\n address _gammaController,\n address _marginPool,\n address _gnosisEasyAuction\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_gnosisEasyAuction != address(0), \"!_gnosisEasyAuction\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n\n WETH = _weth;\n USDC = _usdc;\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n GNOSIS_EASY_AUCTION = _gnosisEasyAuction;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function baseInitialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) internal initializer {\n VaultLifecycle.verifyInitializerParams(\n _owner,\n _keeper,\n _feeRecipient,\n _performanceFee,\n _managementFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_tokenName, _tokenSymbol);\n __Ownable_init();\n transferOwnership(_owner);\n\n keeper = _keeper;\n\n feeRecipient = _feeRecipient;\n performanceFee = _performanceFee;\n managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div(\n WEEKS_PER_YEAR\n );\n vaultParams = _vaultParams;\n\n uint256 assetBalance =\n IERC20(vaultParams.asset).balanceOf(address(this));\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n\n vaultState.round = 1;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n // We are dividing annualized management fee by num weeks in a year\n uint256 tmpManagementFee =\n newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n\n managementFee = tmpManagementFee;\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /**\n * @notice Sets the new rETH mint cutoff\n */\n function updaterETHMintCutoff() external onlyKeeper {\n lastrETHMintCutoff = block.timestamp;\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Deposits ETH into the contract and mint vault shares.\n */\n function depositETH() external payable nonReentrant {\n require(msg.value > 0, \"!value\");\n\n // After minting rETH there is a period of 5760 blocks (~21 hours)\n // where the minting wallet is not able to transfer any of its rETH tokens.\n // To allow rollToNexOption to occur we must pause mints\n require(block.timestamp.sub(lastrETHMintCutoff) >= 1 days, \"!cutoff\");\n\n uint256 rETHAmount = IRETH(vaultParams.asset).getRethValue(msg.value);\n\n _depositFor(rETHAmount, msg.sender);\n\n // Deposit ETH for rETH\n IRETHDepositPool(0x4D05E3d48a938db4b7a9A59A802D5b45011BDe58).deposit{\n value: msg.value\n }();\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender.\n * @param amount is the amount of `asset` to deposit\n */\n function deposit(uint256 amount) external nonReentrant {\n require(amount > 0, \"!amount\");\n\n _depositFor(amount, msg.sender);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.\n * @notice Used for vault -> vault deposits on the user's behalf\n * @param amount is the amount of `asset` to deposit\n * @param creditor is the address that can claim/withdraw deposited amount\n */\n function depositFor(uint256 amount, address creditor)\n external\n nonReentrant\n {\n require(amount > 0, \"!amount\");\n require(creditor != address(0));\n\n _depositFor(amount, creditor);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n */\n function _depositFor(uint256 amount, address creditor) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount = totalBalance().add(amount);\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n\n emit Deposit(creditor, amount, currentRound);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function _initiateWithdraw(uint256 numShares) internal {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n if (\n depositReceipts[msg.sender].amount > 0 ||\n depositReceipts[msg.sender].unredeemedShares > 0\n ) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n bool withdrawalIsSameRound = withdrawal.round == currentRound;\n\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n\n uint256 existingShares = uint256(withdrawal.shares);\n\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return withdrawAmount the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n require(withdrawAmount > 0, \"!withdrawAmount\");\n transferAsset(msg.sender, withdrawAmount);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map.\n * Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k.\n * Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0.\n * @param numRounds is the number of rounds to initialize in the map\n */\n function initRounds(uint256 numRounds) external nonReentrant {\n require(numRounds > 0, \"!numRounds\");\n\n uint256 _round = vaultState.round;\n for (uint256 i = 0; i < numRounds; i++) {\n uint256 index = _round + i;\n require(roundPricePerShare[index] == 0, \"Initialized\"); // AVOID OVERWRITING ACTUAL VALUES\n roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT;\n }\n }\n\n /**\n * @notice Helper function that performs most administrative tasks\n * such as setting next option, minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @param currentQueuedWithdrawShares is the queued withdraw shares for the current round\n * @return newOption is the new option address\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _rollToNextOption(\n uint256 lastQueuedWithdrawAmount,\n uint256 currentQueuedWithdrawShares\n )\n internal\n returns (\n address newOption,\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount\n )\n {\n require(block.timestamp >= optionState.nextOptionReadyAt, \"!ready\");\n\n newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n address recipient = feeRecipient;\n uint256 mintShares;\n uint256 performanceFeeInAsset;\n uint256 totalVaultFee;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n ) = VaultLifecycle.rollover(\n vaultState,\n VaultLifecycle.RolloverParams(\n vaultParams.decimals,\n IERC20(vaultParams.asset).balanceOf(address(this)),\n totalSupply(),\n lastQueuedWithdrawAmount,\n performanceFee,\n managementFee,\n currentQueuedWithdrawShares\n )\n );\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n\n // Finalize the pricePerShare at the end of the round\n uint256 currentRound = vaultState.round;\n roundPricePerShare[currentRound] = newPricePerShare;\n\n emit CollectVaultFees(\n performanceFeeInAsset,\n totalVaultFee,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (totalVaultFee > 0) {\n transferAsset(payable(recipient), totalVaultFee);\n }\n\n return (newOption, lockedBalance, queuedWithdrawAmount);\n }\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) internal {\n address asset = vaultParams.asset;\n if (asset == WETH) {\n IWETH(WETH).withdraw(amount);\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Transfer failed\");\n return;\n }\n IERC20(asset).safeTransfer(recipient, amount);\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n external\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n return\n uint256(vaultState.lockedAmount).add(\n IERC20(vaultParams.asset).balanceOf(address(this))\n );\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n}\n" + }, + "contracts/interfaces/IRETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IRETH is IERC20 {\n function getExchangeRate() external view returns (uint256);\n\n function getETHValue(uint256 rethAmount) external view returns (uint256);\n\n function getRethValue(uint256 ethAmount) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IRETHDepositPool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\ninterface IRETHDepositPool {\n function deposit() external payable;\n}\n" + }, + "contracts/vaults/BaseVaults/RibbonThetaVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport {GnosisAuction} from \"../../libraries/GnosisAuction.sol\";\nimport {\n RibbonThetaVaultStorage\n} from \"../../storage/RibbonThetaVaultStorage.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {VaultLifecycle} from \"../../libraries/VaultLifecycle.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {ILiquidityGauge} from \"../../interfaces/ILiquidityGauge.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\nimport {RibbonVault} from \"./base/RibbonVault.sol\";\n\n/**\n * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe\n * the inheritance chain closely.\n * Any changes/appends in storage variable needs to happen in RibbonThetaVaultStorage.\n * RibbonThetaVault should not inherit from any other contract aside from RibbonVault, RibbonThetaVaultStorage\n */\ncontract RibbonThetaVault is RibbonVault, RibbonThetaVaultStorage {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice oTokenFactory is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event PremiumDiscountSet(\n uint256 premiumDiscount,\n uint256 newPremiumDiscount\n );\n\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event InitiateGnosisAuction(\n address indexed auctioningToken,\n address indexed biddingToken,\n uint256 auctionCounter,\n address indexed manager\n );\n\n /************************************************\n * STRUCTS\n ***********************************************/\n\n /**\n * @notice Initialization parameters for the vault.\n * @param _owner is the owner of the vault with critical permissions\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n * @param _premiumDiscount is the vault's discount applied to the premium\n * @param _auctionDuration is the duration of the gnosis auction\n */\n struct InitParams {\n address _owner;\n address _keeper;\n address _feeRecipient;\n uint256 _managementFee;\n uint256 _performanceFee;\n string _tokenName;\n string _tokenSymbol;\n address _optionsPremiumPricer;\n address _strikeSelection;\n uint32 _premiumDiscount;\n uint256 _auctionDuration;\n }\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions\n */\n constructor(\n address _weth,\n address _usdc,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _gnosisEasyAuction\n )\n RibbonVault(\n _weth,\n _usdc,\n _gammaController,\n _marginPool,\n _gnosisEasyAuction\n )\n {\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n OTOKEN_FACTORY = _oTokenFactory;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n * @param _initParams is the struct with vault initialization parameters\n * @param _vaultParams is the struct with vault general data\n */\n function initialize(\n InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n baseInitialize(\n _initParams._owner,\n _initParams._keeper,\n _initParams._feeRecipient,\n _initParams._managementFee,\n _initParams._performanceFee,\n _initParams._tokenName,\n _initParams._tokenSymbol,\n _vaultParams\n );\n require(\n _initParams._optionsPremiumPricer != address(0),\n \"!_optionsPremiumPricer\"\n );\n require(\n _initParams._strikeSelection != address(0),\n \"!_strikeSelection\"\n );\n require(\n _initParams._premiumDiscount > 0 &&\n _initParams._premiumDiscount <\n 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"!_premiumDiscount\"\n );\n require(\n _initParams._auctionDuration >= MIN_AUCTION_DURATION,\n \"!_auctionDuration\"\n );\n optionsPremiumPricer = _initParams._optionsPremiumPricer;\n strikeSelection = _initParams._strikeSelection;\n premiumDiscount = _initParams._premiumDiscount;\n auctionDuration = _initParams._auctionDuration;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new discount on premiums for options we are selling\n * @param newPremiumDiscount is the premium discount\n */\n function setPremiumDiscount(uint256 newPremiumDiscount)\n external\n onlyKeeper\n {\n require(\n newPremiumDiscount > 0 &&\n newPremiumDiscount <= 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,\n \"Invalid discount\"\n );\n\n emit PremiumDiscountSet(premiumDiscount, newPremiumDiscount);\n\n premiumDiscount = newPremiumDiscount;\n }\n\n /**\n * @notice Sets the new auction duration\n * @param newAuctionDuration is the auction duration\n */\n function setAuctionDuration(uint256 newAuctionDuration) external onlyOwner {\n require(\n newAuctionDuration >= MIN_AUCTION_DURATION,\n \"Invalid auction duration\"\n );\n\n emit AuctionDurationSet(auctionDuration, newAuctionDuration);\n\n auctionDuration = newAuctionDuration;\n }\n\n /**\n * @notice Sets the new strike selection contract\n * @param newStrikeSelection is the address of the new strike selection contract\n */\n function setStrikeSelection(address newStrikeSelection) external onlyOwner {\n require(newStrikeSelection != address(0), \"!newStrikeSelection\");\n strikeSelection = newStrikeSelection;\n }\n\n /**\n * @notice Sets the new options premium pricer contract\n * @param newOptionsPremiumPricer is the address of the new strike selection contract\n */\n function setOptionsPremiumPricer(address newOptionsPremiumPricer)\n external\n onlyOwner\n {\n require(\n newOptionsPremiumPricer != address(0),\n \"!newOptionsPremiumPricer\"\n );\n optionsPremiumPricer = newOptionsPremiumPricer;\n }\n\n /**\n * @notice Optionality to set strike price manually\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice) external onlyOwner {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Sets the new liquidityGauge contract for this vault\n * @param newLiquidityGauge is the address of the new liquidityGauge contract\n */\n function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {\n liquidityGauge = newLiquidityGauge;\n }\n\n /**\n * @notice Sets the new optionsPurchaseQueue contract for this vault\n * @param newOptionsPurchaseQueue is the address of the new optionsPurchaseQueue contract\n */\n function setOptionsPurchaseQueue(address newOptionsPurchaseQueue)\n external\n onlyOwner\n {\n optionsPurchaseQueue = newOptionsPurchaseQueue;\n }\n\n /**\n * @notice Sets oToken Premium\n * @param minPrice is the new oToken Premium in the units of 10**18\n */\n function setMinPrice(uint256 minPrice) external onlyKeeper {\n require(minPrice > 0, \"!minPrice\");\n currentOtokenPremium = minPrice;\n }\n\n /**\n * @notice Sets the new Vault Pauser contract for this vault\n * @param newVaultPauser is the address of the new vaultPauser contract\n */\n function setVaultPauser(address newVaultPauser) external onlyOwner {\n vaultPauser = newVaultPauser;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n transferAsset(msg.sender, amount);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n _initiateWithdraw(numShares);\n currentQueuedWithdrawShares = currentQueuedWithdrawShares.add(\n numShares\n );\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /**\n * @notice Stakes a users vault shares\n * @param numShares is the number of shares to stake\n */\n function stake(uint256 numShares) external nonReentrant {\n address _liquidityGauge = liquidityGauge;\n require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit\n require(numShares > 0);\n uint256 heldByAccount = balanceOf(msg.sender);\n if (heldByAccount < numShares) {\n _redeem(numShares.sub(heldByAccount), false);\n }\n _transfer(msg.sender, address(this), numShares);\n _approve(address(this), _liquidityGauge, numShares);\n ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);\n }\n\n /**\n * @notice Sets the next option the vault will be shorting, and closes the existing short.\n * This allows all the users to withdraw if the next option is malicious.\n */\n function commitAndClose() external nonReentrant {\n address oldOption = optionState.currentOption;\n\n VaultLifecycle.CloseParams memory closeParams =\n VaultLifecycle.CloseParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n currentOption: oldOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n strikeSelection: strikeSelection,\n optionsPremiumPricer: optionsPremiumPricer,\n premiumDiscount: premiumDiscount\n });\n\n (address otokenAddress, uint256 strikePrice, uint256 delta) =\n VaultLifecycle.commitAndClose(closeParams, vaultParams, vaultState);\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n optionState.nextOption = otokenAddress;\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n\n _closeShort(oldOption);\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycle.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares;\n\n (\n address newOption,\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount\n ) =\n _rollToNextOption(\n lastQueuedWithdrawAmount,\n currQueuedWithdrawShares\n );\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(\n currQueuedWithdrawShares\n );\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n currentQueuedWithdrawShares = 0;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n uint256 optionsMintAmount =\n VaultLifecycle.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n VaultLifecycle.allocateOptions(\n optionsPurchaseQueue,\n newOption,\n optionsMintAmount,\n VaultLifecycle.QUEUE_OPTION_ALLOCATION\n );\n\n _startAuction();\n }\n\n /**\n * @notice Initiate the gnosis auction.\n */\n function startAuction() external onlyKeeper nonReentrant {\n _startAuction();\n }\n\n function _startAuction() private {\n GnosisAuction.AuctionDetails memory auctionDetails;\n\n address currentOtoken = optionState.currentOption;\n\n auctionDetails.oTokenAddress = currentOtoken;\n auctionDetails.gnosisEasyAuction = GNOSIS_EASY_AUCTION;\n auctionDetails.asset = vaultParams.asset;\n auctionDetails.assetDecimals = vaultParams.decimals;\n auctionDetails.oTokenPremium = currentOtokenPremium;\n auctionDetails.duration = auctionDuration;\n\n optionAuctionID = VaultLifecycle.startAuction(auctionDetails);\n }\n\n /**\n * @notice Sell the allocated options to the purchase queue post auction settlement\n */\n function sellOptionsToQueue() external onlyKeeper nonReentrant {\n VaultLifecycle.sellOptionsToQueue(\n optionsPurchaseQueue,\n GNOSIS_EASY_AUCTION,\n optionAuctionID\n );\n }\n\n /**\n * @notice Burn the remaining oTokens left over from gnosis auction.\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n uint256 unlockedAssetAmount =\n VaultLifecycle.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n\n vaultState.lockedAmount = uint104(\n uint256(vaultState.lockedAmount).sub(unlockedAssetAmount)\n );\n }\n\n /**\n * @notice Recovery function that returns an ERC20 token to the recipient\n * @param token is the ERC20 token to recover from the vault\n * @param recipient is the recipient of the recovered tokens\n */\n function recoverTokens(address token, address recipient)\n external\n onlyOwner\n {\n require(token != vaultParams.asset, \"Vault asset not recoverable\");\n require(token != address(this), \"Vault share not recoverable\");\n require(recipient != address(this), \"Recipient cannot be vault\");\n\n IERC20(token).safeTransfer(\n recipient,\n IERC20(token).balanceOf(address(this))\n );\n }\n\n /**\n * @notice pause a user's vault position\n */\n function pausePosition() external {\n address _vaultPauserAddress = vaultPauser;\n require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit\n _redeem(0, true);\n uint256 heldByAccount = balanceOf(msg.sender);\n _approve(msg.sender, _vaultPauserAddress, heldByAccount);\n IVaultPauser(_vaultPauserAddress).pausePosition(\n msg.sender,\n heldByAccount\n );\n }\n}\n" + }, + "contracts/vaults/BaseVaults/base/RibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\n\nimport {Vault} from \"../../../libraries/Vault.sol\";\nimport {VaultLifecycle} from \"../../../libraries/VaultLifecycle.sol\";\nimport {ShareMath} from \"../../../libraries/ShareMath.sol\";\nimport {IWETH} from \"../../../interfaces/IWETH.sol\";\n\ncontract RibbonVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure.\n uint256[30] private ____gap;\n\n // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE\n // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaVaultStorage\n // or RibbonDeltaVaultStorage instead. Read this documentation to learn more:\n // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice Deprecated: 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n /// @notice 7 day period between each options sale.\n uint256 public constant PERIOD = 7 days;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // GNOSIS_EASY_AUCTION is Gnosis protocol's contract for initiating auctions and placing bids\n // https://github.com/gnosis/ido-contracts/blob/main/contracts/EasyAuction.sol\n address public immutable GNOSIS_EASY_AUCTION;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectVaultFees(\n uint256 performanceFee,\n uint256 vaultFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions\n */\n constructor(\n address _weth,\n address _usdc,\n address _gammaController,\n address _marginPool,\n address _gnosisEasyAuction\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_gnosisEasyAuction != address(0), \"!_gnosisEasyAuction\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n\n WETH = _weth;\n USDC = _usdc;\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n GNOSIS_EASY_AUCTION = _gnosisEasyAuction;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function baseInitialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) internal initializer {\n VaultLifecycle.verifyInitializerParams(\n _owner,\n _keeper,\n _feeRecipient,\n _performanceFee,\n _managementFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_tokenName, _tokenSymbol);\n __Ownable_init();\n transferOwnership(_owner);\n\n keeper = _keeper;\n\n feeRecipient = _feeRecipient;\n performanceFee = _performanceFee;\n managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div(\n WEEKS_PER_YEAR\n );\n vaultParams = _vaultParams;\n\n uint256 assetBalance =\n IERC20(vaultParams.asset).balanceOf(address(this));\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n\n vaultState.round = 1;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n // We are dividing annualized management fee by num weeks in a year\n uint256 tmpManagementFee =\n newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n\n managementFee = tmpManagementFee;\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Deposits ETH into the contract and mint vault shares. Reverts if the asset is not WETH.\n */\n function depositETH() external payable nonReentrant {\n require(vaultParams.asset == WETH, \"!WETH\");\n require(msg.value > 0, \"!value\");\n\n _depositFor(msg.value, msg.sender);\n\n IWETH(WETH).deposit{value: msg.value}();\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender.\n * @param amount is the amount of `asset` to deposit\n */\n function deposit(uint256 amount) external nonReentrant {\n require(amount > 0, \"!amount\");\n\n _depositFor(amount, msg.sender);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.\n * @notice Used for vault -> vault deposits on the user's behalf\n * @param amount is the amount of `asset` to deposit\n * @param creditor is the address that can claim/withdraw deposited amount\n */\n function depositFor(uint256 amount, address creditor)\n external\n nonReentrant\n {\n require(amount > 0, \"!amount\");\n require(creditor != address(0));\n\n _depositFor(amount, creditor);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n */\n function _depositFor(uint256 amount, address creditor) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount = totalBalance().add(amount);\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n\n emit Deposit(creditor, amount, currentRound);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function _initiateWithdraw(uint256 numShares) internal {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n if (\n depositReceipts[msg.sender].amount > 0 ||\n depositReceipts[msg.sender].unredeemedShares > 0\n ) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n bool withdrawalIsSameRound = withdrawal.round == currentRound;\n\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n\n uint256 existingShares = uint256(withdrawal.shares);\n\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return withdrawAmount the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n require(withdrawAmount > 0, \"!withdrawAmount\");\n transferAsset(msg.sender, withdrawAmount);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map.\n * Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k.\n * Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0.\n * @param numRounds is the number of rounds to initialize in the map\n */\n function initRounds(uint256 numRounds) external nonReentrant {\n require(numRounds > 0, \"!numRounds\");\n\n uint256 _round = vaultState.round;\n for (uint256 i = 0; i < numRounds; i++) {\n uint256 index = _round + i;\n require(roundPricePerShare[index] == 0, \"Initialized\"); // AVOID OVERWRITING ACTUAL VALUES\n roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT;\n }\n }\n\n /**\n * @notice Helper function that performs most administrative tasks\n * such as setting next option, minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @param currentQueuedWithdrawShares is the queued withdraw shares for the current round\n * @return newOption is the new option address\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _rollToNextOption(\n uint256 lastQueuedWithdrawAmount,\n uint256 currentQueuedWithdrawShares\n )\n internal\n returns (\n address newOption,\n uint256 lockedBalance,\n uint256 queuedWithdrawAmount\n )\n {\n require(block.timestamp >= optionState.nextOptionReadyAt, \"!ready\");\n\n newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n address recipient = feeRecipient;\n uint256 mintShares;\n uint256 performanceFeeInAsset;\n uint256 totalVaultFee;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n ) = VaultLifecycle.rollover(\n vaultState,\n VaultLifecycle.RolloverParams(\n vaultParams.decimals,\n IERC20(vaultParams.asset).balanceOf(address(this)),\n totalSupply(),\n lastQueuedWithdrawAmount,\n performanceFee,\n managementFee,\n currentQueuedWithdrawShares\n )\n );\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n\n // Finalize the pricePerShare at the end of the round\n uint256 currentRound = vaultState.round;\n roundPricePerShare[currentRound] = newPricePerShare;\n\n emit CollectVaultFees(\n performanceFeeInAsset,\n totalVaultFee,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (totalVaultFee > 0) {\n transferAsset(payable(recipient), totalVaultFee);\n }\n\n return (newOption, lockedBalance, queuedWithdrawAmount);\n }\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) internal {\n address asset = vaultParams.asset;\n if (asset == WETH) {\n IWETH(WETH).withdraw(amount);\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Transfer failed\");\n return;\n }\n IERC20(asset).safeTransfer(recipient, amount);\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n external\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n return\n uint256(vaultState.lockedAmount).add(\n IERC20(vaultParams.asset).balanceOf(address(this))\n );\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n}\n" + }, + "contracts/vaults/BaseVaultWithSwap/base/RibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n ERC20Upgradeable\n} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\n\nimport {Vault} from \"../../../libraries/Vault.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../../libraries/VaultLifecycleWithSwap.sol\";\nimport {ShareMath} from \"../../../libraries/ShareMath.sol\";\nimport {IWETH} from \"../../../interfaces/IWETH.sol\";\n\ncontract RibbonVault is\n ReentrancyGuardUpgradeable,\n OwnableUpgradeable,\n ERC20Upgradeable\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * NON UPGRADEABLE STORAGE\n ***********************************************/\n\n /// @notice Stores the user's pending deposit for the round\n mapping(address => Vault.DepositReceipt) public depositReceipts;\n\n /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored\n /// This is used to determine the number of shares to be returned\n /// to a user with their DepositReceipt.depositAmount\n mapping(uint256 => uint256) public roundPricePerShare;\n\n /// @notice Stores pending user withdrawals\n mapping(address => Vault.Withdrawal) public withdrawals;\n\n /// @notice Vault's parameters like cap, decimals\n Vault.VaultParams public vaultParams;\n\n /// @notice Vault's lifecycle state like round and locked amounts\n Vault.VaultState public vaultState;\n\n /// @notice Vault's state of the options sold and the timelocked option\n Vault.OptionState public optionState;\n\n /// @notice Fee recipient for the performance and management fees\n address public feeRecipient;\n\n /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens\n // no access to critical vault changes\n address public keeper;\n\n /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.\n uint256 public performanceFee;\n\n /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.\n uint256 public managementFee;\n\n // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure.\n uint256[30] private ____gap;\n\n // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE\n // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaVaultStorage\n // or RibbonDeltaVaultStorage instead. Read this documentation to learn more:\n // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n address public immutable WETH;\n\n /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\n address public immutable USDC;\n\n /// @notice Deprecated: 15 minute timelock between commitAndClose and rollToNexOption.\n uint256 public constant DELAY = 0;\n\n /// @notice 7 day period between each options sale.\n uint256 public constant PERIOD = 7 days;\n\n // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857\n // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)\n uint256 private constant WEEKS_PER_YEAR = 52142857;\n\n // GAMMA_CONTROLLER is the top-level contract in Gamma protocol\n // which allows users to perform multiple actions on their vaults\n // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol\n address public immutable GAMMA_CONTROLLER;\n\n // MARGIN_POOL is Gamma protocol's collateral pool.\n // Needed to approve collateral.safeTransferFrom for minting otokens.\n // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol\n address public immutable MARGIN_POOL;\n\n // SWAP_CONTRACT is a contract for settling bids via signed messages\n // https://github.com/ribbon-finance/ribbon-v2/blob/master/contracts/utils/Swap.sol\n address public immutable SWAP_CONTRACT;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event Deposit(address indexed account, uint256 amount, uint256 round);\n\n event InitiateWithdraw(\n address indexed account,\n uint256 shares,\n uint256 round\n );\n\n event Redeem(address indexed account, uint256 share, uint256 round);\n\n event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);\n\n event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);\n\n event CapSet(uint256 oldCap, uint256 newCap);\n\n event Withdraw(address indexed account, uint256 amount, uint256 shares);\n\n event CollectVaultFees(\n uint256 performanceFee,\n uint256 vaultFee,\n uint256 round,\n address indexed feeRecipient\n );\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _gammaController,\n address _marginPool,\n address _swapContract\n ) {\n require(_weth != address(0), \"!_weth\");\n require(_usdc != address(0), \"!_usdc\");\n require(_swapContract != address(0), \"!_swapContract\");\n require(_gammaController != address(0), \"!_gammaController\");\n require(_marginPool != address(0), \"!_marginPool\");\n\n WETH = _weth;\n USDC = _usdc;\n GAMMA_CONTROLLER = _gammaController;\n MARGIN_POOL = _marginPool;\n SWAP_CONTRACT = _swapContract;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n */\n function baseInitialize(\n address _owner,\n address _keeper,\n address _feeRecipient,\n uint256 _managementFee,\n uint256 _performanceFee,\n string memory _tokenName,\n string memory _tokenSymbol,\n Vault.VaultParams calldata _vaultParams\n ) internal initializer {\n VaultLifecycleWithSwap.verifyInitializerParams(\n _owner,\n _keeper,\n _feeRecipient,\n _performanceFee,\n _managementFee,\n _tokenName,\n _tokenSymbol,\n _vaultParams\n );\n\n __ReentrancyGuard_init();\n __ERC20_init(_tokenName, _tokenSymbol);\n __Ownable_init();\n transferOwnership(_owner);\n\n keeper = _keeper;\n\n feeRecipient = _feeRecipient;\n performanceFee = _performanceFee;\n managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div(\n WEEKS_PER_YEAR\n );\n vaultParams = _vaultParams;\n\n uint256 assetBalance =\n IERC20(vaultParams.asset).balanceOf(address(this));\n ShareMath.assertUint104(assetBalance);\n vaultState.lastLockedAmount = uint104(assetBalance);\n\n vaultState.round = 1;\n }\n\n /**\n * @dev Throws if called by any account other than the keeper.\n */\n modifier onlyKeeper() {\n require(msg.sender == keeper, \"!keeper\");\n _;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new keeper\n * @param newKeeper is the address of the new keeper\n */\n function setNewKeeper(address newKeeper) external onlyOwner {\n require(newKeeper != address(0), \"!newKeeper\");\n keeper = newKeeper;\n }\n\n /**\n * @notice Sets the new fee recipient\n * @param newFeeRecipient is the address of the new fee recipient\n */\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n require(newFeeRecipient != address(0), \"!newFeeRecipient\");\n require(newFeeRecipient != feeRecipient, \"Must be new feeRecipient\");\n feeRecipient = newFeeRecipient;\n }\n\n /**\n * @notice Sets the management fee for the vault\n * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%\n */\n function setManagementFee(uint256 newManagementFee) external onlyOwner {\n require(\n newManagementFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid management fee\"\n );\n\n // We are dividing annualized management fee by num weeks in a year\n uint256 tmpManagementFee =\n newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR);\n\n emit ManagementFeeSet(managementFee, newManagementFee);\n\n managementFee = tmpManagementFee;\n }\n\n /**\n * @notice Sets the performance fee for the vault\n * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%\n */\n function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {\n require(\n newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,\n \"Invalid performance fee\"\n );\n\n emit PerformanceFeeSet(performanceFee, newPerformanceFee);\n\n performanceFee = newPerformanceFee;\n }\n\n /**\n * @notice Sets a new cap for deposits\n * @param newCap is the new cap for deposits\n */\n function setCap(uint256 newCap) external onlyOwner {\n require(newCap > 0, \"!newCap\");\n ShareMath.assertUint104(newCap);\n emit CapSet(vaultParams.cap, newCap);\n vaultParams.cap = uint104(newCap);\n }\n\n /************************************************\n * DEPOSIT & WITHDRAWALS\n ***********************************************/\n\n /**\n * @notice Deposits ETH into the contract and mint vault shares. Reverts if the asset is not WETH.\n */\n function depositETH() external payable nonReentrant {\n require(vaultParams.asset == WETH, \"!WETH\");\n require(msg.value > 0, \"!value\");\n\n _depositFor(msg.value, msg.sender);\n\n IWETH(WETH).deposit{value: msg.value}();\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender.\n * @param amount is the amount of `asset` to deposit\n */\n function deposit(uint256 amount) external nonReentrant {\n require(amount > 0, \"!amount\");\n\n _depositFor(amount, msg.sender);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.\n * @notice Used for vault -> vault deposits on the user's behalf\n * @param amount is the amount of `asset` to deposit\n * @param creditor is the address that can claim/withdraw deposited amount\n */\n function depositFor(uint256 amount, address creditor)\n external\n nonReentrant\n {\n require(amount > 0, \"!amount\");\n require(creditor != address(0));\n\n _depositFor(amount, creditor);\n\n // An approve() by the msg.sender is required beforehand\n IERC20(vaultParams.asset).safeTransferFrom(\n msg.sender,\n address(this),\n amount\n );\n }\n\n /**\n * @notice Mints the vault shares to the creditor\n * @param amount is the amount of `asset` deposited\n * @param creditor is the address to receieve the deposit\n */\n function _depositFor(uint256 amount, address creditor) private {\n uint256 currentRound = vaultState.round;\n uint256 totalWithDepositedAmount = totalBalance().add(amount);\n\n require(totalWithDepositedAmount <= vaultParams.cap, \"Exceed cap\");\n require(\n totalWithDepositedAmount >= vaultParams.minimumSupply,\n \"Insufficient balance\"\n );\n\n emit Deposit(creditor, amount, currentRound);\n\n Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];\n\n // If we have an unprocessed pending deposit from the previous rounds, we have to process it.\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n uint256 depositAmount = amount;\n\n // If we have a pending deposit in the current round, we add on to the pending deposit\n if (currentRound == depositReceipt.round) {\n uint256 newAmount = uint256(depositReceipt.amount).add(amount);\n depositAmount = newAmount;\n }\n\n ShareMath.assertUint104(depositAmount);\n\n depositReceipts[creditor] = Vault.DepositReceipt({\n round: uint16(currentRound),\n amount: uint104(depositAmount),\n unredeemedShares: uint128(unredeemedShares)\n });\n\n uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);\n ShareMath.assertUint128(newTotalPending);\n\n vaultState.totalPending = uint128(newTotalPending);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function _initiateWithdraw(uint256 numShares) internal {\n require(numShares > 0, \"!numShares\");\n\n // We do a max redeem before initiating a withdrawal\n // But we check if they must first have unredeemed shares\n if (\n depositReceipts[msg.sender].amount > 0 ||\n depositReceipts[msg.sender].unredeemedShares > 0\n ) {\n _redeem(0, true);\n }\n\n // This caches the `round` variable used in shareBalances\n uint256 currentRound = vaultState.round;\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n bool withdrawalIsSameRound = withdrawal.round == currentRound;\n\n emit InitiateWithdraw(msg.sender, numShares, currentRound);\n\n uint256 existingShares = uint256(withdrawal.shares);\n\n uint256 withdrawalShares;\n if (withdrawalIsSameRound) {\n withdrawalShares = existingShares.add(numShares);\n } else {\n require(existingShares == 0, \"Existing withdraw\");\n withdrawalShares = numShares;\n withdrawals[msg.sender].round = uint16(currentRound);\n }\n\n ShareMath.assertUint128(withdrawalShares);\n withdrawals[msg.sender].shares = uint128(withdrawalShares);\n\n _transfer(msg.sender, address(this), numShares);\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n * @return withdrawAmount the current withdrawal amount\n */\n function _completeWithdraw() internal returns (uint256) {\n Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];\n\n uint256 withdrawalShares = withdrawal.shares;\n uint256 withdrawalRound = withdrawal.round;\n\n // This checks if there is a withdrawal\n require(withdrawalShares > 0, \"Not initiated\");\n\n require(withdrawalRound < vaultState.round, \"Round not closed\");\n\n // We leave the round number as non-zero to save on gas for subsequent writes\n withdrawals[msg.sender].shares = 0;\n vaultState.queuedWithdrawShares = uint128(\n uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)\n );\n\n uint256 withdrawAmount =\n ShareMath.sharesToAsset(\n withdrawalShares,\n roundPricePerShare[withdrawalRound],\n vaultParams.decimals\n );\n\n emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);\n\n _burn(address(this), withdrawalShares);\n\n require(withdrawAmount > 0, \"!withdrawAmount\");\n transferAsset(msg.sender, withdrawAmount);\n\n return withdrawAmount;\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem\n */\n function redeem(uint256 numShares) external nonReentrant {\n require(numShares > 0, \"!numShares\");\n _redeem(numShares, false);\n }\n\n /**\n * @notice Redeems the entire unredeemedShares balance that is owed to the account\n */\n function maxRedeem() external nonReentrant {\n _redeem(0, true);\n }\n\n /**\n * @notice Redeems shares that are owed to the account\n * @param numShares is the number of shares to redeem, could be 0 when isMax=true\n * @param isMax is flag for when callers do a max redemption\n */\n function _redeem(uint256 numShares, bool isMax) internal {\n Vault.DepositReceipt memory depositReceipt =\n depositReceipts[msg.sender];\n\n // This handles the null case when depositReceipt.round = 0\n // Because we start with round = 1 at `initialize`\n uint256 currentRound = vaultState.round;\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n currentRound,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n numShares = isMax ? unredeemedShares : numShares;\n if (numShares == 0) {\n return;\n }\n require(numShares <= unredeemedShares, \"Exceeds available\");\n\n // If we have a depositReceipt on the same round, BUT we have some unredeemed shares\n // we debit from the unredeemedShares, but leave the amount field intact\n // If the round has past, with no new deposits, we just zero it out for new deposits.\n if (depositReceipt.round < currentRound) {\n depositReceipts[msg.sender].amount = 0;\n }\n\n ShareMath.assertUint128(numShares);\n depositReceipts[msg.sender].unredeemedShares = uint128(\n unredeemedShares.sub(numShares)\n );\n\n emit Redeem(msg.sender, numShares, depositReceipt.round);\n\n _transfer(address(this), msg.sender, numShares);\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map.\n * Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k.\n * Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0.\n * @param numRounds is the number of rounds to initialize in the map\n */\n function initRounds(uint256 numRounds) external nonReentrant {\n require(numRounds > 0, \"!numRounds\");\n\n uint256 _round = vaultState.round;\n for (uint256 i = 0; i < numRounds; i++) {\n uint256 index = _round + i;\n require(roundPricePerShare[index] == 0, \"Initialized\"); // AVOID OVERWRITING ACTUAL VALUES\n roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT;\n }\n }\n\n /**\n * @notice Helper function that performs most administrative tasks\n * such as minting new shares, getting vault fees, etc.\n * @param lastQueuedWithdrawAmount is old queued withdraw amount\n * @param currentQueuedWithdrawShares is the queued withdraw shares for the current round\n * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size\n * @return queuedWithdrawAmount is the new queued withdraw amount for this round\n */\n function _closeRound(\n uint256 lastQueuedWithdrawAmount,\n uint256 currentQueuedWithdrawShares\n ) internal returns (uint256 lockedBalance, uint256 queuedWithdrawAmount) {\n address recipient = feeRecipient;\n uint256 mintShares;\n uint256 performanceFeeInAsset;\n uint256 totalVaultFee;\n {\n uint256 newPricePerShare;\n (\n lockedBalance,\n queuedWithdrawAmount,\n newPricePerShare,\n mintShares,\n performanceFeeInAsset,\n totalVaultFee\n ) = VaultLifecycleWithSwap.closeRound(\n vaultState,\n VaultLifecycleWithSwap.CloseParams(\n vaultParams.decimals,\n IERC20(vaultParams.asset).balanceOf(address(this)),\n totalSupply(),\n lastQueuedWithdrawAmount,\n performanceFee,\n managementFee,\n currentQueuedWithdrawShares\n )\n );\n\n // Finalize the pricePerShare at the end of the round\n uint256 currentRound = vaultState.round;\n roundPricePerShare[currentRound] = newPricePerShare;\n\n emit CollectVaultFees(\n performanceFeeInAsset,\n totalVaultFee,\n currentRound,\n recipient\n );\n\n vaultState.totalPending = 0;\n vaultState.round = uint16(currentRound + 1);\n }\n\n _mint(address(this), mintShares);\n\n if (totalVaultFee > 0) {\n transferAsset(payable(recipient), totalVaultFee);\n }\n\n return (lockedBalance, queuedWithdrawAmount);\n }\n\n /**\n * @notice Helper function to make either an ETH transfer or ERC20 transfer\n * @param recipient is the receiving address\n * @param amount is the transfer amount\n */\n function transferAsset(address recipient, uint256 amount) internal {\n address asset = vaultParams.asset;\n if (asset == WETH) {\n IWETH(WETH).withdraw(amount);\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Transfer failed\");\n return;\n }\n IERC20(asset).safeTransfer(recipient, amount);\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Returns the asset balance held on the vault for the account\n * @param account is the address to lookup balance for\n * @return the amount of `asset` custodied by the vault for the user\n */\n function accountVaultBalance(address account)\n external\n view\n returns (uint256)\n {\n uint256 _decimals = vaultParams.decimals;\n uint256 assetPerShare =\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n _decimals\n );\n return\n ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);\n }\n\n /**\n * @notice Getter for returning the account's share balance including unredeemed shares\n * @param account is the account to lookup share balance for\n * @return the share balance\n */\n function shares(address account) public view returns (uint256) {\n (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);\n return heldByAccount.add(heldByVault);\n }\n\n /**\n * @notice Getter for returning the account's share balance split between account and vault holdings\n * @param account is the account to lookup share balance for\n * @return heldByAccount is the shares held by account\n * @return heldByVault is the shares held on the vault (unredeemedShares)\n */\n function shareBalances(address account)\n public\n view\n returns (uint256 heldByAccount, uint256 heldByVault)\n {\n Vault.DepositReceipt memory depositReceipt = depositReceipts[account];\n\n if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {\n return (balanceOf(account), 0);\n }\n\n uint256 unredeemedShares =\n depositReceipt.getSharesFromReceipt(\n vaultState.round,\n roundPricePerShare[depositReceipt.round],\n vaultParams.decimals\n );\n\n return (balanceOf(account), unredeemedShares);\n }\n\n /**\n * @notice The price of a unit of share denominated in the `asset`\n */\n function pricePerShare() external view returns (uint256) {\n return\n ShareMath.pricePerShare(\n totalSupply(),\n totalBalance(),\n vaultState.totalPending,\n vaultParams.decimals\n );\n }\n\n /**\n * @notice Returns the vault's total balance, including the amounts locked into a short position\n * @return total balance of the vault, including the amounts locked in third party protocols\n */\n function totalBalance() public view returns (uint256) {\n // After calling closeRound, current option is set to none\n // We also commit the lockedAmount but do not deposit into Opyn\n // which results in double counting of asset balance and lockedAmount\n\n return\n optionState.currentOption != address(0)\n ? uint256(vaultState.lockedAmount).add(\n IERC20(vaultParams.asset).balanceOf(address(this))\n )\n : IERC20(vaultParams.asset).balanceOf(address(this));\n }\n\n /**\n * @notice Returns the token decimals\n */\n function decimals() public view override returns (uint8) {\n return vaultParams.decimals;\n }\n\n function cap() external view returns (uint256) {\n return vaultParams.cap;\n }\n\n function nextOptionReadyAt() external view returns (uint256) {\n return optionState.nextOptionReadyAt;\n }\n\n function currentOption() external view returns (address) {\n return optionState.currentOption;\n }\n\n function nextOption() external view returns (address) {\n return optionState.nextOption;\n }\n\n function totalPending() external view returns (uint256) {\n return vaultState.totalPending;\n }\n}\n" + }, + "contracts/vaults/BaseVaultWithSwap/RibbonThetaVaultWithSwap.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport {ISwap} from \"../../interfaces/ISwap.sol\";\nimport {\n RibbonThetaVaultStorage\n} from \"../../storage/RibbonThetaVaultStorage.sol\";\nimport {Vault} from \"../../libraries/Vault.sol\";\nimport {\n VaultLifecycleWithSwap\n} from \"../../libraries/VaultLifecycleWithSwap.sol\";\nimport {ShareMath} from \"../../libraries/ShareMath.sol\";\nimport {ILiquidityGauge} from \"../../interfaces/ILiquidityGauge.sol\";\nimport {RibbonVault} from \"./base/RibbonVault.sol\";\nimport {IVaultPauser} from \"../../interfaces/IVaultPauser.sol\";\n\n/**\n * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe\n * the inheritance chain closely.\n * Any changes/appends in storage variable needs to happen in RibbonThetaVaultStorage.\n * RibbonThetaVault should not inherit from any other contract aside from RibbonVault, RibbonThetaVaultStorage\n */\ncontract RibbonThetaVaultWithSwap is RibbonVault, RibbonThetaVaultStorage {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using ShareMath for Vault.DepositReceipt;\n\n /************************************************\n * IMMUTABLES & CONSTANTS\n ***********************************************/\n\n /// @notice oTokenFactory is the factory contract used to spawn otokens. Used to lookup otokens.\n address public immutable OTOKEN_FACTORY;\n\n // The minimum duration for an option auction.\n uint256 private constant MIN_AUCTION_DURATION = 5 minutes;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n event OpenShort(\n address indexed options,\n uint256 depositAmount,\n address indexed manager\n );\n\n event CloseShort(\n address indexed options,\n uint256 withdrawAmount,\n address indexed manager\n );\n\n event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);\n\n event AuctionDurationSet(\n uint256 auctionDuration,\n uint256 newAuctionDuration\n );\n\n event InstantWithdraw(\n address indexed account,\n uint256 amount,\n uint256 round\n );\n\n event NewOffer(\n uint256 swapId,\n address seller,\n address oToken,\n address biddingToken,\n uint256 minPrice,\n uint256 minBidSize,\n uint256 totalSize\n );\n\n /************************************************\n * STRUCTS\n ***********************************************/\n\n /**\n * @notice Initialization parameters for the vault.\n * @param _owner is the owner of the vault with critical permissions\n * @param _feeRecipient is the address to recieve vault performance and management fees\n * @param _managementFee is the management fee pct.\n * @param _performanceFee is the perfomance fee pct.\n * @param _tokenName is the name of the token\n * @param _tokenSymbol is the symbol of the token\n * @param _optionsPremiumPricer is the address of the contract with the\n black-scholes premium calculation logic\n * @param _strikeSelection is the address of the contract with strike selection logic\n */\n struct InitParams {\n address _owner;\n address _keeper;\n address _feeRecipient;\n uint256 _managementFee;\n uint256 _performanceFee;\n string _tokenName;\n string _tokenSymbol;\n address _optionsPremiumPricer;\n address _strikeSelection;\n }\n\n /************************************************\n * CONSTRUCTOR & INITIALIZATION\n ***********************************************/\n\n /**\n * @notice Initializes the contract with immutable variables\n * @param _weth is the Wrapped Ether contract\n * @param _usdc is the USDC contract\n * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)\n * @param _gammaController is the contract address for opyn actions\n * @param _marginPool is the contract address for providing collateral to opyn\n * @param _swapContract is the contract address that facilitates bids settlement\n */\n constructor(\n address _weth,\n address _usdc,\n address _oTokenFactory,\n address _gammaController,\n address _marginPool,\n address _swapContract\n ) RibbonVault(_weth, _usdc, _gammaController, _marginPool, _swapContract) {\n require(_oTokenFactory != address(0), \"!_oTokenFactory\");\n OTOKEN_FACTORY = _oTokenFactory;\n }\n\n /**\n * @notice Initializes the OptionVault contract with storage variables.\n * @param _initParams is the struct with vault initialization parameters\n * @param _vaultParams is the struct with vault general data\n */\n function initialize(\n InitParams calldata _initParams,\n Vault.VaultParams calldata _vaultParams\n ) external initializer {\n baseInitialize(\n _initParams._owner,\n _initParams._keeper,\n _initParams._feeRecipient,\n _initParams._managementFee,\n _initParams._performanceFee,\n _initParams._tokenName,\n _initParams._tokenSymbol,\n _vaultParams\n );\n require(\n _initParams._optionsPremiumPricer != address(0),\n \"!_optionsPremiumPricer\"\n );\n require(\n _initParams._strikeSelection != address(0),\n \"!_strikeSelection\"\n );\n\n optionsPremiumPricer = _initParams._optionsPremiumPricer;\n strikeSelection = _initParams._strikeSelection;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Sets the new strike selection contract\n * @param newStrikeSelection is the address of the new strike selection contract\n */\n function setStrikeSelection(address newStrikeSelection) external onlyOwner {\n require(newStrikeSelection != address(0), \"!newStrikeSelection\");\n strikeSelection = newStrikeSelection;\n }\n\n /**\n * @notice Sets the new options premium pricer contract\n * @param newOptionsPremiumPricer is the address of the new strike selection contract\n */\n function setOptionsPremiumPricer(address newOptionsPremiumPricer)\n external\n onlyOwner\n {\n require(\n newOptionsPremiumPricer != address(0),\n \"!newOptionsPremiumPricer\"\n );\n optionsPremiumPricer = newOptionsPremiumPricer;\n }\n\n /**\n * @notice Optionality to set strike price manually\n * Should be called after closeRound if we are setting current week's strike\n * @param strikePrice is the strike price of the new oTokens (decimals = 8)\n */\n function setStrikePrice(uint128 strikePrice) external onlyOwner {\n require(strikePrice > 0, \"!strikePrice\");\n overriddenStrikePrice = strikePrice;\n lastStrikeOverrideRound = vaultState.round;\n }\n\n /**\n * @notice Sets the new liquidityGauge contract for this vault\n * @param newLiquidityGauge is the address of the new liquidityGauge contract\n */\n function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {\n liquidityGauge = newLiquidityGauge;\n }\n\n /**\n * @notice Sets oToken Premium\n * @param minPrice is the new oToken Premium in the units of 10**18\n */\n function setMinPrice(uint256 minPrice) external onlyKeeper {\n require(minPrice > 0, \"!minPrice\");\n currentOtokenPremium = minPrice;\n }\n\n /**\n * @notice Sets the new Vault Pauser contract for this vault\n * @param newVaultPauser is the address of the new vaultPauser contract\n */\n function setVaultPauser(address newVaultPauser) external onlyOwner {\n vaultPauser = newVaultPauser;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`\n * @param amount is the amount to withdraw\n */\n function withdrawInstantly(uint256 amount) external nonReentrant {\n Vault.DepositReceipt storage depositReceipt =\n depositReceipts[msg.sender];\n\n uint256 currentRound = vaultState.round;\n require(amount > 0, \"!amount\");\n require(depositReceipt.round == currentRound, \"Invalid round\");\n\n uint256 receiptAmount = depositReceipt.amount;\n require(receiptAmount >= amount, \"Exceed amount\");\n\n // Subtraction underflow checks already ensure it is smaller than uint104\n depositReceipt.amount = uint104(receiptAmount.sub(amount));\n vaultState.totalPending = uint128(\n uint256(vaultState.totalPending).sub(amount)\n );\n\n emit InstantWithdraw(msg.sender, amount, currentRound);\n\n transferAsset(msg.sender, amount);\n }\n\n /**\n * @notice Initiates a withdrawal that can be processed once the round completes\n * @param numShares is the number of shares to withdraw\n */\n function initiateWithdraw(uint256 numShares) external nonReentrant {\n _initiateWithdraw(numShares);\n currentQueuedWithdrawShares = currentQueuedWithdrawShares.add(\n numShares\n );\n }\n\n /**\n * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round\n */\n function completeWithdraw() external nonReentrant {\n uint256 withdrawAmount = _completeWithdraw();\n lastQueuedWithdrawAmount = uint128(\n uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)\n );\n }\n\n /**\n * @notice Stakes a users vault shares\n * @param numShares is the number of shares to stake\n */\n function stake(uint256 numShares) external nonReentrant {\n address _liquidityGauge = liquidityGauge;\n require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit\n require(numShares > 0);\n uint256 heldByAccount = balanceOf(msg.sender);\n if (heldByAccount < numShares) {\n _redeem(numShares.sub(heldByAccount), false);\n }\n _transfer(msg.sender, address(this), numShares);\n _approve(address(this), _liquidityGauge, numShares);\n ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);\n }\n\n /**\n * @notice Closes the existing short and calculate the shares to mint, new price per share &\n amount of funds to re-allocate as collateral for the new round\n * Since we are incrementing the round here, the options are sold in the beginning of a round\n * instead of at the end of the round. For example, at round 1, we don't sell any options. We\n * start selling options at the beginning of round 2.\n */\n function closeRound() external nonReentrant {\n address oldOption = optionState.currentOption;\n require(\n oldOption != address(0) || vaultState.round == 1,\n \"Round closed\"\n );\n _closeShort(oldOption);\n\n uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares;\n (uint256 lockedBalance, uint256 queuedWithdrawAmount) =\n _closeRound(\n uint256(lastQueuedWithdrawAmount),\n currQueuedWithdrawShares\n );\n\n lastQueuedWithdrawAmount = queuedWithdrawAmount;\n\n uint256 newQueuedWithdrawShares =\n uint256(vaultState.queuedWithdrawShares).add(\n currQueuedWithdrawShares\n );\n ShareMath.assertUint128(newQueuedWithdrawShares);\n vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);\n\n currentQueuedWithdrawShares = 0;\n\n ShareMath.assertUint104(lockedBalance);\n vaultState.lockedAmount = uint104(lockedBalance);\n\n uint256 nextOptionReady = block.timestamp.add(DELAY);\n require(\n nextOptionReady <= type(uint32).max,\n \"Overflow nextOptionReady\"\n );\n optionState.nextOptionReadyAt = uint32(nextOptionReady);\n }\n\n /**\n * @notice Closes the existing short position for the vault.\n */\n function _closeShort(address oldOption) private {\n uint256 lockedAmount = vaultState.lockedAmount;\n if (oldOption != address(0)) {\n vaultState.lastLockedAmount = uint104(lockedAmount);\n }\n vaultState.lockedAmount = 0;\n\n optionState.currentOption = address(0);\n\n if (oldOption != address(0)) {\n uint256 withdrawAmount =\n VaultLifecycleWithSwap.settleShort(GAMMA_CONTROLLER);\n emit CloseShort(oldOption, withdrawAmount, msg.sender);\n }\n }\n\n /**\n * @notice Sets the next option the vault will be shorting\n */\n function commitNextOption() external onlyKeeper nonReentrant {\n address currentOption = optionState.currentOption;\n require(\n currentOption == address(0) && vaultState.round != 1,\n \"Round not closed\"\n );\n\n VaultLifecycleWithSwap.CommitParams memory commitParams =\n VaultLifecycleWithSwap.CommitParams({\n OTOKEN_FACTORY: OTOKEN_FACTORY,\n USDC: USDC,\n collateralAsset: vaultParams.asset,\n currentOption: currentOption,\n delay: DELAY,\n lastStrikeOverrideRound: lastStrikeOverrideRound,\n overriddenStrikePrice: overriddenStrikePrice,\n strikeSelection: strikeSelection,\n optionsPremiumPricer: optionsPremiumPricer\n });\n\n (address otokenAddress, uint256 strikePrice, uint256 delta) =\n VaultLifecycleWithSwap.commitNextOption(\n commitParams,\n vaultParams,\n vaultState\n );\n\n emit NewOptionStrikeSelected(strikePrice, delta);\n\n optionState.nextOption = otokenAddress;\n }\n\n /**\n * @notice Rolls the vault's funds into a new short position and create a new offer.\n */\n function rollToNextOption() external onlyKeeper nonReentrant {\n address newOption = optionState.nextOption;\n require(newOption != address(0), \"!nextOption\");\n\n optionState.currentOption = newOption;\n optionState.nextOption = address(0);\n uint256 lockedBalance = vaultState.lockedAmount;\n\n emit OpenShort(newOption, lockedBalance, msg.sender);\n\n VaultLifecycleWithSwap.createShort(\n GAMMA_CONTROLLER,\n MARGIN_POOL,\n newOption,\n lockedBalance\n );\n\n _createOffer();\n }\n\n function _createOffer() private {\n address currentOtoken = optionState.currentOption;\n uint256 currOtokenPremium = currentOtokenPremium;\n\n optionAuctionID = VaultLifecycleWithSwap.createOffer(\n currentOtoken,\n currOtokenPremium,\n SWAP_CONTRACT,\n vaultParams\n );\n }\n\n /**\n * @notice Settle current offer\n */\n function settleOffer(ISwap.Bid[] calldata bids)\n external\n onlyKeeper\n nonReentrant\n {\n ISwap(SWAP_CONTRACT).settleOffer(optionAuctionID, bids);\n }\n\n /**\n * @notice Burn the remaining oTokens left over\n */\n function burnRemainingOTokens() external onlyKeeper nonReentrant {\n uint256 unlockedAssetAmount =\n VaultLifecycleWithSwap.burnOtokens(\n GAMMA_CONTROLLER,\n optionState.currentOption\n );\n\n vaultState.lockedAmount = uint104(\n uint256(vaultState.lockedAmount).sub(unlockedAssetAmount)\n );\n }\n\n /**\n * @notice pause a user's vault position\n */\n function pausePosition() external {\n address _vaultPauserAddress = vaultPauser;\n require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit\n _redeem(0, true);\n uint256 heldByAccount = balanceOf(msg.sender);\n _approve(msg.sender, _vaultPauserAddress, heldByAccount);\n IVaultPauser(_vaultPauserAddress).pausePosition(\n msg.sender,\n heldByAccount\n );\n }\n}\n" + }, + "contracts/utils/STETHDepositHelper.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {ICurveETHSTETHPool} from \"../interfaces/ICurveETHSTETHPool.sol\";\nimport {IRibbonThetaVault} from \"../interfaces/IRibbonThetaVault.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\ncontract STETHDepositHelper {\n using SafeERC20 for IERC20;\n\n ICurveETHSTETHPool public immutable curveETHSTETHPool;\n IRibbonThetaVault public immutable stETHVault;\n IERC20 public immutable stETH;\n\n constructor(\n address _curveETHSTETHPool,\n address _stETHVault,\n address _stETH\n ) {\n require(_curveETHSTETHPool != address(0), \"!curveETHSTETH Pool\");\n require(_stETHVault != address(0), \"!stETHVault\");\n require(_stETH != address(0), \"!_stETH\");\n\n curveETHSTETHPool = ICurveETHSTETHPool(_curveETHSTETHPool);\n stETHVault = IRibbonThetaVault(_stETHVault);\n stETH = IERC20(_stETH);\n }\n\n /**\n * Swaps ETH -> stETH on Curve ETH-stETH pool, and deposits into stETH vault\n */\n function deposit(uint256 minSTETHAmount) external payable {\n curveETHSTETHPool.exchange{value: msg.value}(\n 0,\n 1,\n msg.value,\n minSTETHAmount\n );\n uint256 balance = stETH.balanceOf(address(this));\n stETH.safeApprove(address(stETHVault), balance);\n stETHVault.depositYieldTokenFor(balance, msg.sender);\n }\n}\n" + }, + "contracts/interfaces/ICurveETHSTETHPool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\ninterface ICurveETHSTETHPool {\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 dx,\n uint256 min_dy\n ) external payable returns (uint256);\n}\n" + }, + "contracts/utils/OptionsPurchaseQueue.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {SafeCast} from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport {IOptionsPurchaseQueue} from \"../interfaces/IOptionsPurchaseQueue.sol\";\nimport {IRibbonThetaVault} from \"../interfaces/IRibbonThetaVault.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\ncontract OptionsPurchaseQueue is IOptionsPurchaseQueue, Ownable {\n using SafeERC20 for IERC20;\n\n /************************************************\n * STORAGE\n ***********************************************/\n\n /// @notice Stores the purchase queue for each vault\n mapping(address => Purchase[]) public override purchases;\n\n /// @notice Stores the total options being purchased for each vault\n mapping(address => uint256) public override totalOptionsAmount;\n\n /// @notice Stores the amount of options the vault is allocating towards the purchase queue\n /// @dev When vaultAllocatedOptions != 0, new purchase requests for the vault are blocked since the vault has\n /// already allocated options for this contract\n mapping(address => uint256) public override vaultAllocatedOptions;\n\n /// @notice Stores whether a buyer is whitelisted\n mapping(address => bool) public override whitelistedBuyer;\n\n /// @notice Stores the ceiling price of a vaults options\n /// @dev If the ceilingPrice != 0, then the vault is available for requesting purchases\n mapping(address => uint256) public override ceilingPrice;\n\n /// @notice Minimum amount of options a buyer needs to request from a vault, necessary to prevent the purchase\n /// queue from getting griefed\n /// @dev Buyers on the whitelist are exempted from this requirement\n mapping(address => uint256) public override minPurchaseAmount;\n\n /************************************************\n * EVENTS\n ***********************************************/\n\n /**\n * @notice Emitted when a purchase is requested\n * @param buyer The buyer requesting the purchase\n * @param vault The vault the buyer is purchasing from\n * @param optionsAmount Amount of options requested\n * @param premiums Total premiums from the buyers (optionsAmount * ceilingPrice)\n */\n event PurchaseRequested(\n address indexed buyer,\n address indexed vault,\n uint256 optionsAmount,\n uint256 premiums\n );\n\n /**\n * @notice Emitted when a purchase is cancelled\n * @param buyer The buyer cancelling their purchase\n * @param vault The vault the buyer was purchasing from\n * @param optionsAmount Amount of options cancelled\n * @param premiums Total premiums transferred back to the buyer\n */\n event PurchaseCancelled(\n address indexed buyer,\n address indexed vault,\n uint256 optionsAmount,\n uint256 premiums\n );\n\n /**\n * @notice Emitted when the vault allocates options to be sold to the buyers\n * @param vault The vault allocating options\n * @param allocatedOptions Amount of options allocated\n */\n event OptionsAllocated(address indexed vault, uint256 allocatedOptions);\n\n /**\n * @notice Emitted when the vault sells options to the buyers\n * @param vault The vault selling the options\n * @param totalPremiums Total premiums earnt by the vault\n * @param totalOptions Total options transferred to the buyers (allocatedOptions)\n */\n event OptionsSold(\n address indexed vault,\n uint256 totalPremiums,\n uint256 totalOptions\n );\n\n /**\n * @notice Emitted when a buyer is whitelisted for purchasing options\n * @param buyer The whitelisted buyer\n */\n event AddWhitelist(address indexed buyer);\n\n /**\n * @notice Emitted when a buyer is removed from the whitelist for purchasing options\n * @param buyer The blacklisted buyer\n */\n event RemoveWhitelist(address indexed buyer);\n\n /**\n * @notice Emitted when the ceiling price for a vault is updated\n * @param vault The vault\n * @param ceilingPrice The new ceiling price\n */\n event CeilingPriceUpdated(address indexed vault, uint256 ceilingPrice);\n\n /**\n * @notice Emitted when the minimum purchase amount for a vault is updated\n * @param vault The vault\n * @param optionsAmount The new minimum purchase amount\n */\n event MinPurchaseAmountUpdated(\n address indexed vault,\n uint256 optionsAmount\n );\n\n /************************************************\n * BUYER OPERATIONS\n ***********************************************/\n\n /**\n * @notice Create a request to purchase options from a vault at the auction settlement price\n * @dev The buyer must be whitelisted to prevent the purchase queue from getting griefed (since sellToBuyers()\n * iterates through it). This function transfers the premiums for the options from the buyer at the ceiling\n * price (maximum price the buyer has to pay), however when the options are sold the buyer only pays the\n * auction settlement price and the leftover premiums are transferred back to the buyer. This function will\n * revert after the vault calls allocateOptions. New purchases can be made after the vault calls sellToBuyers().\n * The requests on the purchased queue are filled FIFO. Any unfilled/partially filled requests are refunded\n * their premiums, this can occur when the vault allocates less options than there are on the queue.\n * @param vault The vault to purchase options from\n * @param optionsAmount Amount of options requested\n * @return premiums Amount of premiums transferred from the buyer\n */\n function requestPurchase(address vault, uint256 optionsAmount)\n external\n override\n returns (uint256)\n {\n uint256 _ceilingPrice = ceilingPrice[vault];\n require(_ceilingPrice != 0, \"Invalid vault\");\n require(optionsAmount != 0, \"!optionsAmount\");\n // Exempt buyers on the whitelist from the minimum purchase requirement\n require(\n optionsAmount >= minPurchaseAmount[vault] ||\n whitelistedBuyer[msg.sender],\n \"Minimum purchase requirement\"\n );\n // This prevents new purchase requested after the vault has set its allocation\n require(vaultAllocatedOptions[vault] == 0, \"Vault allocated\");\n\n // premiums = optionsAmount * ceilingPrice\n uint256 premiums =\n (optionsAmount * _ceilingPrice) / (10**Vault.OTOKEN_DECIMALS);\n\n // Add purchase to queue\n purchases[vault].push(\n Purchase(\n SafeCast.toUint128(optionsAmount),\n SafeCast.toUint128(premiums),\n msg.sender\n )\n );\n\n totalOptionsAmount[vault] += optionsAmount;\n\n // Transfer premiums from the buyer to this contract\n IERC20(IRibbonThetaVault(vault).vaultParams().asset).safeTransferFrom(\n msg.sender,\n address(this),\n premiums\n );\n\n emit PurchaseRequested(msg.sender, vault, optionsAmount, premiums);\n\n return premiums;\n }\n\n /************************************************\n * VAULT OPERATIONS\n ***********************************************/\n\n /**\n * @notice Allocate options to the purchase queue\n * @dev Only callable by the vault selling options. Since we cannot allocate more options than there are on the\n * purchase queue, we cap the allocated options at the totalOptionsAmount. The vault decides how many options\n * of its options it wants to allocate. Allows allocating additional options if already called. Transfers the\n * options from the vault to this contract.\n * @param allocatedOptions Maximum amount of options the vault can allocate to buyers\n * @return allocatedOptions The actual amount of options allocated\n */\n function allocateOptions(uint256 allocatedOptions)\n external\n override\n returns (uint256)\n {\n require(ceilingPrice[msg.sender] != 0, \"Not vault\");\n\n // Prevent the vault from allocating more options than there are requested\n allocatedOptions = getOptionsAllocation(msg.sender, allocatedOptions);\n\n // Blocks new purchase requests until sellToBuyers() is called\n vaultAllocatedOptions[msg.sender] += allocatedOptions;\n\n if (allocatedOptions != 0) {\n // Transfer allocated options from the vault to this contract\n IERC20(IRibbonThetaVault(msg.sender).currentOption())\n .safeTransferFrom(msg.sender, address(this), allocatedOptions);\n }\n\n emit OptionsAllocated(msg.sender, allocatedOptions);\n\n return allocatedOptions;\n }\n\n /**\n * @notice Sells allocated options to the buyers on the purchase queue\n * @dev Only callable by the vault. Lets say the vault starts an auction and it doesn't fully fill and\n * settles at a poor price. If this function were callable by anyone, then they could sell the allocated\n * options to the buyers at the poor price. Hence the vault should call this once its auction has settled at a\n * good price. The vault must allocate options first, otherwise all the buyers are returned their premiums.\n * The buyers receive their options at the auction settlement price and any leftover premiums are refunded.\n * If the auction settles above the ceiling price, the vault receives the premiums at the ceiling price (so it\n * receives premiums at a worse price than the auction) and the buyers are not refunded.\n * @param settlementPrice The vault passes in the settlement price of the options\n * @return totalPremiums The total premiums the vault received from the purchase queue\n */\n function sellToBuyers(uint256 settlementPrice)\n external\n override\n returns (uint256)\n {\n require(ceilingPrice[msg.sender] != 0, \"Not vault\");\n\n uint256 totalPremiums;\n uint256 allocatedOptions = vaultAllocatedOptions[msg.sender];\n uint256 totalOptions = allocatedOptions; // Cache allocatedOptions here for emitting an event later\n IERC20 currentOption =\n IERC20(IRibbonThetaVault(msg.sender).currentOption());\n IERC20 asset =\n IERC20(IRibbonThetaVault(msg.sender).vaultParams().asset);\n Purchase[] memory purchaseQueue = purchases[msg.sender];\n\n for (uint256 i; i < purchaseQueue.length; i++) {\n if (allocatedOptions == 0) {\n // Transfer premiums back to the buyer if no options are left\n asset.safeTransfer(\n purchaseQueue[i].buyer,\n purchaseQueue[i].premiums\n );\n } else {\n // Prevent transferring more options than there are allocated\n // optionsAmount = min(purchase.optionsAmount, allocatedOptions)\n uint256 optionsAmount =\n purchaseQueue[i].optionsAmount < allocatedOptions\n ? purchaseQueue[i].optionsAmount\n : allocatedOptions;\n\n // premiums = optionsAmount * settlementPrice\n uint256 premiums =\n (optionsAmount * settlementPrice) /\n (10**Vault.OTOKEN_DECIMALS);\n\n if (premiums < purchaseQueue[i].premiums) {\n // Transfer leftover premiums back to the buyer\n asset.safeTransfer(\n purchaseQueue[i].buyer,\n purchaseQueue[i].premiums - premiums\n );\n\n totalPremiums += premiums;\n } else {\n // If the settlement price exceed the buyer's price (ceiling price), the vault receives all\n // of the buyer's premiums at a worse price than the auction\n totalPremiums += purchaseQueue[i].premiums;\n }\n\n // Transfer options to the buyer\n currentOption.safeTransfer(\n purchaseQueue[i].buyer,\n optionsAmount\n );\n\n // Deduct transferred options from allocatedOptions\n allocatedOptions -= optionsAmount;\n }\n }\n\n // Transfer premiums to the vault\n if (totalPremiums != 0) asset.safeTransfer(msg.sender, totalPremiums);\n\n // Clear purchase queue\n delete purchases[msg.sender];\n totalOptionsAmount[msg.sender] = 0;\n // Purchase requests are unblocked\n vaultAllocatedOptions[msg.sender] = 0;\n\n emit OptionsSold(msg.sender, totalPremiums, totalOptions);\n\n return totalPremiums;\n }\n\n /************************************************\n * OWNER OPERATIONS\n ***********************************************/\n\n /**\n * @notice Cancels all purchase requests for a delisted vault\n * @dev Only callable by the owner. Will revert if options have already been allocated by the vault.\n * @param vault The vault to cancel all purchases for\n */\n function cancelAllPurchases(address vault) external override onlyOwner {\n // Revert if the vault is still listed\n require(ceilingPrice[vault] == 0, \"Vault listed\");\n // This prevents cancellations after the vault has set its allocation\n require(vaultAllocatedOptions[vault] == 0, \"Vault allocated\");\n\n IERC20 asset = IERC20(IRibbonThetaVault(vault).vaultParams().asset);\n Purchase[] memory purchaseQueue = purchases[vault];\n\n for (uint256 i; i < purchaseQueue.length; i++) {\n // Refund premiums to the buyer\n asset.safeTransfer(\n purchaseQueue[i].buyer,\n purchaseQueue[i].premiums\n );\n\n emit PurchaseCancelled(\n purchaseQueue[i].buyer,\n vault,\n purchaseQueue[i].optionsAmount,\n purchaseQueue[i].premiums\n );\n }\n\n // Clear purchase queue\n delete purchases[vault];\n totalOptionsAmount[vault] = 0;\n }\n\n /************************************************\n * SETTERS\n ***********************************************/\n\n /**\n * @notice Adds a buyer to the purchase queue whitelist\n * @dev Only callable by the owner\n * @param buyer The buyer to whitelist\n */\n function addWhitelist(address buyer) external override onlyOwner {\n require(buyer != address(0), \"!buyer\");\n\n whitelistedBuyer[buyer] = true;\n\n emit AddWhitelist(buyer);\n }\n\n /**\n * @notice Removes a buyer from the purchase queue whitelist\n * @dev Only callable by the owner\n * @param buyer The buyer to remove from the whitelist\n */\n function removeWhitelist(address buyer) external override onlyOwner {\n require(buyer != address(0), \"!buyer\");\n\n whitelistedBuyer[buyer] = false;\n\n emit RemoveWhitelist(buyer);\n }\n\n /**\n * @notice Set the ceiling price for a vault\n * @dev Only callable by the owner\n * @param vault The vault to set a ceiling price for\n * @param price The ceiling price\n */\n function setCeilingPrice(address vault, uint256 price)\n external\n override\n onlyOwner\n {\n require(vault != address(0), \"!vault\");\n\n // Setting the ceiling price to 0 is the same as delisting a vault\n ceilingPrice[vault] = price;\n\n emit CeilingPriceUpdated(vault, price);\n }\n\n /**\n * @notice Sets the minimum purchase amount for a vault\n * @dev Only callable by the owner\n * @param vault The vault to set the minimum purchase amount for\n * @param optionsAmount The minimum options purchase amount\n */\n function setMinPurchaseAmount(address vault, uint256 optionsAmount)\n external\n override\n onlyOwner\n {\n require(vault != address(0), \"!vault\");\n\n minPurchaseAmount[vault] = optionsAmount;\n\n emit MinPurchaseAmountUpdated(vault, optionsAmount);\n }\n\n /************************************************\n * GETTERS\n ***********************************************/\n\n /**\n * @notice Gets all the purchase requests for a vault\n * @param vault The vault to get purchase requests for\n * @return purchases The purchase array\n */\n function getPurchases(address vault)\n external\n view\n override\n returns (Purchase[] memory)\n {\n return purchases[vault];\n }\n\n /**\n * @notice Gets the premiums the buyer needs to deposit to request a certain amount of options\n * @param vault The vault to purchase options from\n * @param optionsAmount Amount of options requested\n * @return premiums Premiums required to request a purchase\n */\n function getPremiums(address vault, uint256 optionsAmount)\n external\n view\n override\n returns (uint256)\n {\n // premiums = optionsAmount * ceilingPrice\n return\n (optionsAmount * ceilingPrice[vault]) / (10**Vault.OTOKEN_DECIMALS);\n }\n\n /**\n * @notice Gets the amount of options the vault can allocate to the queue\n * @param vault The vault selling options to the queue\n * @param allocatedOptions Maximum amount of options the vault can allocate to the queue\n * @return allocatedOptions Actual amount of options the vault allocates to the queue\n */\n function getOptionsAllocation(address vault, uint256 allocatedOptions)\n public\n view\n override\n returns (uint256)\n {\n // Prevent the vault from allocating more options than there are requested\n uint256 optionsAmount =\n totalOptionsAmount[vault] - vaultAllocatedOptions[vault];\n // allocatedOptions = min(allocatedOptions, totalOptionsAmount[vault] - vaultAllocatedOptions[vault])\n return\n optionsAmount < allocatedOptions ? optionsAmount : allocatedOptions;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/tests/MockRibbonVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {IOptionsPurchaseQueue} from \"../interfaces/IOptionsPurchaseQueue.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\ncontract MockRibbonVault {\n Vault.VaultParams public vaultParams;\n\n address public currentOption;\n\n function setAsset(address asset) external {\n vaultParams.asset = asset;\n }\n\n function setCurrentOption(address option) external {\n currentOption = option;\n }\n\n function allocateOptions(\n address optionsPurchaseQueue,\n address option,\n uint256 optionsAmount\n ) external {\n IERC20(option).approve(optionsPurchaseQueue, optionsAmount);\n IOptionsPurchaseQueue(optionsPurchaseQueue).allocateOptions(\n optionsAmount\n );\n }\n\n function sellToBuyers(address optionsPurchaseQueue, uint256 settlementPrice)\n external\n {\n IOptionsPurchaseQueue(optionsPurchaseQueue).sellToBuyers(\n settlementPrice\n );\n }\n}\n" + }, + "contracts/storage/RibbonDeltaVaultStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\nimport {IRibbonThetaVault} from \"../interfaces/IRibbonThetaVault.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\nabstract contract RibbonDeltaVaultStorageV1 {\n // Ribbon counterparty theta vault\n IRibbonThetaVault public counterpartyThetaVault;\n // % of funds to be used for weekly option purchase\n uint256 public optionAllocation;\n // Delta vault equivalent of lockedAmount\n uint256 public balanceBeforePremium;\n // User Id of delta vault in latest gnosis auction\n Vault.AuctionSellOrder public auctionSellOrder;\n}\n\nabstract contract RibbonDeltaVaultStorageV2 {\n // Amount locked for scheduled withdrawals last week;\n uint128 public lastQueuedWithdrawAmount;\n}\n\n// We are following Compound's method of upgrading new contract implementations\n// When we need to add new storage variables, we create a new version of RibbonDeltaVaultStorage\n// e.g. RibbonDeltaVaultStorage, so finally it would look like\n// contract RibbonDeltaVaultStorage is RibbonDeltaVaultStorageV1, RibbonDeltaVaultStorageV2\nabstract contract RibbonDeltaVaultStorage is\n RibbonDeltaVaultStorageV1,\n RibbonDeltaVaultStorageV2\n{\n\n}\n" + }, + "contracts/tests/TestVaultLifecycleTreasury.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {VaultLifecycleTreasury} from \"../libraries/VaultLifecycleTreasury.sol\";\n\ncontract TestVaultLifecycleTreasury {\n function getNextExpiry(uint256 currentExpiry, uint256 interval)\n external\n pure\n returns (uint256)\n {\n return VaultLifecycleTreasury.getNextExpiry(currentExpiry, interval);\n }\n}\n" + }, + "contracts/utils/DeltaStrikeSelection.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {\n IPriceOracle\n} from \"@ribbon-finance/rvol/contracts/interfaces/IPriceOracle.sol\";\nimport {IOptionsPremiumPricer} from \"../interfaces/IRibbon.sol\";\nimport {\n IManualVolatilityOracle\n} from \"@ribbon-finance/rvol/contracts/interfaces/IManualVolatilityOracle.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\ncontract DeltaStrikeSelection is Ownable {\n using SafeMath for uint256;\n\n /**\n * Immutables\n */\n IOptionsPremiumPricer public immutable optionsPremiumPricer;\n\n IManualVolatilityOracle public immutable volatilityOracle;\n\n // delta for options strike price selection. 1 is 10000 (10**4)\n uint256 public delta;\n\n // step in absolute terms at which we will increment\n // (ex: 100 * 10 ** assetOracleDecimals means we will move at increments of 100 points)\n uint256 public step;\n\n // multiplier to shift asset prices\n uint256 private immutable assetOracleMultiplier;\n\n // Delta are in 4 decimal places. 1 * 10**4 = 1 delta.\n uint256 private constant DELTA_MULTIPLIER = 10**4;\n\n // ChainLink's USD Price oracles return results in 8 decimal places\n uint256 private constant ORACLE_PRICE_MULTIPLIER = 10**8;\n\n event DeltaSet(uint256 oldDelta, uint256 newDelta, address indexed owner);\n event StepSet(uint256 oldStep, uint256 newStep, address indexed owner);\n\n constructor(\n address _optionsPremiumPricer,\n uint256 _delta,\n uint256 _step\n ) {\n require(_optionsPremiumPricer != address(0), \"!_optionsPremiumPricer\");\n require(_delta > 0, \"!_delta\");\n require(_delta <= DELTA_MULTIPLIER, \"newDelta cannot be more than 1\");\n require(_step > 0, \"!_step\");\n\n optionsPremiumPricer = IOptionsPremiumPricer(_optionsPremiumPricer);\n volatilityOracle = IManualVolatilityOracle(\n IOptionsPremiumPricer(_optionsPremiumPricer).volatilityOracle()\n );\n // ex: delta = 7500 (.75)\n delta = _delta;\n uint256 _assetOracleMultiplier =\n 10 **\n IPriceOracle(\n IOptionsPremiumPricer(_optionsPremiumPricer).priceOracle()\n )\n .decimals();\n\n step = _step;\n\n assetOracleMultiplier = _assetOracleMultiplier;\n }\n\n /**\n * @notice Gets the strike price satisfying the delta value\n * given the expiry timestamp and whether option is call or put\n * @param expiryTimestamp is the unix timestamp of expiration\n * @param isPut is whether option is put or call\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\n * @return newDelta is the delta of the option given its parameters\n */\n function getStrikePrice(uint256 expiryTimestamp, bool isPut)\n external\n view\n returns (uint256 newStrikePrice, uint256 newDelta)\n {\n // asset's annualized volatility\n uint256 annualizedVol =\n volatilityOracle.annualizedVol(optionsPremiumPricer.optionId()).mul(\n 10**10\n );\n return _getStrikePrice(expiryTimestamp, isPut, annualizedVol);\n }\n\n /**\n * @notice Gets the strike price satisfying the delta value\n * given the expiry timestamp and whether option is call or put\n * @param expiryTimestamp is the unix timestamp of expiration\n * @param isPut is whether option is put or call\n * @param annualizedVol is IV of the asset at the specified delta\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\n * @return newDelta is the delta of the option given its parameters\n */\n function getStrikePriceWithVol(\n uint256 expiryTimestamp,\n bool isPut,\n uint256 annualizedVol\n ) external view returns (uint256 newStrikePrice, uint256 newDelta) {\n return\n _getStrikePrice(expiryTimestamp, isPut, annualizedVol.mul(10**10));\n }\n\n /**\n * @notice Gets the strike price satisfying the delta value\n * given the expiry timestamp and whether option is call or put\n * @param expiryTimestamp is the unix timestamp of expiration\n * @param isPut is whether option is put or call\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\n * @return newDelta is the delta of the option given its parameters\n */\n\n function _getStrikePrice(\n uint256 expiryTimestamp,\n bool isPut,\n uint256 annualizedVol\n ) internal view returns (uint256 newStrikePrice, uint256 newDelta) {\n require(\n expiryTimestamp > block.timestamp,\n \"Expiry must be in the future!\"\n );\n\n // asset price\n uint256 assetPrice = optionsPremiumPricer.getUnderlyingPrice();\n\n // For each asset prices with step of 'step' (down if put, up if call)\n // if asset's getOptionDelta(currStrikePrice, spotPrice, annualizedVol, t) == (isPut ? 1 - delta:delta)\n // with certain margin of error\n // return strike price\n\n uint256 strike =\n isPut\n ? assetPrice.sub(assetPrice % step).sub(step)\n : assetPrice.add(step - (assetPrice % step)).add(step);\n uint256 targetDelta = isPut ? DELTA_MULTIPLIER.sub(delta) : delta;\n uint256 prevDelta = isPut ? 0 : DELTA_MULTIPLIER;\n\n while (true) {\n uint256 currDelta =\n optionsPremiumPricer.getOptionDelta(\n assetPrice.mul(ORACLE_PRICE_MULTIPLIER).div(\n assetOracleMultiplier\n ),\n strike,\n annualizedVol,\n expiryTimestamp\n );\n // If the current delta is between the previous\n // strike price delta and current strike price delta\n // then we are done\n bool foundTargetStrikePrice =\n isPut\n ? targetDelta >= prevDelta && targetDelta <= currDelta\n : targetDelta <= prevDelta && targetDelta >= currDelta;\n\n if (foundTargetStrikePrice) {\n uint256 finalDelta =\n _getBestDelta(prevDelta, currDelta, targetDelta, isPut);\n uint256 finalStrike =\n _getBestStrike(finalDelta, prevDelta, strike, isPut);\n require(\n isPut\n ? finalStrike <= assetPrice\n : finalStrike >= assetPrice,\n \"Invalid strike price\"\n );\n // make decimals consistent with oToken strike price decimals (10 ** 8)\n return (\n finalStrike.mul(ORACLE_PRICE_MULTIPLIER).div(\n assetOracleMultiplier\n ),\n finalDelta\n );\n }\n\n strike = isPut ? strike.sub(step) : strike.add(step);\n\n prevDelta = currDelta;\n }\n }\n\n /**\n * @notice Rounds to best delta value\n * @param prevDelta is the delta of the previous strike price\n * @param currDelta is delta of the current strike price\n * @param targetDelta is the delta we are targeting\n * @param isPut is whether its a put\n * @return the best delta value\n */\n function _getBestDelta(\n uint256 prevDelta,\n uint256 currDelta,\n uint256 targetDelta,\n bool isPut\n ) private pure returns (uint256) {\n uint256 finalDelta;\n\n // for tie breaks (ex: 0.05 <= 0.1 <= 0.15) round to higher strike price\n // for calls and lower strike price for puts for deltas\n if (isPut) {\n uint256 upperBoundDiff = currDelta.sub(targetDelta);\n uint256 lowerBoundDiff = targetDelta.sub(prevDelta);\n finalDelta = lowerBoundDiff <= upperBoundDiff\n ? prevDelta\n : currDelta;\n } else {\n uint256 upperBoundDiff = prevDelta.sub(targetDelta);\n uint256 lowerBoundDiff = targetDelta.sub(currDelta);\n finalDelta = lowerBoundDiff <= upperBoundDiff\n ? currDelta\n : prevDelta;\n }\n\n return finalDelta;\n }\n\n /**\n * @notice Rounds to best delta value\n * @param finalDelta is the best delta value we found\n * @param prevDelta is delta of the previous strike price\n * @param strike is the strike of the previous iteration\n * @param isPut is whether its a put\n * @return the best strike\n */\n function _getBestStrike(\n uint256 finalDelta,\n uint256 prevDelta,\n uint256 strike,\n bool isPut\n ) private view returns (uint256) {\n if (finalDelta != prevDelta) {\n return strike;\n }\n return isPut ? strike.add(step) : strike.sub(step);\n }\n\n /**\n * @notice Sets new delta value\n * @param newDelta is the new delta value\n */\n function setDelta(uint256 newDelta) external onlyOwner {\n require(newDelta > 0, \"!newDelta\");\n require(newDelta <= DELTA_MULTIPLIER, \"newDelta cannot be more than 1\");\n uint256 oldDelta = delta;\n delta = newDelta;\n emit DeltaSet(oldDelta, newDelta, msg.sender);\n }\n\n /**\n * @notice Sets new step value\n * @param newStep is the new step value\n */\n function setStep(uint256 newStep) external onlyOwner {\n require(newStep > 0, \"!newStep\");\n uint256 oldStep = step;\n step = newStep;\n emit StepSet(oldStep, newStep, msg.sender);\n }\n}\n" + }, + "contracts/libraries/UniswapRouter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {SafeMath} from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {ISwapRouter} from \"../interfaces/ISwapRouter.sol\";\nimport {IUniswapV3Factory} from \"../interfaces/IUniswapV3Factory.sol\";\nimport \"./Path.sol\";\n\nlibrary UniswapRouter {\n using Path for bytes;\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n /**\n * @notice Check if the path set for swap is valid\n * @param swapPath is the swap path e.g. encodePacked(tokenIn, poolFee, tokenOut)\n * @param validTokenIn is the contract address of the correct tokenIn\n * @param validTokenOut is the contract address of the correct tokenOut\n * @param uniswapFactory is the contract address of UniswapV3 factory\n * @return isValidPath is whether the path is valid\n */\n function checkPath(\n bytes memory swapPath,\n address validTokenIn,\n address validTokenOut,\n address uniswapFactory\n ) internal view returns (bool isValidPath) {\n // Function checks if the tokenIn and tokenOut in the swapPath\n // matches the validTokenIn and validTokenOut specified.\n address tokenIn;\n address tokenOut;\n address tempTokenIn;\n uint24 fee;\n IUniswapV3Factory factory = IUniswapV3Factory(uniswapFactory);\n\n // Return early if swapPath is below the bare minimum (43)\n require(swapPath.length >= 43, \"Path too short\");\n // Return early if swapPath is above the max (66)\n // At worst we have 2 hops e.g. USDC > WETH > asset\n require(swapPath.length <= 66, \"Path too long\");\n\n // Decode the first pool in path\n (tokenIn, tokenOut, fee) = swapPath.decodeFirstPool();\n\n // Check to factory if pool exists\n require(\n factory.getPool(tokenIn, tokenOut, fee) != address(0),\n \"Pool does not exist\"\n );\n\n // Check next pool if multiple pools\n while (swapPath.hasMultiplePools()) {\n // Remove the first pool from path\n swapPath = swapPath.skipToken();\n // Check the next pool and update tokenOut\n (tempTokenIn, tokenOut, fee) = swapPath.decodeFirstPool();\n\n require(\n factory.getPool(tokenIn, tokenOut, fee) != address(0),\n \"Pool does not exist\"\n );\n }\n\n return tokenIn == validTokenIn && tokenOut == validTokenOut;\n }\n\n /**\n * @notice Swaps assets by calling UniswapV3 router\n * @param recipient is the address of recipient of the tokenOut\n * @param tokenIn is the address of the token given to the router\n * @param amountIn is the amount of tokenIn given to the router\n * @param minAmountOut is the minimum acceptable amount of tokenOut received from swap\n * @param router is the contract address of UniswapV3 router\n * @param swapPath is the swap path e.g. encodePacked(tokenIn, poolFee, tokenOut)\n * @return amountOut is the amount of tokenOut received from the swap\n */\n function swap(\n address recipient,\n address tokenIn,\n uint256 amountIn,\n uint256 minAmountOut,\n address router,\n bytes calldata swapPath\n ) internal returns (uint256 amountOut) {\n // Approve router to spend tokenIn\n IERC20(tokenIn).safeApprove(router, amountIn);\n\n // Swap assets using UniswapV3 router\n ISwapRouter.ExactInputParams memory swapParams =\n ISwapRouter.ExactInputParams({\n recipient: recipient,\n path: swapPath,\n deadline: block.timestamp.add(10 minutes),\n amountIn: amountIn,\n amountOutMinimum: minAmountOut\n });\n\n amountOut = ISwapRouter(router).exactInput(swapParams);\n\n return amountOut;\n }\n}\n" + }, + "contracts/interfaces/ISwapRouter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity =0.8.4;\npragma abicoder v2;\n\n/// Source: https://github.com/Uniswap/v3-core/blob/main/contracts/interfaces/callback/IUniswapV3SwapCallback.sol\n\n/// @title Callback for IUniswapV3PoolActions#swap\n/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface\ninterface IUniswapV3SwapCallback {\n /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.\n /// @dev In the implementation you must pay the pool tokens owed for the swap.\n /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.\n /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.\n /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.\n /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.\n /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call\n function uniswapV3SwapCallback(\n int256 amount0Delta,\n int256 amount1Delta,\n bytes calldata data\n ) external;\n}\n\n/// Source: https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/ISwapRouter.sol\n\n/// @title Router token swapping functionality\n/// @notice Functions for swapping tokens via Uniswap V3\ninterface ISwapRouter is IUniswapV3SwapCallback {\n struct ExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n uint24 fee;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInputSingle(ExactInputSingleParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n uint24 fee;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutputSingle(ExactOutputSingleParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n\n struct ExactOutputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutput(ExactOutputParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n}\n" + }, + "contracts/interfaces/IUniswapV3Factory.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// Source: https://github.com/Uniswap/v3-core/blob/main/contracts/interfaces/IUniswapV3Factory.sol\npragma solidity =0.8.4;\n\n/// @title The interface for the Uniswap V3 Factory\n/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees\ninterface IUniswapV3Factory {\n /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist\n /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order\n /// @param tokenA The contract address of either token0 or token1\n /// @param tokenB The contract address of the other token\n /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip\n /// @return pool The pool address\n function getPool(\n address tokenA,\n address tokenB,\n uint24 fee\n ) external view returns (address pool);\n}\n" + }, + "contracts/libraries/Path.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// Source: https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/Path.sol\npragma solidity =0.8.4;\n\nimport \"./BytesLib.sol\";\n\n/// @title Functions for manipulating path data for multihop swaps\nlibrary Path {\n using BytesLib for bytes;\n\n /// @dev The length of the bytes encoded address\n uint256 private constant ADDR_SIZE = 20;\n /// @dev The length of the bytes encoded fee\n uint256 private constant FEE_SIZE = 3;\n\n /// @dev The offset of a single token address and pool fee\n uint256 private constant NEXT_OFFSET = ADDR_SIZE + FEE_SIZE;\n /// @dev The offset of an encoded pool key\n uint256 private constant POP_OFFSET = NEXT_OFFSET + ADDR_SIZE;\n /// @dev The minimum length of an encoding that contains 2 or more pools\n uint256 private constant MULTIPLE_POOLS_MIN_LENGTH =\n POP_OFFSET + NEXT_OFFSET;\n\n /// @notice Returns true iff the path contains two or more pools\n /// @param path The encoded swap path\n /// @return True if path contains two or more pools, otherwise false\n function hasMultiplePools(bytes memory path) internal pure returns (bool) {\n return path.length >= MULTIPLE_POOLS_MIN_LENGTH;\n }\n\n /// @notice Returns the number of pools in the path\n /// @param path The encoded swap path\n /// @return The number of pools in the path\n function numPools(bytes memory path) internal pure returns (uint256) {\n // Ignore the first token address. From then on every fee and token offset indicates a pool.\n return ((path.length - ADDR_SIZE) / NEXT_OFFSET);\n }\n\n /// @notice Decodes the first pool in path\n /// @param path The bytes encoded swap path\n /// @return tokenA The first token of the given pool\n /// @return tokenB The second token of the given pool\n /// @return fee The fee level of the pool\n function decodeFirstPool(bytes memory path)\n internal\n pure\n returns (\n address tokenA,\n address tokenB,\n uint24 fee\n )\n {\n tokenA = path.toAddress(0);\n fee = path.toUint24(ADDR_SIZE);\n tokenB = path.toAddress(NEXT_OFFSET);\n }\n\n /// @notice Gets the segment corresponding to the first pool in the path\n /// @param path The bytes encoded swap path\n /// @return The segment containing all data necessary to target the first pool in the path\n function getFirstPool(bytes memory path)\n internal\n pure\n returns (bytes memory)\n {\n return path.slice(0, POP_OFFSET);\n }\n\n /// @notice Skips a token + fee element from the buffer and returns the remainder\n /// @param path The swap path\n /// @return The remaining token + fee elements in the path\n function skipToken(bytes memory path) internal pure returns (bytes memory) {\n return path.slice(NEXT_OFFSET, path.length - NEXT_OFFSET);\n }\n}\n" + }, + "contracts/libraries/BytesLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Source: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol\n/*\n * @title Solidity Bytes Arrays Utils\n * @author Gonçalo Sá \n *\n * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.\n * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.\n */\npragma solidity =0.8.4;\n\nlibrary BytesLib {\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n ) internal pure returns (bytes memory) {\n require(_length + 31 >= _length, \"slice_overflow\");\n require(_bytes.length >= _start + _length, \"slice_outOfBounds\");\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(\n add(tempBytes, lengthmod),\n mul(0x20, iszero(lengthmod))\n )\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(\n add(\n add(_bytes, lengthmod),\n mul(0x20, iszero(lengthmod))\n ),\n _start\n )\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n\n function toAddress(bytes memory _bytes, uint256 _start)\n internal\n pure\n returns (address)\n {\n require(_bytes.length >= _start + 20, \"toAddress_outOfBounds\");\n address tempAddress;\n\n assembly {\n tempAddress := div(\n mload(add(add(_bytes, 0x20), _start)),\n 0x1000000000000000000000000\n )\n }\n\n return tempAddress;\n }\n\n function toUint24(bytes memory _bytes, uint256 _start)\n internal\n pure\n returns (uint24)\n {\n require(_bytes.length >= _start + 3, \"toUint24_outOfBounds\");\n uint24 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x3), _start))\n }\n\n return tempUint;\n }\n}\n" + }, + "contracts/tests/MockLiquidityGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\ncontract MockLiquidityGauge {\n using SafeERC20 for IERC20;\n\n address public lp_token;\n uint256 public totalSupply;\n mapping(address => uint256) public balanceOf;\n\n constructor(address _lp_token) {\n lp_token = _lp_token;\n }\n\n function deposit(\n uint256 _value,\n address _addr,\n bool\n ) external {\n if (_value != 0) {\n totalSupply += _value;\n balanceOf[_addr] += _value;\n\n IERC20(lp_token).safeTransferFrom(\n msg.sender,\n address(this),\n _value\n );\n }\n }\n}\n" + }, + "contracts/vendor/upgrades/UpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport './Proxy.sol';\nimport '@openzeppelin/contracts/utils/Address.sol';\n\n/**\n * @title UpgradeabilityProxy\n * @dev This contract implements a proxy that allows to change the\n * implementation address to which it will delegate.\n * Such a change is called an implementation upgrade.\n */\ncontract UpgradeabilityProxy is Proxy {\n /**\n * @dev Contract constructor.\n * @param _logic Address of the initial implementation.\n * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\n */\n constructor(address _logic, bytes memory _data) payable {\n assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));\n _setImplementation(_logic);\n if(_data.length > 0) {\n (bool success,) = _logic.delegatecall(_data);\n require(success);\n }\n }\n\n /**\n * @dev Emitted when the implementation is upgraded.\n * @param implementation Address of the new implementation.\n */\n event Upgraded(address indexed implementation);\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Returns the current implementation.\n * @return impl Address of the current implementation\n */\n function _implementation() internal override view returns (address impl) {\n bytes32 slot = IMPLEMENTATION_SLOT;\n assembly {\n impl := sload(slot)\n }\n }\n\n /**\n * @dev Upgrades the proxy to a new implementation.\n * @param newImplementation Address of the new implementation.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Sets the implementation address of the proxy.\n * @param newImplementation Address of the new implementation.\n */\n function _setImplementation(address newImplementation) internal {\n require(Address.isContract(newImplementation), \"Cannot set a proxy implementation to a non-contract address\");\n\n bytes32 slot = IMPLEMENTATION_SLOT;\n\n assembly {\n sstore(slot, newImplementation)\n }\n }\n}\n" + }, + "contracts/vendor/upgrades/Proxy.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\n/**\n * @title Proxy\n * @dev Implements delegation of calls to other contracts, with proper\n * forwarding of return values and bubbling of failures.\n * It defines a fallback function that delegates all calls to the address\n * returned by the abstract _implementation() internal function.\n */\nabstract contract Proxy {\n /**\n * @dev Fallback function.\n * Implemented entirely in `_fallback`.\n */\n fallback () payable external {\n _fallback();\n }\n\n /**\n * @dev Receive function.\n * Implemented entirely in `_fallback`.\n */\n receive () payable external {\n // _fallback();\n }\n\n /**\n * @return The Address of the implementation.\n */\n function _implementation() internal virtual view returns (address);\n\n /**\n * @dev Delegates execution to an implementation contract.\n * This is a low level function that doesn't return to its internal call site.\n * It will return to the external caller whatever the implementation returns.\n * @param implementation Address to delegate.\n */\n function _delegate(address implementation) internal {\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 { revert(0, returndatasize()) }\n default { return(0, returndatasize()) }\n }\n }\n\n /**\n * @dev Function that is run as the first thing in the fallback function.\n * Can be redefined in derived contracts to add functionality.\n * Redefinitions must call super._willFallback().\n */\n function _willFallback() internal virtual {\n }\n\n /**\n * @dev fallback implementation.\n * Extracted to enable manual triggering.\n */\n function _fallback() internal {\n _willFallback();\n _delegate(_implementation());\n }\n}\n" + }, + "contracts/vendor/upgrades/AdminUpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.8;\n\nimport './UpgradeabilityProxy.sol';\n\n/**\n * @title AdminUpgradeabilityProxy\n * @dev This contract combines an upgradeability proxy with an authorization\n * mechanism for administrative tasks.\n * All external functions in this contract must be guarded by the\n * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity\n * feature proposal that would enable this to be done automatically.\n */\ncontract AdminUpgradeabilityProxy is UpgradeabilityProxy {\n /**\n * Contract constructor.\n * @param _logic address of the initial implementation.\n * @param admin_ Address of the proxy administrator.\n * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\n */\n constructor(address _logic, address admin_, bytes memory _data) UpgradeabilityProxy(_logic, _data) payable {\n assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));\n _setAdmin(admin_);\n }\n\n /**\n * @dev Emitted when the administration has been transferred.\n * @param previousAdmin Address of the previous admin.\n * @param newAdmin Address of the new admin.\n */\n event AdminChanged(address previousAdmin, address newAdmin);\n\n /**\n * @dev Storage slot with the admin of the contract.\n * This is the keccak-256 hash of \"eip1967.proxy.admin\" subtracted by 1, and is\n * validated in the constructor.\n */\n\n bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n /**\n * @dev Modifier to check whether the `msg.sender` is the admin.\n * If it is, it will run the function. Otherwise, it will delegate the call\n * to the implementation.\n */\n modifier ifAdmin() {\n if (msg.sender == _admin()) {\n _;\n } else {\n _fallback();\n }\n }\n\n /**\n * @return adminAddress The address of the proxy admin.\n */\n function admin() external ifAdmin returns (address adminAddress) {\n return _admin();\n }\n\n /**\n * @return implementationAddress The address of the implementation.\n */\n function implementation() external ifAdmin returns (address implementationAddress) {\n return _implementation();\n }\n\n /**\n * @dev Changes the admin of the proxy.\n * Only the current admin can call this function.\n * @param newAdmin Address to transfer proxy administration to.\n */\n function changeAdmin(address newAdmin) external ifAdmin {\n require(newAdmin != address(0), \"Cannot change the admin of a proxy to the zero address\");\n emit AdminChanged(_admin(), newAdmin);\n _setAdmin(newAdmin);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy.\n * Only the admin can call this function.\n * @param newImplementation Address of the new implementation.\n */\n function upgradeTo(address newImplementation) external ifAdmin {\n _upgradeTo(newImplementation);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy and call a function\n * on the new implementation.\n * This is useful to initialize the proxied contract.\n * @param newImplementation Address of the new implementation.\n * @param data Data to send as msg.data in the low level call.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {\n _upgradeTo(newImplementation);\n (bool success,) = newImplementation.delegatecall(data);\n require(success);\n }\n\n /**\n * @return adm The admin slot.\n */\n function _admin() internal view returns (address adm) {\n bytes32 slot = ADMIN_SLOT;\n assembly {\n adm := sload(slot)\n }\n }\n\n /**\n * @dev Sets the address of the proxy admin.\n * @param newAdmin Address of the new proxy admin.\n */\n function _setAdmin(address newAdmin) internal {\n bytes32 slot = ADMIN_SLOT;\n\n assembly {\n sstore(slot, newAdmin)\n }\n }\n\n /**\n * @dev Only fall back when the sender is not the admin.\n */\n function _willFallback() internal override virtual {\n require(msg.sender != _admin(), \"Cannot call fallback function from the proxy admin\");\n super._willFallback();\n }\n}\n" + }, + "contracts/tests/TestVaultLifecycle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {VaultLifecycle} from \"../libraries/VaultLifecycle.sol\";\nimport {Vault} from \"../libraries/Vault.sol\";\n\ncontract TestVaultLifecycle {\n Vault.VaultState public vaultState;\n\n function getNextFriday(uint256 currentExpiry)\n external\n pure\n returns (uint256 nextFriday)\n {\n return VaultLifecycle.getNextFriday(currentExpiry);\n }\n\n function getNextExpiry(address currentOption)\n external\n view\n returns (uint256 nextExpiry)\n {\n return VaultLifecycle.getNextExpiry(currentOption);\n }\n\n function balanceOf(address account) public view returns (uint256) {\n if (account == address(this)) {\n return 1 ether;\n }\n return 0;\n }\n\n function setVaultState(Vault.VaultState calldata newVaultState) public {\n vaultState.totalPending = newVaultState.totalPending;\n vaultState.queuedWithdrawShares = newVaultState.queuedWithdrawShares;\n }\n\n function rollover(VaultLifecycle.RolloverParams calldata params)\n external\n view\n returns (\n uint256 newLockedAmount,\n uint256 queuedWithdrawAmount,\n uint256 newPricePerShare,\n uint256 mintShares,\n uint256 performanceFeeInAsset,\n uint256 totalVaultFee\n )\n {\n return VaultLifecycle.rollover(vaultState, params);\n }\n\n function getAuctionSettlementPrice(\n address gnosisEasyAuction,\n uint256 optionAuctionID\n ) external view returns (uint256) {\n return\n VaultLifecycle.getAuctionSettlementPrice(\n gnosisEasyAuction,\n optionAuctionID\n );\n }\n}\n" + }, + "contracts/utils/ManualStrikeSelection.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract ManualStrikeSelection is Ownable {\n /// @dev Selected strike price\n uint256 public strikePrice;\n\n /// @dev Delta for options strike price selection. 1 is 10000 (10**4)\n uint256 public constant delta = 1000;\n\n /**\n * @notice Sets the strike price, only callable by the owner\n * @param _strikePrice is the strike price of the option\n */\n function setStrikePrice(uint256 _strikePrice) external onlyOwner {\n strikePrice = _strikePrice;\n }\n\n /**\n * @notice Gets the strike price satisfying the delta value\n * given the expiry timestamp and whether option is call or put\n * @return newStrikePrice is the strike price of the option (ex: for BTC might be 45000 * 10 ** 8)\n * @return newDelta is the delta of the option given its parameters\n */\n function getStrikePrice(uint256, bool)\n external\n view\n returns (uint256, uint256)\n {\n return (strikePrice, delta);\n }\n}\n" + }, + "contracts/tests/MockERC20.sol": { + "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockERC20 is ERC20 {\n constructor(string memory _name, string memory _symbol)\n ERC20(_name, _symbol)\n {}\n\n function mint(address to, uint256 value) external {\n _mint(to, value);\n }\n\n function burn(address from, uint256 value) external {\n _burn(from, value);\n }\n}\n" + }, + "contracts/tests/TestShareMath.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity =0.8.4;\n\nimport {ShareMath} from \"../libraries/ShareMath.sol\";\n\ncontract TestShareMath {\n function assetToShares(\n uint256 assetAmount,\n uint256 pps,\n uint256 decimals\n ) external pure returns (uint256) {\n return ShareMath.assetToShares(assetAmount, pps, decimals);\n }\n\n function sharesToAsset(\n uint256 shares,\n uint256 pps,\n uint256 decimals\n ) external pure returns (uint256) {\n return ShareMath.sharesToAsset(shares, pps, decimals);\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/hardhat.config.ts b/hardhat.config.ts index 24b251dc3..7e2fd186e 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -63,6 +63,13 @@ export default { mnemonic: process.env.KOVAN_MNEMONIC, }, }, + goerli: { + url: process.env.GOERLI_URI, + chainId: 5, + accounts: { + mnemonic: process.env.GOERLI_MNEMONIC, + }, + }, avax: { url: process.env.AVAX_URI, chainId: 43114, @@ -82,6 +89,7 @@ export default { deployer: { default: 0, 1: "0xF8368119Bb1073Cf01B841848725d81b542A4c19", + 5: "0xF8368119Bb1073Cf01B841848725d81b542A4c19", 42: "0x422f7Bb366608723c8fe61Ac6D923023dCCBC3d7", 43114: "0x422f7Bb366608723c8fe61Ac6D923023dCCBC3d7", 43113: "0x422f7Bb366608723c8fe61Ac6D923023dCCBC3d7", @@ -91,6 +99,7 @@ export default { owner: { default: 0, 1: "0x77DA011d5314D80BE59e939c2f7EC2F702E1DCC4", + 5: "0xF8368119Bb1073Cf01B841848725d81b542A4c19", 42: "0x92Dd37fbc36cB7260F0d2BD09F9672525a028fB8", 43114: "0x939cbb6BaBAad2b0533C2CACa8a4aFEc3ae06492", 43113: "0x004FCF8052D3c7eCb7558ac0068882425a055528", @@ -100,6 +109,7 @@ export default { keeper: { default: 0, 1: "0x691c87dc570563D1D0AD7Fd0bb099ED367858863", + 5: "0xF8368119Bb1073Cf01B841848725d81b542A4c19", 42: "0x691c87dc570563D1D0AD7Fd0bb099ED367858863", 43114: "0x422f7Bb366608723c8fe61Ac6D923023dCCBC3d7", 43113: "0x004FCF8052D3c7eCb7558ac0068882425a055528", @@ -109,6 +119,7 @@ export default { admin: { default: 0, 1: "0x223d59FA315D7693dF4238d1a5748c964E615923", + 5: "0x422f7Bb366608723c8fe61Ac6D923023dCCBC3d7", 42: "0x422f7Bb366608723c8fe61Ac6D923023dCCBC3d7", 43114: "0x31351f2BD9e94813BCf0cA04B5E6e2b7ceAFC7c6", 43113: "0x004FCF8052D3c7eCb7558ac0068882425a055528", @@ -118,6 +129,7 @@ export default { feeRecipient: { default: 0, 1: "0xDAEada3d210D2f45874724BeEa03C7d4BBD41674", // Ribbon DAO + 5: "0x92Dd37fbc36cB7260F0d2BD09F9672525a028fB8", 42: "0x92Dd37fbc36cB7260F0d2BD09F9672525a028fB8", 43114: "0x939cbb6BaBAad2b0533C2CACa8a4aFEc3ae06492", 43113: "0x004FCF8052D3c7eCb7558ac0068882425a055528", @@ -130,7 +142,7 @@ export default { }, etherscan: { apiKey: - CHAINID === 1 || CHAINID === 42 + CHAINID === 1 || CHAINID === 42 || CHAINID === 5 ? process.env.ETHERSCAN_API_KEY : process.env.SNOWTRACE_API_KEY, },