// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
interface IERC20 {
function totalSupply() external view returns (uint256);
function transfer(address recipient, uint amount) external returns (bool);
function decimals() external view returns (uint8);
function symbol() external view returns (string memory);
function balanceOf(address) external view returns (uint);
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool);
function allowance(
address owner,
address spender
) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}
interface VaultAPI is IERC20 {
// NOTE: Vyper produces multiple signatures for a given function with "default" args
function deposit() external returns (uint256);
function deposit(uint256 amount) external returns (uint256);
function deposit(
uint256 amount,
address recipient
) external returns (uint256);
// NOTE: Vyper produces multiple signatures for a given function with "default" args
function withdraw() external returns (uint256);
function withdraw(uint256 maxShares) external returns (uint256);
function withdraw(
uint256 maxShares,
address recipient
) external returns (uint256);
function pricePerShare() external view returns (uint256);
}
interface IBMX is IERC20 {
function glp() external view returns (address);
function usdg() external view returns (address);
function cooldownDuration() external returns (uint256);
function getAumInUsdg(bool maximise) external view returns (uint256);
function underlyingToken() external view returns (address);
function lastAddedAt(address _account) external returns (uint256);
function getPaymentTokenAmountForExerciseLp(
uint256,
uint256
) external view returns (uint256, uint256);
function addLiquidity(
address _token,
uint256 _amount,
uint256 _minUsdg,
uint256 _minGlp
) external returns (uint256);
function unstakeAndRedeemGlp(
address _tokenOut,
uint256 _glpAmount,
uint256 _minOut,
address _receiver
) external returns (uint256);
function addLiquidityForAccount(
address _fundingAccount,
address _account,
address _token,
uint256 _amount,
uint256 _minUsdg,
uint256 _minGlp
) external returns (uint256);
function removeLiquidity(
address _tokenOut,
uint256 _glpAmount,
uint256 _minOut,
address _receiver
) external returns (uint256);
function removeLiquidityForAccount(
address _account,
address _tokenOut,
uint256 _glpAmount,
uint256 _minOut,
address _receiver
) external returns (uint256);
function setShortsTrackerAveragePriceWeight(
uint256 _shortsTrackerAveragePriceWeight
) external;
function setCooldownDuration(uint256 _cooldownDuration) external;
function exerciseLp(
uint256 _amount,
uint256 _maxPaymentAmount,
address _recipient,
uint256 _discount,
uint256 _deadline
) external returns (uint256, uint256);
function claimable(address) external view returns (uint256);
function pairAmounts(address) external view returns (uint256);
function depositBalances(address, address) external view returns (uint256);
function handleRewards(bool, bool, bool) external;
function withdraw() external;
function deposit(uint256) external;
function signalTransfer(address) external;
function acceptTransfer(address) external;
function getPairAmount(address, uint256) external view returns (uint256);
function mintAndStakeGlp(
address,
uint256,
uint256,
uint256
) external returns (uint256);
function updateCumulativeFundingRate(
address _collateralToken,
address _indexToken
) external returns (bool);
function validateIncreasePosition(
address _account,
address _collateralToken,
address _indexToken,
uint256 _sizeDelta,
bool _isLong
) external view;
function validateDecreasePosition(
address _account,
address _collateralToken,
address _indexToken,
uint256 _collateralDelta,
uint256 _sizeDelta,
bool _isLong,
address _receiver
) external view;
function getEntryFundingRate(
address _collateralToken,
address _indexToken,
bool _isLong
) external view returns (uint256);
function getPositionFee(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong,
uint256 _sizeDelta
) external view returns (uint256);
function getFundingFee(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong,
uint256 _size,
uint256 _entryFundingRate
) external view returns (uint256);
function getBuyUsdgFeeBasisPoints(
address _token,
uint256 _usdgAmount
) external view returns (uint256);
function getSellUsdgFeeBasisPoints(
address _token,
uint256 _usdgAmount
) external view returns (uint256);
function getSwapFeeBasisPoints(
address _tokenIn,
address _tokenOut,
uint256 _usdgAmount
) external view returns (uint256);
function isInitialized() external view returns (bool);
function adjustForDecimals(
uint256 _amount,
address _tokenDiv,
address _tokenMul
) external view returns (uint256);
function PRICE_PRECISION() external view returns (uint256);
function BASIS_POINTS_DIVISOR() external view returns (uint256);
function isSwapEnabled() external view returns (bool);
function isLeverageEnabled() external view returns (bool);
function setError(uint256 _errorCode, string calldata _error) external;
function router() external view returns (address);
function gov() external view returns (address);
function whitelistedTokenCount() external view returns (uint256);
function maxLeverage() external view returns (uint256);
function minProfitTime() external view returns (uint256);
function hasDynamicFees() external view returns (bool);
function fundingInterval() external view returns (uint256);
function totalTokenWeights() external view returns (uint256);
function getTargetUsdgAmount(
address _token
) external view returns (uint256);
function inManagerMode() external view returns (bool);
function inPrivateLiquidationMode() external view returns (bool);
function maxGasPrice() external view returns (uint256);
function approvedRouters(
address _account,
address _router
) external view returns (bool);
function isLiquidator(address _account) external view returns (bool);
function isManager(address _account) external view returns (bool);
function minProfitBasisPoints(
address _token
) external view returns (uint256);
function tokenBalances(address _token) external view returns (uint256);
function lastFundingTimes(address _token) external view returns (uint256);
function setMaxLeverage(uint256 _maxLeverage) external;
function setInManagerMode(bool _inManagerMode) external;
function setManager(address _manager, bool _isManager) external;
function setIsSwapEnabled(bool _isSwapEnabled) external;
function setIsLeverageEnabled(bool _isLeverageEnabled) external;
function setMaxGasPrice(uint256 _maxGasPrice) external;
function setUsdgAmount(address _token, uint256 _amount) external;
function setBufferAmount(address _token, uint256 _amount) external;
function setMaxGlobalShortSize(address _token, uint256 _amount) external;
function setInPrivateLiquidationMode(
bool _inPrivateLiquidationMode
) external;
function setLiquidator(address _liquidator, bool _isActive) external;
function setFundingRate(
uint256 _fundingInterval,
uint256 _fundingRateFactor,
uint256 _stableFundingRateFactor
) external;
function setFees(
uint256 _taxBasisPoints,
uint256 _stableTaxBasisPoints,
uint256 _mintBurnFeeBasisPoints,
uint256 _swapFeeBasisPoints,
uint256 _stableSwapFeeBasisPoints,
uint256 _marginFeeBasisPoints,
uint256 _liquidationFeeUsd,
uint256 _minProfitTime,
bool _hasDynamicFees
) external;
function setTokenConfig(
address _token,
uint256 _tokenDecimals,
uint256 _redemptionBps,
uint256 _minProfitBps,
uint256 _maxUsdgAmount,
bool _isStable,
bool _isShortable
) external;
function setPriceFeed(address _priceFeed) external;
function withdrawFees(
address _token,
address _receiver
) external returns (uint256);
function directPoolDeposit(address _token) external;
function buyUSDG(
address _token,
address _receiver
) external returns (uint256);
function sellUSDG(
address _token,
address _receiver
) external returns (uint256);
function swap(
address _tokenIn,
address _tokenOut,
address _receiver
) external returns (uint256);
function increasePosition(
address _account,
address _collateralToken,
address _indexToken,
uint256 _sizeDelta,
bool _isLong
) external;
function decreasePosition(
address _account,
address _collateralToken,
address _indexToken,
uint256 _collateralDelta,
uint256 _sizeDelta,
bool _isLong,
address _receiver
) external returns (uint256);
function validateLiquidation(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong,
bool _raise
) external view returns (uint256, uint256);
function liquidatePosition(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong,
address _feeReceiver
) external;
function tokenToUsdMin(
address _token,
uint256 _tokenAmount
) external view returns (uint256);
function priceFeed() external view returns (address);
function fundingRateFactor() external view returns (uint256);
function stableFundingRateFactor() external view returns (uint256);
function cumulativeFundingRates(
address _token
) external view returns (uint256);
function getNextFundingRate(address _token) external view returns (uint256);
function getFeeBasisPoints(
address _token,
uint256 _usdgDelta,
uint256 _feeBasisPoints,
uint256 _taxBasisPoints,
bool _increment
) external view returns (uint256);
function liquidationFeeUsd() external view returns (uint256);
function taxBasisPoints() external view returns (uint256);
function stableTaxBasisPoints() external view returns (uint256);
function mintBurnFeeBasisPoints() external view returns (uint256);
function swapFeeBasisPoints() external view returns (uint256);
function stableSwapFeeBasisPoints() external view returns (uint256);
function marginFeeBasisPoints() external view returns (uint256);
function allWhitelistedTokensLength() external view returns (uint256);
function allWhitelistedTokens(uint256) external view returns (address);
function whitelistedTokens(address _token) external view returns (bool);
function stableTokens(address _token) external view returns (bool);
function shortableTokens(address _token) external view returns (bool);
function feeReserves(address _token) external view returns (uint256);
function globalShortSizes(address _token) external view returns (uint256);
function globalShortAveragePrices(
address _token
) external view returns (uint256);
function maxGlobalShortSizes(
address _token
) external view returns (uint256);
function tokenDecimals(address _token) external view returns (uint256);
function tokenWeights(address _token) external view returns (uint256);
function guaranteedUsd(address _token) external view returns (uint256);
function poolAmounts(address _token) external view returns (uint256);
function bufferAmounts(address _token) external view returns (uint256);
function reservedAmounts(address _token) external view returns (uint256);
function usdgAmounts(address _token) external view returns (uint256);
function maxUsdgAmounts(address _token) external view returns (uint256);
function getLivePrice() external view returns (uint256);
function getRedemptionAmount(
address _token,
uint256 _usdgAmount
) external view returns (uint256);
function getMaxPrice(address _token) external view returns (uint256);
function getMinPrice(address _token) external view returns (uint256);
function getDelta(
address _indexToken,
uint256 _size,
uint256 _averagePrice,
bool _isLong,
uint256 _lastIncreasedTime
) external view returns (bool, uint256);
function getPosition(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong
)
external
view
returns (
uint256,
uint256,
uint256,
uint256,
uint256,
uint256,
bool,
uint256
);
}
interface IPair {
function metadata()
external
view
returns (
uint dec0,
uint dec1,
uint r0,
uint r1,
bool st,
address t0,
address t1
);
function tokens() external returns (address, address);
function token0() external returns (address);
function token1() external returns (address);
function externalBribe() external returns (address);
function transferFrom(
address src,
address dst,
uint amount
) external returns (bool);
function permit(
address owner,
address spender,
uint value,
uint deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function swap(
uint amount0Out,
uint amount1Out,
address to,
bytes calldata data
) external;
function burn(address to) external returns (uint amount0, uint amount1);
function mint(address to) external returns (uint liquidity);
function getReserves()
external
view
returns (uint _reserve0, uint _reserve1, uint _blockTimestampLast);
function getAmountOut(uint, address) external view returns (uint);
function setHasGauge(bool value) external;
function setExternalBribe(address _externalBribe) external;
function hasGauge() external view returns (bool);
function stable() external view returns (bool);
function prices(
address tokenIn,
uint amountIn,
uint points
) external view returns (uint[] memory);
}
interface IPairFactory {
function allPairsLength() external view returns (uint);
function isPair(address pair) external view returns (bool);
function isPaused() external view returns (bool);
function pairCodeHash() external pure returns (bytes32);
function getFee(address pair) external view returns (uint256);
function getPair(
address tokenA,
address token,
bool stable
) external view returns (address);
function getInitializable() external view returns (address, address, bool);
function createPair(
address tokenA,
address tokenB,
bool stable
) external returns (address pair);
function voter() external view returns (address);
function tank() external view returns (address);
}
interface IBalancer {
function flashLoan(
address recipient,
address[] memory tokens,
uint256[] memory amounts,
bytes memory userData
) external;
}
interface IRouter {
struct route {
address from;
address to;
bool stable;
}
function pairFor(
address tokenA,
address tokenB,
bool stable
) external view returns (address pair);
function getAmountOut(
uint amountIn,
address tokenIn,
address tokenOut,
bool stable
) external view returns (uint amount);
function getReserves(
address tokenA,
address tokenB,
bool stable
) external view returns (uint, uint);
function addLiquidity(
address tokenA,
address tokenB,
bool stable,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint, uint, uint);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
route[] calldata routes,
address to,
uint deadline
) external returns (uint[] memory amounts);
}
interface IWETH is IERC20 {
function deposit() external payable;
function transfer(address to, uint256 value) external returns (bool);
function withdraw(uint256) external;
}
interface IShareHelper {
function sharesToAmount(
address vault,
uint shares,
bool useCeiling
) external view returns (uint);
function amountToShares(
address vault,
uint amount,
bool useCeiling
) external view returns (uint);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.0;
import "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
_transferOwnership(sender);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "./Interfaces.sol";
import {Math} from "@openzeppelin/contracts@4.9.3/utils/math/Math.sol";
import {Ownable2Step} from "@openzeppelin/contracts@4.9.3/access/Ownable2Step.sol";
/**
* @title wBLT Router
* @notice This contract simplifies conversions between wBLT, BMX, and other assets
* using wBLT's underlying tokens as virtual pairs with wBLT.
*/
contract wBLTRouter is Ownable2Step {
struct Route {
address from;
address to;
bool stable;
}
/// @notice Factory address that deployed our Velodrome pool.
address public constant factory =
0xe21Aac7F113Bd5DC2389e4d8a8db854a87fD6951;
IWETH public constant weth =
IWETH(0x4200000000000000000000000000000000000006);
uint256 internal constant MINIMUM_LIQUIDITY = 10 ** 3;
bytes32 internal immutable pairCodeHash;
uint256 internal immutable PRICE_PRECISION;
uint256 internal immutable BASIS_POINTS_DIVISOR;
/// @notice The tokens currently approved for deposit to BLT.
address[] public bltTokens;
// contracts used for wBLT mint/burn
VaultAPI internal constant wBLT =
VaultAPI(0x4E74D4Db6c0726ccded4656d0BCE448876BB4C7A);
IBMX internal constant sBLT =
IBMX(0x64755939a80BC89E1D2d0f93A312908D348bC8dE);
IBMX internal constant rewardRouter =
IBMX(0x49A97680938B4F1f73816d1B70C3Ab801FAd124B);
IBMX internal constant morphexVault =
IBMX(0xec8d8D4b215727f3476FF0ab41c406FA99b4272C);
IBMX internal constant bltManager =
IBMX(0x9fAc7b75f367d5B35a6D6D0a09572eFcC3D406C5);
IBMX internal constant vaultUtils =
IBMX(0xec31c83C5689C66cb77DdB5378852F3707022039);
IShareHelper internal constant shareValueHelper =
IShareHelper(0x4d2ED72285206D2b4b59CDA21ED0a979ad1F497f);
constructor() {
pairCodeHash = IPairFactory(factory).pairCodeHash();
// do approvals for wBLT
sBLT.approve(address(wBLT), type(uint256).max);
// update our allowances
updateAllowances();
PRICE_PRECISION = morphexVault.PRICE_PRECISION();
BASIS_POINTS_DIVISOR = morphexVault.BASIS_POINTS_DIVISOR();
}
modifier ensure(uint256 _deadline) {
require(_deadline >= block.timestamp, "Router: EXPIRED");
_;
}
// only accept ETH via fallback from the WETH contract
receive() external payable {
assert(msg.sender == address(weth));
}
/* ========== NEW/MODIFIED FUNCTIONS ========== */
/**
* @notice
* Checks for current tokens in BLT, approves them, and updates our stored array.
* @dev This may only be called by owner.
*/
function updateAllowances() public onlyOwner {
// first, set all of our allowances to zero
for (uint256 i = 0; i < bltTokens.length; ++i) {
IERC20 token = IERC20(bltTokens[i]);
token.approve(address(bltManager), 0);
}
// clear out our saved array
delete bltTokens;
// add our new tokens
uint256 tokensCount = morphexVault.whitelistedTokenCount();
for (uint256 i = 0; i < tokensCount; ++i) {
IERC20 token = IERC20(morphexVault.allWhitelistedTokens(i));
token.approve(address(bltManager), type(uint256).max);
bltTokens.push(address(token));
}
}
/**
* @notice
* Performs chained getAmountOut calculations on any number of pairs.
* @dev This is mainly used when conducting swaps.
* @param _amountIn The amount of our first token to swap.
* @param _routes Array of structs that we use for our swap path.
* @return amounts Amount of each token in the swap path.
*/
function getAmountsOut(
uint256 _amountIn,
Route[] memory _routes
) public view returns (uint256[] memory amounts) {
require(_routes.length >= 1, "Router: INVALID_PATH");
amounts = new uint256[](_routes.length + 1);
amounts[0] = _amountIn;
for (uint256 i = 0; i < _routes.length; i++) {
// check if we need to convert to or from wBLT
if (_routes[i].from == address(wBLT)) {
// check to make sure it's one of the tokens in BLT
if (_isBLTToken(_routes[i].to)) {
amounts[i + 1] = getRedeemAmountWrappedBLT(
_routes[i].to,
amounts[i],
false
);
continue;
}
} else if (_routes[i].to == address(wBLT)) {
// check to make sure it's one of the tokens in BLT
if (_isBLTToken(_routes[i].from)) {
// make sure to underestimate the amount out here
amounts[i + 1] = getMintAmountWrappedBLT(
_routes[i].from,
amounts[i]
);
continue;
}
}
// if it's not depositing or withdrawing from wBLT, we can treat it like
// normal
address pair = pairFor(
_routes[i].from,
_routes[i].to,
_routes[i].stable
);
if (IPairFactory(factory).isPair(pair)) {
amounts[i + 1] = IPair(pair).getAmountOut(
amounts[i],
_routes[i].from
);
}
}
}
/**
* @notice
* Swap wBLT or our paired token for ether.
* @param _amountIn The amount of our first token to swap.
* @param _amountOutMin Minimum amount of ether we must receive.
* @param _routes Array of structs that we use for our swap path.
* @param _to Address that will receive the ether.
* @param _deadline Deadline for transaction to complete.
* @return amounts Amount of each token in the swap path.
*/
function swapExactTokensForETH(
uint256 _amountIn,
uint256 _amountOutMin,
Route[] calldata _routes,
address _to,
uint256 _deadline
) external ensure(_deadline) returns (uint256[] memory amounts) {
amounts = getAmountsOut(_amountIn, _routes);
require(
amounts[amounts.length - 1] >= _amountOutMin,
"Router: INSUFFICIENT_OUTPUT_AMOUNT"
);
require(
_routes[_routes.length - 1].to == address(weth),
"Router: END_ROUTE_IN_ETH_BOZO"
);
// if our first pair is mint/burn of wBLT, transfer to the router
if (
_routes[0].from == address(wBLT) || _routes[0].to == address(wBLT)
) {
if (_isBLTToken(_routes[0].from) || _isBLTToken(_routes[0].to)) {
_safeTransferFrom(
_routes[0].from,
msg.sender,
address(this),
amounts[0]
);
} else {
// if it's not wBLT AND an underlying, it's just a normal wBLT swap
// (likely w/ BMX)
_safeTransferFrom(
_routes[0].from,
msg.sender,
pairFor(_routes[0].from, _routes[0].to, _routes[0].stable),
amounts[0]
);
}
} else {
_safeTransferFrom(
_routes[0].from,
msg.sender,
pairFor(_routes[0].from, _routes[0].to, _routes[0].stable),
amounts[0]
);
}
_swap(amounts, _routes, address(this));
// WETH -> ETH
uint256 amountUnderlying = weth.balanceOf(address(this));
weth.withdraw(amountUnderlying);
_safeTransferETH(_to, amountUnderlying);
}
/**
* @notice
* Swap ETH for tokens, with special handling for wBLT pairs.
* @param _amountIn The amount of ether to swap.
* @param _amountOutMin Minimum amount of our final token we must receive.
* @param _routes Array of structs that we use for our swap path.
* @param _to Address that will receive the final token in the swap path.
* @param _deadline Deadline for transaction to complete.
* @return amounts Amount of each token in the swap path.
*/
function swapExactETHForTokens(
uint256 _amountIn,
uint256 _amountOutMin,
Route[] calldata _routes,
address _to,
uint256 _deadline
) public payable ensure(_deadline) returns (uint256[] memory amounts) {
amounts = getAmountsOut(_amountIn, _routes);
require(
amounts[amounts.length - 1] >= _amountOutMin,
"Router: INSUFFICIENT_OUTPUT_AMOUNT"
);
// deposit to weth first
weth.deposit{value: _amountIn}();
if (weth.balanceOf(address(this)) != _amountIn) {
revert("WETH not sent");
}
if (
_routes[0].from != address(weth) || _routes[0].to != address(wBLT)
) {
revert("Route must start WETH -> wBLT");
}
_swap(amounts, _routes, _to);
}
/**
* @notice
* Swap tokens for tokens, with special handling for wBLT pairs.
* @param _amountIn The amount of our first token to swap.
* @param _amountOutMin Minimum amount of our final token we must receive.
* @param _routes Array of structs that we use for our swap path.
* @param _to Address that will receive the final token in the swap path.
* @param _deadline Deadline for transaction to complete.
* @return amounts Amount of each token in the swap path.
*/
function swapExactTokensForTokens(
uint256 _amountIn,
uint256 _amountOutMin,
Route[] calldata _routes,
address _to,
uint256 _deadline
) external ensure(_deadline) returns (uint256[] memory amounts) {
amounts = getAmountsOut(_amountIn, _routes);
require(
amounts[amounts.length - 1] >= _amountOutMin,
"Router: INSUFFICIENT_OUTPUT_AMOUNT"
);
// if our first pair is mint/burn of wBLT, transfer to the router
if (
_routes[0].from == address(wBLT) || _routes[0].to == address(wBLT)
) {
if (_isBLTToken(_routes[0].from) || _isBLTToken(_routes[0].to)) {
_safeTransferFrom(
_routes[0].from,
msg.sender,
address(this),
amounts[0]
);
} else {
// if it's not wBLT AND an underlying, it's just a normal wBLT swap
// (likely w/ BMX)
_safeTransferFrom(
_routes[0].from,
msg.sender,
pairFor(_routes[0].from, _routes[0].to, _routes[0].stable),
amounts[0]
);
}
} else {
_safeTransferFrom(
_routes[0].from,
msg.sender,
pairFor(_routes[0].from, _routes[0].to, _routes[0].stable),
amounts[0]
);
}
_swap(amounts, _routes, _to);
}
// **** SWAP ****
// requires the initial amount to have already been sent to the first pair
// or in this case, our underlying or wBLT to have been sent to the router
function _swap(
uint256[] memory _amounts,
Route[] memory _routes,
address _to
) internal virtual {
for (uint256 i = 0; i < _routes.length; i++) {
(address token0, ) = sortTokens(_routes[i].from, _routes[i].to);
uint256 amountOut = _amounts[i + 1];
(uint256 amount0Out, uint256 amount1Out) = _routes[i].from == token0
? (uint256(0), amountOut)
: (amountOut, uint256(0));
// only if we're doing a wBLT deposit/withdrawal in the middle of a route
bool directSend;
uint256 received;
address to;
// check if we need to convert to or from wBLT
if (_routes[i].from == address(wBLT)) {
// check to see if it's one of the tokens in BLT
if (_isBLTToken(_routes[i].to)) {
received = _withdrawFromWrappedBLT(_routes[i].to);
if (i < (_routes.length - 1)) {
// if we're not done, send our underlying to the next pair
directSend = true;
} else {
// if this is the last token, send to our _to address
_safeTransfer(_routes[i].to, _to, received);
return;
}
}
} else if (_routes[i].to == address(wBLT)) {
// check to make sure it's one of the tokens in BLT
if (_isBLTToken(_routes[i].from)) {
received = _depositToWrappedBLT(_routes[i].from);
if (i < (_routes.length - 1)) {
// if we're not done, directly send our wBLT to the next pair
directSend = true;
} else {
// if this is the last token, send to our _to address
_safeTransfer(_routes[i].to, _to, received);
return;
}
}
}
if (i == _routes.length - 1) {
// end of the route, send to the receiver
to = _to;
} else if (
(_isBLTToken(_routes[i + 1].from) &&
_routes[i + 1].to == address(wBLT)) ||
(_isBLTToken(_routes[i + 1].to) &&
_routes[i + 1].from == address(wBLT))
) {
// if we're about to go underlying -> wBLT or wBLT -> underlying, then
// make sure we get our needed token back to the router
to = address(this);
} else {
// normal mid-route swap
to = pairFor(
_routes[i + 1].from,
_routes[i + 1].to,
_routes[i + 1].stable
);
}
if (directSend) {
_safeTransfer(_routes[i].to, to, received);
} else {
IPair(
pairFor(_routes[i].from, _routes[i].to, _routes[i].stable)
).swap(amount0Out, amount1Out, to, new bytes(0));
}
}
}
/**
* @notice
* Add liquidity for wBLT-TOKEN with an underlying token for wBLT.
* @dev Removed the stable and tokenA params from the standard function
* as they're not needed and so stack isn't too deep.
* @param _underlyingToken The token to zap into wBLT for creating the LP.
* @param _amountToZapIn Amount of underlying token to deposit to wBLT.
* @param token The token to pair with wBLT for the LP.
* @param _amountWrappedBLTDesired The amount of wBLT we would like to deposit to the
* LP.
* @param _amountTokenDesired The amount of other token we would like to deposit to the
* LP.
* @param _amountWrappedBLTMin The minimum amount of wBLT we will accept in the LP.
* @param _amountTokenMin The minimum amount of other token we will accept in the LP.
* @param _to Address that will receive the LP token.
* @return amountWrappedBLT Amount of wBLT actually deposited in the LP.
* @return amountToken Amount of our other token actually deposited in the LP.
* @return liquidity Amount of LP token generated.
*/
function addLiquidity(
address _underlyingToken,
uint256 _amountToZapIn,
address token,
uint256 _amountWrappedBLTDesired,
uint256 _amountTokenDesired,
uint256 _amountWrappedBLTMin,
uint256 _amountTokenMin,
address _to
)
external
returns (
uint256 amountWrappedBLT,
uint256 amountToken,
uint256 liquidity
)
{
_safeTransferFrom(
_underlyingToken,
msg.sender,
address(this),
_amountToZapIn
);
// first, deposit the underlying to wBLT, deposit function checks that underlying
// is actually in the LP
_amountWrappedBLTDesired = _depositToWrappedBLT(_underlyingToken);
(amountWrappedBLT, amountToken) = _addLiquidity(
address(wBLT),
token,
false, // stable LPs with wBLT would be kind dumb
_amountWrappedBLTDesired,
_amountTokenDesired,
_amountWrappedBLTMin,
_amountTokenMin
);
address pair = pairFor(address(wBLT), token, false);
// wBLT will already be in the router, so transfer for it. transferFrom for other
// token.
_safeTransfer(address(wBLT), pair, amountWrappedBLT);
_safeTransferFrom(token, msg.sender, pair, amountToken);
liquidity = IPair(pair).mint(_to);
uint256 remainingBalance = wBLT.balanceOf(address(this));
// return any leftover wBLT
if (remainingBalance > 0) {
_safeTransfer(address(wBLT), msg.sender, remainingBalance);
}
}
/**
* @notice
* Add liquidity for wBLT-TOKEN with ether.
* @param _amountToZapIn Amount of ether to deposit to wBLT.
* @param token The token to pair with wBLT for the LP.
* @param _amountWrappedBLTDesired The amount of wBLT we would like to deposit to the
* LP.
* @param _amountTokenDesired The amount of other token we would like to deposit to the
* LP.
* @param _amountWrappedBLTMin The minimum amount of wBLT we will accept in the LP.
* @param _amountTokenMin The minimum amount of other token we will accept in the LP.
* @param _to Address that will receive the LP token.
* @return amountWrappedBLT Amount of wBLT actually deposited in the LP.
* @return amountToken Amount of our other token actually deposited in the LP.
* @return liquidity Amount of LP token generated.
*/
function addLiquidityETH(
uint256 _amountToZapIn,
address token,
uint256 _amountWrappedBLTDesired,
uint256 _amountTokenDesired,
uint256 _amountWrappedBLTMin,
uint256 _amountTokenMin,
address _to
)
external
payable
returns (
uint256 amountWrappedBLT,
uint256 amountToken,
uint256 liquidity
)
{
// deposit to weth, then everything is the same
weth.deposit{value: _amountToZapIn}();
if (weth.balanceOf(address(this)) != _amountToZapIn) {
revert("WETH not sent");
}
// first, deposit the underlying to wBLT, deposit function checks that underlying
// is actually in the LP
_amountWrappedBLTDesired = _depositToWrappedBLT(address(weth));
(amountWrappedBLT, amountToken) = _addLiquidity(
address(wBLT),
token,
false, // stable LPs with wBLT would be kind dumb
_amountWrappedBLTDesired,
_amountTokenDesired,
_amountWrappedBLTMin,
_amountTokenMin
);
address pair = pairFor(address(wBLT), token, false);
// wBLT will already be in the router, so transfer for it. transferFrom for other
// token.
_safeTransfer(address(wBLT), pair, amountWrappedBLT);
_safeTransferFrom(token, msg.sender, pair, amountToken);
liquidity = IPair(pair).mint(_to);
// return any leftover wBLT
uint256 remainingBalance = wBLT.balanceOf(address(this));
if (remainingBalance > 0) {
_safeTransfer(address(wBLT), msg.sender, remainingBalance);
}
}
/**
* @notice
* Remove liquidity from a wBLT-TOKEN LP, and convert wBLT to a given underlying.
* @param _targetToken Address of our desired wBLT underlying to withdraw to.
* @param _token The other token paired with wBLT in our LP.
* @param _liquidity The amount of LP tokens we want to burn.
* @param _amountWrappedBLTMin The minimum amount of wBLT we will accept from the LP.
* @param _amountTokenMin The minimum amount of our other token we will accept from the
* LP.
* @param _to Address that will receive the LP token.
* @return amountWrappedBLT Amount of wBLT actually received from the LP.
* @return amountToken Amount of other token actually received from the LP.
* @return amountUnderlying Amount of our underlying token received from the wBLT.
*/
function removeLiquidity(
address _targetToken,
address _token,
uint256 _liquidity,
uint256 _amountWrappedBLTMin,
uint256 _amountTokenMin,
address _to
)
external
returns (
uint256 amountWrappedBLT,
uint256 amountToken,
uint256 amountUnderlying
)
{
// stable is dumb with wBLT
address pair = pairFor(address(wBLT), _token, false);
// send liquidity to pair
require(IPair(pair).transferFrom(msg.sender, pair, _liquidity));
(uint256 amount0, uint256 amount1) = IPair(pair).burn(address(this));
(address token0, ) = sortTokens(address(wBLT), _token);
(amountWrappedBLT, amountToken) = address(wBLT) == token0
? (amount0, amount1)
: (amount1, amount0);
require(
amountWrappedBLT >= _amountWrappedBLTMin,
"Router: INSUFFICIENT_A_AMOUNT"
);
require(
amountToken >= _amountTokenMin,
"Router: INSUFFICIENT_B_AMOUNT"
);
_safeTransfer(_token, _to, amountToken);
amountUnderlying = _withdrawFromWrappedBLT(_targetToken);
_safeTransfer(_targetToken, _to, amountUnderlying);
}
/**
* @notice
* Remove liquidity from a wBLT-TOKEN LP, and convert wBLT to ether.
* @param _token The other token paired with wBLT in our LP.
* @param _liquidity The amount of LP tokens we want to burn.
* @param _amountWrappedBLTMin The minimum amount of wBLT we will accept from the LP.
* @param _amountTokenMin The minimum amount of our other token we will accept from the
* LP.
* @param _to Address that will receive the LP token.
* @return amountWrappedBLT Amount of wBLT actually received from the LP.
* @return amountToken Amount of other token actually received from the LP.
* @return amountUnderlying Amount of ether received from the wBLT.
*/
function removeLiquidityETH(
address _token,
uint256 _liquidity,
uint256 _amountWrappedBLTMin,
uint256 _amountTokenMin,
address _to
)
external
returns (
uint256 amountWrappedBLT,
uint256 amountToken,
uint256 amountUnderlying
)
{
// stable is dumb with wBLT
address pair = pairFor(address(wBLT), _token, false);
// send liquidity to pair
require(IPair(pair).transferFrom(msg.sender, pair, _liquidity));
(uint256 amount0, uint256 amount1) = IPair(pair).burn(address(this));
(address token0, ) = sortTokens(address(wBLT), _token);
(amountWrappedBLT, amountToken) = address(wBLT) == token0
? (amount0, amount1)
: (amount1, amount0);
require(
amountWrappedBLT >= _amountWrappedBLTMin,
"Router: INSUFFICIENT_A_AMOUNT"
);
require(
amountToken >= _amountTokenMin,
"Router: INSUFFICIENT_B_AMOUNT"
);
// send our ether and token to their final destination
_safeTransfer(_token, _to, amountToken);
amountUnderlying = _withdrawFromWrappedBLT(address(weth));
weth.withdraw(amountUnderlying);
_safeTransferETH(_to, amountUnderlying);
}
/**
* @notice
* Exercise our oToken options using one of wBLT's underlying tokens.
* @param _oToken The option token we are exercising.
* @param _tokenToUse Address of our desired wBLT underlying to use for exercising
* our option.
* @param _amount The amount of our token to use to generate our wBLT for exercising.
* @param _oTokenAmount The amount of option tokens to exercise.
* @param _discount Our discount in exercising the option; this determines our lockup
* time.
* @param _deadline Deadline for transaction to complete.
* @return paymentAmount How much wBLT we spend to exercise.
* @return lpAmount Amount of our LP we generate.
*/
function exerciseLpWithUnderlying(
address _oToken,
address _tokenToUse,
uint256 _amount,
uint256 _oTokenAmount,
uint256 _discount,
uint256 _deadline
) external returns (uint256 paymentAmount, uint256 lpAmount) {
// first person does the approvals for everyone else, what a nice person!
_checkAllowance(_oToken);
// transfer in our funds
_safeTransferFrom(_tokenToUse, msg.sender, address(this), _amount);
_safeTransferFrom(_oToken, msg.sender, address(this), _oTokenAmount);
uint256 wBltToLp = _depositToWrappedBLT(_tokenToUse);
(paymentAmount, lpAmount) = IBMX(_oToken).exerciseLp(
_oTokenAmount,
wBltToLp,
msg.sender,
_discount,
_deadline
);
// return any leftover wBLT or underlying
IERC20 token = IERC20(_tokenToUse);
uint256 remainingUnderlying = token.balanceOf(address(this));
uint256 remainingBalance = wBLT.balanceOf(address(this));
if (remainingBalance > 0) {
_safeTransfer(address(wBLT), msg.sender, remainingBalance);
}
if (remainingUnderlying > 0) {
_safeTransfer(_tokenToUse, msg.sender, remainingUnderlying);
}
}
/**
* @notice
* Exercise our oToken options using raw ether.
* @param _oToken The option token we are exercising.
* @param _amount The amount of ETH to use to generate our wBLT for exercising.
* @param _oTokenAmount The amount of option tokens to exercise.
* @param _discount Our discount in exercising the option; this determines our lockup
* time.
* @param _deadline Deadline for transaction to complete.
* @return paymentAmount How much wBLT we spend to exercise.
* @return lpAmount Amount of our LP we generate.
*/
function exerciseLpWithUnderlyingETH(
address _oToken,
uint256 _amount,
uint256 _oTokenAmount,
uint256 _discount,
uint256 _deadline
) external payable returns (uint256 paymentAmount, uint256 lpAmount) {
// first person does the approvals for everyone else, what a nice person!
_checkAllowance(_oToken);
// deposit to weth, then everything is the same
weth.deposit{value: _amount}();
if (weth.balanceOf(address(this)) != _amount) {
revert("WETH not sent");
}
// pull oToken
_safeTransferFrom(_oToken, msg.sender, address(this), _oTokenAmount);
// deposit our WETH to wBLT
uint256 wBltToLp = _depositToWrappedBLT(address(weth));
// exercise as normal
(paymentAmount, lpAmount) = IBMX(_oToken).exerciseLp(
_oTokenAmount,
wBltToLp,
msg.sender,
_discount,
_deadline
);
// return any leftover wBLT or WETH
uint256 remainingUnderlying = weth.balanceOf(address(this));
uint256 remainingBalance = wBLT.balanceOf(address(this));
if (remainingBalance > 0) {
_safeTransfer(address(wBLT), msg.sender, remainingBalance);
}
if (remainingUnderlying > 0) {
_safeTransfer(address(weth), msg.sender, remainingUnderlying);
}
}
// helper to approve new oTokens to spend wBLT from this router
function _checkAllowance(address _token) internal {
if (wBLT.allowance(address(this), _token) == 0) {
wBLT.approve(_token, type(uint256).max);
}
}
/**
* @notice
* Check how much underlying (or ETH) we need to exercise to LP.
* @param _oToken The option token we are exercising.
* @param _tokenToUse The token to deposit to wBLT.
* @param _oTokenAmount The amount of oToken to exercise.
* @param _discount Our discount in exercising the option; this determines our lockup
* time.
* @return atomicAmount The amount of token needed if exercising atomically from this
* calculation.
* @return safeAmount Add an extra 0.01% to allow for per-second wBLT share price
* increases.
*/
function quoteTokenNeededToExerciseLp(
address _oToken,
address _tokenToUse,
uint256 _oTokenAmount,
uint256 _discount
) external view returns (uint256 atomicAmount, uint256 safeAmount) {
// calculate the exact amount we need
(uint256 amountNeeded, uint256 amount2) = IBMX(_oToken)
.getPaymentTokenAmountForExerciseLp(_oTokenAmount, _discount);
amountNeeded += amount2;
atomicAmount = quoteMintAmountBLT(_tokenToUse, amountNeeded);
// give ourselves 0.01% of space for wBLT share price rising
safeAmount = (atomicAmount * 10_001) / 10_000;
}
/**
* @notice
* Check how much wBLT we get from a given amount of underlying.
* @dev Since this uses minPrice, we likely underestimate wBLT received. By using
* normal solidity division, we are also truncating (rounding down) all operations.
* @param _token The token to deposit to wBLT.
* @param _amount The amount of the token to deposit.
* @return wrappedBLTMintAmount Amount of wBLT received.
*/
function getMintAmountWrappedBLT(
address _token,
uint256 _amount
) public view returns (uint256 wrappedBLTMintAmount) {
require(_amount > 0, "invalid _amount");
// calculate aum before buyUSDG
(uint256 aumInUsdg, uint256 bltSupply) = _getBltInfo(true);
uint256 price = morphexVault.getMinPrice(_token);
// save some gas
uint256 _precision = PRICE_PRECISION;
uint256 _divisor = BASIS_POINTS_DIVISOR;
uint256 usdgAmount = (_amount * price) / _precision;
usdgAmount = morphexVault.adjustForDecimals(
usdgAmount,
_token,
morphexVault.usdg()
);
uint256 feeBasisPoints = vaultUtils.getBuyUsdgFeeBasisPoints(
_token,
usdgAmount
);
uint256 afterFeeAmount = (_amount * (_divisor - feeBasisPoints)) /
_divisor;
uint256 usdgMintAmount = (afterFeeAmount * price) / _precision;
usdgMintAmount = morphexVault.adjustForDecimals(
usdgMintAmount,
_token,
morphexVault.usdg()
);
uint256 BLTMintAmount = aumInUsdg == 0
? usdgMintAmount
: (usdgMintAmount * bltSupply) / aumInUsdg;
// convert our BLT amount to wBLT
wrappedBLTMintAmount = shareValueHelper.amountToShares(
address(wBLT),
BLTMintAmount,
false
);
}
/**
* @notice
* Check how much underlying we get from redeeming a given amount of wBLT.
* @dev By default we round down and use getMaxPrice to underestimate underlying
* received. This is important so that we don't ever revert in a swap due to
* overestimation, as getAmountsOut calls this function.
* @param _tokenOut The token to withdraw from wBLT.
* @param _amount The amount of wBLT to burn.
* @param _roundUp Whether we round up or not.
* @return underlyingReceived Amount of underlying token received.
*/
function getRedeemAmountWrappedBLT(
address _tokenOut,
uint256 _amount,
bool _roundUp
) public view returns (uint256 underlyingReceived) {
require(_amount > 0, "invalid _amount");
// convert our wBLT amount to BLT
_amount = shareValueHelper.sharesToAmount(
address(wBLT),
_amount,
_roundUp
);
// convert our BLT to bUSD (USDG)
(uint256 aumInUsdg, uint256 bltSupply) = _getBltInfo(false);
uint256 usdgAmount;
// round up if needed
if (_roundUp) {
usdgAmount = Math.ceilDiv((_amount * aumInUsdg), bltSupply);
} else {
usdgAmount = (_amount * aumInUsdg) / bltSupply;
}
// use min or max price depending on how we want to estimate
uint256 price;
if (_roundUp) {
price = morphexVault.getMinPrice(_tokenOut);
} else {
price = morphexVault.getMaxPrice(_tokenOut);
}
// convert USDG to _tokenOut amounts. no need to round this one since we adjust
// decimals and compensate below
uint256 redeemAmount = (usdgAmount * PRICE_PRECISION) / price;
redeemAmount = morphexVault.adjustForDecimals(
redeemAmount,
morphexVault.usdg(),
_tokenOut
);
// add one wei to compensate for truncating when adjusting decimals
if (_roundUp) {
redeemAmount += 1;
}
// calculate our fees
uint256 feeBasisPoints = vaultUtils.getSellUsdgFeeBasisPoints(
_tokenOut,
usdgAmount
);
// save some gas
uint256 _divisor = BASIS_POINTS_DIVISOR;
// adjust for fees, round up if needed
if (_roundUp) {
underlyingReceived = Math.ceilDiv(
(redeemAmount * (_divisor - feeBasisPoints)),
_divisor
);
} else {
underlyingReceived = ((redeemAmount * (_divisor - feeBasisPoints)) /
_divisor);
}
}
/**
* @notice
* Check how much wBLT we need to redeem for a given amount of underlying.
* @dev Here we do everything we can, including adding an additional Wei of Defeat, to
* ensure that our estimated wBLT amount always provides enough underlying.
* @param _underlyingToken The token to withdraw from wBLT.
* @param _amount The amount of underlying we need.
* @return wBLTAmount Amount of wBLT needed.
*/
function quoteRedeemAmountBLT(
address _underlyingToken,
uint256 _amount
) external view returns (uint256 wBLTAmount) {
require(_amount > 0, "invalid _amount");
// add an additional wei to our input amount because of persistent rounding
// issues, AKA the Wei of Defeat
_amount += 1;
// get our info for BLT
(uint256 aumInUsdg, uint256 bltSupply) = _getBltInfo(false);
// convert our underlying amount to USDG
uint256 underlyingPrice = morphexVault.getMaxPrice(_underlyingToken);
uint256 usdgNeeded = Math.ceilDiv(
(_amount * underlyingPrice),
PRICE_PRECISION
);
// convert USDG needed to BLT. no need for rounding here since we will truncate
// in the next step anyway
uint256 bltAmount = (usdgNeeded * bltSupply) / aumInUsdg;
bltAmount = morphexVault.adjustForDecimals(
bltAmount,
morphexVault.usdg(),
_underlyingToken
);
// add one wei since adjustForDecimals truncates instead of rounding up
bltAmount += 1;
// save some gas
uint256 _divisor = BASIS_POINTS_DIVISOR;
// check current fees
uint256 feeBasisPoints = vaultUtils.getSellUsdgFeeBasisPoints(
_underlyingToken,
usdgNeeded
);
// adjust for fees
bltAmount = Math.ceilDiv(
(bltAmount * _divisor),
(_divisor - feeBasisPoints)
);
// convert our BLT to wBLT
wBLTAmount = shareValueHelper.amountToShares(
address(wBLT),
bltAmount,
true
);
}
/**
* @notice
* Check how much underlying we need to mint a given amount of wBLT.
* @dev Since this uses minPrice, we likely overestimate underlying
* needed. To be cautious of rounding down, use ceiling division.
* @param _underlyingToken The token to deposit to wBLT.
* @param _amount The amount of wBLT we need.
* @return startingTokenAmount Amount of underlying token needed.
*/
function quoteMintAmountBLT(
address _underlyingToken,
uint256 _amount
) public view returns (uint256 startingTokenAmount) {
require(_amount > 0, "invalid _amount");
// convert our wBLT amount to BLT
_amount = shareValueHelper.sharesToAmount(address(wBLT), _amount, true);
// convert our BLT to bUSD (USDG)
// maximize here to use max BLT price, to make sure we get enough BLT out
(uint256 aumInUsdg, uint256 bltSupply) = _getBltInfo(true);
uint256 usdgAmount = Math.ceilDiv((_amount * aumInUsdg), bltSupply);
// price is returned in 1e30 from vault
uint256 tokenPrice = morphexVault.getMinPrice(_underlyingToken);
startingTokenAmount = Math.ceilDiv(
usdgAmount * PRICE_PRECISION,
tokenPrice
);
startingTokenAmount = morphexVault.adjustForDecimals(
startingTokenAmount,
morphexVault.usdg(),
_underlyingToken
);
// add one wei since adjustForDecimals truncates instead of rounding up
startingTokenAmount += 1;
// calculate extra needed due to fees
uint256 feeBasisPoints = vaultUtils.getBuyUsdgFeeBasisPoints(
_underlyingToken,
usdgAmount
);
// save some gas
uint256 _divisor = BASIS_POINTS_DIVISOR;
startingTokenAmount = Math.ceilDiv(
startingTokenAmount * _divisor,
(_divisor - feeBasisPoints)
);
}
// standard data needed to calculate BLT pricing
function _getBltInfo(
bool _maximize
) internal view returns (uint256 aumInUsdg, uint256 bltSupply) {
bltSupply = sBLT.totalSupply();
aumInUsdg = bltManager.getAumInUsdg(_maximize);
}
// check if a token is in BLT
function _isBLTToken(address _tokenToCheck) internal view returns (bool) {
for (uint256 i = 0; i < bltTokens.length; ++i) {
if (bltTokens[i] == _tokenToCheck) {
return true;
}
}
return false;
}
// withdraw all of the wBLT we have to a given underlying token
function _withdrawFromWrappedBLT(
address _targetToken
) internal returns (uint256) {
if (!_isBLTToken(_targetToken)) {
revert("Token not in wBLT");
}
// withdraw from the vault first, make sure it comes here
uint256 toWithdraw = wBLT.withdraw(type(uint256).max, address(this));
// withdraw our targetToken
return
rewardRouter.unstakeAndRedeemGlp(
_targetToken,
toWithdraw,
0,
address(this)
);
}
/**
* @notice Withdraws a specified amount of wBLT to a target underlying token.
* @param _receiver The address to receive underlying tokens to.
* @param _targetToken The address of the target token to which the wBLT is withdrawn.
* @param _amount The amount of wBLT to withdraw.
* @return amountWithdrawn The amount of target tokens received from the withdrawal.
*/
function withdrawFromWrappedBLT(address _receiver, address _targetToken, uint256 _amount) external returns (uint256) {
// Transfer wBLT from the user to the router
_safeTransferFrom(address(wBLT), msg.sender, address(this), _amount);
if (!_isBLTToken(_targetToken)) {
revert("Token not in wBLT");
}
// withdraw from the vault first, make sure it comes here
uint256 toWithdraw = wBLT.withdraw(type(uint256).max, address(this));
// withdraw our targetToken
return
rewardRouter.unstakeAndRedeemGlp(
_targetToken,
toWithdraw,
0,
_receiver
);
}
// deposit all of the underlying we have to wBLT
function _depositToWrappedBLT(
address _fromToken
) internal returns (uint256 tokens) {
if (!_isBLTToken(_fromToken)) {
revert("Token not in wBLT");
}
// deposit to BLT and then the vault
IERC20 token = IERC20(_fromToken);
uint256 newMlp = rewardRouter.mintAndStakeGlp(
address(_fromToken),
token.balanceOf(address(this)),
0,
0
);
// specify that router should get the vault tokens
tokens = wBLT.deposit(newMlp, address(this));
}
/**
* @notice Deposits a specified amount of an underlying token to wBLT.
* @param _receiver The address to receive underlying tokens to.
* @param _fromToken The address of the token to be deposited to wBLT.
* @param _amount The amount of the token to deposit.
* @return amountReceived The amount of wBLT received from the deposit.
*/
function depositToWrappedBLT(address _receiver, address _fromToken, uint256 _amount) external returns (uint256 amountReceived) {
// Transfer the _fromToken from the user to the contract
_safeTransferFrom(_fromToken, msg.sender, address(this), _amount);
if (!_isBLTToken(_fromToken)) {
revert("Token not in wBLT");
}
// deposit to BLT and then the vault
IERC20 token = IERC20(_fromToken);
uint256 newMlp = rewardRouter.mintAndStakeGlp(
address(_fromToken),
token.balanceOf(address(this)),
0,
0
);
// specify that user should get the vault tokens
amountReceived = wBLT.deposit(newMlp, _receiver);
}
/**
* @notice
* Zap out into a wBLT LP with an underlying token.
* @param _underlyingToken The token to zap in to wBLT.
* @param _token The token paired with wBLT.
* @param _amountUnderlyingDesired The amount of underlying we would like to deposit.
* @param _amountTokenDesired The amount of token to pair with our wBLT.
* @return amountUnderlying Amount of underlying token to deposit.
* @return amountWrappedBLT Amount of wBLT we will deposit.
* @return amountToken Amount of other token to deposit.
* @return liquidity Amount of LP token received.
*/
function quoteAddLiquidityUnderlying(
address _underlyingToken,
address _token,
uint256 _amountUnderlyingDesired,
uint256 _amountTokenDesired
)
external
view
returns (
uint256 amountUnderlying,
uint256 amountWrappedBLT,
uint256 amountToken,
uint256 liquidity
)
{
// create the pair if it doesn't exist yet
address _pair = IPairFactory(factory).getPair(
address(wBLT),
_token,
false
);
(uint256 reserveA, uint256 reserveB) = (0, 0);
uint256 _totalSupply = 0;
// convert our _amountUnderlyingDesired to amountWrappedBLTDesired. make sure to
// underestimate the amount out here so no risk of reverting
uint256 amountWrappedBLTDesired = getMintAmountWrappedBLT(
_underlyingToken,
_amountUnderlyingDesired
);
if (_pair != address(0)) {
_totalSupply = IERC20(_pair).totalSupply();
(reserveA, reserveB) = getReserves(address(wBLT), _token, false);
}
if (reserveA == 0 && reserveB == 0) {
(amountWrappedBLT, amountToken) = (
amountWrappedBLTDesired,
_amountTokenDesired
);
liquidity =
Math.sqrt(amountWrappedBLT * amountToken) -
MINIMUM_LIQUIDITY;
} else {
uint256 amountTokenOptimal = _quoteLiquidity(
amountWrappedBLTDesired,
reserveA,
reserveB
);
if (amountTokenOptimal <= _amountTokenDesired) {
(amountWrappedBLT, amountToken) = (
amountWrappedBLTDesired,
amountTokenOptimal
);
liquidity = Math.min(
(amountWrappedBLT * _totalSupply) / reserveA,
(amountToken * _totalSupply) / reserveB
);
} else {
uint256 amountWrappedBLTOptimal = _quoteLiquidity(
_amountTokenDesired,
reserveB,
reserveA
);
(amountWrappedBLT, amountToken) = (
amountWrappedBLTOptimal,
_amountTokenDesired
);
liquidity = Math.min(
(amountWrappedBLT * _totalSupply) / reserveA,
(amountToken * _totalSupply) / reserveB
);
}
}
// based on the amount of wBLT, calculate how much of our underlying token we
// need to zap in
amountUnderlying = quoteMintAmountBLT(
_underlyingToken,
amountWrappedBLT
);
}
/**
* @notice
* Zap out from a wBLT LP to an underlying token.
* @param _underlyingToken The token to withdraw from wBLT.
* @param _token The token paired with wBLT.
* @param _liquidity The amount of wBLT LP to burn.
* @return amountUnderlying Amount of underlying token received.
* @return amountWrappedBLT Amount of wBLT token received before being converted to
* underlying.
* @return amountToken Amount of other token received.
*/
function quoteRemoveLiquidityUnderlying(
address _underlyingToken,
address _token,
uint256 _liquidity
)
external
view
returns (
uint256 amountUnderlying,
uint256 amountWrappedBLT,
uint256 amountToken
)
{
// create the pair if it doesn't exist yet
address _pair = IPairFactory(factory).getPair(
address(wBLT),
_token,
false
);
if (_pair == address(0)) {
return (0, 0, 0);
}
(uint256 reserveA, uint256 reserveB) = getReserves(
address(wBLT),
_token,
false
);
uint256 _totalSupply = IERC20(_pair).totalSupply();
// using balances ensures pro-rata distribution
amountWrappedBLT = (_liquidity * reserveA) / _totalSupply;
// using balances ensures pro-rata distribution
amountToken = (_liquidity * reserveB) / _totalSupply;
// simulate zapping out of wBLT to the selected underlying
amountUnderlying = getRedeemAmountWrappedBLT(
_underlyingToken,
amountWrappedBLT,
false
);
}
/* ========== UNMODIFIED FUNCTIONS ========== */
// calculates the CREATE2 address for a pair without making any external calls
function pairFor(
address _tokenA,
address _tokenB,
bool _stable
) public view returns (address pair) {
(address token0, address token1) = sortTokens(_tokenA, _tokenB);
pair = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex"ff",
factory,
keccak256(
abi.encodePacked(token0, token1, _stable)
),
pairCodeHash // init code hash
)
)
)
)
);
}
function sortTokens(
address _tokenA,
address _tokenB
) public pure returns (address token0, address token1) {
require(_tokenA != _tokenB, "Router: IDENTICAL_ADDRESSES");
(token0, token1) = _tokenA < _tokenB
? (_tokenA, _tokenB)
: (_tokenB, _tokenA);
require(token0 != address(0), "Router: ZERO_ADDRESS");
}
// fetches and sorts the reserves for a pair
function getReserves(
address _tokenA,
address _tokenB,
bool _stable
) public view returns (uint256 reserveA, uint256 reserveB) {
(address token0, ) = sortTokens(_tokenA, _tokenB);
(uint256 reserve0, uint256 reserve1, ) = IPair(
pairFor(_tokenA, _tokenB, _stable)
).getReserves();
(reserveA, reserveB) = _tokenA == token0
? (reserve0, reserve1)
: (reserve1, reserve0);
}
// determine whether to use stable or volatile pools for a given pair of tokens
function getAmountOut(
uint256 _amountIn,
address _tokenIn,
address _tokenOut
) public view returns (uint256 amount, bool stable) {
address pair = pairFor(_tokenIn, _tokenOut, true);
uint256 amountStable;
uint256 amountVolatile;
if (IPairFactory(factory).isPair(pair)) {
amountStable = IPair(pair).getAmountOut(_amountIn, _tokenIn);
}
pair = pairFor(_tokenIn, _tokenOut, false);
if (IPairFactory(factory).isPair(pair)) {
amountVolatile = IPair(pair).getAmountOut(_amountIn, _tokenIn);
}
return
amountStable > amountVolatile
? (amountStable, true)
: (amountVolatile, false);
}
//@override
//getAmountOut : bool stable
//Gets exact output for specific pair-type(S|V)
function getAmountOut(
uint256 _amountIn,
address _tokenIn,
address _tokenOut,
bool _stable
) public view returns (uint256 amount) {
address pair = pairFor(_tokenIn, _tokenOut, _stable);
if (IPairFactory(factory).isPair(pair)) {
amount = IPair(pair).getAmountOut(_amountIn, _tokenIn);
}
}
// given some amount of an asset and pair reserves, returns an equivalent amount of
// the other asset
function _quoteLiquidity(
uint256 _amountA,
uint256 _reserveA,
uint256 _reserveB
) internal pure returns (uint256 amountB) {
require(_amountA > 0, "Router: INSUFFICIENT_AMOUNT");
require(
_reserveA > 0 && _reserveB > 0,
"Router: INSUFFICIENT_LIQUIDITY"
);
amountB = (_amountA * _reserveB) / _reserveA;
}
function _addLiquidity(
address _tokenA,
address _tokenB,
bool _stable,
uint256 _amountADesired,
uint256 _amountBDesired,
uint256 _amountAMin,
uint256 _amountBMin
) internal returns (uint256 amountA, uint256 amountB) {
require(_amountADesired >= _amountAMin);
require(_amountBDesired >= _amountBMin);
address _pair = IPairFactory(factory).getPair(
_tokenA,
_tokenB,
_stable
);
if (_pair == address(0)) {
_pair = IPairFactory(factory).createPair(_tokenA, _tokenB, _stable);
}
// desired is the amount desired to be deposited for each token
// optimal of one asset is the amount that is equal in value to our desired of the
// other asset
// so, if our optimal is less than our min, we have an issue and pricing is likely
// off
(uint256 reserveA, uint256 reserveB) = getReserves(
_tokenA,
_tokenB,
_stable
);
if (reserveA == 0 && reserveB == 0) {
(amountA, amountB) = (_amountADesired, _amountBDesired);
} else {
uint256 amountBOptimal = _quoteLiquidity(
_amountADesired,
reserveA,
reserveB
);
if (amountBOptimal <= _amountBDesired) {
require(
amountBOptimal >= _amountBMin,
"Router: INSUFFICIENT_B_AMOUNT"
);
(amountA, amountB) = (_amountADesired, amountBOptimal);
} else {
uint256 amountAOptimal = _quoteLiquidity(
_amountBDesired,
reserveB,
reserveA
);
assert(amountAOptimal <= _amountADesired);
require(
amountAOptimal >= _amountAMin,
"Router: INSUFFICIENT_A_AMOUNT"
);
(amountA, amountB) = (amountAOptimal, _amountBDesired);
}
}
}
function _safeTransferETH(address _to, uint256 _value) internal {
(bool success, ) = _to.call{value: _value}(new bytes(0));
require(success, "TransferHelper: ETH_TRANSFER_FAILED");
}
function _safeTransfer(
address _token,
address _to,
uint256 _value
) internal {
require(_token.code.length > 0);
(bool success, bytes memory data) = _token.call(
abi.encodeWithSelector(IERC20.transfer.selector, _to, _value)
);
require(success && (data.length == 0 || abi.decode(data, (bool))));
}
function _safeTransferFrom(
address _token,
address _from,
address _to,
uint256 _value
) internal {
require(_token.code.length > 0);
(bool success, bytes memory data) = _token.call(
abi.encodeWithSelector(
IERC20.transferFrom.selector,
_from,
_to,
_value
)
);
require(success && (data.length == 0 || abi.decode(data, (bool))));
}
}
{
"compilationTarget": {
"wBLTRouter.sol": "wBLTRouter"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","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"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_underlyingToken","type":"address"},{"internalType":"uint256","name":"_amountToZapIn","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"_amountWrappedBLTDesired","type":"uint256"},{"internalType":"uint256","name":"_amountTokenDesired","type":"uint256"},{"internalType":"uint256","name":"_amountWrappedBLTMin","type":"uint256"},{"internalType":"uint256","name":"_amountTokenMin","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"amountWrappedBLT","type":"uint256"},{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountToZapIn","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"_amountWrappedBLTDesired","type":"uint256"},{"internalType":"uint256","name":"_amountTokenDesired","type":"uint256"},{"internalType":"uint256","name":"_amountWrappedBLTMin","type":"uint256"},{"internalType":"uint256","name":"_amountTokenMin","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"addLiquidityETH","outputs":[{"internalType":"uint256","name":"amountWrappedBLT","type":"uint256"},{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"bltTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"address","name":"_fromToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"depositToWrappedBLT","outputs":[{"internalType":"uint256","name":"amountReceived","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_oToken","type":"address"},{"internalType":"address","name":"_tokenToUse","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_oTokenAmount","type":"uint256"},{"internalType":"uint256","name":"_discount","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"exerciseLpWithUnderlying","outputs":[{"internalType":"uint256","name":"paymentAmount","type":"uint256"},{"internalType":"uint256","name":"lpAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_oToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_oTokenAmount","type":"uint256"},{"internalType":"uint256","name":"_discount","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"exerciseLpWithUnderlyingETH","outputs":[{"internalType":"uint256","name":"paymentAmount","type":"uint256"},{"internalType":"uint256","name":"lpAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"address","name":"_tokenOut","type":"address"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"stable","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"address","name":"_tokenOut","type":"address"},{"internalType":"bool","name":"_stable","type":"bool"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"}],"internalType":"struct wBLTRouter.Route[]","name":"_routes","type":"tuple[]"}],"name":"getAmountsOut","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"getMintAmountWrappedBLT","outputs":[{"internalType":"uint256","name":"wrappedBLTMintAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenOut","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"_roundUp","type":"bool"}],"name":"getRedeemAmountWrappedBLT","outputs":[{"internalType":"uint256","name":"underlyingReceived","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenA","type":"address"},{"internalType":"address","name":"_tokenB","type":"address"},{"internalType":"bool","name":"_stable","type":"bool"}],"name":"getReserves","outputs":[{"internalType":"uint256","name":"reserveA","type":"uint256"},{"internalType":"uint256","name":"reserveB","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenA","type":"address"},{"internalType":"address","name":"_tokenB","type":"address"},{"internalType":"bool","name":"_stable","type":"bool"}],"name":"pairFor","outputs":[{"internalType":"address","name":"pair","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_underlyingToken","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amountUnderlyingDesired","type":"uint256"},{"internalType":"uint256","name":"_amountTokenDesired","type":"uint256"}],"name":"quoteAddLiquidityUnderlying","outputs":[{"internalType":"uint256","name":"amountUnderlying","type":"uint256"},{"internalType":"uint256","name":"amountWrappedBLT","type":"uint256"},{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_underlyingToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"quoteMintAmountBLT","outputs":[{"internalType":"uint256","name":"startingTokenAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_underlyingToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"quoteRedeemAmountBLT","outputs":[{"internalType":"uint256","name":"wBLTAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_underlyingToken","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_liquidity","type":"uint256"}],"name":"quoteRemoveLiquidityUnderlying","outputs":[{"internalType":"uint256","name":"amountUnderlying","type":"uint256"},{"internalType":"uint256","name":"amountWrappedBLT","type":"uint256"},{"internalType":"uint256","name":"amountToken","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_oToken","type":"address"},{"internalType":"address","name":"_tokenToUse","type":"address"},{"internalType":"uint256","name":"_oTokenAmount","type":"uint256"},{"internalType":"uint256","name":"_discount","type":"uint256"}],"name":"quoteTokenNeededToExerciseLp","outputs":[{"internalType":"uint256","name":"atomicAmount","type":"uint256"},{"internalType":"uint256","name":"safeAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_targetToken","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_liquidity","type":"uint256"},{"internalType":"uint256","name":"_amountWrappedBLTMin","type":"uint256"},{"internalType":"uint256","name":"_amountTokenMin","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"amountWrappedBLT","type":"uint256"},{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountUnderlying","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_liquidity","type":"uint256"},{"internalType":"uint256","name":"_amountWrappedBLTMin","type":"uint256"},{"internalType":"uint256","name":"_amountTokenMin","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"removeLiquidityETH","outputs":[{"internalType":"uint256","name":"amountWrappedBLT","type":"uint256"},{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountUnderlying","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenA","type":"address"},{"internalType":"address","name":"_tokenB","type":"address"}],"name":"sortTokens","outputs":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_amountOutMin","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"}],"internalType":"struct wBLTRouter.Route[]","name":"_routes","type":"tuple[]"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"swapExactETHForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_amountOutMin","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"}],"internalType":"struct wBLTRouter.Route[]","name":"_routes","type":"tuple[]"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"swapExactTokensForETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_amountOutMin","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"}],"internalType":"struct wBLTRouter.Route[]","name":"_routes","type":"tuple[]"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateAllowances","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"address","name":"_targetToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawFromWrappedBLT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]