wooPP_full_spread
11 removals
648 lines
9 additions
645 lines
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT
pragma solidity =0.8.14;
pragma solidity =0.8.14;
/*
/*
░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝
*
*
* MIT License
* MIT License
* ===========
* ===========
*
*
* Copyright (c) 2020 WooTrade
* Copyright (c) 2020 WooTrade
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in all
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
*/
import "./interfaces/IWooracleV2_2.sol";
import "./interfaces/IWooracleV2_2.sol";
import "./interfaces/IWooPPV2.sol";
import "./interfaces/IWooPPV2.sol";
import "./interfaces/AggregatorV3Interface.sol";
import "./interfaces/AggregatorV3Interface.sol";
import "./interfaces/IWooLendingManager.sol";
import "./interfaces/IWooLendingManager.sol";
import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
// OpenZeppelin contracts
// OpenZeppelin contracts
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
// REMOVE IT IN PROD
// REMOVE IT IN PROD
// import "hardhat/console.sol";
// import "hardhat/console.sol";
/// @title Woo pool for token swap, version 2.
/// @title Woo pool for token swap, version 2, with full spread in base to base swap.
/// @notice the implementation class for interface IWooPPV2, mainly for query and swap tokens.
/// @notice the implementation class for interface IWooPPV2, mainly for query and swap tokens.
contract WooPPV2 is Ownable, ReentrancyGuard, Pausable, IWooPPV2 {
contract WooPPV2_FS is Ownable, ReentrancyGuard, Pausable, IWooPPV2 {
/* ----- Type declarations ----- */
/* ----- Type declarations ----- */
struct DecimalInfo {
struct DecimalInfo {
uint64 priceDec; // 10**(price_decimal)
uint64 priceDec; // 10**(price_decimal)
uint64 quoteDec; // 10**(quote_decimal)
uint64 quoteDec; // 10**(quote_decimal)
uint64 baseDec; // 10**(base_decimal)
uint64 baseDec; // 10**(base_decimal)
}
}
struct TokenInfo {
struct TokenInfo {
uint192 reserve; // balance reserve
uint192 reserve; // balance reserve
uint16 feeRate; // 1 in 100000; 10 = 1bp = 0.01%; max = 65535
uint16 feeRate; // 1 in 100000; 10 = 1bp = 0.01%; max = 65535
uint128 maxGamma; // max range of `balance * k`
uint128 maxGamma; // max range of `balance * k`
uint128 maxNotionalSwap; // max volume per swap
uint128 maxNotionalSwap; // max volume per swap
}
}
/* ----- State variables ----- */
/* ----- State variables ----- */
address constant ETH_PLACEHOLDER_ADDR = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
address constant ETH_PLACEHOLDER_ADDR = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
uint256 public unclaimedFee; // NOTE: in quote token
uint256 public unclaimedFee; // NOTE: in quote token
// wallet address --> is admin
// wallet address --> is admin
mapping(address => bool) public isAdmin;
mapping(address => bool) public isAdmin;
// wallet address --> is pause role
// wallet address --> is pause role
mapping(address => bool) public isPauseRole;
mapping(address => bool) public isPauseRole;
// token address --> fee rate
// token address --> fee rate
mapping(address => TokenInfo) public tokenInfos;
mapping(address => TokenInfo) public tokenInfos;
/// @inheritdoc IWooPPV2
/// @inheritdoc IWooPPV2
address public immutable override quoteToken;
address public immutable override quoteToken;
IWooracleV2_2 public wooracle;
IWooracleV2_2 public wooracle;
address public feeAddr;
address public feeAddr;
mapping(address => IWooLendingManager) public lendManagers;
mapping(address => IWooLendingManager) public lendManagers;
/* ----- Modifiers ----- */
/* ----- Modifiers ----- */
modifier onlyAdmin() {
modifier onlyAdmin() {
require(msg.sender == owner() || isAdmin[msg.sender], "WooPPV2: !admin");
require(msg.sender == owner() || isAdmin[msg.sender], "WooPPV2: !admin");
_;
_;
}
}
modifier onlyAdminOrPauseRole() {
modifier onlyAdminOrPauseRole() {
require(msg.sender == owner() || isAdmin[msg.sender] || isPauseRole[msg.sender], "WooPPV2: !isPauseRole");
require(msg.sender == owner() || isAdmin[msg.sender] || isPauseRole[msg.sender], "WooPPV2: !isPauseRole");
_;
_;
}
}
constructor(address _quoteToken) {
constructor(address _quoteToken) {
quoteToken = _quoteToken;
quoteToken = _quoteToken;
}
}
function init(address _wooracle, address _feeAddr) external onlyOwner {
function init(address _wooracle, address _feeAddr) external onlyOwner {
require(address(wooracle) == address(0), "WooPPV2: INIT_INVALID");
require(address(wooracle) == address(0), "WooPPV2: INIT_INVALID");
wooracle = IWooracleV2_2(_wooracle);
wooracle = IWooracleV2_2(_wooracle);
feeAddr = _feeAddr;
feeAddr = _feeAddr;
}
}
/* ----- External Functions ----- */
/* ----- External Functions ----- */
/// @inheritdoc IWooPPV2
/// @inheritdoc IWooPPV2
function tryQuery(
function tryQuery(
address fromToken,
address fromToken,
address toToken,
address toToken,
uint256 fromAmount
uint256 fromAmount
) external view override returns (uint256 toAmount) {
) external view override returns (uint256 toAmount) {
if (fromToken == quoteToken) {
if (fromToken == quoteToken) {
toAmount = _tryQuerySellQuote(toToken, fromAmount);
toAmount = _tryQuerySellQuote(toToken, fromAmount);
} else if (toToken == quoteToken) {
} else if (toToken == quoteToken) {
toAmount = _tryQuerySellBase(fromToken, fromAmount);
toAmount = _tryQuerySellBase(fromToken, fromAmount);
} else {
} else {
(toAmount, ) = _tryQueryBaseToBase(fromToken, toToken, fromAmount);
(toAmount, ) = _tryQueryBaseToBase(fromToken, toToken, fromAmount);
}
}
}
}
/// @inheritdoc IWooPPV2
/// @inheritdoc IWooPPV2
function query(
function query(
address fromToken,
address fromToken,
address toToken,
address toToken,
uint256 fromAmount
uint256 fromAmount
) external view override returns (uint256 toAmount) {
) external view override returns (uint256 toAmount) {
if (fromToken == quoteToken) {
if (fromToken == quoteToken) {
toAmount = _tryQuerySellQuote(toToken, fromAmount);
toAmount = _tryQuerySellQuote(toToken, fromAmount);
} else if (toToken == quoteToken) {
} else if (toToken == quoteToken) {
toAmount = _tryQuerySellBase(fromToken, fromAmount);
toAmount = _tryQuerySellBase(fromToken, fromAmount);
} else {
} else {
uint256 swapFee;
uint256 swapFee;
(toAmount, swapFee) = _tryQueryBaseToBase(fromToken, toToken, fromAmount);
(toAmount, swapFee) = _tryQueryBaseToBase(fromToken, toToken, fromAmount);
require(swapFee <= tokenInfos[quoteToken].reserve, "WooPPV2: INSUFF_QUOTE_FOR_SWAPFEE");
require(swapFee <= tokenInfos[quoteToken].reserve, "WooPPV2: INSUFF_QUOTE_FOR_SWAPFEE");
}
}
require(toAmount <= tokenInfos[toToken].reserve, "WooPPV2: INSUFF_BALANCE");
require(toAmount <= tokenInfos[toToken].reserve, "WooPPV2: INSUFF_BALANCE");
}
}
/// @inheritdoc IWooPPV2
/// @inheritdoc IWooPPV2
function swap(
function swap(
address fromToken,
address fromToken,
address toToken,
address toToken,
uint256 fromAmount,
uint256 fromAmount,
uint256 minToAmount,
uint256 minToAmount,
address to,
address to,
address rebateTo
address rebateTo
) external override returns (uint256 realToAmount) {
) external override returns (uint256 realToAmount) {
if (fromToken == quoteToken) {
if (fromToken == quoteToken) {
// case 1: quoteToken --> baseToken
// case 1: quoteToken --> baseToken
realToAmount = _sellQuote(toToken, fromAmount, minToAmount, to, rebateTo);
realToAmount = _sellQuote(toToken, fromAmount, minToAmount, to, rebateTo);
} else if (toToken == quoteToken) {
} else if (toToken == quoteToken) {
// case 2: fromToken --> quoteToken
// case 2: fromToken --> quoteToken
realToAmount = _sellBase(fromToken, fromAmount, minToAmount, to, rebateTo);
realToAmount = _sellBase(fromToken, fromAmount, minToAmount, to, rebateTo);
} else {
} else {
// case 3: fromToken --> toToken (base to base)
// case 3: fromToken --> toToken (base to base)
realToAmount = _swapBaseToBase(fromToken, toToken, fromAmount, minToAmount, to, rebateTo);
realToAmount = _swapBaseToBase(fromToken, toToken, fromAmount, minToAmount, to, rebateTo);
}
}
}
}
/// @dev OKAY to be public method
/// @dev OKAY to be public method
function claimFee() external nonReentrant {
function claimFee() external nonReentrant {
require(feeAddr != address(0), "WooPPV2: !feeAddr");
require(feeAddr != address(0), "WooPPV2: !feeAddr");
uint256 amountToTransfer = unclaimedFee;
uint256 amountToTransfer = unclaimedFee;
unclaimedFee = 0;
unclaimedFee = 0;
TransferHelper.safeTransfer(quoteToken, feeAddr, amountToTransfer);
TransferHelper.safeTransfer(quoteToken, feeAddr, amountToTransfer);
}
}
/// @inheritdoc IWooPPV2
/// @inheritdoc IWooPPV2
/// @dev pool size = tokenInfo.reserve
/// @dev pool size = tokenInfo.reserve
function poolSize(address token) public view override returns (uint256) {
function poolSize(address token) public view override returns (uint256) {
return tokenInfos[token].reserve;
return tokenInfos[token].reserve;
}
}
/// @dev User pool balance (substracted unclaimed fee)
/// @dev User pool balance (substracted unclaimed fee)
function balance(address token) public view returns (uint256) {
function balance(address token) public view returns (uint256) {
return token == quoteToken ? _rawBalance(token) - unclaimedFee : _rawBalance(token);
return token == quoteToken ? _rawBalance(token) - unclaimedFee : _rawBalance(token);
}
}
function decimalInfo(address baseToken) public view returns (DecimalInfo memory) {
function decimalInfo(address baseToken) public view returns (DecimalInfo memory) {
return
return
DecimalInfo({
DecimalInfo({
priceDec: uint64(10)**(IWooracleV2_2(wooracle).decimals(baseToken)), // 8
priceDec: uint64(10)**(IWooracleV2_2(wooracle).decimals(baseToken)), // 8
quoteDec: uint64(10)**(IERC20Metadata(quoteToken).decimals()), // 18 or 6
quoteDec: uint64(10)**(IERC20Metadata(quoteToken).decimals()), // 18 or 6
baseDec: uint64(10)**(IERC20Metadata(baseToken).decimals()) // 18 or 8
baseDec: uint64(10)**(IERC20Metadata(baseToken).decimals()) // 18 or 8
});
});
}
}
/* ----- Admin Functions ----- */
/* ----- Admin Functions ----- */
function setWooracle(address _wooracle) external onlyAdmin {
function setWooracle(address _wooracle) external onlyAdmin {
wooracle = IWooracleV2_2(_wooracle);
wooracle = IWooracleV2_2(_wooracle);
emit WooracleUpdated(_wooracle);
emit WooracleUpdated(_wooracle);
}
}
function setFeeAddr(address _feeAddr) external onlyAdmin {
function setFeeAddr(address _feeAddr) external onlyAdmin {
feeAddr = _feeAddr;
feeAddr = _feeAddr;
emit FeeAddrUpdated(_feeAddr);
emit FeeAddrUpdated(_feeAddr);
}
}
function setFeeRate(address token, uint16 rate) external onlyAdmin {
function setFeeRate(address token, uint16 rate) external onlyAdmin {
require(rate <= 1e5, "!rate");
require(rate <= 1e5, "!rate");
tokenInfos[token].feeRate = rate;
tokenInfos[token].feeRate = rate;
}
}
function setMaxGamma(address token, uint128 maxGamma) external onlyAdmin {
function setMaxGamma(address token, uint128 maxGamma) external onlyAdmin {
tokenInfos[token].maxGamma = maxGamma;
tokenInfos[token].maxGamma = maxGamma;
}
}
function setMaxNotionalSwap(address token, uint128 maxNotionalSwap) external onlyAdmin {
function setMaxNotionalSwap(address token, uint128 maxNotionalSwap) external onlyAdmin {
tokenInfos[token].maxNotionalSwap = maxNotionalSwap;
tokenInfos[token].maxNotionalSwap = maxNotionalSwap;
}
}
function setTokenInfo(
function setTokenInfo(
address token,
address token,
uint16 _feeRate,
uint16 _feeRate,
uint128 _maxGamma,
uint128 _maxGamma,
uint128 _maxNotionalSwap
uint128 _maxNotionalSwap
) external onlyAdmin {
) external onlyAdmin {
tokenInfos[token].feeRate = _feeRate;
tokenInfos[token].feeRate = _feeRate;
tokenInfos[token].maxGamma = _maxGamma;
tokenInfos[token].maxGamma = _maxGamma;
tokenInfos[token].maxNotionalSwap = _maxNotionalSwap;
tokenInfos[token].maxNotionalSwap = _maxNotionalSwap;
}
}
function pause() external onlyAdminOrPauseRole {
function pause() external onlyAdminOrPauseRole {
super._pause();
super._pause();
}
}
function unpause() external onlyAdmin {
function unpause() external onlyAdmin {
super._unpause();
super._unpause();
}
}
function setAdmin(address addr, bool flag) external onlyAdmin {
function setAdmin(address addr, bool flag) external onlyAdmin {
require(addr != address(0), "WooPPV2: !admin");
require(addr != address(0), "WooPPV2: !admin");
isAdmin[addr] = flag;
isAdmin[addr] = flag;
emit AdminUpdated(addr, flag);
emit AdminUpdated(addr, flag);
}
}
function setPauseRole(address addr, bool flag) external onlyAdmin {
function setPauseRole(address addr, bool flag) external onlyAdmin {
isPauseRole[addr] = flag;
isPauseRole[addr] = flag;
emit PauseRoleUpdated(addr, flag);
emit PauseRoleUpdated(addr, flag);
}
}
function deposit(address token, uint256 amount) public override nonReentrant onlyAdmin {
function deposit(address token, uint256 amount) public override nonReentrant onlyAdmin {
uint256 balanceBefore = balance(token);
uint256 balanceBefore = balance(token);
TransferHelper.safeTransferFrom(token, msg.sender, address(this), amount);
TransferHelper.safeTransferFrom(token, msg.sender, address(this), amount);
uint256 amountReceived = balance(token) - balanceBefore;
uint256 amountReceived = balance(token) - balanceBefore;
require(amountReceived >= amount, "AMOUNT_INSUFF");
require(amountReceived >= amount, "AMOUNT_INSUFF");
tokenInfos[token].reserve = uint192(tokenInfos[token].reserve + amount);
tokenInfos[token].reserve = uint192(tokenInfos[token].reserve + amount);
emit Deposit(token, msg.sender, amount);
emit Deposit(token, msg.sender, amount);
}
}
function depositAll(address token) external onlyAdmin {
function depositAll(address token) external onlyAdmin {
deposit(token, IERC20(token).balanceOf(msg.sender));
deposit(token, IERC20(token).balanceOf(msg.sender));
}
}
function repayWeeklyLending(address wantToken) external nonReentrant onlyAdmin returns (uint256 repaidAmount) {
function repayWeeklyLending(address wantToken) external nonReentrant onlyAdmin returns (uint256 repaidAmount) {
IWooLendingManager lendManager = lendManagers[wantToken];
IWooLendingManager lendManager = lendManagers[wantToken];
lendManager.accureInterest();
lendManager.accureInterest();
uint256 amount = lendManager.weeklyRepayment();
uint256 amount = lendManager.weeklyRepayment();
address repaidToken = lendManager.want();
address repaidToken = lendManager.want();
if (amount > 0) {
if (amount > 0) {
tokenInfos[repaidToken].reserve = uint192(tokenInfos[repaidToken].reserve - amount);
tokenInfos[repaidToken].reserve = uint192(tokenInfos[repaidToken].reserve - amount);
TransferHelper.safeApprove(repaidToken, address(lendManager), amount);
TransferHelper.safeApprove(repaidToken, address(lendManager), amount);
repaidAmount = lendManager.repayWeekly();
repaidAmount = lendManager.repayWeekly();
TransferHelper.safeApprove(repaidToken, address(lendManager), 0);
TransferHelper.safeApprove(repaidToken, address(lendManager), 0);
}
}
emit Withdraw(repaidToken, address(lendManager), amount);
emit Withdraw(repaidToken, address(lendManager), amount);
}
}
function repayPrincipal(address repaidToken, uint256 principalAmount)
function repayPrincipal(address repaidToken, uint256 principalAmount)
external
external
nonReentrant
nonReentrant
onlyAdmin
onlyAdmin
returns (uint256 repaidAmount)
returns (uint256 repaidAmount)
{
{
IWooLendingManager lendManager = lendManagers[repaidToken];
IWooLendingManager lendManager = lendManagers[repaidToken];
lendManager.accureInterest();
lendManager.accureInterest();
uint256 interest = lendManager.borrowedInterest();
uint256 interest = lendManager.borrowedInterest();
uint256 perfFee = (lendManager.perfRate() * interest) / 10000;
uint256 perfFee = (lendManager.perfRate() * interest) / 10000;
uint256 amount = principalAmount + interest + perfFee;
uint256 amount = principalAmount + interest + perfFee;
if (amount > 0) {
if (amount > 0) {
tokenInfos[repaidToken].reserve = uint192(tokenInfos[repaidToken].reserve - amount);
tokenInfos[repaidToken].reserve = uint192(tokenInfos[repaidToken].reserve - amount);
TransferHelper.safeApprove(repaidToken, address(lendManager), amount);
TransferHelper.safeApprove(repaidToken, address(lendManager), amount);
repaidAmount = lendManager.repayPrincipal(principalAmount);
repaidAmount = lendManager.repayPrincipal(principalAmount);
TransferHelper.safeApprove(repaidToken, address(lendManager), 0);
TransferHelper.safeApprove(repaidToken, address(lendManager), 0);
}
}
emit Withdraw(repaidToken, address(lendManager), amount);
emit Withdraw(repaidToken, address(lendManager), amount);
}
}
/// NOTE: Used for legacy lending manager only support `repay(amount)` method.
/// NOTE: Used for legacy lending manager only support `repay(amount)` method.
/// e.g. lending managers on arbitrum, OP and etc
/// e.g. lending managers on arbitrum, OP and etc
/// https://arbiscan.io/address/0x5c7ff24fa7af62bc25ad6747a6193183b4bb7bc5#code
/// https://arbiscan.io/address/0x5c7ff24fa7af62bc25ad6747a6193183b4bb7bc5#code
function repayLegacy(address repaidToken, uint256 amount) external nonReentrant onlyAdmin {
function repayLegacy(address repaidToken, uint256 amount) external nonReentrant onlyAdmin {
IWooLendingManager lendManager = lendManagers[repaidToken];
IWooLendingManager lendManager = lendManagers[repaidToken];
if (amount > 0) {
if (amount > 0) {
tokenInfos[repaidToken].reserve = uint192(tokenInfos[repaidToken].reserve - amount);
tokenInfos[repaidToken].reserve = uint192(tokenInfos[repaidToken].reserve - amount);
TransferHelper.safeApprove(repaidToken, address(lendManager), amount);
TransferHelper.safeApprove(repaidToken, address(lendManager), amount);
lendManager.repay(amount);
lendManager.repay(amount);
TransferHelper.safeApprove(repaidToken, address(lendManager), 0);
TransferHelper.safeApprove(repaidToken, address(lendManager), 0);
}
}
emit Withdraw(repaidToken, address(lendManager), amount);
emit Withdraw(repaidToken, address(lendManager), amount);
}
}
function withdraw(address token, uint256 amount) public nonReentrant onlyAdmin {
function withdraw(address token, uint256 amount) public nonReentrant onlyAdmin {
require(tokenInfos[token].reserve >= amount, "WooPPV2: !amount");
require(tokenInfos[token].reserve >= amount, "WooPPV2: !amount");
tokenInfos[token].reserve = uint192(tokenInfos[token].reserve - amount);
tokenInfos[token].reserve = uint192(tokenInfos[token].reserve - amount);
TransferHelper.safeTransfer(token, owner(), amount);
TransferHelper.safeTransfer(token, owner(), amount);
emit Withdraw(token, owner(), amount);
emit Withdraw(token, owner(), amount);
}
}
function withdrawAll(address token) external onlyAdmin {
function withdrawAll(address token) external onlyAdmin {
withdraw(token, poolSize(token));
withdraw(token, poolSize(token));
}
}
function skim(address token) public nonReentrant onlyAdmin {
function skim(address token) public nonReentrant onlyAdmin {
TransferHelper.safeTransfer(token, owner(), balance(token) - tokenInfos[token].reserve);
TransferHelper.safeTransfer(token, owner(), balance(token) - tokenInfos[token].reserve);
}
}
function skimMulTokens(address[] memory tokens) external onlyAdmin {
function skimMulTokens(address[] memory tokens) external onlyAdmin {
unchecked {
unchecked {
uint256 len = tokens.length;
uint256 len = tokens.length;
for (uint256 i = 0; i < len; i++) {
for (uint256 i = 0; i < len; i++) {
skim(tokens[i]);
skim(tokens[i]);
}
}
}
}
}
}
function sync(address token) external nonReentrant onlyAdmin {
function sync(address token) external nonReentrant onlyAdmin {
tokenInfos[token].reserve = uint192(balance(token));
tokenInfos[token].reserve = uint192(balance(token));
}
}
/* ----- Owner Functions ----- */
/* ----- Owner Functions ----- */
function setLendManager(IWooLendingManager _lendManager) external onlyOwner {
function setLendManager(IWooLendingManager _lendManager) external onlyOwner {
lendManagers[_lendManager.want()] = _lendManager;
lendManagers[_lendManager.want()] = _lendManager;
isAdmin[address(_lendManager)] = true;
isAdmin[address(_lendManager)] = true;
emit AdminUpdated(address(_lendManager), true);
emit AdminUpdated(address(_lendManager), true);
}
}
function migrateToNewPool(address token, address newPool) external onlyOwner {
function migrateToNewPool(address token, address newPool) external onlyOwner {
require(token != address(0), "WooPPV2: !token");
require(token != address(0), "WooPPV2: !token");
require(newPool != address(0), "WooPPV2: !newPool");
require(newPool != address(0), "WooPPV2: !newPool");
tokenInfos[token].reserve = 0;
tokenInfos[token].reserve = 0;
uint256 bal = balance(token);
uint256 bal = balance(token);
TransferHelper.safeApprove(token, newPool, bal);
TransferHelper.safeApprove(token, newPool, bal);
WooPPV2(newPool).depositAll(token);
WooPPV2_FS(newPool).depositAll(token);
emit Migrate(token, newPool, bal);
emit Migrate(token, newPool, bal);
}
}
function inCaseTokenGotStuck(address stuckToken) external onlyOwner {
function inCaseTokenGotStuck(address stuckToken) external onlyOwner {
if (stuckToken == ETH_PLACEHOLDER_ADDR) {
if (stuckToken == ETH_PLACEHOLDER_ADDR) {
TransferHelper.safeTransferETH(msg.sender, address(this).balance);
TransferHelper.safeTransferETH(msg.sender, address(this).balance);
} else {
} else {
uint256 amount = IERC20(stuckToken).balanceOf(address(this));
uint256 amount = IERC20(stuckToken).balanceOf(address(this));
TransferHelper.safeTransfer(stuckToken, msg.sender, amount);
TransferHelper.safeTransfer(stuckToken, msg.sender, amount);
}
}
}
}
/* ----- Private Functions ----- */
/* ----- Private Functions ----- */
function _tryQuerySellBase(address baseToken, uint256 baseAmount)
function _tryQuerySellBase(address baseToken, uint256 baseAmount)
private
private
view
view
whenNotPaused
whenNotPaused
returns (uint256 quoteAmount)
returns (uint256 quoteAmount)
{
{
IWooracleV2_2.State memory state = IWooracleV2_2(wooracle).state(baseToken);
IWooracleV2_2.State memory state = IWooracleV2_2(wooracle).state(baseToken);
(quoteAmount, ) = _calcQuoteAmountSellBase(baseToken, baseAmount, state);
(quoteAmount, ) = _calcQuoteAmountSellBase(baseToken, baseAmount, state);
uint256 fee = (quoteAmount * tokenInfos[baseToken].feeRate) / 1e5;
uint256 fee = (quoteAmount * tokenInfos[baseToken].feeRate) / 1e5;
quoteAmount = quoteAmount - fee;
quoteAmount = quoteAmount - fee;
}
}
function _tryQuerySellQuote(address baseToken, uint256 quoteAmount)
function _tryQuerySellQuote(address baseToken, uint256 quoteAmount)
private
private
view
view
whenNotPaused
whenNotPaused
returns (uint256 baseAmount)
returns (uint256 baseAmount)
{
{
uint256 swapFee = (quoteAmount * tokenInfos[baseToken].feeRate) / 1e5;
uint256 swapFee = (quoteAmount * tokenInfos[baseToken].feeRate) / 1e5;
quoteAmount = quoteAmount - swapFee;
quoteAmount = quoteAmount - swapFee;
IWooracleV2_2.State memory state = IWooracleV2_2(wooracle).state(baseToken);
IWooracleV2_2.State memory state = IWooracleV2_2(wooracle).state(baseToken);
(baseAmount, ) = _calcBaseAmountSellQuote(baseToken, quoteAmount, state);
(baseAmount, ) = _calcBaseAmountSellQuote(baseToken, quoteAmount, state);
}
}
function _tryQueryBaseToBase(
function _tryQueryBaseToBase(
address baseToken1,
address baseToken1,
address baseToken2,
address baseToken2,
uint256 base1Amount
uint256 base1Amount
) private view whenNotPaused returns (uint256 base2Amount, uint256 swapFee) {
) private view whenNotPaused returns (uint256 base2Amount, uint256 swapFee) {
if (
if (
baseToken1 == address(0) || baseToken2 == address(0) || baseToken1 == quoteToken || baseToken2 == quoteToken
baseToken1 == address(0) || baseToken2 == address(0) || baseToken1 == quoteToken || baseToken2 == quoteToken
) {
) {
return (0, 0);
return (0, 0);
}
}
IWooracleV2_2.State memory state1 = IWooracleV2_2(wooracle).state(baseToken1);
IWooracleV2_2.State memory state1 = IWooracleV2_2(wooracle).state(baseToken1);
IWooracleV2_2.State memory state2 = IWooracleV2_2(wooracle).state(baseToken2);
IWooracleV2_2.State memory state2 = IWooracleV2_2(wooracle).state(baseToken2);
uint64 spread = _maxUInt64(state1.spread, state2.spread) / 2;
uint16 feeRate = _maxUInt16(tokenInfos[baseToken1].feeRate, tokenInfos[baseToken2].feeRate);
uint16 feeRate = _maxUInt16(tokenInfos[baseToken1].feeRate, tokenInfos[baseToken2].feeRate);
state1.spread = spread;
state2.spread = spread;
(uint256 quoteAmount, ) = _calcQuoteAmountSellBase(baseToken1, base1Amount, state1);
(uint256 quoteAmount, ) = _calcQuoteAmountSellBase(baseToken1, base1Amount, state1);
swapFee = (quoteAmount * feeRate) / 1e5;
swapFee = (quoteAmount * feeRate) / 1e5;
quoteAmount = quoteAmount - swapFee;
quoteAmount = quoteAmount - swapFee;
(base2Amount, ) = _calcBaseAmountSellQuote(baseToken2, quoteAmount, state2);
(base2Amount, ) = _calcBaseAmountSellQuote(baseToken2, quoteAmount, state2);
}
}
function _sellBase(
function _sellBase(
address baseToken,
address baseToken,
uint256 baseAmount,
uint256 baseAmount,
uint256 minQuoteAmount,
uint256 minQuoteAmount,
address to,
address to,
address rebateTo
address rebateTo
) private nonReentrant whenNotPaused returns (uint256 quoteAmount) {
) private nonReentrant whenNotPaused returns (uint256 quoteAmount) {
require(baseToken != address(0), "WooPPV2: !baseToken");
require(baseToken != address(0), "WooPPV2: !baseToken");
require(to != address(0), "WooPPV2: !to");
require(to != address(0), "WooPPV2: !to");
require(baseToken != quoteToken, "WooPPV2: baseToken==quoteToken");
require(baseToken != quoteToken, "WooPPV2: baseToken==quoteToken");
require(balance(baseToken) - tokenInfos[baseToken].reserve >= baseAmount, "WooPPV2: !BASE");
require(balance(baseToken) - tokenInfos[baseToken].reserve >= baseAmount, "WooPPV2: !BASE");
{
{
uint256 newPrice;
uint256 newPrice;
IWooracleV2_2.State memory state = IWooracleV2_2(wooracle).state(baseToken);
IWooracleV2_2.State memory state = IWooracleV2_2(wooracle).state(baseToken);
(quoteAmount, newPrice) = _calcQuoteAmountSellBase(baseToken, baseAmount, state);
(quoteAmount, newPrice) = _calcQuoteAmountSellBase(baseToken, baseAmount, state);
IWooracleV2_2(wooracle).postPrice(baseToken, uint128(newPrice));
IWooracleV2_2(wooracle).postPrice(baseToken, uint128(newPrice));
// console.log('Post new price:', newPrice, newPrice/1e8);
// console.log('Post new price:', newPrice, newPrice/1e8);
}
}
uint256 swapFee = (quoteAmount * tokenInfos[baseToken].feeRate) / 1e5;
uint256 swapFee = (quoteAmount * tokenInfos[baseToken].feeRate) / 1e5;
quoteAmount = quoteAmount - swapFee;
quoteAmount = quoteAmount - swapFee;
require(quoteAmount >= minQuoteAmount, "WooPPV2: quoteAmount_LT_minQuoteAmount");
require(quoteAmount >= minQuoteAmount, "WooPPV2: quoteAmount_LT_minQuoteAmount");
unclaimedFee = unclaimedFee + swapFee;
unclaimedFee = unclaimedFee + swapFee;
tokenInfos[baseToken].reserve = uint192(tokenInfos[baseToken].reserve + baseAmount);
tokenInfos[baseToken].reserve = uint192(tokenInfos[baseToken].reserve + baseAmount);
tokenInfos[quoteToken].reserve = uint192(tokenInfos[quoteToken].reserve - quoteAmount - swapFee);
tokenInfos[quoteToken].reserve = uint192(tokenInfos[quoteToken].reserve - quoteAmount - swapFee);
if (to != address(this)) {
if (to != address(this)) {
TransferHelper.safeTransfer(quoteToken, to, quoteAmount);
TransferHelper.safeTransfer(quoteToken, to, quoteAmount);
}
}
emit WooSwap(
emit WooSwap(
baseToken,
baseToken,
quoteToken,
quoteToken,
baseAmount,
baseAmount,
quoteAmount,
quoteAmount,
msg.sender,
msg.sender,
to,
to,
rebateTo,
rebateTo,
quoteAmount + swapFee,
quoteAmount + swapFee,
swapFee
swapFee
);
);
}
}
function _sellQuote(
function _sellQuote(
address baseToken,
address baseToken,
uint256 quoteAmount,
uint256 quoteAmount,
uint256 minBaseAmount,
uint256 minBaseAmount,
address to,
address to,
address rebateTo
address rebateTo
) private nonReentrant whenNotPaused returns (uint256 baseAmount) {
) private nonReentrant whenNotPaused returns (uint256 baseAmount) {
require(baseToken != address(0), "WooPPV2: !baseToken");
require(baseToken != address(0), "WooPPV2: !baseToken");
require(to != address(0), "WooPPV2: !to");
require(to != address(0), "WooPPV2: !to");
require(baseToken != quoteToken, "WooPPV2: baseToken==quoteToken");
require(baseToken != quoteToken, "WooPPV2: baseToken==quoteToken");
require(balance(quoteToken) - tokenInfos[quoteToken].reserve >= quoteAmount, "WooPPV2: !QUOTE");
require(balance(quoteToken) - tokenInfos[quoteToken].reserve >= quoteAmount, "WooPPV2: !QUOTE");
uint256 swapFee = (quoteAmount * tokenInfos[baseToken].feeRate) / 1e5;
uint256 swapFee = (quoteAmount * tokenInfos[baseToken].feeRate) / 1e5;
quoteAmount = quoteAmount - swapFee;
quoteAmount = quoteAmount - swapFee;
unclaimedFee = unclaimedFee + swapFee;
unclaimedFee = unclaimedFee + swapFee;
{
{
uint256 newPrice;
uint256 newPrice;
IWooracleV2_2.State memory state = IWooracleV2_2(wooracle).state(baseToken);
IWooracleV2_2.State memory state = IWooracleV2_2(wooracle).state(baseToken);
(baseAmount, newPrice) = _calcBaseAmountSellQuote(baseToken, quoteAmount, state);
(baseAmount, newPrice) = _calcBaseAmountSellQuote(baseToken, quoteAmount, state);
IWooracleV2_2(wooracle).postPrice(baseToken, uint128(newPrice));
IWooracleV2_2(wooracle).postPrice(baseToken, uint128(newPrice));
// console.log('Post new price:', newPrice, newPrice/1e8);
// console.log('Post new price:', newPrice, newPrice/1e8);
require(baseAmount >= minBaseAmount, "WooPPV2: baseAmount_LT_minBaseAmount");
require(baseAmount >= minBaseAmount, "WooPPV2: baseAmount_LT_minBaseAmount");
}
}
tokenInfos[baseToken].reserve = uint192(tokenInfos[baseToken].reserve - baseAmount);
tokenInfos[baseToken].reserve = uint192(tokenInfos[baseToken].reserve - baseAmount);
tokenInfos[quoteToken].reserve = uint192(tokenInfos[quoteToken].reserve + quoteAmount);
tokenInfos[quoteToken].reserve = uint192(tokenInfos[quoteToken].reserve + quoteAmount);
if (to != address(this)) {
if (to != address(this)) {
TransferHelper.safeTransfer(baseToken, to, baseAmount);
TransferHelper.safeTransfer(baseToken, to, baseAmount);
}
}
emit WooSwap(
emit WooSwap(
quoteToken,
quoteToken,
baseToken,
baseToken,
quoteAmount + swapFee,
quoteAmount + swapFee,
baseAmount,
baseAmount,
msg.sender,
msg.sender,
to,
to,
rebateTo,
rebateTo,
quoteAmount + swapFee,
quoteAmount + swapFee,
swapFee
swapFee
);
);
}
}
function _swapBaseToBase(
function _swapBaseToBase(
address baseToken1,
address baseToken1,
address baseToken2,
address baseToken2,
uint256 base1Amount,
uint256 base1Amount,
uint256 minBase2Amount,
uint256 minBase2Amount,
address to,
address to,
address rebateTo
address rebateTo
) private nonReentrant whenNotPaused returns (uint256 base2Amount) {
) private nonReentrant whenNotPaused returns (uint256 base2Amount) {
require(baseToken1 != address(0) && baseToken1 != quoteToken, "WooPPV2: !baseToken1");
require(baseToken1 != address(0) && baseToken1 != quoteToken, "WooPPV2: !baseToken1");
require(baseToken2 != address(0) && baseToken2 != quoteToken, "WooPPV2: !baseToken2");
require(baseToken2 != address(0) && baseToken2 != quoteToken, "WooPPV2: !baseToken2");
require(baseToken1 != baseToken2, "WooPPV2: base1==base2");
require(baseToken1 != baseToken2, "WooPPV2: base1==base2");
require(to != address(0), "WooPPV2: !to");
require(to != address(0), "WooPPV2: !to");
require(balance(baseToken1) - tokenInfos[baseToken1].reserve >= base1Amount, "WooPPV2: !BASE1_BALANCE");
require(balance(baseToken1) - tokenInfos[baseToken1].reserve >= base1Amount, "WooPPV2: !BASE1_BALANCE");
IWooracleV2_2.State memory state1 = IWooracleV2_2(wooracle).state(baseToken1);
IWooracleV2_2.State memory state1 = IWooracleV2_2(wooracle).state(baseToken1);
IWooracleV2_2.State memory state2 = IWooracleV2_2(wooracle).state(baseToken2);
IWooracleV2_2.State memory state2 = IWooracleV2_2(wooracle).state(baseToken2);
uint256 swapFee;
uint256 swapFee;
uint256 quoteAmount;
uint256 quoteAmount;
{
{
uint64 spread = _maxUInt64(state1.spread, state2.spread) / 2;
uint16 feeRate = _maxUInt16(tokenInfos[baseToken1].feeRate, tokenInfos[baseToken2].feeRate);
uint16 feeRate = _maxUInt16(tokenInfos[baseToken1].feeRate, tokenInfos[baseToken2].feeRate);
state1.spread = spread;
state2.spread = spread;
uint256 newBase1Price;
uint256 newBase1Price;
(quoteAmount, newBase1Price) = _calcQuoteAmountSellBase(baseToken1, base1Amount, state1);
(quoteAmount, newBase1Price) = _calcQuoteAmountSellBase(baseToken1, base1Amount, state1);
IWooracleV2_2(wooracle).postPrice(baseToken1, uint128(newBase1Price));
IWooracleV2_2(wooracle).postPrice(baseToken1, uint128(newBase1Price));
// console.log('Post new base1 price:', newBase1Price, newBase1Price/1e8);
// console.log('Post new base1 price:', newBase1Price, newBase1Price/1e8);
swapFee = (quoteAmount * feeRate) / 1e5;
swapFee = (quoteAmount * feeRate) / 1e5;
}
}
quoteAmount = quoteAmount - swapFee;
quoteAmount = quoteAmount - swapFee;
unclaimedFee = unclaimedFee + swapFee;
unclaimedFee = unclaimedFee + swapFee;
tokenInfos[quoteToken].reserve = uint192(tokenInfos[quoteToken].reserve - swapFee);
tokenInfos[quoteToken].reserve = uint192(tokenInfos[quoteToken].reserve - swapFee);
tokenInfos[baseToken1].reserve = uint192(tokenInfos[baseToken1].reserve + base1Amount);
tokenInfos[baseToken1].reserve = uint192(tokenInfos[baseToken1].reserve + base1Amount);
{
{
uint256 newBase2Price;
uint256 newBase2Price;
(base2Amount, newBase2Price) = _calcBaseAmountSellQuote(baseToken2, quoteAmount, state2);
(base2Amount, newBase2Price) = _calcBaseAmountSellQuote(baseToken2, quoteAmount, state2);
IWooracleV2_2(wooracle).postPrice(baseToken2, uint128(newBase2Price));
IWooracleV2_2(wooracle).postPrice(baseToken2, uint128(newBase2Price));
// console.log('Post new base2 price:', newBase2Price, newBase2Price/1e8);
// console.log('Post new base2 price:', newBase2Price, newBase2Price/1e8);
require(base2Amount >= minBase2Amount, "WooPPV2: base2Amount_LT_minBase2Amount");
require(base2Amount >= minBase2Amount, "WooPPV2: base2Amount_LT_minBase2Amount");
}
}
tokenInfos[baseToken2].reserve = uint192(tokenInfos[baseToken2].reserve - base2Amount);
tokenInfos[baseToken2].reserve = uint192(tokenInfos[baseToken2].reserve - base2Amount);
if (to != address(this)) {
if (to != address(this)) {
TransferHelper.safeTransfer(baseToken2, to, base2Amount);
TransferHelper.safeTransfer(baseToken2, to, base2Amount);
}
}
emit WooSwap(
emit WooSwap(
baseToken1,
baseToken1,
baseToken2,
baseToken2,
base1Amount,
base1Amount,
base2Amount,
base2Amount,
msg.sender,
msg.sender,
to,
to,
rebateTo,
rebateTo,
quoteAmount + swapFee,
quoteAmount + swapFee,
swapFee
swapFee
);
);
}
}
/// @dev Get the pool's balance of the specified token
/// @dev Get the pool's balance of the specified token
/// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize
/// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize
/// @dev forked and curtesy by Uniswap v3 core
/// @dev forked and curtesy by Uniswap v3 core
function _rawBalance(address token) private view returns (uint256) {
function _rawBalance(address token) private view returns (uint256) {
(bool success, bytes memory data) = token.staticcall(
(bool success, bytes memory data) = token.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector, address(this))
abi.encodeWithSelector(IERC20.balanceOf.selector, address(this))
);
);
require(success && data.length >= 32, "WooPPV2: !BALANCE");
require(success && data.length >= 32, "WooPPV2: !BALANCE");
return abi.decode(data, (uint256));
return abi.decode(data, (uint256));
}
}
function _calcQuoteAmountSellBase(
function _calcQuoteAmountSellBase(
address baseToken,
address baseToken,
uint256 baseAmount,
uint256 baseAmount,
IWooracleV2_2.State memory state
IWooracleV2_2.State memory state
) private view returns (uint256 quoteAmount, uint256 newPrice) {
) private view returns (uint256 quoteAmount, uint256 newPrice) {
require(state.woFeasible, "WooPPV2: !ORACLE_FEASIBLE");
require(state.woFeasible, "WooPPV2: !ORACLE_FEASIBLE");
require(state.price > 0, "WooPPV2: !ORACE_PRICE");
require(state.price > 0, "WooPPV2: !ORACE_PRICE");
DecimalInfo memory decs = decimalInfo(baseToken);
DecimalInfo memory decs = decimalInfo(baseToken);
// gamma = k * price * base_amount; and decimal 18
// gamma = k * price * base_amount; and decimal 18
uint256 gamma;
uint256 gamma;
{
{
uint256 notionalSwap = (baseAmount * state.price * decs.quoteDec) / decs.baseDec / decs.priceDec;
uint256 notionalSwap = (baseAmount * state.price * decs.quoteDec) / decs.baseDec / decs.priceDec;
require(notionalSwap <= tokenInfos[baseToken].maxNotionalSwap, "WooPPV2: !maxNotionalValue");
require(notionalSwap <= tokenInfos[baseToken].maxNotionalSwap, "WooPPV2: !maxNotionalValue");
gamma = (baseAmount * state.price * state.coeff) / decs.priceDec / decs.baseDec;
gamma = (baseAmount * state.price * state.coeff) / decs.priceDec / decs.baseDec;
require(gamma <= tokenInfos[baseToken].maxGamma, "WooPPV2: !gamma");
require(gamma <= tokenInfos[baseToken].maxGamma, "WooPPV2: !gamma");
// Formula: quoteAmount = baseAmount * oracle.price * (1 - oracle.k * baseAmount * oracle.price - oracle.spread)
// Formula: quoteAmount = baseAmount * oracle.price * (1 - oracle.k * baseAmount * oracle.price - oracle.spread)
quoteAmount =
quoteAmount =
(((baseAmount * state.price * decs.quoteDec) / decs.priceDec) *
(((baseAmount * state.price * decs.quoteDec) / decs.priceDec) *
(uint256(1e18) - gamma - state.spread)) /
(uint256(1e18) - gamma - state.spread)) /
1e18 /
1e18 /
decs.baseDec;
decs.baseDec;
}
}
// newPrice = oracle.price * (1 - k * oracle.price * baseAmount)
// newPrice = oracle.price * (1 - k * oracle.price * baseAmount)
newPrice = ((uint256(1e18) - gamma) * state.price) / 1e18;
newPrice = ((uint256(1e18) - gamma) * state.price) / 1e18;
}
}
function _calcBaseAmountSellQuote(
function _calcBaseAmountSellQuote(
address baseToken,
address baseToken,
uint256 quoteAmount,
uint256 quoteAmount,
IWooracleV2_2.State memory state
IWooracleV2_2.State memory state
) private view returns (uint256 baseAmount, uint256 newPrice) {
) private view returns (uint256 baseAmount, uint256 newPrice) {
require(state.woFeasible, "WooPPV2: !ORACLE_FEASIBLE");
require(state.woFeasible, "WooPPV2: !ORACLE_FEASIBLE");
require(state.price > 0, "WooPPV2: !ORACE_PRICE");
require(state.price > 0, "WooPPV2: !ORACE_PRICE");
DecimalInfo memory decs = decimalInfo(baseToken);
DecimalInfo memory decs = decimalInfo(baseToken);
// gamma = k * quote_amount; and decimal 18
// gamma = k * quote_amount; and decimal 18
uint256 gamma;
uint256 gamma;
{
{
require(quoteAmount <= tokenInfos[baseToken].maxNotionalSwap,
require(quoteAmount <= tokenInfos[baseToken].maxNotionalSwap, "WooPPV2: !maxNotionalValue");
gamma = (quoteAmount * state.coeff) / decs.quoteDec;
require(gamma <= tokenInfos[baseToken].maxGamma, "WooPPV2: !gamma");
// Formula: baseAmount = quoteAmount / oracle