Description:
Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"laxwin contracts/BondingCurve.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./ILaxcePadInterfaces.sol";
import "./BondingCurveMath.sol";
contract BondingCurve is
Initializable,
ReentrancyGuardUpgradeable,
UUPSUpgradeable,
AccessControlUpgradeable,
PausableUpgradeable,
IBondingCurve
{
using SafeERC20 for IERC20;
using BondingCurveMath for BondingCurveMath.CurveConfig;
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
bytes32 public constant FACTORY_ROLE = keccak256("FACTORY_ROLE");
uint256 public constant BASIS_POINTS = 10000;
uint256 public constant PRECISION = 1e18;
uint256 public constant MIN_PURCHASE = 0.001 ether;
uint256 public constant MAX_SLIPPAGE = 1000;
mapping(address => CurveParams) public curveParams;
mapping(address => BondingCurveMath.CurveConfig) public curveConfigs;
mapping(address => TokenStats) public tokenStats;
mapping(address => mapping(address => UserTokenStats)) public userStats;
struct GlobalSettings {
uint256 tradingFeeRate;
uint256 protocolFeeRate;
address feeRecipient;
address graduationManager;
uint256 minLiquidity;
uint256 maxPriceImpact;
}
GlobalSettings public globalSettings;
struct TokenStats {
uint256 totalBuys;
uint256 totalSells;
uint256 totalVolume;
uint256 totalFees;
uint256 uniqueTraders;
uint256 lastTradeTime;
uint256 allTimeHigh;
uint256 allTimeLow;
}
struct UserTokenStats {
uint256 totalBought;
uint256 totalSold;
uint256 totalSpent;
uint256 totalReceived;
uint256 firstBuyTime;
uint256 lastTradeTime;
bool hasTraded;
}
struct SellCalculation {
uint256 currentSupply;
uint256 ethOut;
uint256 priceImpact;
uint256 tradingFee;
uint256 protocolFee;
uint256 totalFee;
uint256 netETH;
uint256 newPrice;
}
address[] public activeTokens;
mapping(address => bool) public isTokenActive;
mapping(address => uint256) public tokenIndex;
mapping(address => uint256) public userDailyVolume;
mapping(address => uint256) public userLastReset;
mapping(address => bool) public userBlacklist;
mapping(address => uint256) public tokenGraduationDeadline;
mapping(address => bool) public tokenFailed;
mapping(address => mapping(address => uint256)) public userContributions;
mapping(address => bool) public refundApproved;
event CurveInitialized(
address indexed token,
uint256 supply,
uint256 startPrice,
uint256 targetMarketCap,
BondingCurveMath.CurveType curveType
);
event TokenTraded(
address indexed token,
address indexed trader,
bool isBuy,
uint256 tokenAmount,
uint256 ethAmount,
uint256 newPrice,
uint256 priceImpact,
uint256 fee
);
event GlobalSettingsUpdated(GlobalSettings settings);
event TokenGraduationReady(address indexed token, uint256 marketCap);
event TokenFailed(address indexed token, uint256 deadline, uint256 raisedAmount);
event RefundApproved(address indexed token, uint256 timestamp);
event RefundClaimed(address indexed token, address indexed user, uint256 amount, uint256 adminFee);
error BondingCurve__TokenNotInitialized();
error BondingCurve__TokenAlreadyGraduated();
error BondingCurve__InsufficientETH();
error BondingCurve__InsufficientTokens();
error BondingCurve__SlippageExceeded();
error BondingCurve__PriceImpactTooHigh();
error BondingCurve__UserBlacklisted();
error BondingCurve__DailyLimitExceeded();
error BondingCurve__InvalidParameters();
error BondingCurve__NotAuthorized();
error BondingCurve__TokenFailed();
error BondingCurve__DeadlineNotReached();
error BondingCurve__NoContribution();
error BondingCurve__RefundNotApproved();
modifier onlyActiveToken(address token) {
if (!isTokenActive[token]) revert BondingCurve__TokenNotInitialized();
if (curveParams[token].isGraduated) revert BondingCurve__TokenAlreadyGraduated();
_;
}
modifier notBlacklisted() {
if (userBlacklist[msg.sender]) revert BondingCurve__UserBlacklisted();
_;
}
modifier checkDailyLimit(uint256 ethAmount) {
_checkDailyLimit(msg.sender, ethAmount);
_;
}
function initialize(
address _admin,
GlobalSettings memory _settings
) external initializer {
__ReentrancyGuard_init();
__UUPSUpgradeable_init();
__AccessControl_init();
__Pausable_init();
_grantRole(DEFAULT_ADMIN_ROLE, _admin);
_grantRole(ADMIN_ROLE, _admin);
_grantRole(OPERATOR_ROLE, _admin);
_grantRole(UPGRADER_ROLE, _admin);
globalSettings = _settings;
}
function initializeCurve(
address token,
uint256 supply,
uint256 startPrice,
uint256 targetMarketCap
) external onlyRole(FACTORY_ROLE) {
if (isTokenActive[token]) revert BondingCurve__InvalidParameters();
if (token == address(0) || supply == 0 || startPrice == 0 || targetMarketCap == 0) {
revert BondingCurve__InvalidParameters();
}
BondingCurveMath.CurveConfig memory config = BondingCurveMath.createPumpFunConfig(
startPrice,
targetMarketCap,
supply
);
curveParams[token] = CurveParams({
startPrice: startPrice,
targetMarketCap: targetMarketCap,
currentMarketCap: 0,
totalSupply: supply,
availableSupply: supply,
reserveETH: 0,
isGraduated: false
});
curveConfigs[token] = config;
tokenIndex[token] = activeTokens.length;
activeTokens.push(token);
isTokenActive[token] = true;
tokenStats[token].allTimeLow = startPrice;
tokenStats[token].lastTradeTime = block.timestamp;
tokenGraduationDeadline[token] = block.timestamp + 90 days;
emit CurveInitialized(token, supply, startPrice, targetMarketCap, config.curveType);
}
function buy(
address token,
uint256 minTokensOut
) external payable nonReentrant whenNotPaused onlyActiveToken(token) notBlacklisted checkDailyLimit(msg.value) returns (uint256 tokensOut) {
if (msg.value < MIN_PURCHASE) revert BondingCurve__InsufficientETH();
CurveParams storage params = curveParams[token];
BondingCurveMath.CurveConfig memory config = curveConfigs[token];
uint256 currentSupply = params.totalSupply - params.availableSupply;
tokensOut = BondingCurveMath.calculateTokensOut(config, currentSupply, msg.value);
if (tokensOut == 0 || tokensOut < minTokensOut) revert BondingCurve__SlippageExceeded();
if (tokensOut > params.availableSupply) revert BondingCurve__InsufficientTokens();
uint256 priceImpact = _calculatePriceImpact(token, tokensOut, true);
if (priceImpact > globalSettings.maxPriceImpact) revert BondingCurve__PriceImpactTooHigh();
uint256 tradingFee = (msg.value * globalSettings.tradingFeeRate) / BASIS_POINTS;
uint256 protocolFee = (msg.value * globalSettings.protocolFeeRate) / BASIS_POINTS;
uint256 totalFee = tradingFee + protocolFee;
uint256 netETH = msg.value - totalFee;
params.availableSupply -= tokensOut;
params.reserveETH += netETH;
params.currentMarketCap = BondingCurveMath.calculateMarketCap(config, currentSupply + tokensOut);
userContributions[token][msg.sender] += msg.value;
IERC20(token).safeTransfer(msg.sender, tokensOut);
if (totalFee > 0 && globalSettings.feeRecipient != address(0)) {
payable(globalSettings.feeRecipient).transfer(totalFee);
}
_updateStats(token, msg.sender, tokensOut, msg.value, true, totalFee);
if (_checkGraduation(token)) {
emit TokenGraduationReady(token, params.currentMarketCap);
}
uint256 newPrice = getCurrentPrice(token);
emit TokenTraded(token, msg.sender, true, tokensOut, msg.value, newPrice, priceImpact, totalFee);
}
function sell(
address token,
uint256 tokenAmount,
uint256 minETHOut
) external nonReentrant whenNotPaused onlyActiveToken(token) notBlacklisted returns (uint256 ethOut) {
if (tokenAmount == 0) revert BondingCurve__InsufficientTokens();
CurveParams storage params = curveParams[token];
BondingCurveMath.CurveConfig memory config = curveConfigs[token];
if (IERC20(token).balanceOf(msg.sender) < tokenAmount) revert BondingCurve__InsufficientTokens();
SellCalculation memory calc;
calc.currentSupply = params.totalSupply - params.availableSupply;
calc.ethOut = BondingCurveMath.calculateETHOut(config, calc.currentSupply, tokenAmount);
if (calc.ethOut == 0 || calc.ethOut < minETHOut) revert BondingCurve__SlippageExceeded();
if (calc.ethOut > params.reserveETH) revert BondingCurve__InsufficientETH();
calc.priceImpact = _calculatePriceImpact(token, tokenAmount, false);
if (calc.priceImpact > globalSettings.maxPriceImpact) revert BondingCurve__PriceImpactTooHigh();
calc.tradingFee = (calc.ethOut * globalSettings.tradingFeeRate) / BASIS_POINTS;
calc.protocolFee = (calc.ethOut * globalSettings.protocolFeeRate) / BASIS_POINTS;
calc.totalFee = calc.tradingFee + calc.protocolFee;
calc.netETH = calc.ethOut - calc.totalFee;
params.availableSupply += tokenAmount;
params.reserveETH -= calc.ethOut;
params.currentMarketCap = BondingCurveMath.calculateMarketCap(config, calc.currentSupply - tokenAmount);
IERC20(token).safeTransferFrom(msg.sender, address(this), tokenAmount);
payable(msg.sender).transfer(calc.netETH);
if (calc.totalFee > 0 && globalSettings.feeRecipient != address(0)) {
payable(globalSettings.feeRecipient).transfer(calc.totalFee);
}
_updateStats(token, msg.sender, tokenAmount, calc.ethOut, false, calc.totalFee);
calc.newPrice = getCurrentPrice(token);
emit TokenTraded(token, msg.sender, false, tokenAmount, calc.ethOut, calc.newPrice, calc.priceImpact, calc.totalFee);
ethOut = calc.netETH;
}
function getCurrentPrice(address token) public view returns (uint256) {
if (!isTokenActive[token]) return 0;
CurveParams memory params = curveParams[token];
BondingCurveMath.CurveConfig memory config = curveConfigs[token];
uint256 currentSupply = params.totalSupply - params.availableSupply;
return BondingCurveMath.calculatePrice(config, currentSupply);
}
function getMarketCap(address token) external view returns (uint256) {
return curveParams[token].currentMarketCap;
}
function canGraduate(address token) public view returns (bool) {
if (!isTokenActive[token]) return false;
return _checkGraduation(token);
}
function getBuyQuote(address token, uint256 ethAmount) external view returns (
uint256 tokensOut,
uint256 priceImpact,
uint256 newPrice,
uint256 totalFee
) {
if (!isTokenActive[token] || ethAmount == 0) return (0, 0, 0, 0);
CurveParams memory params = curveParams[token];
BondingCurveMath.CurveConfig memory config = curveConfigs[token];
uint256 currentSupply = params.totalSupply - params.availableSupply;
tokensOut = BondingCurveMath.calculateTokensOut(config, currentSupply, ethAmount);
if (tokensOut > 0) {
priceImpact = _calculatePriceImpact(token, tokensOut, true);
newPrice = BondingCurveMath.calculatePrice(config, currentSupply + tokensOut);
totalFee = (ethAmount * (globalSettings.tradingFeeRate + globalSettings.protocolFeeRate)) / BASIS_POINTS;
}
}
function getSellQuote(address token, uint256 tokenAmount) external view returns (
uint256 ethOut,
uint256 priceImpact,
uint256 newPrice,
uint256 totalFee
) {
if (!isTokenActive[token] || tokenAmount == 0) return (0, 0, 0, 0);
CurveParams memory params = curveParams[token];
BondingCurveMath.CurveConfig memory config = curveConfigs[token];
uint256 currentSupply = params.totalSupply - params.availableSupply;
if (tokenAmount > currentSupply) return (0, 0, 0, 0);
ethOut = BondingCurveMath.calculateETHOut(config, currentSupply, tokenAmount);
if (ethOut > 0) {
priceImpact = _calculatePriceImpact(token, tokenAmount, false);
newPrice = BondingCurveMath.calculatePrice(config, currentSupply - tokenAmount);
totalFee = (ethOut * (globalSettings.tradingFeeRate + globalSettings.protocolFeeRate)) / BASIS_POINTS;
}
}
function _calculatePriceImpact(address token, uint256 amount, bool isBuy) internal view returns (uint256) {
CurveParams memory params = curveParams[token];
BondingCurveMath.CurveConfig memory config = curveConfigs[token];
uint256 currentSupply = params.totalSupply - params.availableSupply;
uint256 currentPrice = BondingCurveMath.calculatePrice(config, currentSupply);
uint256 newSupply = isBuy ? currentSupply + amount : currentSupply - amount;
uint256 newPrice = BondingCurveMath.calculatePrice(config, newSupply);
if (currentPrice == 0) return 0;
uint256 priceDiff = newPrice > currentPrice ? newPrice - currentPrice : currentPrice - newPrice;
return (priceDiff * BASIS_POINTS) / currentPrice;
}
function _checkGraduation(address token) internal view returns (bool) {
CurveParams memory params = curveParams[token];
return params.currentMarketCap >= params.targetMarketCap;
}
function _updateStats(
address token,
address user,
uint256 tokenAmount,
uint256 ethAmount,
bool isBuy,
uint256 fee
) internal {
TokenStats storage stats = tokenStats[token];
UserTokenStats storage userStat = userStats[user][token];
if (isBuy) {
stats.totalBuys++;
} else {
stats.totalSells++;
}
stats.totalVolume += ethAmount;
stats.totalFees += fee;
stats.lastTradeTime = block.timestamp;
uint256 currentPrice = getCurrentPrice(token);
if (currentPrice > stats.allTimeHigh) {
stats.allTimeHigh = currentPrice;
}
if (stats.allTimeLow == 0 || currentPrice < stats.allTimeLow) {
stats.allTimeLow = currentPrice;
}
if (!userStat.hasTraded) {
userStat.hasTraded = true;
userStat.firstBuyTime = block.timestamp;
stats.uniqueTraders++;
}
if (isBuy) {
userStat.totalBought += tokenAmount;
userStat.totalSpent += ethAmount;
} else {
userStat.totalSold += tokenAmount;
userStat.totalReceived += ethAmount;
}
userStat.lastTradeTime = block.timestamp;
}
function _checkDailyLimit(address user, uint256 ethAmount) internal {
if (userLastReset[user] + 1 days < block.timestamp) {
userDailyVolume[user] = 0;
userLastReset[user] = block.timestamp;
}
userDailyVolume[user] += ethAmount;
uint256 dailyLimit = 10 ether;
if (userDailyVolume[user] > dailyLimit) {
revert BondingCurve__DailyLimitExceeded();
}
}
function updateGlobalSettings(GlobalSettings calldata settings) external onlyRole(ADMIN_ROLE) {
globalSettings = settings;
emit GlobalSettingsUpdated(settings);
}
function setUserBlacklist(address user, bool blacklisted) external onlyRole(ADMIN_ROLE) {
userBlacklist[user] = blacklisted;
}
function updateCurveParams(
address token,
uint256 newSupply,
uint256 newStartPrice,
uint256 newTargetMarketCap
) external onlyRole(ADMIN_ROLE) {
if (!isTokenActive[token]) revert BondingCurve__TokenNotInitialized();
if (newSupply == 0 || newStartPrice == 0 || newTargetMarketCap == 0) {
revert BondingCurve__InvalidParameters();
}
CurveParams storage params = curveParams[token];
uint256 soldSupply = params.totalSupply - params.availableSupply;
if (newSupply < soldSupply) revert BondingCurve__InvalidParameters();
params.totalSupply = newSupply;
params.availableSupply = newSupply - soldSupply;
params.startPrice = newStartPrice;
params.targetMarketCap = newTargetMarketCap;
BondingCurveMath.CurveConfig memory newConfig = BondingCurveMath.createPumpFunConfig(
newStartPrice,
newTargetMarketCap,
newSupply
);
curveConfigs[token] = newConfig;
params.currentMarketCap = BondingCurveMath.calculateMarketCap(newConfig, soldSupply);
emit CurveParamsUpdated(token, newSupply, newStartPrice, newTargetMarketCap);
}
function getCurveParams(address token) external view returns (CurveParams memory) {
return curveParams[token];
}
function graduateToken(address token) external returns (bool) {
if (msg.sender != globalSettings.graduationManager) revert BondingCurve__NotAuthorized();
if (!canGraduate(token)) return false;
curveParams[token].isGraduated = true;
uint256 index = tokenIndex[token];
uint256 lastIndex = activeTokens.length - 1;
if (index != lastIndex) {
address lastToken = activeTokens[lastIndex];
activeTokens[index] = lastToken;
tokenIndex[lastToken] = index;
}
activeTokens.pop();
delete tokenIndex[token];
isTokenActive[token] = false;
return true;
}
function extendDeadline(address token, uint256 additionalTime) external onlyRole(ADMIN_ROLE) {
if (!isTokenActive[token]) revert BondingCurve__TokenNotInitialized();
if (tokenFailed[token]) revert BondingCurve__TokenFailed();
tokenGraduationDeadline[token] += additionalTime;
}
function markTokenAsFailed(address token) external {
if (!isTokenActive[token]) revert BondingCurve__TokenNotInitialized();
if (tokenFailed[token]) revert BondingCurve__TokenFailed();
if (curveParams[token].isGraduated) revert BondingCurve__TokenAlreadyGraduated();
if (block.timestamp < tokenGraduationDeadline[token]) revert BondingCurve__DeadlineNotReached();
if (_checkGraduation(token)) revert BondingCurve__InvalidParameters();
tokenFailed[token] = true;
emit TokenFailed(token, tokenGraduationDeadline[token], curveParams[token].reserveETH);
}
function approveRefund(address token) external onlyRole(ADMIN_ROLE) {
if (!tokenFailed[token]) revert BondingCurve__InvalidParameters();
refundApproved[token] = true;
emit RefundApproved(token, block.timestamp);
}
function claimRefund(address token) external nonReentrant {
if (!tokenFailed[token]) revert BondingCurve__InvalidParameters();
if (!refundApproved[token]) revert BondingCurve__RefundNotApproved();
uint256 contribution = userContributions[token][msg.sender];
if (contribution == 0) revert BondingCurve__NoContribution();
userContributions[token][msg.sender] = 0;
uint256 adminFee = (contribution * 2000) / BASIS_POINTS;
uint256 refundAmount = contribution - adminFee;
uint256 userTokenBalance = IERC20(token).balanceOf(msg.sender);
if (userTokenBalance > 0) {
IERC20(token).safeTransferFrom(msg.sender, address(this), userTokenBalance);
}
payable(msg.sender).transfer(refundAmount);
if (adminFee > 0 && globalSettings.feeRecipient != address(0)) {
payable(globalSettings.feeRecipient).transfer(adminFee);
}
emit RefundClaimed(token, msg.sender, refundAmount, adminFee);
}
function getDeadline(address token) external view returns (uint256) {
return tokenGraduationDeadline[token];
}
function getTimeUntilDeadline(address token) external view returns (uint256) {
if (block.timestamp >= tokenGraduationDeadline[token]) return 0;
return tokenGraduationDeadline[token] - block.timestamp;
}
function getUserContribution(address token, address user) external view returns (uint256) {
return userContributions[token][user];
}
function emergencyWithdraw(address to) external onlyRole(ADMIN_ROLE) {
if (to == address(0)) revert BondingCurve__InvalidParameters();
uint256 balance = address(this).balance;
if (balance > 0) {
payable(to).transfer(balance);
}
}
function _authorizeUpgrade(address newImplementation) internal view override onlyRole(UPGRADER_ROLE) {
newImplementation;
}
}
"
},
"laxwin contracts/BondingCurveMath.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
library BondingCurveMath {
uint256 public constant BASIS_POINTS = 10000;
uint256 public constant PRECISION = 1e18;
uint256 public constant MAX_SUPPLY = 1e15;
uint256 public constant DEFAULT_MAX_SUPPLY = 1e28;
uint256 public constant MIN_START_PRICE = 1e9;
uint256 public constant MAX_TARGET_MARKET_CAP = 100000 ether;
uint256 public constant ABSOLUTE_MAX_SUPPLY = 1e32;
error BondingCurve__InvalidParameters();
error BondingCurve__InsufficientSupply();
error BondingCurve__PriceCalculationFailed();
error BondingCurve__Overflow();
enum CurveType {
LINEAR,
EXPONENTIAL,
LOGARITHMIC,
SQUARE_ROOT,
QUADRATIC
}
struct CurveConfig {
CurveType curveType;
uint256 a;
uint256 b;
uint256 c;
uint256 startPrice;
uint256 targetPrice;
uint256 maxSupply;
}
function calculatePrice(
CurveConfig memory config,
uint256 currentSupply
) internal pure returns (uint256 price) {
if (currentSupply > config.maxSupply) revert BondingCurve__InsufficientSupply();
if (config.curveType == CurveType.LINEAR) {
price = _calculateLinearPrice(config, currentSupply);
} else if (config.curveType == CurveType.EXPONENTIAL) {
price = _calculateExponentialPrice(config, currentSupply);
} else if (config.curveType == CurveType.LOGARITHMIC) {
price = _calculateLogarithmicPrice(config, currentSupply);
} else if (config.curveType == CurveType.SQUARE_ROOT) {
price = _calculateSquareRootPrice(config, currentSupply);
} else if (config.curveType == CurveType.QUADRATIC) {
price = _calculateQuadraticPrice(config, currentSupply);
} else {
revert BondingCurve__InvalidParameters();
}
if (price == 0) revert BondingCurve__PriceCalculationFailed();
}
function calculateTokensOut(
CurveConfig memory config,
uint256 currentSupply,
uint256 ethAmount
) internal pure returns (uint256 tokensOut) {
if (ethAmount == 0) return 0;
uint256 low = 0;
uint256 high = config.maxSupply - currentSupply;
uint256 tolerance = PRECISION / 1000;
while (high - low > tolerance && high > low) {
uint256 mid = (low + high) / 2;
uint256 cost = calculateCost(config, currentSupply, mid);
if (cost < ethAmount) {
low = mid;
} else if (cost > ethAmount) {
high = mid;
} else {
return mid;
}
}
tokensOut = low;
}
function calculateCost(
CurveConfig memory config,
uint256 currentSupply,
uint256 tokenAmount
) internal pure returns (uint256 cost) {
if (tokenAmount == 0) return 0;
if (currentSupply + tokenAmount > config.maxSupply) {
revert BondingCurve__InsufficientSupply();
}
cost = _calculateIntegral(config, currentSupply, currentSupply + tokenAmount);
}
function calculateETHOut(
CurveConfig memory config,
uint256 currentSupply,
uint256 tokenAmount
) internal pure returns (uint256 ethOut) {
if (tokenAmount == 0) return 0;
if (tokenAmount > currentSupply) revert BondingCurve__InsufficientSupply();
ethOut = _calculateIntegral(config, currentSupply - tokenAmount, currentSupply);
}
function calculateMarketCap(
CurveConfig memory config,
uint256 currentSupply
) internal pure returns (uint256 marketCap) {
uint256 currentPrice = calculatePrice(config, currentSupply);
if (currentSupply > type(uint256).max / currentPrice) {
revert BondingCurve__Overflow();
}
marketCap = (currentSupply * currentPrice) / PRECISION;
}
function _calculateLinearPrice(
CurveConfig memory config,
uint256 supply
) internal pure returns (uint256) {
return config.startPrice + (config.a * supply) / PRECISION;
}
function _calculateExponentialPrice(
CurveConfig memory config,
uint256 supply
) internal pure returns (uint256) {
uint256 rate = config.a;
uint256 multiplier = PRECISION + (rate * supply) / config.maxSupply;
return (config.startPrice * multiplier) / PRECISION;
}
function _calculateLogarithmicPrice(
CurveConfig memory config,
uint256 supply
) internal pure returns (uint256) {
if (supply == 0) return config.startPrice;
uint256 logValue = _approximateLog(supply + PRECISION);
return config.startPrice + (config.a * logValue) / PRECISION;
}
function _calculateSquareRootPrice(
CurveConfig memory config,
uint256 supply
) internal pure returns (uint256) {
uint256 sqrtValue = _sqrt(supply * PRECISION);
return config.startPrice + (config.a * sqrtValue) / PRECISION;
}
function _calculateQuadraticPrice(
CurveConfig memory config,
uint256 supply
) internal pure returns (uint256) {
uint256 term1 = (config.a * supply * supply) / (PRECISION * PRECISION);
uint256 term2 = (config.b * supply) / PRECISION;
return term1 + term2 + config.c;
}
function _calculateIntegral(
CurveConfig memory config,
uint256 from,
uint256 to
) internal pure returns (uint256 integral) {
if (from >= to) return 0;
if (config.curveType == CurveType.LINEAR) {
uint256 diff = to - from;
uint256 sum = to + from;
uint256 term1_numerator = _safeMul(config.a, sum);
uint256 term1 = term1_numerator / (2 * PRECISION);
uint256 term2 = config.startPrice;
uint256 avgPrice = term1 + term2;
integral = _safeMul(diff, avgPrice) / PRECISION;
} else {
integral = _numericalIntegration(config, from, to);
}
}
function _numericalIntegration(
CurveConfig memory config,
uint256 from,
uint256 to
) internal pure returns (uint256 integral) {
uint256 n = 100;
uint256 h = (to - from) / n;
uint256 sum = calculatePrice(config, from) + calculatePrice(config, to);
for (uint256 i = 1; i < n; i++) {
uint256 x = from + i * h;
uint256 price = calculatePrice(config, x);
sum += (i % 2 == 0) ? 2 * price : 4 * price;
}
integral = (sum * h) / (3 * PRECISION);
}
function _sqrt(uint256 x) internal pure returns (uint256) {
if (x == 0) return 0;
uint256 z = (x + 1) / 2;
uint256 y = x;
while (z < y) {
y = z;
z = (x / z + z) / 2;
}
return y;
}
function _approximateLog(uint256 x) internal pure returns (uint256) {
if (x <= PRECISION) return 0;
uint256 result = 0;
while (x >= 2 * PRECISION) {
x = x / 2;
result += 693147180559945309;
}
if (x > PRECISION) {
uint256 y = x - PRECISION;
result += (y * PRECISION) / x;
}
return result;
}
function createPumpFunConfig(
uint256 startPrice,
uint256 targetMarketCap,
uint256 maxSupply
) internal pure returns (CurveConfig memory) {
if (targetMarketCap == 0 || maxSupply == 0 || startPrice == 0) {
revert BondingCurve__InvalidParameters();
}
if (maxSupply > ABSOLUTE_MAX_SUPPLY) revert BondingCurve__Overflow();
if (startPrice < MIN_START_PRICE) revert BondingCurve__InvalidParameters();
uint256 targetPrice;
if (targetMarketCap > type(uint256).max / PRECISION) {
targetPrice = (targetMarketCap / maxSupply) * PRECISION;
} else {
targetPrice = (targetMarketCap * PRECISION) / maxSupply;
}
if (targetPrice <= startPrice) {
revert BondingCurve__InvalidParameters();
}
uint256 priceDiff = targetPrice - startPrice;
uint256 a;
if (priceDiff > type(uint256).max / PRECISION) {
a = (priceDiff / maxSupply) * PRECISION;
} else {
a = (priceDiff * PRECISION) / maxSupply;
}
return CurveConfig({
curveType: CurveType.LINEAR,
a: a,
b: 0,
c: 0,
startPrice: startPrice,
targetPrice: targetPrice,
maxSupply: maxSupply
});
}
function _safeMul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
if (c / a != b) revert BondingCurve__Overflow();
return c;
}
function _safeDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) revert BondingCurve__InvalidParameters();
return a / b;
}
}
"
},
"laxwin contracts/ILaxcePadInterfaces.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface ITokenFactory {
struct TokenParams {
string name;
string symbol;
string description;
string imageUrl;
string[] socialLinks;
uint256 totalSupply;
uint256 bondingCurveSupply;
uint256 liquiditySupply;
uint256 startPrice;
uint256 targetMarketCap;
}
struct SupplyLimits {
uint256 minTotalSupply;
uint256 maxTotalSupply;
uint256 minBondingCurvePercent;
uint256 maxBondingCurvePercent;
uint256 minStartPrice;
uint256 maxStartPrice;
uint256 minTargetMarketCap;
uint256 maxTargetMarketCap;
}
function createToken(TokenParams calldata params) external payable returns (address token);
function getTokenInfo(address token) external view returns (TokenParams memory);
function isTokenCreated(address token) external view returns (bool);
function getSupplyLimits() external view returns (SupplyLimits memory);
}
interface IBondingCurve {
struct CurveParams {
uint256 startPrice;
uint256 targetMarketCap;
uint256 currentMarketCap;
uint256 totalSupply;
uint256 availableSupply;
uint256 reserveETH;
bool isGraduated;
}
function initializeCurve(
address token,
uint256 supply,
uint256 startPrice,
uint256 targetMarketCap
) external;
function updateCurveParams(
address token,
uint256 newSupply,
uint256 newStartPrice,
uint256 newTargetMarketCap
) external;
function buy(address token, uint256 minTokensOut) external payable returns (uint256 tokensOut);
function sell(address token, uint256 tokenAmount, uint256 minETHOut) external returns (uint256 ethOut);
function getCurrentPrice(address token) external view returns (uint256);
function getMarketCap(address token) external view returns (uint256);
function canGraduate(address token) external view returns (bool);
function graduateToken(address token) external returns (bool);
function getCurveParams(address token) external view returns (CurveParams memory);
event TokenBought(address indexed token, address indexed buyer, uint256 ethAmount, uint256 tokenAmount, uint256 newPrice);
event TokenSold(address indexed token, address indexed seller, uint256 tokenAmount, uint256 ethAmount, uint256 newPrice);
event CurveInitialized(address indexed token, uint256 supply, uint256 startPrice, uint256 targetMarketCap);
event CurveParamsUpdated(address indexed token, uint256 supply, uint256 startPrice, uint256 targetMarketCap);
}
interface IGraduationManager {
function graduateToken(address token) external returns (address pool);
function isGraduated(address token) external view returns (bool);
function getGraduatedPool(address token) external view returns (address);
event TokenGraduated(address indexed token, address indexed pool, uint256 marketCap, uint256 liquidityAdded);
}
interface IRevenueDistribution {
struct RevenueShare {
address recipient;
uint256 percentage;
string description;
}
function distributeRevenue() external;
function setRevenueShare(address recipient, uint256 percentage, string calldata description) external;
function getRevenueShare(address recipient) external view returns (uint256);
function getTotalRevenue() external view returns (uint256);
event RevenueDistributed(uint256 totalAmount, uint256 timestamp);
event RevenueShareUpdated(address indexed recipient, uint256 percentage);
}
interface ILaxcePadToken {
function mint(address to, uint256 amount) external;
function burn(uint256 amount) external;
function setMetadata(string calldata description, string calldata imageUrl, string[] calldata socialLinks) external;
function getMetadata() external view returns (string memory description, string memory imageUrl, string[] memory socialLinks);
function creator() external view returns (address);
function createdAt() external view returns (uint256);
}
interface IDEXIntegration {
function getTokenRegistry() external view returns (address);
function getPoolFactory() external view returns (address);
function getDEXEngine() external view returns (address);
function getCoreRegistry() external view returns (address);
}
"
},
"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 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 {
/**
* @dev An operation with an ERC-20 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 Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(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.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
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.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
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.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
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 Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
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 {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
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 silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}
"
},
"@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
},
"@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../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 {
/// @custom:storage-location erc7201:openzeppelin.storage.Pausable
struct PausableStorage {
bool _paused;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;
function _getPausableStorage() private pure returns (PausableStorage storage $) {
assembly {
$.slot := PausableStorageLocation
}
}
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev 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();
_;
}
function __Pausable_init() internal onlyInitializing {
}
function __Pausable_init_unchained() internal onlyInitializing {
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
PausableStorage storage $ = _getPausableStorage();
return $._paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
emit Unpaused(_msgSender());
}
}
"
},
"@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/utils/ReentrancyGuardUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.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 EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* 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("openzeppe
Submitted on: 2025-10-24 19:13:23
Comments
Log in to comment.
No comments yet.