Comparing sensitive data, confidential files or internal emails?

Most legal and privacy policies prohibit uploading sensitive data online. Diffchecker Desktop ensures your confidential information never leaves your computer. Work offline and compare documents securely.

wooPP_full_spread

Created Diff never expires
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