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": {
"XPayr.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
/*
XXXXXXX X0MBO0XPPPPPPPPPPPPPPPPP AAA Y0MBO0Y YYYYYYYRRRRRRRRRRRRRRRRR
X:::::X X:::::XP::::::::::::::::P A:::A Y:::::Y Y:::::YR::::::::::::::::R
X:::::X X:::::XP::::::PPPPPP:::::P A:::::A Y:::::Y Y:::::YR::::::RRRRRR:::::R
X::::::X X::::::XPP:::::P P:::::P A:::::::A Y::::::Y Y::::::YRR:::::R R:::::R
XXX:::::X X:::::XXX P::::P P:::::P A:::::::::A YYY:::::Y Y:::::YYY R::::R R:::::R
X:::::X X:::::X P::::P P:::::PA:::::A:::::A Y:::::Y Y:::::Y R::::R R:::::R
X:::::X:::::X P::::PPMB0P:::::PA:::::A A:::::A Y:::::Y:::::Y R::::RRRRRR:::::R
X:::::::::X P:::::::::::::PPA:::::A A:::::A Y:::::::::Y R:::::::::::::RR
X:::::::::X P::::PPPPPPPPP A:::::A A:::::A Y:::::::Y R::::RRRRRR:::::R
X:::::X:::::X P::::P A:::::AAAAAAAAA:::::A Y:::::Y R::::R R:::::R
X:::::X X:::::X P::::P A:::::::::::::::::::::A Y:::::Y R::::R R:::::R
XXX:::::X X:::::XXX P::::P A:::::AAAAAMB0AAAAA:::::A Y:::::Y R::::R R:::::R
X::::::X X::::::XPP::::::PP A:::::A A:::::A Y:::::Y RR:::::R R:::::R
X:::::X X:::::XP::::::::P A:::::A A:::::A YYYY:::::YYYY R::::::R R:::::R
X:::::X X:::::XP::::::::P A:::::A A:::::A Y:::::::::::Y R::::::R R:::::R
XXXXXXX XXXXXXXPPPPPPPPPPAAAAAAA AAAAAAAYYYYYMB0YYYYY RRRRRRRR RRRRRRR
x.com/devburaq | x.com/0mbo0
*/
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";
/**
* @title XPayr
* @author devburaq
* @notice An upgradeable, multi-chain, multi-vendor marketplace protocol.
* @dev Facilitates payments from buyers to multiple sellers in a single transaction.
*/
contract XPayr is Initializable, UUPSUpgradeable, AccessControlUpgradeable, PausableUpgradeable, ReentrancyGuardUpgradeable {
using SafeERC20Upgradeable for IERC20MetadataUpgradeable;
// --- ROLES ---
bytes32 public constant CONFIG_MANAGER_ROLE = keccak256("CONFIG_MANAGER_ROLE");
bytes32 public constant SECURITY_ROLE = keccak256("SECURITY_ROLE");
// --- STRUCTS ---
/// @notice Configuration for a specific blockchain network.
struct NetworkConfig {
uint256 nativeTransferGasLimit;
bool isActive;
}
/// @notice A single item in a multi-vendor shopping cart.
struct CartItem {
address payable seller;
uint256 productId;
bytes32 sku;
uint256 price;
uint256 quantity;
uint256 fixedDiscount;
uint16 discountRateBps;
uint256 fixedTax;
uint16 taxRateBps;
}
// --- STATE VARIABLES ---
address payable[] public xPayrWallets;
uint256 public walletIndex;
uint256 public contractFee;
mapping(uint256 => uint256[]) public feeStructures;
mapping(uint256 => NetworkConfig) public networkConfigs;
uint256[] public tokenFeeThresholds;
uint256[] public feeRates;
mapping(address => bool) public registeredUsers;
mapping(address => bool) public blacklisted;
mapping(address => bool) public supportedTokens;
mapping(address => string) public tokenTypes;
uint256 public totalCollectedNative;
uint256 public totalCollectedUsdc;
uint256 public totalCollectedUsdt;
mapping(address => mapping(address => uint256)) public userPayments;
address public xpayrTokenAddress;
mapping(address => address) public userToReferrer;
mapping(address => mapping(address => uint256)) public claimableRewards;
uint256 public referralRewardRate;
mapping(address => uint256) public minClaimAmounts;
// --- EVENTS ---
event NewPurchase(address indexed buyer, uint256 totalAmount, uint256 timestamp, address token);
event SellerPaid(address indexed seller, address indexed token, uint256 amount);
event UserRegistered(address indexed user);
event BlacklistUpdated(address indexed user, bool status);
event FeeStructureUpdated(uint256 indexed chainId, uint256[] thresholds);
event TokenFeeStructureUpdated(uint256[] thresholds);
event NetworkConfigured(uint256 indexed chainId, uint256 gasLimit, bool isActive);
event ContractFeeUpdated(uint256 newFee);
event ReferralRewardRateUpdated(uint256 newRate);
event XPayrWalletAdded(address indexed wallet);
event XPayrWalletRemoved(address indexed wallet, uint256 index);
event UserReferred(address indexed referred, address indexed referrer, uint256 timestamp);
event ReferralRewardCredited(address indexed referrer, address indexed token, uint256 amount);
event ReferralRewardClaimed(address indexed referrer, address indexed token, uint256 amount);
event TokenSupportUpdated(address indexed token, bool isSupported, string tokenType);
event XPayrTokenAddressUpdated(address indexed tokenAddress);
event MinClaimAmountUpdated(address indexed token, uint256 amount);
event EmergencyWithdrawal(address indexed recipient, address indexed token, uint256 amount);
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/**
* @notice Initializes the contract, setting up roles and default configurations.
*/
function initialize(
address _initialAdmin,
address payable _initialXPayrWallet,
uint256 _initialContractFee,
uint256 _initialReferralRate
) public initializer {
__UUPSUpgradeable_init();
__AccessControl_init();
__Pausable_init();
__ReentrancyGuard_init();
_grantRole(DEFAULT_ADMIN_ROLE, _initialAdmin);
_grantRole(CONFIG_MANAGER_ROLE, _initialAdmin);
_grantRole(SECURITY_ROLE, _initialAdmin);
xPayrWallets.push(_initialXPayrWallet);
contractFee = _initialContractFee;
referralRewardRate = _initialReferralRate;
feeRates = [1500, 1200, 1000, 800, 600, 400, 300, 100];
tokenFeeThresholds = [ 1000 * 10**6, 5000 * 10**6, 10000 * 10**6, 50000 * 10**6, 100000 * 10**6, 500000 * 10**6, 1000000 * 10**6 ];
_configureDefaultNetworks();
}
/**
* @dev Authorizes an upgrade. Can only be called by an admin.
*/
function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE) {}
// --- USER-FACING FUNCTIONS ---
/**
* @notice Registers the caller (`msg.sender`) as a new seller.
*/
function registerUser(address _referrer) external {
require(!registeredUsers[msg.sender], "User: Already registered");
registeredUsers[msg.sender] = true;
if (_referrer != address(0) && _referrer != msg.sender && registeredUsers[_referrer]) {
userToReferrer[msg.sender] = _referrer;
emit UserReferred(msg.sender, _referrer, block.timestamp);
}
emit UserRegistered(msg.sender);
}
/**
* @notice Processes a marketplace purchase using native currency.
*/
function purchase(CartItem[] memory cartItems) external payable nonReentrant whenNotPaused {
require(cartItems.length > 0, "Purchase: Cart empty");
require(cartItems.length <= 50, "Purchase: Cart too large");
require(networkConfigs[block.chainid].isActive, "Purchase: Chain not active");
uint256 grandTotal = _validateCartAndCalculateTotal(cartItems);
require(msg.value >= grandTotal, "Purchase: Insufficient payment");
_distributeMarketplacePayment(cartItems, grandTotal);
uint256 excess = msg.value - grandTotal;
if (excess > 0) {
(bool success, ) = msg.sender.call{value: excess}("");
require(success, "Purchase: Refund failed");
}
emit NewPurchase(msg.sender, grandTotal, block.timestamp, address(0));
}
/**
* @notice Processes a marketplace purchase using an ERC20 token.
*/
function purchaseWithToken(CartItem[] memory cartItems, address tokenAddress) external nonReentrant whenNotPaused {
require(cartItems.length > 0, "Purchase: Cart empty");
require(cartItems.length <= 50, "Purchase: Cart too large");
require(networkConfigs[block.chainid].isActive, "Purchase: Chain not active");
require(supportedTokens[tokenAddress], "Purchase: Token not supported");
IERC20MetadataUpgradeable token = IERC20MetadataUpgradeable(tokenAddress);
uint256 grandTotal = _validateCartAndCalculateTotal(cartItems);
require(token.allowance(msg.sender, address(this)) >= grandTotal, "Purchase: Insufficient allowance");
token.safeTransferFrom(msg.sender, address(this), grandTotal);
_distributeMarketplaceTokenPayment(cartItems, token, grandTotal);
emit NewPurchase(msg.sender, grandTotal, block.timestamp, tokenAddress);
}
/**
* @notice Allows a user to claim their referral rewards.
*/
function claimReferralRewards(address _token) external nonReentrant {
uint256 rewardAmount = claimableRewards[msg.sender][_token];
require(rewardAmount > 0, "Claim: No rewards");
require(rewardAmount >= minClaimAmounts[_token], "Claim: Amount below minimum");
claimableRewards[msg.sender][_token] = 0;
if (_token == address(0)) {
(bool sent, ) = msg.sender.call{value: rewardAmount}("");
require(sent, "Claim: Native transfer failed");
} else {
IERC20MetadataUpgradeable(_token).safeTransfer(msg.sender, rewardAmount);
}
emit ReferralRewardClaimed(msg.sender, _token, rewardAmount);
}
// --- MANAGEMENT FUNCTIONS (CONFIG_MANAGER_ROLE) ---
function setFeeStructureInEther(uint256 _chainId, uint256[] memory _thresholdsInEther) external onlyRole(CONFIG_MANAGER_ROLE) {
require(_thresholdsInEther.length == 7, "Input: Invalid thresholds length");
uint256[] memory thresholdsInWei = new uint256[](7);
for (uint256 i = 0; i < _thresholdsInEther.length; i++) {
thresholdsInWei[i] = _thresholdsInEther[i] * 1 ether;
}
feeStructures[_chainId] = thresholdsInWei;
emit FeeStructureUpdated(_chainId, thresholdsInWei);
}
function setTokenFeeStructure(uint256[] memory _thresholds) external onlyRole(CONFIG_MANAGER_ROLE) {
require(_thresholds.length == 7, "Input: Invalid thresholds length");
tokenFeeThresholds = _thresholds;
emit TokenFeeStructureUpdated(_thresholds);
}
function configureNetwork(uint256 _chainId, uint256 _gasLimit, bool _isActive) external onlyRole(CONFIG_MANAGER_ROLE) {
networkConfigs[_chainId] = NetworkConfig({
nativeTransferGasLimit: _gasLimit,
isActive: _isActive
});
emit NetworkConfigured(_chainId, _gasLimit, _isActive);
}
function setContractFee(uint256 _newFee) external onlyRole(CONFIG_MANAGER_ROLE) {
require(_newFee <= 100, "Fee: Cannot exceed 100%");
contractFee = _newFee;
emit ContractFeeUpdated(_newFee);
}
function setReferralRewardRate(uint256 _newRate) external onlyRole(CONFIG_MANAGER_ROLE) {
require(_newRate <= 100, "Rate: Cannot exceed 100%");
referralRewardRate = _newRate;
emit ReferralRewardRateUpdated(_newRate);
}
function addXPayrWallet(address payable _wallet) external onlyRole(CONFIG_MANAGER_ROLE) {
require(_wallet != address(0), "Input: Invalid address");
xPayrWallets.push(_wallet);
emit XPayrWalletAdded(_wallet);
}
function removeXPayrWallet(uint256 _index) external onlyRole(CONFIG_MANAGER_ROLE) {
require(_index < xPayrWallets.length, "Input: Invalid index");
address removedWallet = xPayrWallets[_index];
xPayrWallets[_index] = xPayrWallets[xPayrWallets.length - 1];
xPayrWallets.pop();
emit XPayrWalletRemoved(removedWallet, _index);
}
function setTokenSupport(address _token, bool _isSupported, string memory _tokenType) external onlyRole(CONFIG_MANAGER_ROLE) {
supportedTokens[_token] = _isSupported;
tokenTypes[_token] = _tokenType;
emit TokenSupportUpdated(_token, _isSupported, _tokenType);
}
function setXpayrTokenAddress(address _tokenAddress) external onlyRole(CONFIG_MANAGER_ROLE) {
xpayrTokenAddress = _tokenAddress;
emit XPayrTokenAddressUpdated(_tokenAddress);
}
function setMinClaimAmount(address _token, uint256 _amount) external onlyRole(CONFIG_MANAGER_ROLE) {
minClaimAmounts[_token] = _amount;
emit MinClaimAmountUpdated(_token, _amount);
}
// --- SECURITY FUNCTIONS (SECURITY_ROLE) ---
function pause() external onlyRole(SECURITY_ROLE) {
_pause();
}
function unpause() external onlyRole(SECURITY_ROLE) {
_unpause();
}
function setBlacklist(address user, bool status) external onlyRole(SECURITY_ROLE) {
blacklisted[user] = status;
emit BlacklistUpdated(user, status);
}
function batchSetBlacklist(address[] memory users, bool status) external onlyRole(SECURITY_ROLE) {
for (uint i = 0; i < users.length; i++) {
if (users[i] != address(0)) {
blacklisted[users[i]] = status;
emit BlacklistUpdated(users[i], status);
}
}
}
function emergencyWithdraw() external nonReentrant whenPaused onlyRole(SECURITY_ROLE) {
uint256 balance = address(this).balance;
require(balance > 0, "Emergency: No native balance");
(bool success, ) = msg.sender.call{value: balance}("");
require(success, "Emergency: Withdraw failed");
emit EmergencyWithdrawal(msg.sender, address(0), balance);
}
function emergencyWithdrawToken(address _token) external nonReentrant whenPaused onlyRole(SECURITY_ROLE) {
uint256 balance = IERC20MetadataUpgradeable(_token).balanceOf(address(this));
require(balance > 0, "Emergency: No token balance");
IERC20MetadataUpgradeable(_token).safeTransfer(msg.sender, balance);
emit EmergencyWithdrawal(msg.sender, _token, balance);
}
// --- INTERNAL & PRIVATE FUNCTIONS ---
function _configureDefaultNetworks() private {
// Ethereum
feeStructures[1] = [ 1 ether, 3 ether, 5 ether, 25 ether, 50 ether, 250 ether, 500 ether ];
feeStructures[11155111] = [ 1 ether, 3 ether, 5 ether, 25 ether, 50 ether, 250 ether, 500 ether ];
networkConfigs[1] = NetworkConfig(50000, true);
networkConfigs[11155111] = NetworkConfig(50000, true);
// BNB Chain
feeStructures[56] = [ 2 ether, 10 ether, 25 ether, 100 ether, 250 ether, 750 ether, 1500 ether ];
feeStructures[97] = [ 2 ether, 10 ether, 25 ether, 100 ether, 250 ether, 750 ether, 1500 ether ];
networkConfigs[56] = NetworkConfig(30000, true);
networkConfigs[97] = NetworkConfig(30000, true);
}
function _calculateLineTotal(CartItem memory item) internal pure returns (uint256) {
uint256 linePrice = item.price * item.quantity;
uint256 discountAmount = item.fixedDiscount + (linePrice * item.discountRateBps) / 10000;
if (discountAmount > linePrice) discountAmount = linePrice;
uint256 priceAfterDiscount = linePrice - discountAmount;
uint256 taxAmount = item.fixedTax + (priceAfterDiscount * item.taxRateBps) / 10000;
return priceAfterDiscount + taxAmount;
}
function _validateCartAndCalculateTotal(CartItem[] memory cartItems) internal view returns (uint256) {
uint256 grandTotal = 0;
for (uint256 i = 0; i < cartItems.length; i++) {
CartItem memory item = cartItems[i];
require(item.seller != address(0), "Validation: Invalid seller address");
require(!blacklisted[item.seller], "Validation: Seller blacklisted");
grandTotal += _calculateLineTotal(item);
}
return grandTotal;
}
function _creditReferralReward(address _seller, address _token, uint256 _commission) private {
address referrer = userToReferrer[_seller];
if (referrer != address(0) && _commission > 0) {
uint256 rewardAmount = (_commission * referralRewardRate) / 100;
if (rewardAmount > 0) {
claimableRewards[referrer][_token] += rewardAmount;
emit ReferralRewardCredited(referrer, _token, rewardAmount);
}
}
}
function _distributeMarketplacePayment(CartItem[] memory cartItems, uint256 grandTotal) internal {
userPayments[msg.sender][address(0)] += grandTotal;
totalCollectedNative += grandTotal;
uint256 gasLimit = networkConfigs[block.chainid].nativeTransferGasLimit;
if(gasLimit == 0) gasLimit = 50000; // Fallback
for (uint256 i = 0; i < cartItems.length; i++) {
CartItem memory item = cartItems[i];
uint256 lineTotal = _calculateLineTotal(item);
uint256 feeRate = getDynamicPeraFeeNative(lineTotal);
uint256 xPayrShare = (lineTotal * feeRate) / 100000;
_creditReferralReward(item.seller, address(0), xPayrShare);
uint256 contractShare = (lineTotal * contractFee) / 100;
uint256 netToSeller = lineTotal - xPayrShare - contractShare;
if (netToSeller > 0) {
(bool success, ) = item.seller.call{value: netToSeller, gas: gasLimit}("");
require(success, "Distribution: Seller payment failed");
emit SellerPaid(item.seller, address(0), netToSeller);
}
if (xPayrShare > 0 && xPayrWallets.length > 0) {
address payable targetWallet = xPayrWallets[walletIndex % xPayrWallets.length];
(bool success, ) = targetWallet.call{value: xPayrShare}("");
if(success) walletIndex++;
}
}
}
function _distributeMarketplaceTokenPayment(CartItem[] memory cartItems, IERC20MetadataUpgradeable token, uint256 grandTotal) internal {
userPayments[msg.sender][address(token)] += grandTotal;
string memory tType = tokenTypes[address(token)];
if (keccak256(abi.encodePacked(tType)) == keccak256(abi.encodePacked("USDC"))) {
totalCollectedUsdc += grandTotal;
} else if (keccak256(abi.encodePacked(tType)) == keccak256(abi.encodePacked("USDT"))) {
totalCollectedUsdt += grandTotal;
}
for (uint256 i = 0; i < cartItems.length; i++) {
CartItem memory item = cartItems[i];
uint256 lineTotal = _calculateLineTotal(item);
uint256 feeRate = (address(token) == xpayrTokenAddress) ? 0 : getDynamicPeraFeeToken(lineTotal);
uint256 xPayrShare = (lineTotal * feeRate) / 100000;
_creditReferralReward(item.seller, address(token), xPayrShare);
uint256 contractShare = (lineTotal * contractFee) / 100;
uint256 netToSeller = lineTotal - xPayrShare - contractShare;
if (netToSeller > 0) {
token.safeTransfer(item.seller, netToSeller);
emit SellerPaid(item.seller, address(token), netToSeller);
}
if (xPayrShare > 0 && xPayrWallets.length > 0) {
address payable targetWallet = xPayrWallets[walletIndex % xPayrWallets.length];
token.safeTransfer(targetWallet, xPayrShare);
walletIndex++;
}
}
}
// --- VIEW FUNCTIONS ---
function getDynamicPeraFeeNative(uint256 weiAmount) public view returns (uint256) {
uint256[] storage thresholds = feeStructures[block.chainid];
if (thresholds.length == 0) {
thresholds = feeStructures[1]; // Fallback to Ethereum
}
if (weiAmount >= thresholds[6]) return feeRates[7];
if (weiAmount >= thresholds[5]) return feeRates[6];
if (weiAmount >= thresholds[4]) return feeRates[5];
if (weiAmount >= thresholds[3]) return feeRates[4];
if (weiAmount >= thresholds[2]) return feeRates[3];
if (weiAmount >= thresholds[1]) return feeRates[2];
if (weiAmount >= thresholds[0]) return feeRates[1];
return feeRates[0];
}
function getDynamicPeraFeeToken(uint256 tokenAmount) public view returns (uint256) {
uint256[] storage thresholds = tokenFeeThresholds;
if (tokenAmount >= thresholds[6]) return feeRates[7];
if (tokenAmount >= thresholds[5]) return feeRates[6];
if (tokenAmount >= thresholds[4]) return feeRates[5];
if (tokenAmount >= thresholds[3]) return feeRates[4];
if (tokenAmount >= thresholds[2]) return feeRates[3];
if (tokenAmount >= thresholds[1]) return feeRates[2];
if (tokenAmount >= thresholds[0]) return feeRates[1];
return feeRates[0];
}
function getContractBalance() external view returns (uint256) {
return address(this).balance;
}
function getUserPayment(address user, address token) external view returns (uint256) {
return userPayments[user][token];
}
function getTotalCollectedNative() external view returns (uint256) {
return totalCollectedNative;
}
function getTotalCollectedUsdc() external view returns (uint256) {
return totalCollectedUsdc;
}
function getTotalCollectedUsdt() external view returns (uint256) {
return totalCollectedUsdt;
}
}"
},
"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
"
},
"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
import "../extensions/IERC20PermitUpgradeable.sol";
import "../../../utils/AddressUpgradeable.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 SafeERC20Upgradeable {
using AddressUpgradeable for address;
/**
* @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(IERC20Upgradeable token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, 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(IERC20Upgradeable token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 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(IERC20Upgradeable token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @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(IERC20Upgradeable token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20PermitUpgradeable token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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(IERC20Upgradeable 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, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @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(IERC20Upgradeable 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))) && AddressUpgradeable.isContract(address(token));
}
}
"
},
"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
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;
uint256 private _status;
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
_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 {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// 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) {
return _status == _ENTERED;
}
/**
* @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;
}
"
},
"@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.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 {
/**
* @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);
bool private _paused;
/**
* @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 {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
/**
* @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;
}
"
},
"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
struct AccessControlStorage {
mapping(bytes32 role => RoleData) _roles;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;
function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
assembly {
$.slot := AccessControlStorageLocation
}
}
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
AccessControlStorage storage $ = _getAccessControlStorage();
bytes32 previousAdminRole = getRoleAdmin(role);
$._roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (!hasRole(role, account)) {
$._roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (hasRole(role, account)) {
$._roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}
"
},
"@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.22;
import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable __self = address(this);
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
* and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev The call is from an unauthorized context.
*/
error UUPSUnauthorizedCallContext();
/**
* @dev The storage `slot` is unsupported as a UUID.
*/
error UUPSUnsupportedProxiableUUID(bytes32 slot);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
_checkProxy();
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
_checkNotDelegated();
_;
}
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/**
* @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data);
}
/**
* @dev Reverts if the execution is not performed via delegatecall or the execution
* context is not of a proxy with an ERC-1967 compliant implementation pointing to self.
*/
function _checkProxy() internal view virtual {
if (
address(this) == __self || // Must be called through delegatecall
ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
) {
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Reverts if the execution is performed via delegatecall.
* See {notDelegated}.
*/
function _checkNotDelegated() internal view virtual {
if (address(this) != __self) {
// Must not be called through delegatecall
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
*
* As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
* is expected to be the implementation slot in ERC-1967.
*
* Emits an {IERC1967-Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
revert UUPSUnsupportedProxiableUUID(slot);
}
ERC1967Utils.upgradeToAndCall(newImplementation, data);
} catch {
// The implementation is not UUPS
revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
}
}
}
"
},
"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.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 reinitialization) 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 Pointer to storage slot. Allows integrators to override it with a custom storage location.
*
* NOTE: Consider following the ERC-7201 formula to derive storage locations.
*/
function _initializableStorageSlot() internal pure virtual returns (bytes32) {
return INITIALIZABLE_STORAGE;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
bytes32 slot = _initializableStorageSlot();
assembly {
$.slot := slot
}
}
}
"
},
"@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
//
Submitted on: 2025-09-17 20:40:24
Comments
Log in to comment.
No comments yet.