Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"contracts/SwapX.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity = 0.8.28;
import "./Storage.sol";
import "./contracts-upgradeable/access/OwnableUpgradeable.sol";
import "./contracts-upgradeable/interfaces/IERC1271Upgradeable.sol";
import "./contracts-upgradeable/proxy/utils/Initializable.sol";
import "./contracts-upgradeable/security/PausableUpgradeable.sol";
import "./contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "./contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "./contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import {FlapshLib} from "./extLib/FlapshLib.sol";
import {FourMemeLib} from "./extLib/FourMemeLib.sol";
import {UniV2Lib} from "./extLib/UniV2Lib.sol";
import {UniV3Lib} from "./extLib/UniV3Lib.sol";
import {UniV4Lib} from "./extLib/UniV4Lib.sol";
import {Common} from "./extLib/common.sol";
import "./interfaces/IFlapTokenManager.sol";
import "./interfaces/IFourTokenManager.sol";
import "./interfaces/IPancakeV3SwapCallback.sol";
import "./interfaces/IUniswapV2Pair.sol";
import "./interfaces/IUniswapV3Pool.sol";
import "./interfaces/IUniswapV3SwapCallback.sol";
import "./interfaces/IWETH.sol";
import "./libs/CallbackValidation.sol";
import "./libs/Path.sol";
import "./libs/SafeCast.sol";
import "./libs/SafeMath.sol";
import "./libs/TickMath.sol";
import "./libs/UniswapV2Library.sol";
contract SwapX is
Storage,
IUniswapV3SwapCallback,
IPancakeV3SwapCallback,
Initializable,
OwnableUpgradeable,
ReentrancyGuardUpgradeable,
PausableUpgradeable
{
address public pancakeFactoryV3;
address internal constant UNISWAP_V3_FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984;
address internal constant PANCAKE_V3_FACTORY = 0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865;
address internal constant SUSHISWAP_V3_FACTORY = 0xbACEB8eC6b9355Dfc0269C18bac9d6E2Bdc29C4F;
using SafeERC20Upgradeable for IERC20Upgradeable;
using SafeMath for uint256;
using Path for bytes;
using SafeCast for uint256;
struct ExactInputSingleParams {
address factoryAddress;
address poolAddress;
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
struct ExactInputParams {
address[] factoryAddresses;
address[] poolAddresses;
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
struct ExactOutputSingleParams {
address factoryAddress;
address poolAddress;
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
struct ExactOutputParams {
address[] factoryAddresses;
address[] poolAddresses;
bytes path;
address tokenIn;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}
struct ExactInputMixedParams {
string[] routes;
bytes path1;
address factory1;
address poolAddress1;
bytes path2;
address factory2;
address poolAddress2;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
struct ExactOutputMixedParams {
string[] routes;
bytes path1;
address factory1;
bytes path2;
address factory2;
uint256 amountIn2; // only for v2-v3 router
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}
struct SwapCallbackData {
bytes path;
address payer;
address payerOrigin;
}
event Swap(
address indexed payer,
address indexed receiver,
address indexed feeToken,
uint256 amountIn,
uint256 amountOut,
Common.SwapDesc[] descs
);
event FeeCollected(address indexed token, address indexed payer, uint256 amount, uint256 timestamp);
modifier checkDeadline(uint256 deadline) {
require(block.timestamp <= deadline, "Transaction too old");
_;
}
receive() external payable {}
function initialize(
address _factoryV3,
address _pancakeFactoryV3,
address _WETH,
address _feeCollector,
uint256 _feeRate
) public initializer {
__Ownable_init(msg.sender);
__ReentrancyGuard_init();
__Pausable_init();
factoryV3 = _factoryV3;
pancakeFactoryV3 = _pancakeFactoryV3;
feeCollector = _feeCollector;
feeRate = _feeRate;
feeDenominator = 10000;
WETH = _WETH;
amountInCached = type(uint256).max;
}
// take fee
function takeFee(address tokenIn, uint256 amountIn) internal returns (uint256) {
if (feeExcludeList[msg.sender]) {
return 0;
}
uint256 fee = amountIn.mul(feeRate).div(feeDenominator);
if (tokenIn == address(0) || tokenIn == WETH) {
require(address(this).balance > fee, "insufficient funds");
(bool success,) = address(feeCollector).call{value: fee}("");
require(success, "SwapX: take fee error");
emit FeeCollected(address(0), msg.sender, fee, block.timestamp);
} else {
IERC20Upgradeable(tokenIn).safeTransferFrom(msg.sender, feeCollector, fee);
emit FeeCollected(tokenIn, msg.sender, fee, block.timestamp);
}
return fee;
}
function takeFeeTo(address tokenIn, uint256 amountIn) internal returns (uint256) {
uint256 fee = amountIn.mul(feeRate).div(feeDenominator);
if (fee > 0) {
Common.pay(tokenIn, address(this), feeCollector, fee);
emit FeeCollected(tokenIn, msg.sender, fee, block.timestamp);
}
return fee;
}
// function sellMemeToken2(FourMemeLib.SellFourMeme memory data) external nonReentrant whenNotPaused {
// // rounding down to 1e9
// data.amountIn = data.amountIn / 1e9 * 1e9;
// uint256 amount = FourMemeLib.sellMemeToken2(data);
// uint256 fee = takeFee(address(0), amount);
// require(amount - fee >= data.amountOutMinimum, "min return not reached");
// (bool success,) = address(msg.sender).call{value: amount - fee}("");
// require(success, "refund bnb error");
// }
// function buyMemeToken2(FourMemeLib.BuyFourMeme memory data) external payable nonReentrant whenNotPaused {
// require(msg.value >= data.amountIn, "invalid funds");
// uint256 fee = takeFee(address(0), data.amountIn);
// data.amountIn -= fee;
// FourMemeLib.buyMemeToken2(data);
// }
// // buy meme function, exact bnb amount
// function buyMemeToken(address tokenManager, address token, address recipient, uint256 funds, uint256 minAmount)
// external
// payable
// nonReentrant
// whenNotPaused
// returns (uint256 amountOut)
// {
// require(msg.value >= funds, "invalid funds");
// uint256 fee = takeFee(address(0), funds);
// FourMemeLib.buyMemeToken(tokenManager, token, recipient, funds - fee, minAmount);
// }
// // sell meme function, exact bnb amount
// function sellMemeToken(FourMemeLib.SellFourMemeSingle memory data) external nonReentrant whenNotPaused {
// data.feeRate = feeRate;
// data.feeRecipient = feeCollector;
// data.payerOrigin = msg.sender;
// // rounding down to 1e9
// data.amountIn = data.amountIn / 1e9 * 1e9;
// FourMemeLib.sellMemeTokenSingle(data);
// }
// // buy meme function, exact bnb amount
// function buyFlapToken(address manager, address token, address recipient, uint256 minAmount)
// external
// payable
// nonReentrant
// whenNotPaused
// returns (uint256 amountOut)
// {
// uint256 funds = msg.value;
// uint256 fee = takeFee(address(0), funds);
// FlapshLib.BuyFlapshParams memory buyParams;
// buyParams.manager = manager;
// buyParams.token = token;
// buyParams.recipient = recipient;
// buyParams.funds = funds - fee;
// buyParams.minAmount = minAmount;
// FlapshLib.buyFlapsh(buyParams);
// }
// function sellFlapToken(address manager, address token, address recipient, uint256 amount, uint256 minAmount)
// external
// nonReentrant
// whenNotPaused
// {
// uint256 balanceBefore = address(this).balance;
// FlapshLib.SellFlapshParams memory sellParams;
// sellParams.manager = manager;
// sellParams.token = token;
// sellParams.recipient = recipient;
// sellParams.funds = amount;
// sellParams.minAmount = minAmount;
// FlapshLib.sellFlapsh(sellParams);
// uint256 balanceAfter = address(this).balance;
// uint256 realAmountOut = balanceAfter - balanceBefore;
// uint256 fee = takeFee(address(0), realAmountOut);
// (bool success,) = address(msg.sender).call{value: realAmountOut - fee}("");
// require(success, "refund bnb error");
// }
function swap(Common.SwapDesc[] memory descs, address feeToken, uint256 amountIn, uint256 minReturn)
public
payable
nonReentrant
whenNotPaused
{
require(descs.length > 0, "ERR_INVALID_ARGUMENT: descs is empty");
require(
feeToken == descs[0].tokenIn || feeToken == descs[descs.length - 1].tokenOut,
"ERR_INVALID_ARGUMENT: fee token is not the same as token in or out"
);
address payer = msg.sender;
address receiver = msg.sender;
address payerOrigin = msg.sender;
uint256 amountOut = 0;
uint256 balanceBefore = Common.getBalance(descs[descs.length - 1].tokenOut, msg.sender);
// fromToken is feeToken
if (feeToken == descs[0].tokenIn) {
uint256 fee = takeFee(feeToken, amountIn);
amountIn -= fee;
} else {
receiver = address(this);
}
for (uint256 i = 0; i < descs.length; i++) {
Common.SwapDesc memory desc = descs[i];
address middleReceiver = Common.getMiddleReceiver(descs, i, receiver);
if (desc.swapType == Common.SwapType.V2_EXACT_IN) {
amountOut = UniV2Lib.swap(payer, middleReceiver, payerOrigin, amountIn, desc);
} else if (desc.swapType == Common.SwapType.V3_EXACT_IN) {
amountOut = UniV3Lib.swap(payer, middleReceiver, payerOrigin, amountIn, desc);
} else if (desc.swapType == Common.SwapType.V4_EXACT_IN) {
amountOut = UniV4Lib.swap(payer, middleReceiver, payerOrigin, amountIn, desc);
} else {
revert("SwapX: invalid swap type");
}
// update amountIn for next swap
if (i != descs.length - 1) {
payer = address(this);
amountIn = amountOut;
}
}
if (feeToken == descs[descs.length - 1].tokenOut) {
uint256 fee = takeFeeTo(feeToken, amountOut);
amountOut -= fee;
Common.pay(feeToken, address(this), msg.sender, amountOut);
}
uint256 balanceAfter = Common.getBalance(descs[descs.length - 1].tokenOut, msg.sender);
require(balanceAfter - balanceBefore >= minReturn, "ERR_EXCEEDED_SLIPPAGE:minReturn not reached");
emit Swap(msg.sender, msg.sender, feeToken, amountIn, amountOut, descs);
}
function unlockCallback(bytes memory data) external returns (bytes memory) {
return UniV4Lib.unlockCallback(data);
}
// V2: Any swap, ExactIn single-hop - SupportingFeeOnTransferTokens
function swapV2ExactIn(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOutMin,
address poolAddress
) public payable nonReentrant whenNotPaused returns (uint256 amountOut) {
require(poolAddress != address(0), "SwapX: invalid pool address");
require(amountIn > 0, "SwapX: amout in is zero");
bool nativeIn = false;
if (tokenIn == address(0)) {
require(msg.value >= amountIn, "SwapX: amount in and value mismatch");
nativeIn = true;
tokenIn = WETH;
// refund
uint256 amount = msg.value - amountIn;
if (amount > 0) {
(bool success,) = address(msg.sender).call{value: amount}("");
require(success, "SwapX: refund ETH error");
}
uint256 fee = takeFee(address(0), amountIn);
amountIn = amountIn - fee;
}
if (nativeIn) {
pay(tokenIn, address(this), poolAddress, amountIn);
} else {
pay(tokenIn, msg.sender, poolAddress, amountIn);
}
bool nativeOut = false;
if (tokenOut == address(0)) {
nativeOut = true;
}
uint256 balanceBefore = nativeOut
? IERC20Upgradeable(WETH).balanceOf(address(this))
: IERC20Upgradeable(tokenOut).balanceOf(msg.sender);
IUniswapV2Pair pair = IUniswapV2Pair(poolAddress);
address token0 = pair.token0();
uint256 amountInput;
uint256 amountOutput;
{
// scope to avoid stack too deep errors
(uint256 reserve0, uint256 reserve1,) = pair.getReserves();
(uint256 reserveInput, uint256 reserveOutput) =
tokenIn == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
amountInput = IERC20Upgradeable(tokenIn).balanceOf(address(pair)).sub(reserveInput);
amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);
}
(uint256 amount0Out, uint256 amount1Out) =
tokenIn == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0));
address to = nativeOut ? address(this) : msg.sender;
pair.swap(amount0Out, amount1Out, to, new bytes(0));
if (nativeOut) {
amountOut = IERC20Upgradeable(WETH).balanceOf(address(this)).sub(balanceBefore);
IWETH(WETH).withdraw(amountOut);
uint256 fee = takeFee(address(0), amountOut);
(bool success,) = address(msg.sender).call{value: amountOut - fee}("");
require(success, "SwapX: send ETH out error");
} else {
amountOut = IERC20Upgradeable(tokenOut).balanceOf(msg.sender).sub(balanceBefore);
}
require(amountOut >= amountOutMin, "SwapX: insufficient output amount");
}
// V2: Any swap, ExactOut single-hop - * not support fee-on-transfer tokens *
// function swapV2ExactOut(
// address tokenIn,
// address tokenOut,
// uint256 amountInMax,
// uint256 amountOut,
// address poolAddress
// ) payable public nonReentrant whenNotPaused returns (uint amountIn){
// require(poolAddress != address(0), "SwapX: invalid pool address");
// require(amountInMax > 0, "SwapX: amout in max is zero");
// bool nativeIn = false;
// if (tokenIn == address(0)) {
// tokenIn = WETH;
// nativeIn = true;
// require(msg.value >= amountInMax, "SwapX: amount in and value mismatch");
// }
// bool nativeOut = false;
// if (tokenOut == address(0))
// nativeOut = true;
// IUniswapV2Pair pair = IUniswapV2Pair(poolAddress);
// address token0 = pair.token0();
// { // scope to avoid stack too deep errors
// (uint256 reserve0, uint256 reserve1,) = pair.getReserves();
// (uint256 reserveInput, uint256 reserveOutput) = tokenIn == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
// amountIn = UniswapV2Library.getAmountIn(amountOut, reserveInput, reserveOutput);
// require(amountIn <= amountInMax, "SwapX: eswapV2ExactOutxcessive input amount");
// uint256 fee = 0;
// if (!nativeOut) {
// fee = takeFee(tokenIn, amountIn);
// amountIn = amountIn - fee;
// }
// if(nativeIn) {
// pay(tokenIn, address(this), poolAddress, amountIn);
// uint256 amount = msg.value - amountIn - fee;
// // refund
// if (amount > 0) {
// (bool success, ) = address(msg.sender).call{value: amount}("");
// require(success, "SwapX: refund ETH error");
// }
// } else {
// pay(tokenIn, msg.sender, poolAddress, amountIn);
// }
// }
// (uint256 amount0Out, uint256 amount1Out) = tokenIn == token0 ? (uint256(0), amountOut) : (amountOut, uint256(0));
// address to = nativeOut ? address(this) : msg.sender;
// pair.swap(amount0Out, amount1Out, to, new bytes(0));
// if (nativeOut) {
// IWETH(WETH).withdraw(amountOut);
// uint256 fee = takeFee(address(0), amountOut);
// (bool success, ) = address(msg.sender).call{value: amountOut-fee}("");
// require(success, "SwapX: send ETH out error");
// }
// }
// **** SWAP ****
// requires the initial amount to have already been sent to the first pair
function _swap(uint256[] memory amounts, address[] memory path, address _to, address _factory) internal virtual {
for (uint256 i; i < path.length - 1; i++) {
(address input, address output) = (path[i], path[i + 1]);
(address token0,) = UniswapV2Library.sortTokens(input, output);
uint256 amountOut = amounts[i + 1];
(uint256 amount0Out, uint256 amount1Out) =
input == token0 ? (uint256(0), amountOut) : (amountOut, uint256(0));
address to = i < path.length - 2 ? UniswapV2Library.pairFor(_factory, output, path[i + 2]) : _to;
IUniswapV2Pair(UniswapV2Library.pairFor(_factory, input, output)).swap(
amount0Out, amount1Out, to, new bytes(0)
);
}
}
// **** SWAP (supporting fee-on-transfer tokens) ****
// requires the initial amount to have already been sent to the first pair
function _swapSupportingFeeOnTransferTokens(address[] memory path, address _to, address _factory)
internal
virtual
{
for (uint256 i; i < path.length - 1; i++) {
(address input, address output) = (path[i], path[i + 1]);
(address token0,) = UniswapV2Library.sortTokens(input, output);
IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(_factory, input, output));
uint256 amountInput;
uint256 amountOutput;
{
// scope to avoid stack too deep errors
(uint256 reserve0, uint256 reserve1,) = pair.getReserves();
(uint256 reserveInput, uint256 reserveOutput) =
input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
amountInput = IERC20Upgradeable(input).balanceOf(address(pair)).sub(reserveInput);
amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);
}
(uint256 amount0Out, uint256 amount1Out) =
input == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0));
address to = i < path.length - 2 ? UniswapV2Library.pairFor(_factory, output, path[i + 2]) : _to;
pair.swap(amount0Out, amount1Out, to, new bytes(0));
}
}
// V2-V2: Uniswap/Sushiswap, SupportingFeeOnTransferTokens and multi-hop
function swapV2MultiHopExactIn(
address tokenIn,
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address recipient,
uint256 deadline,
address factory
) public payable nonReentrant whenNotPaused checkDeadline(deadline) returns (uint256[] memory amounts) {
require(amountIn > 0, "SwapX: amout in is zero");
bool nativeIn = false;
if (tokenIn == address(0)) {
require(msg.value >= amountIn, "SwapX: amount in and value mismatch");
nativeIn = true;
tokenIn = WETH;
// refund
uint256 amount = msg.value - amountIn;
if (amount > 0) {
(bool success,) = address(msg.sender).call{value: amount}("");
require(success, "SwapX: refund ETH error");
}
}
bool nativeOut = false;
address tokenOut = path[path.length - 1];
if (tokenOut == WETH) {
nativeOut = true;
}
if (!nativeOut) {
uint256 fee = takeFee(tokenIn, amountIn);
amountIn = amountIn - fee;
}
address firstPool = UniswapV2Library.pairFor(factory, path[0], path[1]);
if (nativeIn) {
pay(tokenIn, address(this), firstPool, amountIn);
} else {
pay(tokenIn, msg.sender, firstPool, amountIn);
}
require(tokenIn == path[0], "invalid path");
amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
uint256 balanceBefore = IERC20Upgradeable(tokenOut).balanceOf(nativeOut ? address(this) : recipient);
_swapSupportingFeeOnTransferTokens(path, nativeOut ? address(this) : recipient, factory);
uint256 amountOut =
IERC20Upgradeable(tokenOut).balanceOf(nativeOut ? address(this) : recipient).sub(balanceBefore);
amounts[path.length - 1] = amountOut;
require(amountOut >= amountOutMin, "SwapX: insufficient output amount");
if (nativeOut) {
IWETH(WETH).withdraw(amountOut);
uint256 fee = takeFee(address(0), amountOut);
(bool success,) = address(recipient).call{value: amountOut - fee}("");
require(success, "SwapX: send ETH out error");
}
}
// // V2-V2: Uniswap, ExactOut multi-hop, not support fee-on-transfer token in output
// function swapV2MultiHopExactOut(
// address tokenIn,
// uint256 amountInMax,
// uint256 amountOut,
// address[] calldata path,
// address recipient,
// uint deadline,
// address factory
// ) payable public nonReentrant whenNotPaused checkDeadline(deadline) returns (uint[] memory amounts){
// require(amountInMax > 0, "SwapX: amount in max is zero");
// bool nativeIn = false;
// if (tokenIn == address(0)) {
// nativeIn = true;
// tokenIn = WETH;
// require(msg.value >= amountInMax, "SwapX: amount in and value mismatch");
// }
// bool nativeOut = false;
// address tokenOut = path[path.length-1];
// if (tokenOut == WETH)
// nativeOut = true;
// amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
// uint256 fee = 0;
// if (!nativeOut) {
// fee = takeFee(tokenIn, amounts[0]);
// amounts[0] = amounts[0] - fee;
// require(amounts[0] + fee <= amountInMax, 'SwapX: excessive input amount');
// }
// address firstPool = UniswapV2Library.pairFor(factory, path[0], path[1]);
// if (nativeIn) {
// pay(tokenIn, address(this), firstPool, amounts[0]);
// uint amount = msg.value - amounts[0] - fee;
// // refund
// if (amount > 0) {
// (bool success, ) = address(msg.sender).call{value: amount}("");
// require(success, "SwapX: refund ETH error");
// }
// } else
// pay(tokenIn, msg.sender, firstPool, amounts[0]);
// _swap(amounts, path, nativeOut ? address(this) : recipient, factory);
// if (nativeOut) {
// IWETH(WETH).withdraw(amountOut);
// fee = takeFee(address(0), amountOut);
// (bool success, ) = address(recipient).call{value: amountOut-fee}("");
// require(success, "SwapX: send ETH out error");
// }
// }
/// UniswapV3SwapCallback
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata _data) external override {
require(amount0Delta > 0 || amount1Delta > 0); // swaps entirely within 0-liquidity regions are not supported
SwapCallbackData memory data = abi.decode(_data, (SwapCallbackData));
(address tokenIn, address tokenOut, uint24 fee) = data.path.decodeFirstPool();
address factory = IUniswapV3Pool(msg.sender).factory();
require(factory == UNISWAP_V3_FACTORY || factory == SUSHISWAP_V3_FACTORY, "SwapX: Invalid pool");
if (factory == UNISWAP_V3_FACTORY) {
CallbackValidation.verifyCallback(UNISWAP_V3_FACTORY, tokenIn, tokenOut, fee);
} else {
CallbackValidation.verifyCallback(SUSHISWAP_V3_FACTORY, tokenIn, tokenOut, fee);
}
(bool isExactInput, uint256 amountToPay) =
amount0Delta > 0 ? (tokenIn < tokenOut, uint256(amount0Delta)) : (tokenOut < tokenIn, uint256(amount1Delta));
if (isExactInput) {
pay(tokenIn, data.payer, msg.sender, amountToPay);
uint256 amount =
tokenIn == WETH ? address(this).balance : IERC20Upgradeable(tokenIn).balanceOf(address(this));
if (amount > 0) {
pay(tokenIn, address(this), data.payerOrigin, amount);
}
}
// } else {
// // either initiate the next swap or pay
// if (data.path.hasMultiplePools()) {
// data.path = data.path.skipToken();
// exactOutputInternal(amountToPay, msg.sender, 0, data);
// } else {
// amountInCached = amountToPay;
// tokenIn = tokenOut; // swap in/out because exact output swaps are reversed
// pay(tokenIn, data.payer, msg.sender, amountToPay);
// }
// }
}
/// PancakeV3SwapCallback
function pancakeV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata _data) external override {
require(amount0Delta > 0 || amount1Delta > 0); // swaps entirely within 0-liquidity regions are not supported
SwapCallbackData memory data = abi.decode(_data, (SwapCallbackData));
(address tokenIn, address tokenOut, uint24 fee) = data.path.decodeFirstPool();
CallbackValidation.verifyCallback(PANCAKE_V3_FACTORY, tokenIn, tokenOut, fee);
(bool isExactInput, uint256 amountToPay) =
amount0Delta > 0 ? (tokenIn < tokenOut, uint256(amount0Delta)) : (tokenOut < tokenIn, uint256(amount1Delta));
if (isExactInput) {
pay(tokenIn, data.payer, msg.sender, amountToPay);
uint256 amount =
tokenIn == WETH ? address(this).balance : IERC20Upgradeable(tokenIn).balanceOf(address(this));
if (amount > 0) {
pay(tokenIn, address(this), data.payerOrigin, amount);
}
}
// } else {
// // either initiate the next swap or pay
// if (data.path.hasMultiplePools()) {
// data.path = data.path.skipToken();
// exactOutputInternal(amountToPay, msg.sender, 0, data);
// } else {
// amountInCached = amountToPay;
// tokenIn = tokenOut; // swap in/out because exact output swaps are reversed
// pay(tokenIn, data.payer, msg.sender, amountToPay);
// }
// }
}
// V3: ExactIn single-hop
function swapV3ExactIn(ExactInputSingleParams memory params)
external
payable
nonReentrant
whenNotPaused
checkDeadline(params.deadline)
returns (uint256 amountOut)
{
require(params.amountIn > 0, "SwapX: amount in is zero");
if (params.tokenIn == WETH || params.tokenIn == address(0)) {
params.tokenIn = WETH;
require(msg.value >= params.amountIn, "SwapX: amount in and value mismatch");
// refund
uint256 amount = msg.value - params.amountIn;
if (amount > 0) {
(bool success,) = address(msg.sender).call{value: amount}("");
require(success, "SwapX: refund ETH error");
}
}
bool nativeOut = false;
if (params.tokenOut == WETH) {
nativeOut = true;
}
if (!nativeOut) {
uint256 fee = takeFee(params.tokenIn, params.amountIn);
params.amountIn = params.amountIn - fee;
}
factoryV3 = params.factoryAddress;
amountOut = exactInputInternal(
params.poolAddress,
params.amountIn,
nativeOut ? address(0) : params.recipient,
params.sqrtPriceLimitX96,
SwapCallbackData({
path: abi.encodePacked(params.tokenIn, params.fee, params.tokenOut),
payer: msg.sender,
payerOrigin: msg.sender
})
);
require(amountOut >= params.amountOutMinimum, "SwapX: insufficient out amount");
if (nativeOut) {
IWETH(WETH).withdraw(amountOut);
uint256 fee = takeFee(address(0), amountOut);
(bool success,) = address(params.recipient).call{value: amountOut - fee}("");
require(success, "SwapX: send ETH out error");
}
}
// // V3: ExactOut single-hop
// function swapV3ExactOut (
// ExactOutputSingleParams memory params
// ) payable public nonReentrant whenNotPaused checkDeadline(params.deadline) returns (uint256 amountIn) {
// require(params.amountInMaximum > 0, "SwapX: amount in max is zero");
// bool nativeIn = false;
// if (params.tokenIn == WETH || params.tokenIn == address(0)) {
// params.tokenIn = WETH;
// nativeIn = true;
// require(msg.value >= params.amountInMaximum, "SwapX: amount in max and value mismatch");
// }
// bool nativeOut = false;
// if (params.tokenOut == WETH || params.tokenOut == address(0)) {
// params.tokenOut = WETH;
// nativeOut = true;
// }
// uint256 fee = 0;
// if (!nativeOut) {
// fee = takeFee(params.tokenIn, amountIn);
// amountIn = amountIn - fee;
// require(amountIn + fee <= params.amountInMaximum, "SwapX: too much requested");
// }
// amountIn = exactOutputInternal(
// params.amountOut,
// nativeOut ? address(0) : params.recipient,
// params.sqrtPriceLimitX96,
// SwapCallbackData({path: abi.encodePacked(params.tokenOut, params.fee, params.tokenIn), payer: msg.sender, payerOrigin: msg.sender})
// );
// if (nativeIn) {
// uint amount = msg.value - amountIn - fee;
// // refund
// if (amount > 0) {
// (bool success, ) = address(msg.sender).call{value: amount}("");
// require(success, "SwapX: refund ETH error");
// }
// }
// if (nativeOut) {
// IWETH(WETH).withdraw(params.amountOut);
// fee = takeFee(address(0), params.amountOut);
// (bool success, ) = address(params.recipient).call{value: params.amountOut - fee}("");
// require(success, "SwapX: send ETH out error");
// }
// amountInCached = type(uint256).max;
// }
// V3-V3: ExactIn multi-hop
function swapV3MultiHopExactIn(ExactInputParams memory params)
public
payable
nonReentrant
whenNotPaused
checkDeadline(params.deadline)
returns (uint256 amountOut)
{
require(params.amountIn > 0, "SwapX: amount in is zero");
if (msg.value > 0) {
require(msg.value >= params.amountIn, "SwapX: amount in and value mismatch");
// refund
uint256 amount = msg.value - params.amountIn;
if (amount > 0) {
(bool success,) = address(msg.sender).call{value: amount}("");
require(success, "SwapX: refund ETH error");
}
}
(address tokenIn,,) = params.path.decodeFirstPool();
if (tokenIn == WETH || tokenIn == address(0)) {
tokenIn = WETH;
uint256 fee = takeFee(address(0), params.amountIn);
params.amountIn = params.amountIn - fee;
}
address payer = msg.sender; // msg.sender pays for the first hop
bool nativeOut = false;
uint256 i = 0;
while (true) {
bool hasMultiplePools = params.path.hasMultiplePools();
if (!hasMultiplePools) {
(, address tokenOut,) = params.path.decodeFirstPool();
if (tokenOut == WETH) {
nativeOut = true;
}
}
// the outputs of prior swaps become the inputs to subsequent ones
factoryV3 = params.factoryAddresses[i];
params.amountIn = exactInputInternal(
params.poolAddresses[i],
params.amountIn,
hasMultiplePools ? address(this) : (nativeOut ? address(this) : params.recipient),
0,
SwapCallbackData({path: params.path.getFirstPool(), payer: payer, payerOrigin: msg.sender})
);
// decide whether to continue or terminate
if (hasMultiplePools) {
payer = address(this); // at this point, the caller has paid
params.path = params.path.skipToken();
i++;
} else {
amountOut = params.amountIn;
break;
}
}
require(amountOut >= params.amountOutMinimum, "SwapX: too little received");
if (nativeOut) {
IWETH(WETH).withdraw(amountOut);
uint256 fee = takeFee(address(0), amountOut);
(bool success,) = address(params.recipient).call{value: amountOut - fee}("");
require(success, "SwapX: send ETH out error");
}
}
// V3-V3: ExactOut multi-hop
// function swapV3MultiHopExactOut(
// ExactOutputParams memory params
// ) external payable nonReentrant whenNotPaused checkDeadline(params.deadline) returns (uint256 amountIn) {
// require(params.amountInMaximum > 0, "SwapX: amount in max is zero");
// if (msg.value > 0)
// require(msg.value >= params.amountInMaximum, "SwapX: amount in max and value mismatch");
// bool nativeOut = false;
// (address tokenOut, , ) = params.path.decodeFirstPool();
// if (tokenOut == WETH)
// nativeOut = true;
// uint256 fee = 0;
// if (!nativeOut) {
// fee = takeFee(params.tokenIn, amountIn);
// amountIn = amountIn - fee;
// require(amountIn + fee <= params.amountInMaximum, 'SwapX: too much requested');
// }
// exactOutputInternal(
// params.amountOut,
// nativeOut ? address(this) : params.recipient,
// 0,
// SwapCallbackData({path: params.path, payer: msg.sender, payerOrigin: msg.sender})
// );
// amountIn = amountInCached;
// if (params.tokenIn == WETH || params.tokenIn == address(0)) {
// if (msg.value > 0) {
// // refund
// uint256 amount = msg.value - amountIn - fee;
// if (amount > 0) {
// (bool success, ) = address(msg.sender).call{value: amount}("");
// require(success, "SwapX: refund ETH error");
// }
// }
// }
// if (nativeOut) {
// IWETH(WETH).withdraw(params.amountOut);
// fee = takeFee(address(0), params.amountOut);
// (bool success, ) = address(params.recipient).call{value: params.amountOut-fee}("");
// require(success, "SwapX: send ETH out error");
// }
// amountInCached = type(uint256).max;
// }
function isStrEqual(string memory str1, string memory str2) internal pure returns (bool) {
return keccak256(bytes(str1)) == keccak256(bytes(str2));
}
// Mixed: ExactIn multi-hop, token not supporting zero address
function swapMixedMultiHopExactIn(ExactInputMixedParams memory params)
public
payable
nonReentrant
whenNotPaused
checkDeadline(params.deadline)
returns (uint256 amountOut)
{
require(params.routes.length == 2, "SwapX: only 2 routes supported");
require(params.amountIn > 0, "SwapX: amount in is zero");
(address tokenIn, address tokenOut1, uint24 fee1) = params.path1.decodeFirstPool();
bool nativeIn = false;
if (tokenIn == WETH || tokenIn == address(0)) {
require(msg.value >= params.amountIn, "SwapX: amount in and value mismatch");
nativeIn = true;
tokenIn = WETH;
// refund
uint256 amount = msg.value - params.amountIn;
if (amount > 0) {
(bool success,) = address(msg.sender).call{value: amount}("");
require(success, "SwapX: refund ETH error");
}
uint256 fee = takeFee(address(0), params.amountIn);
params.amountIn = params.amountIn - fee;
}
if (isStrEqual(params.routes[0], "v2") && isStrEqual(params.routes[1], "v2")) {
// uni - sushi, or verse
address poolAddress1 = UniswapV2Library.pairFor(params.factory1, tokenIn, tokenOut1);
if (nativeIn) {
pay(tokenIn, address(this), poolAddress1, params.amountIn);
} else {
pay(tokenIn, msg.sender, poolAddress1, params.amountIn);
}
address[] memory path1 = new address[](2);
path1[0] = tokenIn;
path1[1] = tokenOut1;
(, address tokenOut,) = params.path2.decodeFirstPool();
address[] memory path2 = new address[](2);
path2[0] = tokenOut1;
path2[1] = tokenOut;
address poolAddress2 = UniswapV2Library.pairFor(params.factory2, tokenOut1, tokenOut);
bool nativeOut = tokenOut == WETH;
uint256 balanceBefore = IERC20Upgradeable(tokenOut).balanceOf(nativeOut ? address(this) : params.recipient);
_swapSupportingFeeOnTransferTokens(path1, poolAddress2, params.factory1);
_swapSupportingFeeOnTransferTokens(path2, nativeOut ? address(this) : params.recipient, params.factory2);
amountOut =
IERC20Upgradeable(tokenOut).balanceOf(nativeOut ? address(this) : params.recipient).sub(balanceBefore);
if (nativeOut) {
IWETH(WETH).withdraw(amountOut);
uint256 fee = takeFee(address(0), amountOut);
(bool success,) = address(params.recipient).call{value: amountOut - fee}("");
require(success, "SwapX: send ETH out error");
}
} else if (isStrEqual(params.routes[0], "v2") && isStrEqual(params.routes[1], "v3")) {
address poolAddress1 = UniswapV2Library.pairFor(params.factory1, tokenIn, tokenOut1);
if (nativeIn) {
pay(tokenIn, address(this), poolAddress1, params.amountIn);
} else {
pay(tokenIn, msg.sender, poolAddress1, params.amountIn);
}
address[] memory path1 = new address[](2);
path1[0] = tokenIn;
path1[1] = tokenOut1;
(, address tokenOut,) = params.path2.decodeFirstPool();
bool nativeOut = tokenOut == WETH;
uint256 balanceBefore = IERC20Upgradeable(tokenOut).balanceOf(nativeOut ? address(this) : params.recipient);
_swapSupportingFeeOnTransferTokens(path1, address(this), params.factory1);
factoryV3 = params.factory2;
amountOut = exactInputInternal(
params.poolAddress2,
IERC20Upgradeable(tokenOut1).balanceOf(address(this)),
nativeOut ? address(this) : params.recipient,
0,
SwapCallbackData({path: params.path2, payer: address(this), payerOrigin: msg.sender})
);
amountOut =
IERC20Upgradeable(tokenOut).balanceOf(nativeOut ? address(this) : params.recipient).sub(balanceBefore);
if (nativeOut) {
IWETH(WETH).withdraw(amountOut);
uint256 fee = takeFee(address(0), amountOut);
(bool success,) = address(params.recipient).call{value: amountOut - fee}("");
require(success, "SwapX: send ETH out error");
}
} else if (isStrEqual(params.routes[0], "v3") && isStrEqual(params.routes[1], "v2")) {
(address tokenIn2, address tokenOut,) = params.path2.decodeFirstPool();
address pairV2Address = UniswapV2Library.pairFor(params.factory2, tokenIn2, tokenOut);
factoryV3 = params.factory1;
uint256 amountOut1 = exactInputInternal(
params.poolAddress1,
params.amountIn,
pairV2Address,
0,
SwapCallbackData({
path: abi.encodePacked(tokenIn, fee1, tokenOut1),
payer: msg.sender,
payerOrigin: msg.sender
})
);
address[] memory path2 = new address[](2);
path2[0] = tokenIn2;
path2[1] = tokenOut;
uint256[] memory amounts2 = UniswapV2Library.getAmountsOut(params.factory2, amountOut1, path2);
amountOut = amounts2[amounts2.length - 1];
bool nativeOut = tokenOut == WETH;
uint256 balanceBefore = IERC20Upgradeable(tokenOut).balanceOf(nativeOut ? address(this) : params.recipient);
_swapSupportingFeeOnTransferTokens(path2, nativeOut ? address(this) : params.recipient, params.factory2);
amountOut =
IERC20Upgradeable(tokenOut).balanceOf(nativeOut ? address(this) : params.recipient).sub(balanceBefore);
if (nativeOut) {
IWETH(WETH).withdraw(amountOut);
uint256 fee = takeFee(address(0), amountOut);
(bool success,) = address(params.recipient).call{value: amountOut - fee}("");
require(success, "SwapX: send ETH out error");
}
}
require(amountOut >= params.amountOutMinimum, "SwapX: too little received");
}
// V3: compute pool address
function getPool(address tokenA, address tokenB, uint24 fee) public view returns (IUniswapV3Pool) {
return IUniswapV3Pool(PoolAddress.computeAddress(factoryV3, PoolAddress.getPoolKey(tokenA, tokenB, fee)));
}
/// V3: Performs a single exact input swap
function exactInputInternal(
address poolAddress,
uint256 amountIn,
address recipient,
uint160 sqrtPriceLimitX96,
SwapCallbackData memory data
) private returns (uint256 amountOut) {
// allow swapping to the router address with address 0
if (recipient == address(0)) recipient = address(this);
(address tokenIn, address tokenOut,) = data.path.decodeFirstPool();
bool zeroForOne = tokenIn < tokenOut;
(int256 amount0, int256 amount1) = IUniswapV3Pool(poolAddress).swap(
recipient,
zeroForOne,
amountIn.toInt256(),
sqrtPriceLimitX96 == 0
? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1)
: sqrtPriceLimitX96,
abi.encode(data)
);
return uint256(-(zeroForOne ? amount1 : amount0));
}
/// Performs a single exact output swap
// function exactOutputInternal(
// uint256 amountOut,
// address recipient,
// uint160 sqrtPriceLimitX96,
// SwapCallbackData memory data
// ) private returns (uint256 amountIn) {
// // allow swapping to the router address with address 0
// if (recipient == address(0)) recipient = address(this);
// (address tokenOut, address tokenIn, uint24 fee) = data.path.decodeFirstPool();
// bool zeroForOne = tokenIn < tokenOut;
// (int256 amount0Delta, int256 amount1Delta) =
// getPool(tokenIn, tokenOut, fee).swap(
// recipient,
// zeroForOne,
// -amountOut.toInt256(),
// sqrtPriceLimitX96 == 0
// ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1)
// : sqrtPriceLimitX96,
// abi.encode(data)
// );
// uint256 amountOutReceived;
// (amountIn, amountOutReceived) = zeroForOne
// ? (uint256(amount0Delta), uint256(-amount1Delta))
// : (uint256(amount1Delta), uint256(-amount0Delta));
// // it's technically possible to not receive the full output amount,
// // so if no price limit has been specified, require this possibility away
// if (sqrtPriceLimitX96 == 0) require(amountOutReceived == amountOut);
// }
function pay(address token, address payer, address recipient, uint256 value) internal {
if (token == WETH && address(this).balance >= value) {
// pay with WETH
IWETH(WETH).deposit{value: value}(); // wrap only what is needed to pay
IWETH(WETH).transfer(recipient, value);
} else if (payer == address(this)) {
// pay with tokens already in the contract (for the exact input multihop case)
IERC20Upgradeable(token).safeTransfer(recipient, value);
} else {
// pull payment
IERC20Upgradeable(token).safeTransferFrom(payer, recipient, value);
}
}
function pause() external onlyOwner {
require(paused() == false, "paused already");
_pause();
}
function unpause() external onlyOwner {
require(paused() == true, "unpaused already");
_unpause();
}
// function setWETH(address addr) external onlyOwner {
// require(addr != address(0), "invalid addr");
// WETH = addr;
// }
// function setWhitelistAddr(address addr) external onlyOwner {
// require(addr != address(0),"invalid addr");
// feeExcludeList[addr] = true;
// }
function withdraw(address token, address receiver, uint256 amount) external onlyOwner {
if (token == address(0)) {
(bool s,) = payable(receiver).call{value: amount}("");
require(s, "withdraw native failed");
} else {
IERC20Upgradeable(token).safeTransfer(receiver, amount);
}
}
}
"
},
"contracts/Storage.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >= 0.8.0;
contract Storage {
uint256 public amountInCached;
uint256 public feeRate;
uint256 public feeDenominator;
address public feeCollector;
address public WETH;
// special for ETH chain
address public factoryV2;
address public factoryV3;
mapping(address => bool) public feeExcludeList;
}
"
},
"contracts/contracts-upgradeable/access/OwnableUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import "../proxy/utils/Initializable.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.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.
*
* The initial owner is set to the address provided by the deployer. 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 OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
function __Ownable_init(address initialOwner) internal onlyInitializing {
__Ownable_init_unchained(initialOwner);
}
function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
_transferOwnership(initialOwner);
}
/**
* @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 {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @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 {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_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);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}
"
},
"contracts/contracts-upgradeable/interfaces/IERC1271Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*/
interface IERC1271Upgradeable {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}
"
},
"contracts/contracts-upgradeable/proxy/utils/Initializable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0e;
/**
* @dev The contract is already initialized.
*/
error AlreadyInitialized();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
if (!(isTopLevelCall && initialized < 1) && !(address(this).code.length == 0 && initialized == 1)) {
revert AlreadyInitialized();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert AlreadyInitialized();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert AlreadyInitialized();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}
"
},
"contracts/contracts-upgradeable/security/PausableUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.20;
import "../proxy/utils/Initializable.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal onlyInitializing {
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
Submitted on: 2025-09-17 13:15:17
Comments
Log in to comment.
No comments yet.