Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
//dx.fun//base.fun powered by dextools and dxsale.
/**
* @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 ReentrancyGuard {
// 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;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_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
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// 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;
}
}
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(
address recipient,
uint256 amount
) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function allowance(
address owner,
address spender
) external returns (uint256);
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
function decimals() external returns (uint8);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
address public voter;
event OwnershipRenounced(address indexed previousOwner);
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor() {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
modifier onlyVoter() {
require(msg.sender == voter);
_;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipRenounced(owner);
owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function transferOwnership(address _newOwner) public onlyOwner {
_transferOwnership(_newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function _transferOwnership(address _newOwner) internal {
require(_newOwner != address(0));
emit OwnershipTransferred(owner, _newOwner);
owner = _newOwner;
}
}
interface UniswapRouter02 {
function factory() external pure returns (address);
function WETH() external pure returns (address);
// function WBNB() external pure returns(address);
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
)
external
payable
returns (uint amountToken, uint amountETH, uint liquidity);
function swapExactETHForTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable returns (uint[] memory amounts);
function getAmountsOut(
uint amountIn,
address[] calldata path
) external view returns (uint[] memory amounts);
function getAmountsIn(
uint amountOut,
address[] calldata path
) external view returns (uint[] memory amounts);
}
interface UniswapFactory {
event PairCreated(
address indexed token0,
address indexed token1,
address pair,
uint
);
function getPair(
address tokenA,
address tokenB
) external view returns (address pair);
function createPair(
address tokenA,
address tokenB
) external returns (address pair);
}
interface LPToken {
function sync() external;
}
interface ILpLockDeployerInterface {
function createLPLocker(
address _lockingToken,
uint256 _lockerEndTimeStamp,
string memory _logo,
uint256 _lockingAmount,
address _funOwner
) external payable returns (address);
}
interface IFunDeployerInterface {
function getAffiliatePer(
address _affiliateAddrs
) external view returns (uint256);
function getOwnerPer() external view returns (uint256);
function emitRoyal(
address funContract,
address tokenAddress,
address router,
address baseAddress,
uint256 liquidityAmount,
uint256 tokenAmount,
uint256 _time,
uint256 totalVolume
) external;
}
interface IFunEventTracker {
function buyEvent(
address _caller,
address _funContract,
uint256 _buyAmount,
uint256 _tokenRecieved
) external;
function sellEvent(
address _caller,
address _funContract,
uint256 _sellAmount,
uint256 _nativeRecieved
) external;
function createFunEvent(
address creator,
address funContract,
address tokenAddress,
string memory name,
string memory symbol,
string memory data,
uint256 totalSupply,
uint256 initialReserve,
uint256 timestamp
) external;
function listEvent(
address user,
address tokenAddress,
address router,
uint256 liquidityAmount,
uint256 tokenAmount,
uint256 _time,
uint256 totalVolume
) external;
function callerValidate(address _newFunContract) external;
}
interface IFunToken {
function initialize(
uint256 initialSupply,
string memory _name,
string memory _symbol,
address _midDeployer,
address _deployer
) external;
function initiateDex() external;
}
import "./lib/Clones.sol";
contract FunPool is Ownable, ReentrancyGuard {
address public constant DEAD = 0x000000000000000000000000000000000000dEaD;
uint256 public constant HUNDRED = 100;
uint256 public constant BASIS_POINTS = 10000;
struct FunTokenPoolData {
uint256 reserveTokens;
uint256 reserveETH;
uint256 volume;
uint256 listThreshold;
uint256 initialReserveEth;
uint8 nativePer;
bool tradeActive;
bool lpBurn;
bool royalemitted;
}
struct FunTokenPool {
address creator;
address token;
address baseToken;
address router;
address lockerAddress;
address storedLPAddress;
address deployer;
FunTokenPoolData pool;
}
// deployer allowed to create fun tokens
mapping(address => bool) public allowedDeployers;
// user => array of fun tokens
mapping(address => address[]) public userFunTokens;
// fun token => fun token details
mapping(address => FunTokenPool) public tokenPools;
address public implementation;
address public feeContract;
address public stableAddress;
address public lpLockDeployer;
address public eventTracker;
uint16 public feePer;
event LiquidityAdded(
address indexed provider,
uint tokenAmount,
uint ethAmount
);
event sold(
address indexed user,
uint256 amountIn,
uint256 amountOut,
uint256 _time,
uint256 reserveEth,
uint256 reserveTokens,
uint256 totalVolume
);
event bought(
address indexed user,
uint256 amountIn,
uint256 amountOut,
uint256 _time,
uint256 reserveEth,
uint256 reserveTokens,
uint256 totalVolume
);
event funTradeCall(
address indexed user,
uint256 amountIn,
uint256 amountOut,
uint256 _time,
uint256 reserveEth,
uint256 reserveTokens,
string tradeType,
uint256 totalVolume
);
event listed(
address indexed user,
address indexed tokenAddress,
address indexed router,
uint256 liquidityAmount,
uint256 tokenAmount,
uint256 _time,
uint256 totalVolume
);
constructor(
address _implementation,
address _feeContract,
address _lpLockDeployer,
address _stableAddress,
address _eventTracker,
uint16 _feePer
) payable {
implementation = _implementation;
feeContract = _feeContract;
lpLockDeployer = _lpLockDeployer;
stableAddress = _stableAddress;
eventTracker = _eventTracker;
feePer = _feePer;
}
function createFun(
string[2] memory _name_symbol,
uint256 _totalSupply,
address _creator,
address _baseToken,
address _router,
uint256[2] memory listThreshold_initReserveEth,
bool lpBurn
) public payable returns (address) {
require(allowedDeployers[msg.sender], "not deployer");
address funToken = Clones.clone(implementation);
IFunToken(funToken).initialize(
_totalSupply,
_name_symbol[0],
_name_symbol[1],
address(this),
msg.sender
);
// add tokens to the tokens user list
userFunTokens[_creator].push(funToken);
// create the pool data
FunTokenPool memory pool;
pool.creator = _creator;
pool.token = funToken;
pool.baseToken = _baseToken;
pool.router = _router;
pool.deployer = msg.sender;
pool.pool.nativePer = 100;
pool.pool.tradeActive = true;
pool.pool.lpBurn = lpBurn;
pool.pool.reserveTokens += _totalSupply;
pool.pool.reserveETH += (listThreshold_initReserveEth[1] + msg.value);
pool.pool.listThreshold = listThreshold_initReserveEth[0];
pool.pool.initialReserveEth = listThreshold_initReserveEth[1];
// add the fun data for the fun token
tokenPools[funToken] = pool;
// tokenPoolData[funToken] = funPoolData;
emit LiquidityAdded(address(this), _totalSupply, msg.value);
return address(funToken); // return fun token address
}
// Calculate amount of output tokens or ETH to give out
function getAmountOutTokens(
address funToken,
uint amountIn
) public view returns (uint amountOut) {
require(amountIn > 0, "Invalid input amount");
FunTokenPool storage token = tokenPools[funToken];
require(
token.pool.reserveTokens > 0 && token.pool.reserveETH > 0,
"Invalid reserves"
);
uint numerator = amountIn * token.pool.reserveTokens;
uint denominator = (token.pool.reserveETH) + amountIn;
amountOut = numerator / denominator;
}
function getAmountOutETH(
address funToken,
uint amountIn
) public view returns (uint amountOut) {
require(amountIn > 0, "Invalid input amount");
FunTokenPool storage token = tokenPools[funToken];
require(
token.pool.reserveTokens > 0 && token.pool.reserveETH > 0,
"Invalid reserves"
);
uint numerator = amountIn * token.pool.reserveETH;
uint denominator = (token.pool.reserveTokens) + amountIn;
amountOut = numerator / denominator;
}
function getBaseToken(address funToken) public view returns (address) {
FunTokenPool storage token = tokenPools[funToken];
return address(token.baseToken);
}
function getWrapAddr(address funToken) public view returns (address) {
return UniswapRouter02(tokenPools[funToken].router).WETH();
}
function getAmountsMinToken(
address funToken,
address _tokenAddress,
uint256 _ethIN
) public view returns (uint256) {
// generate the pair path of token -> weth
uint256[] memory amountMinArr;
address[] memory path = new address[](2);
path[0] = getWrapAddr(funToken);
path[1] = address(_tokenAddress);
amountMinArr = UniswapRouter02(tokenPools[funToken].router)
.getAmountsOut(_ethIN, path);
return uint256(amountMinArr[1]);
}
function getCurrentCap(address funToken) public view returns (uint256) {
FunTokenPool storage token = tokenPools[funToken];
return token.pool.reserveETH - token.pool.initialReserveEth;
}
function getFuntokenPool(address funToken) public view returns(FunTokenPool memory) {
return tokenPools[funToken];
}
function getFuntokenPools(address[] memory funTokens) public view returns(FunTokenPool[] memory) {
uint length = funTokens.length;
FunTokenPool[] memory pools = new FunTokenPool[](length);
for( uint i=0 ;i < length; ) {
pools[i] = tokenPools[funTokens[i]];
unchecked {
i++;
}
}
return pools;
}
function getUserFuntokens(address user) public view returns(address[] memory) {
return userFunTokens[user];
}
function sellTokens(
address funToken,
uint256 tokenAmount,
uint256 minEth,
address _affiliate
) public nonReentrant returns (bool, bool) {
FunTokenPool storage token = tokenPools[funToken];
require(token.pool.tradeActive, "Trading not active");
uint256 tokenToSell = tokenAmount;
uint256 ethAmount = getAmountOutETH(funToken, tokenToSell);
uint256 ethAmountFee = (ethAmount * feePer) / BASIS_POINTS;
uint256 ethAmountOwnerFee = (ethAmountFee *
(IFunDeployerInterface(token.deployer).getOwnerPer())) /
BASIS_POINTS;
uint256 affiliateFee = (ethAmountFee *
(
IFunDeployerInterface(token.deployer).getAffiliatePer(
_affiliate
)
)) / BASIS_POINTS;
require(ethAmount > 0 && ethAmount >= minEth, "Slippage too high");
token.pool.reserveTokens += tokenAmount;
token.pool.reserveETH -= ethAmount;
token.pool.volume += ethAmount;
IERC20(funToken).transferFrom(msg.sender, address(this), tokenToSell);
(bool success, ) = feeContract.call{
value: ethAmountFee - ethAmountOwnerFee - affiliateFee
}(""); // paying plat fee
require(success, "fee ETH transfer failed");
(success, ) = _affiliate.call{value: affiliateFee}(""); // paying affiliate fee which is same amount as plat fee %
require(success, "aff ETH transfer failed");
(success, ) = owner.call{value: ethAmountOwnerFee}(""); // paying owner fee per tx
require(success, "ownr ETH transfer failed");
(success, ) = msg.sender.call{value: ethAmount - ethAmountFee}("");
require(success, "seller ETH transfer failed");
emit sold(
msg.sender,
tokenAmount,
ethAmount,
block.timestamp,
token.pool.reserveETH,
token.pool.reserveTokens,
token.pool.volume
);
emit funTradeCall(
msg.sender,
tokenAmount,
ethAmount,
block.timestamp,
token.pool.reserveETH,
token.pool.reserveTokens,
"sell",
token.pool.volume
);
IFunEventTracker(eventTracker).sellEvent(
msg.sender,
funToken,
tokenToSell,
ethAmount
);
return (true, true);
}
function buyTokens(
address funToken,
uint256 minTokens,
address _affiliate
) public payable nonReentrant {
require(msg.value > 0, "Invalid buy value");
FunTokenPool storage token = tokenPools[funToken];
require(token.pool.tradeActive, "Trading not active");
{
uint256 ethAmount = msg.value;
uint256 ethAmountFee = (ethAmount * feePer) / BASIS_POINTS;
uint256 ethAmountOwnerFee = (ethAmountFee *
(IFunDeployerInterface(token.deployer).getOwnerPer())) /
BASIS_POINTS;
uint256 affiliateFee = (ethAmountFee *
(
IFunDeployerInterface(token.deployer).getAffiliatePer(
_affiliate
)
)) / BASIS_POINTS;
uint256 tokenAmount = getAmountOutTokens(
funToken,
ethAmount - ethAmountFee
);
require(tokenAmount >= minTokens, "Slippage too high");
token.pool.reserveETH += (ethAmount - ethAmountFee);
token.pool.reserveTokens -= tokenAmount;
token.pool.volume += ethAmount;
(bool success, ) = feeContract.call{
value: ethAmountFee - ethAmountOwnerFee - affiliateFee
}(""); // paying plat fee
require(success, "fee ETH transfer failed");
(success, ) = _affiliate.call{value: affiliateFee}(""); // paying affiliate fee which is same amount as plat fee %
require(success, "fee ETH transfer failed");
(success, ) = owner.call{value: ethAmountOwnerFee}(""); // paying owner fee per tx
require(success, "fee ETH transfer failed");
IERC20(funToken).transfer(msg.sender, tokenAmount);
emit bought(
msg.sender,
msg.value,
tokenAmount,
block.timestamp,
token.pool.reserveETH,
token.pool.reserveTokens,
token.pool.volume
);
emit funTradeCall(
msg.sender,
msg.value,
tokenAmount,
block.timestamp,
token.pool.reserveETH,
token.pool.reserveTokens,
"buy",
token.pool.volume
);
IFunEventTracker(eventTracker).buyEvent(
msg.sender,
funToken,
msg.value,
tokenAmount
);
}
uint currentMarketCap = getCurrentCap(funToken);
uint listThresholdCap = token.pool.listThreshold;
// using liquidity value inside contract to check when to add liquidity to DEX
if (
currentMarketCap >= (listThresholdCap / 2) &&
!token.pool.royalemitted
) {
IFunDeployerInterface(token.deployer).emitRoyal(
funToken,
funToken,
token.router,
token.baseToken,
token.pool.reserveETH,
token.pool.reserveTokens,
block.timestamp,
token.pool.volume
);
token.pool.royalemitted = true;
}
// using marketcap value of token to check when to add liquidity to DEX
if (currentMarketCap >= listThresholdCap) {
token.pool.tradeActive = false;
IFunToken(funToken).initiateDex();
token.pool.reserveETH -= token.pool.initialReserveEth;
if (token.pool.nativePer > 0) {
_addLiquidityETH(
funToken,
(IERC20(funToken).balanceOf(address(this)) *
token.pool.nativePer) / HUNDRED,
(token.pool.reserveETH * token.pool.nativePer) / HUNDRED,
token.pool.lpBurn
);
token.pool.reserveETH = 0;
}
}
}
function _addLiquidityETH(
address funToken,
uint256 amountTokenDesired,
uint256 nativeForDex,
bool lpBurn
) internal {
uint256 amountETH = nativeForDex;
uint256 amountETHMin = (amountETH * 90) / HUNDRED;
uint256 amountTokenToAddLiq = amountTokenDesired;
uint256 amountTokenMin = (amountTokenToAddLiq * 90) / HUNDRED;
uint256 LP_WBNB_exp_balance;
uint256 LP_token_balance;
uint256 tokenToSend = 0;
FunTokenPool storage token = tokenPools[funToken];
address wrapperAddress = getWrapAddr(funToken);
token.storedLPAddress = _getpair(funToken, funToken, wrapperAddress);
address storedLPAddress = token.storedLPAddress;
LP_WBNB_exp_balance = IERC20(wrapperAddress).balanceOf(storedLPAddress);
LP_token_balance = IERC20(funToken).balanceOf(storedLPAddress);
if (
storedLPAddress != address(0x0) &&
(LP_WBNB_exp_balance > 0 && LP_token_balance <= 0)
) {
tokenToSend =
(amountTokenToAddLiq * LP_WBNB_exp_balance) /
amountETH;
IERC20(funToken).transfer(storedLPAddress, tokenToSend);
LPToken(storedLPAddress).sync();
// sync after adding token
}
_approve(funToken, false);
if (lpBurn) {
UniswapRouter02(token.router).addLiquidityETH{
value: amountETH - LP_WBNB_exp_balance
}(
funToken,
amountTokenToAddLiq - tokenToSend,
amountTokenMin,
amountETHMin,
DEAD,
block.timestamp + (300)
);
} else {
UniswapRouter02(token.router).addLiquidityETH{
value: amountETH - LP_WBNB_exp_balance
}(
funToken,
amountTokenToAddLiq - tokenToSend,
amountTokenMin,
amountETHMin,
address(this),
block.timestamp + (300)
);
_approveLock(storedLPAddress, lpLockDeployer);
token.lockerAddress = ILpLockDeployerInterface(lpLockDeployer)
.createLPLocker(
storedLPAddress,
32503698000,
"logo",
IERC20(storedLPAddress).balanceOf(address(this)),
token.creator
);
}
IFunEventTracker(eventTracker).listEvent(
msg.sender,
funToken,
token.router,
amountETH - LP_WBNB_exp_balance,
amountTokenToAddLiq - tokenToSend,
block.timestamp,
token.pool.volume
);
emit listed(
msg.sender,
funToken,
token.router,
amountETH - LP_WBNB_exp_balance,
amountTokenToAddLiq - tokenToSend,
block.timestamp,
token.pool.volume
);
}
function _approve(
address funToken,
bool isBaseToken
) internal returns (bool) {
FunTokenPool storage token = tokenPools[funToken];
IERC20 token_ = IERC20(funToken);
if (isBaseToken) {
token_ = IERC20(token.baseToken);
}
if (token_.allowance(address(this), token.router) == 0) {
token_.approve(token.router, type(uint256).max);
}
return true;
}
function _approveLock(
address _lp,
address _lockDeployer
) internal returns (bool) {
IERC20 lp_ = IERC20(_lp);
if (lp_.allowance(address(this), _lockDeployer) == 0) {
lp_.approve(_lockDeployer, type(uint256).max);
}
return true;
}
function _getpair(
address funToken,
address _token1,
address _token2
) internal returns (address) {
address router = tokenPools[funToken].router;
address factory = UniswapRouter02(router).factory();
address pair = UniswapFactory(factory).getPair(_token1, _token2);
if (pair != address(0)) {
return pair;
} else {
return UniswapFactory(factory).createPair(_token1, _token2);
}
}
function _isUserFunToken(address funToken) internal view returns (bool) {
for (uint i = 0; i < userFunTokens[msg.sender].length; ) {
if (funToken == userFunTokens[msg.sender][i]) {
return true;
}
unchecked {
i++;
}
}
return false;
}
function addDeployer(address _deployer) public onlyOwner {
allowedDeployers[_deployer] = true;
}
function removeDeployer(address _deployer) public onlyOwner {
allowedDeployers[_deployer] = false;
}
function updateImplementation(address _implementation) public onlyOwner {
require(_implementation != address(0));
implementation = _implementation;
}
function updateFeeContract(address _newFeeContract) public onlyOwner {
feeContract = _newFeeContract;
}
function updateLpLockDeployer(address _newLpLockDeployer) public onlyOwner {
lpLockDeployer = _newLpLockDeployer;
}
function updateEventTracker(address _newEventTracker) public onlyOwner {
eventTracker = _newEventTracker;
}
function updateteamFeeper(uint16 _newFeePer) public onlyOwner {
feePer = _newFeePer;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/Clones.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for
* deploying minimal proxy contracts, also known as "clones".
*
* > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
* > a minimal bytecode implementation that delegates all calls to a known, fixed address.
*
* The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
* (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
* deterministic method.
*/
library Clones {
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create opcode, which should never revert.
*/
function clone(address implementation) internal returns (address instance) {
return clone(implementation, 0);
}
/**
* @dev Same as {xref-Clones-clone-address-}[clone], but with a `value` parameter to send native currency
* to the new contract.
*
* NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
* to always have enough balance for new deployments. Consider exposing this function under a payable method.
*/
function clone(
address implementation,
uint256 value
) internal returns (address instance) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
/// @solidity memory-safe-assembly
assembly {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(
0x00,
or(
shr(0xe8, shl(0x60, implementation)),
0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000
)
)
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(
0x20,
or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)
)
instance := create(value, 0x09, 0x37)
}
if (instance == address(0)) {
revert Errors.FailedDeployment();
}
}
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy
* the clone. Using the same `implementation` and `salt` multiple time will revert, since
* the clones cannot be deployed twice at the same address.
*/
function cloneDeterministic(
address implementation,
bytes32 salt
) internal returns (address instance) {
return cloneDeterministic(implementation, salt, 0);
}
/**
* @dev Same as {xref-Clones-cloneDeterministic-address-bytes32-}[cloneDeterministic], but with
* a `value` parameter to send native currency to the new contract.
*
* NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
* to always have enough balance for new deployments. Consider exposing this function under a payable method.
*/
function cloneDeterministic(
address implementation,
bytes32 salt,
uint256 value
) internal returns (address instance) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
/// @solidity memory-safe-assembly
assembly {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(
0x00,
or(
shr(0xe8, shl(0x60, implementation)),
0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000
)
)
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(
0x20,
or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)
)
instance := create2(value, 0x09, 0x37, salt)
}
if (instance == address(0)) {
revert Errors.FailedDeployment();
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(add(ptr, 0x38), deployer)
mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
mstore(add(ptr, 0x14), implementation)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
mstore(add(ptr, 0x58), salt)
mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
predicted := and(
keccak256(add(ptr, 0x43), 0x55),
0xffffffffffffffffffffffffffffffffffffffff
)
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt
) internal view returns (address predicted) {
return predictDeterministicAddress(implementation, salt, address(this));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*/
library Errors {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedCall();
/**
* @dev The deployment failed.
*/
error FailedDeployment();
/**
* @dev A necessary precompile is missing.
*/
error MissingPrecompile(address);
}