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/XStakingPool.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./interfaces/IToken.sol";
import "./interfaces/IXStakingPool.sol";
import "./interfaces/IXBRStakingPool.sol";
import "./interfaces/IXStakingFactory.sol";
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
/// @title XStakingPool
/// @dev This contract handles individual staking operations,
/// including deposits and withdrawals of different assets.
/// @custom:oz-upgrades-from XStakingPool
contract XStakingPool is
Initializable,
ERC20Upgradeable,
Ownable2StepUpgradeable,
IXStakingPool,
ReentrancyGuardUpgradeable
{
using SafeERC20 for IToken;
/// @dev 10000 = 100%
uint256 public constant FEE_DENOMINATOR = 100_00;
/// @dev Minimal deposit fee value ($1)
uint256 public constant MIN_FIXED_DEPOSIT_FEE = 1_000000;
uint256 public constant MAX_FIXED_DEPOSIT_FEE = 1_000_000_000000;
uint256 public constant MIN_PERCENT_DEPOSIT_FEE = 100;
/// @notice Reference to the XStakingFactory that deployed this pool.
IXStakingFactory public xstakingFactory;
/// @notice Unique identifier for this staking pool.
uint256 public poolId;
/// @notice profit sharing fee numerator
uint256 public profitSharingFeeNumerator;
/// @notice tokens in the pool
address[] public tokens;
/// @notice the allocations of tokens
uint256[] public allocations;
/// @notice the sum of array `allocations`
uint256 public totalAllocation;
/// @notice the cap of
uint256 public capitalizationCap;
/// @notice true - deposit paused, false - deposit not paused
bool public isDepositPaused;
/// @notice profit sharing fees
mapping(address depositToken => uint256) public totalProfitSharingFee;
/// @notice allocated tokens to user
mapping(address user => mapping(address token => uint256))
public userTokenAmount;
/// @notice Mapping to track the total tokens amount for each token.
/// @dev Key is the token address, and value is the total amount deposited in the pool.
mapping(address token => uint256) public totalTokenAmount;
/// @notice List of tokens that have been added to the pool to prevent duplicates.
mapping(address => bool) isTokenAlreadyAdded;
uint256 private tempTotalAmountDepositTokenOut = 0;
uint256 public depositFeeNumerator = 1_000000;
bool public isPercentageFee = false;
mapping(address => uint256) public totalDepositFee;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/// @notice Initializes the XStakingPool with a specific pool ID and initial owner.
/// @dev Sets the pool ID and the initial owner. Links the pool to the XStakingFactory.
/// @param _poolId Unique identifier for the pool.
/// @param _poolOwner Address of the initial owner of the pool.
function initialize(
uint256 _poolId,
address _poolOwner,
uint256 _capitalizationCap,
address[] memory _tokens,
uint256[] memory _allocations,
uint256 _profitSharingFeeNumerator,
uint256[] memory _tokensAmounts,
address initialDepositToken,
uint256 initialDepositTokenAmount,
uint256 _depositFeeNumerator,
bool _isPercentageFee
) public initializer {
__Ownable_init(_poolOwner);
__ERC20_init_unchained(
string.concat("XBR XStakingPool #", Strings.toString(_poolId)),
string.concat("XBR", Strings.toString(_poolId))
);
__ReentrancyGuard_init();
xstakingFactory = IXStakingFactory(msg.sender); // deployer is XStakingFactory
poolId = _poolId;
capitalizationCap = _capitalizationCap;
uint256 tokenLength = _tokens.length;
require(
tokenLength == _allocations.length &&
tokenLength == _tokensAmounts.length,
"XStakingPool: invalid length of _tokens and _allocations"
);
allocations = _allocations;
uint256 _totalAllocation = 0;
address poolOwner = _poolOwner;
_setProfitSharingFeeNumerator(_profitSharingFeeNumerator);
_setDepositFeeNumerator(_depositFeeNumerator, _isPercentageFee);
address token;
for (uint256 i = 0; i < tokenLength; ) {
token = _tokens[i];
require(
isTokenAlreadyAdded[token] == false,
"XStakingPool: token already added"
);
isTokenAlreadyAdded[token] = true;
tokens.push(token);
uint256 tokenAmountBefore = IToken(token).balanceOf(address(this));
IToken(token).safeTransferFrom(
msg.sender,
address(this),
_tokensAmounts[i]
);
uint256 tokenAmountAfter = IToken(token).balanceOf(address(this));
uint256 incommingAmount = tokenAmountAfter - tokenAmountBefore;
userTokenAmount[poolOwner][token] += incommingAmount;
totalTokenAmount[token] += incommingAmount;
_totalAllocation += _allocations[i];
unchecked {
i++;
}
}
for (uint256 i = 0; i < tokenLength; ) {
emit TokenSwap(
_tokens[i],
_tokensAmounts[i],
(initialDepositTokenAmount * _allocations[i]) / _totalAllocation
);
unchecked {
i++;
}
}
uint256 amountToMint = calcMintAmount(
initialDepositToken,
initialDepositTokenAmount
);
_mint(poolOwner, amountToMint);
emit Volume(initialDepositTokenAmount);
emit Deposit(
address(this),
poolOwner,
initialDepositTokenAmount,
getUserTokenAmounts(poolOwner)
);
emitTokenAmouts();
totalAllocation = _totalAllocation;
}
function claimProfitSharingFee(address depositToken) public onlyOwner {
totalProfitSharingFee[depositToken] = 0;
IToken(depositToken).safeTransfer(
msg.sender,
totalProfitSharingFee[depositToken]
);
}
function claimDepositFee(address depositToken) public onlyOwner {
uint256 depositFee = totalDepositFee[depositToken];
totalDepositFee[depositToken] = 0;
IToken(depositToken).safeTransfer(
msg.sender,
depositFee
);
}
function setDepositFeeNumerator(
uint256 depositFeeNumerator,
bool isPercentageFee
) public onlyOwner {
_setDepositFeeNumerator(depositFeeNumerator, isPercentageFee);
}
/// @notice set the deposit paused
/// @param _depositPaused true - deposit paused, false - deposit unpaused
function setPausedDeposit(bool _depositPaused) public onlyOwner {
isDepositPaused = _depositPaused;
emit DepositStatusUpdated(_depositPaused);
}
/// @notice set the new capitalization cap
/// @param _capitalizationCap the amount of dollars in capitalization
function setCapitalizationCap(uint256 _capitalizationCap) public onlyOwner {
capitalizationCap = _capitalizationCap;
}
/// @notice sets profit sharing fee
/// @param _profitSharingFeeNumerator the numerator of profit sharing fee
function setProfitSharingFeeNumerator(
uint256 _profitSharingFeeNumerator
) public onlyOwner {
_setProfitSharingFeeNumerator(_profitSharingFeeNumerator);
}
function _setProfitSharingFeeNumerator(
uint256 _profitSharingFeeNumerator
) internal {
require(
1_00 <= _profitSharingFeeNumerator &&
_profitSharingFeeNumerator <= 49_00,
"XStakingPool: _profitSharingFeeNumerator out of bound"
);
profitSharingFeeNumerator = _profitSharingFeeNumerator;
}
function execOneInchSwap(
address oneInchRouter,
bytes memory oneInchSwapData
) internal returns (uint256) {
(bool success, bytes memory response) = oneInchRouter.call(
oneInchSwapData
);
require(success, "1inch swap failed");
uint256 amountOut = abi.decode(response, (uint256));
return amountOut;
}
/// @notice deposit the baseToken to pool to `msg.sender`
/// @param depositToken address of deposit token
/// @param depositTokenAmount amount of base token
/// @param oneInchSwapData the data for swap on 1inch
/// @return the amount of minted Liquidity Pool Token
function deposit(
address depositToken,
uint256 depositTokenAmount,
bytes[] calldata oneInchSwapData
) public returns (uint256) {
return
depositTo(
depositToken,
msg.sender,
depositTokenAmount,
oneInchSwapData
);
}
/// @notice deposit the baseToken to pool
/// @param depositToken address of deposit token
/// @param to address of receiver the LP tokens
/// @param depositTokenAmount amount of base token
/// @param oneInchSwapData the data for swap on 1inch
/// @return the amount of minted Liquidity Pool Token
function depositTo(
address depositToken,
address to,
uint256 depositTokenAmount,
bytes[] calldata oneInchSwapData
) public returns (uint256) {
_checkIfOwnerHaveLockedTokens();
require(
xstakingFactory.isDepositToken(depositToken),
"XStakingPool: not deposit token"
);
require(
tokens.length == oneInchSwapData.length,
"XStakingPool: invalid length of tokens and oneInchSwapData"
);
require(!isDepositPaused, "XStakingPool: deposit is paused");
address oneInchRouter = xstakingFactory.oneInchRouter();
address treasuryWallet = xstakingFactory.treasuryWallet();
uint256 totalDepositToken = calculateTotalDepositToken(
depositToken,
depositTokenAmount
);
uint256 depositFee = calculateDepositFee(depositTokenAmount);
{
IToken(depositToken).safeTransferFrom(
msg.sender,
address(this),
totalDepositToken + depositFee
);
uint256 stakingFee = totalDepositToken - depositTokenAmount;
IToken(depositToken).safeTransfer(treasuryWallet, stakingFee);
}
totalDepositFee[depositToken] += depositFee;
IToken(depositToken).forceApprove(oneInchRouter, depositTokenAmount);
_depositTo(
depositToken,
to,
depositTokenAmount,
oneInchRouter,
oneInchSwapData
);
emit Volume(depositTokenAmount);
emit Deposit(
address(this),
to,
depositTokenAmount,
getUserTokenAmounts(to)
);
emitTokenAmouts();
return depositTokenAmount;
}
function _depositTo(
address depositToken,
address to,
uint256 depositTokenAmount,
address oneInchRouter,
bytes[] calldata oneInchSwapData
) internal nonReentrant {
uint256 len = tokens.length;
uint256 amountOut;
uint256 depositTokenAmountAllocated;
address token;
uint256 capitalization = 0;
uint256 totalSwappedDepositTokenAmount = 0;
for (uint256 i = 0; i < len; ) {
token = tokens[i];
uint256 balanceBefore = IToken(token).balanceOf(address(this));
require(
oneInchSwapData[i].length >= 4,
"XStakingPool: Incorrect data length"
);
(
,
address receiver,
address srcToken,
uint256 srcTokenAmount,
uint256 minReturnAmount
) = decodeSwapData(oneInchSwapData[i]);
totalSwappedDepositTokenAmount += srcTokenAmount;
require(
address(this) == receiver,
"XStakingPool: swap receiver have to be pool address"
);
require(
depositToken == srcToken,
"XStakingPool: deposit token does not match with src token in swap data"
);
uint256 tokenAmountBefore = IToken(token).balanceOf(address(this));
execOneInchSwap(oneInchRouter, oneInchSwapData[i]); // [amountOut]=token
uint256 tokenAmountAfter = IToken(token).balanceOf(address(this));
amountOut = tokenAmountAfter - tokenAmountBefore;
require(
amountOut >= minReturnAmount,
"XStakingPool: output amount does not match with encoded amount from swap data"
);
uint256 balanceAfter = IToken(token).balanceOf(address(this));
require(
balanceAfter != balanceBefore,
"XStakingPool: pool balance was not changed after swap"
);
userTokenAmount[to][token] += amountOut;
totalTokenAmount[token] += amountOut;
depositTokenAmountAllocated =
(depositTokenAmount * allocations[i]) /
totalAllocation;
capitalization +=
(totalTokenAmount[token] * depositTokenAmountAllocated) /
amountOut;
emit TokenSwap(token, amountOut, depositTokenAmountAllocated);
unchecked {
i++;
}
}
require(
totalSwappedDepositTokenAmount == depositTokenAmount,
"XStakingPool: swapped tokens amount does not match with sum of amount in every swap"
);
_mint(to, calcMintAmount(depositToken, depositTokenAmount));
if (totalSupply() >= capitalizationCap * 10 ** 12) {
isDepositPaused = true;
emit DepositStatusUpdated(true);
}
emit PoolCapitalization(address(this), capitalization);
}
/// @notice return the allocation tokens for deposit using 1inch
/// @dev this helper view function uses for forming swap request to 1inch API
/// @param depositTokenAmount the amount of base token to deposit
/// @return the array of allocation of depositTokenAmount for swap using 1inch and sum of array elements
function calcDepositTokenAllocationForDeposit(
uint256 depositTokenAmount
) public view returns (uint256[] memory, uint256) {
uint256 len = tokens.length;
uint256[] memory allocatedBaseTokens = new uint256[](len);
uint256 totalSumOfAllocatedBaseToken;
for (uint256 i = 0; i < len; ) {
allocatedBaseTokens[i] =
(depositTokenAmount * allocations[i]) /
totalAllocation;
totalSumOfAllocatedBaseToken += allocatedBaseTokens[i];
unchecked {
i++;
}
}
return (allocatedBaseTokens, totalSumOfAllocatedBaseToken);
}
function calculateTotalDepositToken(
address depositToken,
uint256 depositTokenAmount
) public view returns (uint256 totalDepositToken) {
uint256 stakingFee = xstakingFactory.calculateFeeAmount(
depositTokenAmount,
true
);
totalDepositToken = depositTokenAmount + stakingFee;
}
function calculateDepositFee(
uint256 depositTokenAmount
) public view returns (uint256) {
if(msg.sender == owner()){
return 0;
}
if (isPercentageFee) {
return (depositTokenAmount * depositFeeNumerator) / FEE_DENOMINATOR;
} else {
return
depositFeeNumerator != 0
? depositFeeNumerator
: MIN_FIXED_DEPOSIT_FEE;
}
}
/// @notice withdraw the base token amount from `from` to `msg.sender`
/// @param depositToken address of deposit token
/// @param amountLP the amount of Liquidity Pool token
/// @param oneInchSwapData the data for swap on 1inch
function withdraw(
address depositToken,
uint256 amountLP,
bytes[] calldata oneInchSwapData
) public returns (uint256) {
return
withdrawFrom(
depositToken,
msg.sender,
msg.sender,
amountLP,
oneInchSwapData
);
}
function withdrawFrom(
address depositToken,
address from,
address to,
uint256 amountLP,
bytes[] calldata oneInchSwapData
) internal returns (uint256) {
require(
xstakingFactory.isDepositToken(depositToken),
"XStakingPool: not deposit token"
);
uint256 balanceOfLP = balanceOf(from);
require(amountLP <= balanceOfLP, "XStakingPool: amountLP > balanceOf");
address oneInchRouter = xstakingFactory.oneInchRouter();
address treasuryWallet = xstakingFactory.treasuryWallet();
uint256 totalAmountDepositTokenOut = _withdrawFrom(
from,
amountLP,
oneInchRouter,
oneInchSwapData
);
uint256 unstakingFee;
uint256 lpDecimals = decimals();
uint256 depositTokenDecimals = IToken(depositToken).decimals();
uint256 adaptedAmountLP = amountLP;
uint256 adaptedTotalAmountDepositTokenOut = totalAmountDepositTokenOut;
// adapt `adaptedAmountLP` decimals to `depositTokenDecimals`.
if (lpDecimals > depositTokenDecimals) {
uint256 scaleDifference = lpDecimals - depositTokenDecimals;
adaptedAmountLP = adaptedAmountLP / (10 ** scaleDifference);
} else if (depositTokenDecimals > lpDecimals) {
uint256 scaleDifference = depositTokenDecimals - lpDecimals;
adaptedAmountLP = adaptedAmountLP * (10 ** scaleDifference);
}
if (adaptedAmountLP < adaptedTotalAmountDepositTokenOut) {
uint256 profit = adaptedTotalAmountDepositTokenOut -
adaptedAmountLP;
uint256 profitSharingFee = (profit * profitSharingFeeNumerator) /
FEE_DENOMINATOR;
totalProfitSharingFee[depositToken] += profitSharingFee;
totalAmountDepositTokenOut -= profitSharingFee;
}
unstakingFee = xstakingFactory.calculateFeeAmount(
totalAmountDepositTokenOut,
false
);
IToken(depositToken).safeTransfer(
to,
totalAmountDepositTokenOut - unstakingFee
);
IToken(depositToken).safeTransfer(treasuryWallet, unstakingFee);
emit Volume(totalAmountDepositTokenOut);
emit Withdraw(
address(this),
from,
totalAmountDepositTokenOut,
getUserTokenAmounts(from)
);
emitTokenAmouts();
_burn(from, amountLP);
return totalAmountDepositTokenOut;
}
function _withdrawFrom(
address from,
uint256 amountLP,
address oneInchRouter,
bytes[] calldata oneInchSwapData
) internal nonReentrant returns (uint256) {
address token;
uint256 capitalization = 0;
uint256[] memory allocatedTokens = calcAllocatedTokensForWithdraw(
from,
amountLP
);
for (uint256 i = 0; i < tokens.length; ) {
bytes memory swapData = oneInchSwapData[i];
uint256 amountDepositTokenOut;
token = tokens[i];
userTokenAmount[from][token] -= allocatedTokens[i];
totalTokenAmount[token] -= allocatedTokens[i];
IToken(token).forceApprove(oneInchRouter, allocatedTokens[i]);
uint256 balanceBefore = IToken(token).balanceOf(address(this));
if (swapData.length == 0) {
IToken(token).safeTransfer(from, allocatedTokens[i]);
} else {
amountDepositTokenOut = execOneInchSwap(
oneInchRouter,
oneInchSwapData[i]
);
uint256 balanceAfter = IToken(token).balanceOf(address(this));
require(
balanceBefore == balanceAfter + allocatedTokens[i],
"XStakingPool: swapped amount does not match with allocated amount"
);
require(
balanceBefore != IToken(token).balanceOf(address(this)),
"XStakingPool: deposit token does not match with src token in swap data"
);
require(
oneInchSwapData[i].length >= 4,
"XStakingPool: Incorrect data length"
);
(
,
address receiver,
,
uint256 srcTokenAmount,
uint256 minReturnAmount
) = decodeSwapData(oneInchSwapData[i]);
tempTotalAmountDepositTokenOut += amountDepositTokenOut;
require(
srcTokenAmount == allocatedTokens[i],
"XStakingPool: srcTokenAmount does not match with allocatedTokens"
);
require(
address(this) == receiver,
"XStakingPool: swap receiver have to be pool address"
);
require(
amountDepositTokenOut >= minReturnAmount,
"XStakingPool: amountDepositTokenOut less than minReturnAmount"
);
capitalization +=
(totalTokenAmount[token] * amountDepositTokenOut) /
allocatedTokens[i];
emit TokenSwap(
token,
allocatedTokens[i],
amountDepositTokenOut
);
}
unchecked {
i++;
}
}
if (totalSupply() < capitalizationCap * 10 ** 12) {
isDepositPaused = false;
emit DepositStatusUpdated(false);
}
uint256 result = tempTotalAmountDepositTokenOut;
tempTotalAmountDepositTokenOut = 0;
emit PoolCapitalization(address(this), capitalization);
return result;
}
/// @notice overrides for handle proper LP tokens transfers
function _update(
address from,
address to,
uint256 amountLP
) internal override {
super._update(from, to, amountLP);
if (from == address(0) || to == address(0)) {
// if mint or burn
return;
}
revert("LP non transferable");
}
/// @notice return the allocation tokens for withdraw using 1inch
/// @dev this helper view function uses for forming swap request to 1inch API
/// @param amountLP amount of Liquidity Pool token
/// @return allocatedTokens the array of tokens amount
function calcAllocatedTokensForWithdraw(
address user,
uint256 amountLP
) public view returns (uint256[] memory allocatedTokens) {
require(
amountLP <= balanceOf(user),
"XStakingPool: exceeds amount of LP"
);
uint256 len = tokens.length;
allocatedTokens = new uint256[](len);
address token;
for (uint256 i = 0; i < len; ) {
token = tokens[i];
allocatedTokens[i] =
(userTokenAmount[user][token] * amountLP) /
balanceOf(user);
unchecked {
i++;
}
}
}
function emitTokenAmouts() internal {
uint256 len = tokens.length;
uint256[] memory tokenAmounts = new uint256[](len);
for (uint256 i = 0; i < len; ) {
tokenAmounts[i] = totalTokenAmount[tokens[i]];
unchecked {
i++;
}
}
emit TokensAmounts(tokens, tokenAmounts);
}
/// @notice total amount of tokens
function tokensLength() public view returns (uint256) {
return tokens.length;
}
/// @notice the tokens array and it`s allocations
function getTokensAndAllocation()
public
view
returns (address[] memory, uint256[] memory)
{
return (tokens, allocations);
}
/// @notice returns capitalization decimals
function capitalizationDecimals() public pure returns (uint8) {
return 6;
}
/// @notice return LP based on depositTokenAmount
function calcMintAmount(
address depositToken,
uint256 depositTokenAmount
) public view returns (uint256) {
uint8 depositTokenDecimals = IToken(depositToken).decimals();
if (decimals() >= depositTokenDecimals) {
return
depositTokenAmount * 10 ** (decimals() - depositTokenDecimals);
} else {
return
depositTokenAmount / 10 ** (depositTokenDecimals - decimals());
}
}
/// @notice returns user amounts
function getUserTokenAmounts(
address user
) public view returns (uint256[] memory tokenAmounts) {
uint256 tokenLen = tokens.length;
tokenAmounts = new uint256[](tokenLen);
for (uint256 i = 0; i < tokenLen; i++) {
tokenAmounts[i] = userTokenAmount[user][tokens[i]];
}
}
function decodeSwapData(
bytes calldata data
)
public
view
returns (
address sender,
address receiver,
address srcToken,
uint256 srcTokenAmount,
uint256 minReturnAmount
)
{
bytes4 selector;
assembly {
selector := calldataload(data.offset)
}
/// @dev `0x0502b1c5` - unoswap selector
if (selector == bytes4(0x0502b1c5)) {
(srcToken, srcTokenAmount, minReturnAmount, ) = abi.decode(
data[4:],
(address, uint256, uint256, uint256[])
);
sender = address(this);
receiver = address(this);
}
/// @dev `0x12aa3caf` - swap selector
else if (selector == bytes4(0x12aa3caf)) {
(address executor, SwapDescription memory desc, , ) = abi.decode(
data[4:], // Skip selector (4 bytes)
(address, SwapDescription, bytes, bytes)
);
sender = executor;
receiver = desc.dstReceiver;
srcToken = address(desc.srcToken);
srcTokenAmount = desc.amount;
minReturnAmount = desc.minReturnAmount;
} else if (selector == bytes4(0xe449022e)) {
uint256[] memory pools;
(srcTokenAmount, minReturnAmount, pools) = abi.decode(
data[4:], // Skip selector (4 bytes)
(uint256, uint256, uint256[])
);
address token0 = IUniswapV3Pool(address(uint160(pools[0])))
.token0();
if (!xstakingFactory.isDepositToken(token0)) {
srcToken = IUniswapV3Pool(address(uint160(pools[0]))).token1();
} else {
srcToken = IUniswapV3Pool(address(uint160(pools[0]))).token0();
}
sender = address(this);
receiver = address(this);
} else if (selector == bytes4(0x62e238bb)) {
(
Order memory order,
,
,
uint256 makingAmount,
uint256 takingAmount,
) = abi.decode(
data[4:], // Skip selector (4 bytes)
(Order, bytes, bytes, uint256, uint256, uint256)
);
sender = address(this);
receiver = address(this);
srcToken = order.takerAsset;
srcTokenAmount = takingAmount;
minReturnAmount = makingAmount;
} else {
revert("XStakingFactory: unknown selector");
}
}
function _checkIfOwnerHaveLockedTokens() internal view {
if (msg.sender != owner()) {
require(
balanceOf(owner()) > 0,
"XStakingPool: Pool is locked such as pool's owner withdrawn all investments"
);
}
}
function _setDepositFeeNumerator(
uint256 _depositFeeNumerator,
bool _isPercentageFee
) internal {
if (_isPercentageFee) {
require(
_depositFeeNumerator < FEE_DENOMINATOR &&
_depositFeeNumerator >= MIN_PERCENT_DEPOSIT_FEE,
"XStakingPool: Deposit fee numerator is out of range"
);
} else {
require(
_depositFeeNumerator >= MIN_FIXED_DEPOSIT_FEE &&
_depositFeeNumerator <= MAX_FIXED_DEPOSIT_FEE,
"XStakingPool: Deposit fee numerator is out of range"
);
}
depositFeeNumerator = _depositFeeNumerator;
isPercentageFee = _isPercentageFee;
emit DepositFeeNumeratorSet(_depositFeeNumerator, _isPercentageFee);
}
}
"
},
"contracts/interfaces/IToken.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
interface IToken is IERC20Metadata {
}"
},
"contracts/interfaces/IXStakingPool.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IXStakingPool {
struct SwapDescription {
IERC20 srcToken;
IERC20 dstToken;
address payable srcReceiver;
address payable dstReceiver;
uint256 amount;
uint256 minReturnAmount;
uint256 flags;
}
struct Order {
uint256 salt;
address makerAsset;
address takerAsset;
address maker;
address receiver;
address allowedSender; // equals to Zero address on public orders
uint256 makingAmount;
uint256 takingAmount;
uint256 offsets;
bytes interactions; // concat(makerAssetData, takerAssetData, getMakingAmount, getTakingAmount, predicate, permit, preIntercation, postInteraction)
}
event TokenSwap(
address token,
uint256 tokenAmount,
uint256 baseTokenAmount
);
event TokensAmounts(address[] tokens, uint256[] tokenAmounts);
event Volume(uint256 amount);
event PoolCapitalization(address pool, uint256 capitalization);
event UserCapitalization(
address pool,
address user,
uint256 capitalization
);
event Deposit(
address pool,
address depositor,
uint256 amount,
uint256[] userTokenAmounts
);
event Withdraw(
address pool,
address depositor,
uint256 amount,
uint256[] userTokenAmounts
);
event DepositStatusUpdated(bool isPaused);
function initialize(
uint256 _poolId,
address _poolOwner,
uint256 _capitalizationCap,
address[] memory _tokens,
uint256[] memory _allocations,
uint256 _profitSharingFeeNumerator,
uint256[] memory _tokensAmounts,
address initialDepositToken,
uint256 initialBaseTokenAmount,
uint256 _depositFeeNumerator,
bool _isPercentageFee
) external;
function deposit(
address depositToken,
uint256 baseTokenAmount,
bytes[] calldata oneInchSwapData
) external returns (uint256);
function depositTo(
address depositToken,
address to,
uint256 baseTokenAmount,
bytes[] calldata oneInchSwapData
) external returns (uint256);
function withdraw(
address depositToken,
uint256 amountLP,
bytes[] calldata oneInchSwapData
) external returns (uint256);
event DepositFeeNumeratorSet(uint256 feeNumerator, bool isPercentageFee);
}
"
},
"contracts/interfaces/IXBRStakingPool.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IXBRStakingPool {
event Stake(address user, address lp, uint256 stakeAmount);
event Unstake(address user, address lp, uint256 unstakeAmount);
event ClaimReward(address user, uint256 claimedRewardAmount);
event Exit(address user, address lp, uint256 exitAmount);
function getLockedTokensByUserAndPool(address user, address pool) external view returns (uint256);
}"
},
"contracts/interfaces/IXStakingFactory.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IXStakingFactory {
/// @notice argument for oneInch swap function.
struct SwapDescription {
IERC20 srcToken;
IERC20 dstToken;
address payable srcReceiver;
address payable dstReceiver;
uint256 amount;
uint256 minReturnAmount;
uint256 flags;
}
struct Order {
uint256 salt;
address makerAsset;
address takerAsset;
address maker;
address receiver;
address allowedSender; // equals to Zero address on public orders
uint256 makingAmount;
uint256 takingAmount;
uint256 offsets;
bytes interactions; // concat(makerAssetData, takerAssetData, getMakingAmount, getTakingAmount, predicate, permit, preIntercation, postInteraction)
}
function FEE_DENOMINATOR() external view returns (uint256);
function treasuryWallet() external view returns (address);
function oneInchRouter() external view returns (address);
function getDepositToken(uint8 index) external view returns (address);
function getXStakingPools() external view returns (address[] memory);
function isDepositToken(address token) external view returns (bool);
function isXStakingPool(address pool) external view returns (bool);
function decodeSwapData(
bytes calldata data
)
external
returns (
address sender,
address receiver,
address srcToken,
uint256 srcTokenAmount,
uint256 minReturnAmount
);
function calculateFeeAmount(
uint256 amount,
bool isDeposit
) external view returns (uint256);
event DeployPool(
address deployer,
address pool,
uint256 poolId,
address[] tokens,
uint256[] allocations,
string description,
uint256 capitalizationCap,
uint256 profitSharingFeeNumerator,
uint256 depositFeeNumerator,
bool isPercentageFee
);
}
"
},
"node_modules/@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';
/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface ISwapRouter is IUniswapV3SwapCallback {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
/// @return amountOut The amount of the received token
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
/// @return amountOut The amount of the received token
function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another token
/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
/// @return amountIn The amount of the input token
function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);
struct ExactOutputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
/// @return amountIn The amount of the input token
function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}
"
},
"node_modules/@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import './pool/IUniswapV3PoolImmutables.sol';
import './pool/IUniswapV3PoolState.sol';
import './pool/IUniswapV3PoolDerivedState.sol';
import './pool/IUniswapV3PoolActions.sol';
import './pool/IUniswapV3PoolOwnerActions.sol';
import './pool/IUniswapV3PoolEvents.sol';
/// @title The interface for a Uniswap V3 Pool
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface IUniswapV3Pool is
IUniswapV3PoolImmutables,
IUniswapV3PoolState,
IUniswapV3PoolDerivedState,
IUniswapV3PoolActions,
IUniswapV3PoolOwnerActions,
IUniswapV3PoolEvents
{
}
"
},
"node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}
"
},
"node_modules/@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
/// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
struct ReentrancyGuardStorage {
uint256 _status;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
assembly {
$.slot := ReentrancyGuardStorageLocation
}
}
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
$._status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// On the first call to nonReentrant, _status will be NOT_ENTERED
if ($._status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
$._status = ENTERED;
}
function _nonReentrantAfter() private {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
$._status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
return $._status == ENTERED;
}
}
"
},
"node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @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 in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._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 2**64 - 1 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 InvalidInitialization();
}
$._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 InvalidInitialization();
}
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
}
}
}
"
},
"node_modules/@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*/
abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {
/// @custom:storage-location erc7201:openzeppelin.storage.ERC20
struct ERC20Storage {
mapping(address account => uint256) _balances;
mapping(address account => mapping(address spender => uint256)) _allowances;
uint256 _totalSupply;
string _name;
string _symbol;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;
function _getERC20Storage() private pure returns (ERC20Storage storage $) {
assembly {
$.slot := ERC20StorageLocation
}
}
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
__ERC20_init_unchained(name_, symbol_);
}
function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
ERC20Storage storage $ = _getERC20Storage();
$._name = name_;
$._symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
ERC20Storage storage $ = _getERC20Storage();
return $._name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
ERC20Storage storage $ = _getERC20Storage();
return $._symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
ERC20Storage storage $ = _getERC20Storage();
return $._totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
ERC20Storage storage $ = _getERC20Storage();
return $._balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address
Submitted on: 2025-10-28 10:02:03
Comments
Log in to comment.
No comments yet.