Contract Source Code:
//SPDX-License-Identifier: Prima Nocta
pragma solidity ^0.8.20;
import "./interfaces/IThresholdERC1155.sol";
import "./interfaces/INftVault.sol";
import "./interfaces/IAggregatorV3.sol";
import "./interfaces/IUniswapV3PoolState.sol";
import "./interfaces/IMagicSwapV2Router.sol";
import "./interfaces/INftVaultFactory.sol";
import "./interfaces/IMagicSwapUniswapV2Factory.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
contract PRESALE {
struct AltPairInfo {
address lpaddress;
address vaultaddress;
bool approved;
uint256 tokenid;
string symbol;
}
struct PresaleInfo {
bool readyToGraduate;
bool graduated;
uint256 targetBaseTokenToRaise;
uint256 presalePrice;
uint256 returnForOne;
uint256 baseTokenRaised;
uint256 totalsupply;
address paircoin;
uint256 amounttolp;
uint256 amounttosell;
IThresholdERC1155 memecoin;
}
IERC20 public baseToken;
address public owner;
address public stakingRewards;
uint256 public constant MAX_TOTAL_SUPPLY = 1_000_000_000_000; // 1 trillion
uint256 public constant MIN_TOTAL_SUPPLY = 1_000_000_000; // 1 billion
uint256 public TARGETMCAP = 42000;
uint256[] IDALWAYSZERO;
mapping(address => PresaleInfo) public presaleInfo;
mapping(address => AltPairInfo) public approvedpaircoins;
INftVaultFactory factory;
IMagicSwapV2Router router;
IMagicSwapUniswapV2Factory msUniFactory;
IThresholdERC1155 memecoinImplementation;
address public immutable BASE_USD_AGGREGATOR;
event MemeMade(string name, string symbol, string uri, uint256 amount, PresaleInfo presaleinfo);
event Buy(address indexed memeCoinAddress, address indexed buyer, uint256 amountNFT, uint256 amountBaseToken);
event Sell(address indexed memeCoinAddress, address indexed seller, uint256 amountNFT, uint256 amountBaseToken);
event GraduationReady(address indexed memeCoinAddress, PresaleInfo presaleinfo);
event Graduation(address indexed lpaddress, PresaleInfo presaleinfo);
event PairCoinApproved(address indexed _collectionAddress, AltPairInfo alt);
event PairCoinRemoved(address indexed _collectionAddress);
constructor(IERC20 _baseToken,
address _stakingRewards,
INftVaultFactory _factory,
IMagicSwapV2Router _router,
IMagicSwapUniswapV2Factory _msufactory,
IThresholdERC1155 _memecoinImplementation,
address _baseUsdAggregator) {
owner = msg.sender;
baseToken = _baseToken;
IDALWAYSZERO.push(0);
stakingRewards = _stakingRewards;
factory = _factory;
router = _router;
msUniFactory = _msufactory;
memecoinImplementation = _memecoinImplementation;
BASE_USD_AGGREGATOR = _baseUsdAggregator;
}
modifier onlyOwner() {
require(msg.sender == owner, 'not owner');
_;
}
function startPresale(string memory _name, string memory _symbol, uint256 _totalsupply, string memory _uri, uint256[] memory _thresholds) external returns (address) {
require(_totalsupply >= MIN_TOTAL_SUPPLY, "Total supply too low - min 10 million");
require(_totalsupply <= MAX_TOTAL_SUPPLY, "Total supply too high - max 1 trillion");
IThresholdERC1155 memecoin = IThresholdERC1155(Clones.clone(address(memecoinImplementation)));
memecoin.initialize(msg.sender, stakingRewards, _totalsupply, _name, _symbol, _uri, _thresholds);
(uint amountmemecointolp, uint amountmemecointosell, uint baseTokenNeededToFill) = _calculateAmounts(_totalsupply, address(0));
uint adjustment = baseTokenNeededToFill % amountmemecointosell;
PresaleInfo memory info;
info.amounttolp = amountmemecointolp;
info.amounttosell = amountmemecointosell;
info.totalsupply = _totalsupply;
info.memecoin = memecoin;
info.targetBaseTokenToRaise = baseTokenNeededToFill - adjustment;
info.presalePrice = info.targetBaseTokenToRaise / amountmemecointosell;
info.returnForOne = 0;
info.paircoin = address(baseToken);
presaleInfo[address(memecoin)] = info;
emit MemeMade(_name, _symbol, _uri, _totalsupply, info);
return address(memecoin);
}
function startPresale1155(string memory _name, string memory _symbol, string memory _uri, address _paircoin1155, uint256[] memory _thresholds) external returns (address) {
uint256 _totalsupply = 1000000000;
AltPairInfo memory alt = approvedpaircoins[_paircoin1155];
require(alt.approved, "not approved");
IThresholdERC1155 memecoin = IThresholdERC1155(Clones.clone(address(memecoinImplementation)));
memecoin.initialize(msg.sender, stakingRewards, _totalsupply, _name, _symbol, _uri, _thresholds);
(uint amountmemecointolp, uint amountmemecointosell, uint paircoinneededtofill) = _calculateAmounts(_totalsupply, _paircoin1155);
paircoinneededtofill = paircoinneededtofill / 1e18;
PresaleInfo memory info;
info.amounttolp = amountmemecointolp;
info.amounttosell = amountmemecointosell;
info.graduated = false;
info.totalsupply = _totalsupply;
info.memecoin = memecoin;
info.paircoin = _paircoin1155;
if (paircoinneededtofill > _totalsupply) {
// this tweaks the targetBaseTokenToRaise so it is evenly divisable by the presalePrice
info.presalePrice = paircoinneededtofill / amountmemecointosell;
info.returnForOne = 0;
uint adjustment = paircoinneededtofill % amountmemecointosell;
info.targetBaseTokenToRaise = paircoinneededtofill - adjustment;
} else {
info.returnForOne = amountmemecointosell / paircoinneededtofill;
info.presalePrice = 0;
uint adjustment = amountmemecointosell % paircoinneededtofill;
info.targetBaseTokenToRaise = paircoinneededtofill - adjustment;
}
presaleInfo[address(memecoin)] = info;
emit MemeMade(_name, _symbol, _uri, _totalsupply, info);
return address(memecoin);
}
function buyPresale(address _memeCoinAddress, uint256 _amountNftToBuy) external {
PresaleInfo memory info = presaleInfo[_memeCoinAddress];
require(!info.readyToGraduate, "already ready to graduate");
(uint totalamountbaseToken, uint tax) = getQuote(_memeCoinAddress, _amountNftToBuy);
// Check if purchase would exceed target and adjust if needed
if (info.baseTokenRaised + totalamountbaseToken > info.targetBaseTokenToRaise) {
totalamountbaseToken = info.targetBaseTokenToRaise - info.baseTokenRaised;
_amountNftToBuy = totalamountbaseToken / info.presalePrice;
}
if (info.paircoin == address(baseToken)) {
bool chaching = IERC20(info.paircoin).transferFrom(msg.sender, address(this), totalamountbaseToken + tax);
bool taxed = IERC20(info.paircoin).transfer(stakingRewards, tax);
require(chaching && taxed, "buy failed");
presaleInfo[_memeCoinAddress].baseTokenRaised = info.baseTokenRaised + totalamountbaseToken;
info.memecoin.safeTransferFrom(address(this), msg.sender, 0, _amountNftToBuy, "");
} else {
IERC1155(info.paircoin).safeTransferFrom(msg.sender, address(this), 0, totalamountbaseToken, "");
presaleInfo[_memeCoinAddress].baseTokenRaised = info.baseTokenRaised + totalamountbaseToken;
info.memecoin.safeTransferFrom(address(this), msg.sender, 0, _amountNftToBuy, "");
}
// Mark as ready to graduate if target is met
if (info.baseTokenRaised + totalamountbaseToken >= info.targetBaseTokenToRaise) {
_graduate(_memeCoinAddress);
}
emit Buy(_memeCoinAddress, msg.sender, _amountNftToBuy, totalamountbaseToken);
}
function sellPresale(address _memeCoinAddress, uint256 _sellAmountNFT) external {
PresaleInfo memory info = presaleInfo[_memeCoinAddress];
require(!info.readyToGraduate, "already graduated");
(uint baseTokenToReturn, uint tax) = getQuote(_memeCoinAddress, _sellAmountNFT);
require(baseTokenToReturn <= info.baseTokenRaised, "plunge protection");
if (info.paircoin == address(baseToken)) {
bool sendtouser = IERC20(info.paircoin).transfer(msg.sender, baseTokenToReturn - tax);
bool taxed = IERC20(info.paircoin).transfer(stakingRewards, tax);
require(sendtouser && taxed, "sell failed");
presaleInfo[_memeCoinAddress].baseTokenRaised = info.baseTokenRaised - baseTokenToReturn;
info.memecoin.safeTransferFrom(msg.sender, address(this), 0, _sellAmountNFT, "");
} else {
info.memecoin.safeTransferFrom(msg.sender, address(this), 0, _sellAmountNFT, "");
presaleInfo[_memeCoinAddress].baseTokenRaised = info.baseTokenRaised - baseTokenToReturn;
IERC1155(info.paircoin).safeTransferFrom(address(this), msg.sender, 0, baseTokenToReturn, "");
}
emit Sell(_memeCoinAddress, msg.sender, _sellAmountNFT, baseTokenToReturn);
}
function _graduateBaseTokenPool(PresaleInfo memory info, INftVault vaultMemeCoin, IMagicSwapV2Router.NftVaultLiquidityData memory vaultdataMemeCoin) internal returns (address lpaddy) {
IERC20(info.paircoin).approve(address(router), info.baseTokenRaised);
(uint256 amountA, uint256 amountB, uint256 lpAmount) = router.addLiquidityNFT(
vaultdataMemeCoin,
address(info.paircoin),
info.baseTokenRaised,
info.baseTokenRaised,
address(0),
block.timestamp
);
require(lpAmount > 0, "something bad happened");
lpaddy = msUniFactory.getPair(info.paircoin, address(vaultMemeCoin));
}
function _graduate1155Pool(PresaleInfo memory info, INftVault vaultMemeCoin, IMagicSwapV2Router.NftVaultLiquidityData memory vaultdataMemeCoin) internal returns (address lpaddy) {
IERC1155(info.paircoin).setApprovalForAll(address(router), true);
AltPairInfo memory alt = approvedpaircoins[info.paircoin];
INftVault vaultPaircoin = INftVault(alt.vaultaddress);
address[] memory collection = new address[](1);
uint256[] memory amount = new uint256[](1);
collection[0] = info.paircoin; // cd.addr;
amount[0] = info.baseTokenRaised;
IMagicSwapV2Router.NftVaultLiquidityData memory vaultdataPairCoin = IMagicSwapV2Router.NftVaultLiquidityData(
vaultPaircoin,
collection,
IDALWAYSZERO,
amount
);
(uint256 amountA, uint256 amountB,) = router.addLiquidityNFTNFT(
vaultdataMemeCoin, // shitcoin
vaultdataPairCoin, // pair coin
info.memecoin.balanceOf(address(this), 0),
info.baseTokenRaised,
address(0),
block.timestamp
);
lpaddy = msUniFactory.getPair(address(vaultPaircoin), address(vaultMemeCoin));
}
function _graduate(address _memeCoinAddress) internal {
PresaleInfo storage info = presaleInfo[_memeCoinAddress];
require(!info.readyToGraduate, "already ready to graduate");
require(info.baseTokenRaised >= info.targetBaseTokenToRaise, "target not met");
// Mark the presale as ready to graduate
info.readyToGraduate = true;
emit GraduationReady(_memeCoinAddress, info);
}
function graduatePresale(address _memeCoinAddress) external returns (address lpaddy) {
require(
presaleInfo[_memeCoinAddress].readyToGraduate &&
!presaleInfo[_memeCoinAddress].graduated,
"not ready to graduate or already graduated"
);
PresaleInfo memory info = presaleInfo[_memeCoinAddress];
presaleInfo[_memeCoinAddress].graduated = true;
info.memecoin.setTradingOpen(true);
(INftVault vaultMemeCoin, IMagicSwapV2Router.NftVaultLiquidityData memory vaultdataMemeCoin) =
_createMemeCoinVault(_memeCoinAddress);
info.memecoin.setApprovalForAll(address(router), true);
if (info.paircoin == address(baseToken)) {
lpaddy = _graduateBaseTokenPool(info, vaultMemeCoin, vaultdataMemeCoin);
} else {
lpaddy = _graduate1155Pool(info, vaultMemeCoin, vaultdataMemeCoin);
}
require(lpaddy != address(0), "lp failed");
emit Graduation(lpaddy, presaleInfo[_memeCoinAddress]);
}
function _createMemeCoinVault(address _memeCoinAddress) internal returns (INftVault, IMagicSwapV2Router.NftVaultLiquidityData memory) {
// creates a magicswap vault for the presaled 1155
address[] memory collection = new address[](1);
uint256[] memory amount = new uint256[](1);
INftVault.CollectionData[] memory vaultCD = new INftVault.CollectionData[](1);
INftVault.CollectionData memory vaultCDData = INftVault.CollectionData(address(_memeCoinAddress), INftVault.NftType.ERC1155, false, IDALWAYSZERO);
vaultCD[0] = vaultCDData;
collection[0] = _memeCoinAddress;
amount[0] = presaleInfo[_memeCoinAddress].memecoin.balanceOf(address(this), 0);
bool exists = factory.exists(vaultCD);
INftVault vault;
if (exists) {
vault = factory.getVault(vaultCD);
} else {
vault = factory.createVault(vaultCD);
}
IMagicSwapV2Router.NftVaultLiquidityData memory vaultdata = IMagicSwapV2Router.NftVaultLiquidityData(
vault,
collection,
IDALWAYSZERO,
amount
);
return (vault, vaultdata);
}
function getQuote(address _memeCoinAddress, uint256 _amountOfNftToBuy) public view returns(uint totalbaseToken, uint tax ){
PresaleInfo memory info = presaleInfo[_memeCoinAddress];
if (info.presalePrice > 0) {
totalbaseToken = info.presalePrice * _amountOfNftToBuy;
tax = totalbaseToken / 200; // 0.5%
} else {
require(_amountOfNftToBuy % info.returnForOne == 0, "wrong multiple");
totalbaseToken = _amountOfNftToBuy / info.returnForOne;
require(totalbaseToken > 0, 'xxxxx');
tax = 0;
}
}
function _calculateAmounts(uint256 _totalsupply, address _paircoin1155) internal view returns (uint256, uint256, uint256) {
require(_totalsupply >= MIN_TOTAL_SUPPLY, "Min 10M");
require(_totalsupply <= MAX_TOTAL_SUPPLY, "Max 1T");
require(_totalsupply % 1000 == 0, "Must be divisible by 1000");
// Calculate initial amount after staking rewards (10%)
uint256 amount = (_totalsupply * 90) / 100;
// Calculate amounts for LP and presale (50/50 split)
uint256 amountmemecointolp = amount / 2;
uint256 amountmemecointosell = amount / 2;
uint256 baseTokenNeededToFill;
if (_paircoin1155 == address(0)) {
baseTokenNeededToFill = calculateBaseTokenNeededForTargetMarketCap(
TARGETMCAP * 1e6, // $42k total target
amountmemecointolp,
getBaseTokenPriceUSD(),
18
);
} else {
baseTokenNeededToFill = calculateBaseTokenNeededForTargetMarketCap(
TARGETMCAP * 1e6,
amountmemecointolp,
getAltPairCoinPriceUSD(_paircoin1155),
18 // Assuming the ERC1155 are using 18 decimals
);
}
require(baseTokenNeededToFill > 0, "bad amounts");
return (amountmemecointolp, amountmemecointosell, baseTokenNeededToFill);
}
function getAltPairCoinPriceUSD(address _altcoin) public view returns(uint256) {
AltPairInfo memory alt = approvedpaircoins[_altcoin];
require(alt.approved, "Pair not approved");
uint256 baseTokenBal = baseToken.balanceOf(alt.lpaddress);
uint256 altPairBal = IERC20(alt.vaultaddress).balanceOf(alt.lpaddress);
require(altPairBal > 0, "No liquidity");
uint256 altPairValInBaseToken = (baseTokenBal * 1e18) / altPairBal;
uint256 baseTokenPriceUSD = getBaseTokenPriceUSD();
return (altPairValInBaseToken * baseTokenPriceUSD) / 1e18;
}
function addAltPair(address _ca, AltPairInfo memory _alp) external onlyOwner {
require(_ca != address(0), "Zero address not allowed");
require(_alp.lpaddress != address(0), "Invalid LP address");
require(_alp.vaultaddress != address(0), "Invalid vault address");
require(_alp.approved, "Pair must be approved");
approvedpaircoins[_ca] = _alp;
emit PairCoinApproved(_ca, _alp);
}
function removePairCoin(address _ca) external onlyOwner {
require(approvedpaircoins[_ca].approved, "Pair not found");
delete approvedpaircoins[_ca];
emit PairCoinRemoved(_ca);
}
function changeTargetMcap(uint256 _mcapindollars) external onlyOwner {
require(_mcapindollars > 0, "mcap must be greater than 0");
TARGETMCAP = _mcapindollars;
}
function changeOwner(address _newowner) external onlyOwner {
require(_newowner != address(0), "new owner is the zero address");
owner = _newowner;
}
function getBaseTokenPriceUSD() public view returns (uint baseTokenPrice) {
// Get the latest BASE_TOKEN/USD price from the Chainlink oracle
IAggregatorV3 baseUsdOracle = IAggregatorV3(BASE_USD_AGGREGATOR);
(
,
int256 basePrice,
,
,
) = baseUsdOracle.latestRoundData();
// Convert from Chainlink's 8 decimals to 6 decimals
baseTokenPrice = uint256(basePrice) / 100;
}
function calculateBaseTokenNeededForTargetMarketCap(
uint256 targetMcapUSD, // $42k in 6 decimals
uint256 lpTokenAmount,
uint256 pairPriceUSD, // Price in 6 decimals
uint256 pairDecimals
) internal pure returns (uint256) {
require(pairPriceUSD > 0, "Invalid price");
// We want half the target for each side (presale/LP)
uint256 valuePerSideUSD = targetMcapUSD / 2; // $21k in 6 decimals
// Calculate pair tokens needed for one side
uint256 pairTokensForOneSide = (valuePerSideUSD * (10 ** pairDecimals)) / pairPriceUSD;
// Double it (same amount needed for both presale and LP)
return pairTokensForOneSide * 2;
}
function ceil(uint a, uint m) internal pure returns (uint r) {
return (a + m - 1) / m * m;
}
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return this.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] calldata,
uint256[] calldata,
bytes calldata
) external virtual returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Vault contract for wrapping NFTs (ERC721/ERC1155) to ERC20
interface INftVault {
enum NftType {
ERC721,
ERC1155
}
/// @notice Vault configuration struct that specifies which NFTs are accepted in vault.
/// @param addr address of nft contract
/// @param nftType standard that NFT supports { ERC721, ERC1155 }
/// @param allowAllIds if true, all tokens are allowed in the vault. If false, tokenIds must be
/// listed one by one.
/// @param tokenIds list of tokens supported by vault. If allowAllIds is true, list must be empty.
struct CollectionData {
address addr;
NftType nftType;
bool allowAllIds;
uint256[] tokenIds;
}
/// @notice Struct for allowed tokens. Stores data in an optimized way to read it in vault.
/// @param tokenIds mapping from tokenid to is-allowed
/// @param tokenIdList list of all tokens that are allowed
/// @param allowAllIds if true, all tokens are allowed
struct AllowedTokenIds {
mapping(uint256 => bool) tokenIds;
uint256[] tokenIdList;
bool allowAllIds;
}
/// @notice Emitted during initiation when collection added to allowed list
/// @param collection collection details
event CollectionAllowed(CollectionData collection);
/// @notice Emitted on depositing NFT to vault
/// @param to address that gets vault ERC20 tokens
/// @param collection NFT address that is deposited
/// @param tokenId token id that is deposited
/// @param amount amount of token that is deposited, for ERC721 always 1
event Deposit(address indexed to, address indexed collection, uint256 tokenId, uint256 amount);
/// @notice Emitted on withdrawing NFT from vault
/// @param to address that gets withdrawn NFTs
/// @param collection NFT address that is withdrawn
/// @param tokenId token id that is withdrawn
/// @param amount amount of token that is withdrawn, for ERC721 always 1
event Withdraw(address indexed to, address indexed collection, uint256 tokenId, uint256 amount);
/// @dev Contract is already initialized
error Initialized();
/// @dev Collection data is empty
error InvalidCollections();
/// @dev Collection already added
error DuplicateCollection();
/// @dev Token id is listed twice in CollectionData.tokenIds array
error TokenIdAlreadySet();
/// @dev Token ids in CollectionData.tokenIds array are not sorted
error TokenIdsMustBeSorted();
/// @dev ERC165 suggests that NFT is supporting ERC721 but ERC1155 is claimed
error ExpectedERC721();
/// @dev ERC165 suggests that NFT is supporting ERC1155 but ERC721 is claimed
error ExpectedERC1155();
/// @dev Collection does not support all token IDs however list of IDs is empty.
/// CollectionData.tokenIds is empty and CollectionData.allowAllIds is false.
error MissingTokenIds();
/// @dev CollectionData.tokenIds is not empty however Collection supports all token IDs.
error TokenIdsMustBeEmpty();
/// @dev Token is not allowed in vault
error DisallowedToken();
/// @dev Token amount is invalid eg. amount == 0
error WrongAmount();
/// @dev Token amount is invalid for ERC721, amount != 1
error WrongERC721Amount();
/// @dev Trying to interact with token that does not support ERC721 nor ERC1155
error UnsupportedNft();
/// @dev Token is allowed in vault but must not be
error MustBeDisallowedToken();
/// @notice value of 1 token, including decimals
function ONE() external view returns (uint256);
/// @notice amount of token required for last NFT to be redeemed
function LAST_NFT_AMOUNT() external view returns (uint256);
/// @notice unique id of the vault generated using its configuration
function VAULT_HASH() external view returns (bytes32);
/// @notice Initialize Vault with collection config
/// @dev Called by factory during deployment
/// @param collections struct array of allowed collections and token IDs
function init(CollectionData[] memory collections) external;
/// @notice Returns hash of vault configuration
/// @param collections struct array of allowed collections and token IDs
/// @return configuration hash
function hashVault(CollectionData[] memory collections) external pure returns (bytes32);
/// @notice Returns balances of NFT deposited to the vault
/// @param collectionAddr NFT address
/// @param tokenId NFT's token ID
/// @return amount amount of NFT deposited to the vault
function balances(address collectionAddr, uint256 tokenId) external view returns (uint256 amount);
/// @notice Get array of NFT addresses that are allowed to be deposited to the vault
/// @dev Keep in mind that returned address(es) can be further restricted on token ID level
/// @return collections array of NFT addresses that are allowed to be deposited to the vault
function getAllowedCollections() external view returns (address[] memory collections);
/// @return number of NFT addresses that are allowed to be deposited to the vault
function getAllowedCollectionsLength() external view returns (uint256);
/// @notice Get details of allowed collection
/// @return struct with details of allowed collection
function getAllowedCollectionData(address collectionAddr) external view returns (CollectionData memory);
/// @notice Validates type of collection (ERC721 or ERC1155)
/// @dev It uses ERC165 to check interface support. If support can not be detected without doubt, user input is trusted.
/// @param collectionAddr NFT address
/// @param nftType NFT type, ERC721 or ERC1155
/// @return validatedNftType returns validated enum NftType as uint256
function validateNftType(address collectionAddr, NftType nftType)
external
view
returns (uint256 validatedNftType);
/// @notice Returns if true token can be deposited
/// @param collection NFT address
/// @param tokenId NFT token ID
/// @return true if allowed
function isTokenAllowed(address collection, uint256 tokenId) external view returns (bool);
/// @notice Returns balance of token sent to the vault
/// @dev Reads balance of tokens freshy sent to the vault
/// @param collection NFT address
/// @param tokenId NFT token ID
/// @return balance of sent token, for ERC721 it's always 1
function getSentTokenBalance(address collection, uint256 tokenId) external view returns (uint256);
/// @notice Deposit NFT to vault
/// @dev This low-level function should be called from a contract which performs important safety checks
/// @param to address that gets minted ERC20 token
/// @param collection address of deposited NFT
/// @param tokenId token ID of deposited NFT
/// @param amount amount of deposited NFT, for ERC721 it's always 1
/// @return amountMinted amount of minted ERC20 token
function deposit(address to, address collection, uint256 tokenId, uint256 amount)
external
returns (uint256 amountMinted);
/// @notice Deposit NFTs to vault
/// @dev This low-level function should be called from a contract which performs important safety checks
/// @param to address that gets minted ERC20 token
/// @param collection array of addresses of deposited NFTs
/// @param tokenId array of token IDs of deposited NFTs
/// @param amount array if amounts of deposited NFTs, for ERC721 it's always 1
/// @return amountMinted amount of minted ERC20 token
function depositBatch(address to, address[] memory collection, uint256[] memory tokenId, uint256[] memory amount)
external
returns (uint256 amountMinted);
/// @notice Withdraw NFT from vault
/// @dev This low-level function should be called from a contract which performs important safety checks
/// @param to address that gets NFT
/// @param collection address of NFT to withdraw
/// @param tokenId token ID of NFT to withdraw
/// @param amount amount of NFT to withdraw, for ERC721 it's always 1
/// @return amountBurned amount of burned ERC20
function withdraw(address to, address collection, uint256 tokenId, uint256 amount)
external
returns (uint256 amountBurned);
/// @notice Withdraw NFTs from vault
/// @dev This low-level function should be called from a contract which performs important safety checks
/// @param to address that gets NFT
/// @param collection array of addresses of NFTs to withdraw
/// @param tokenId array of token IDs of NFTs to withdraw
/// @param amount array of amounts of NFTs to withdraw, for ERC721 it's always 1
/// @return amountBurned amount of burned ERC20
function withdrawBatch(address to, address[] memory collection, uint256[] memory tokenId, uint256[] memory amount)
external
returns (uint256 amountBurned);
/// @notice Allow anyone to withdraw tokens sent to this vault by accident
/// Only unsupported NFTs can be skimmed.
/// @param to address that gets NFT
/// @param nftType NftType of skimmed NFT
/// @param collection address of NFT to skim
/// @param tokenId token ID of NFT to skim
/// @param amount amount of NFT to skim, for ERC721 it's always 1
function skim(address to, NftType nftType, address collection, uint256 tokenId, uint256 amount) external;
}
//SPDX-License-Identifier: Prima Nocta
pragma solidity ^0.8.20;
interface IUniswapV3PoolState {
function slot0() external view returns (
uint160 sqrtPriceX96,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
uint8 feeProtocol,
bool unlocked
);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
interface IThresholdERC1155 {
// Events
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 amount);
event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] amounts);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
// ERC1155 core functions
function setApprovalForAll(address operator, bool approved) external;
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
function balanceOf(address account, uint256 id) external view returns (uint256);
function balanceOfBatch(address[] calldata owners, uint256[] calldata ids) external view returns (uint256[] memory);
function isApprovedForAll(address owner, address operator) external view returns (bool);
function supportsInterface(bytes4 interfaceId) external view returns (bool);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function uri(uint256 id) external view returns (string memory);
// Additional view functions
function totalBalanceOf(address account) external view returns (uint256);
function thresholds(uint256 index) external view returns (uint256);
// Admin functions
function initialize(address _creator, address _teamwallet, uint256 _totalSupply, string memory _name, string memory _symbol, string memory baseURI, uint256[] memory _thresholds) external;
function setTradingOpen(bool _x) external;
// State view functions
function initialized() external view returns (bool);
function creator() external view returns (address);
function presalefactory() external view returns (address);
function tradingEnabled() external view returns (bool);
}
//SPDX-License-Identifier: Prima Nocta
pragma solidity ^0.8.20;
interface IAggregatorV3 {
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
//SPDX-License-Identifier: Prima Nocta
pragma solidity ^0.8.20;
import "./INftVault.sol";
interface INftVaultFactory {
function createVault(INftVault.CollectionData[] memory collections) external returns (INftVault vault);
function getVault(INftVault.CollectionData[] memory collections) external view returns (INftVault vault);
function getVaultLength() external view returns (uint256);
function getVaultAt(uint256 index) external view returns (address);
function exists(INftVault.CollectionData[] memory collections) external view returns (bool);
}
//SPDX-License-Identifier: Prima Nocta
pragma solidity ^0.8.20;
interface IMagicSwapUniswapV2Factory {
function getPair(address tokenA, address tokenB) external view returns (address pair);
}
//SPDX-License-Identifier: Prima Nocta
pragma solidity ^0.8.20;
import "./INftVault.sol";
interface IMagicSwapV2Router {
struct NftVaultLiquidityData {
INftVault token;
address[] collection;
uint256[] tokenId;
uint256[] amount;
}
function addLiquidityNFT(
NftVaultLiquidityData calldata _vault,
address _tokenB,
uint256 _amountBDesired,
uint256 _amountBMin,
address _to,
uint256 _deadline
) external returns (uint256 amountA, uint256 amountB, uint256 lpAmount);
function addLiquidityNFTNFT(
NftVaultLiquidityData calldata _vaultA,
NftVaultLiquidityData calldata _vaultB,
uint256 _amountAMin,
uint256 _amountBMin,
address _to,
uint256 _deadline
) external returns (uint256 amountA, uint256 amountB, uint256 lpAmount);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/IERC1155.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC-1155 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1155[ERC].
*/
interface IERC1155 is IERC165 {
/**
* @dev Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`.
*/
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
/**
* @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
*
* If an {URI} event was emitted for `id`, the standard
* https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
* returned by {IERC1155MetadataURI-uri}.
*/
event URI(string value, uint256 indexed id);
/**
* @dev Returns the value of tokens of token type `id` owned by `account`.
*/
function balanceOf(address account, uint256 id) external view returns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) external view returns (uint256[] memory);
/**
* @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the zero address.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address account, address operator) external view returns (bool);
/**
* @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`.
*
* WARNING: This function can potentially allow a reentrancy attack when transferring tokens
* to an untrusted contract, when invoking {onERC1155Received} on the receiver.
* Ensure to follow the checks-effects-interactions pattern and consider employing
* reentrancy guards when interacting with untrusted contracts.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `value` amount.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external;
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
*
* WARNING: This function can potentially allow a reentrancy attack when transferring tokens
* to an untrusted contract, when invoking {onERC1155BatchReceived} on the receiver.
* Ensure to follow the checks-effects-interactions pattern and consider employing
* reentrancy guards when interacting with untrusted contracts.
*
* Emits either a {TransferSingle} or a {TransferBatch} event, depending on the length of the array arguments.
*
* Requirements:
*
* - `ids` and `values` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @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);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (proxy/Clones.sol)
pragma solidity ^0.8.20;
import {Errors} from "../utils/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);
}
assembly ("memory-safe") {
// 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);
}
assembly ("memory-safe") {
// 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) {
assembly ("memory-safe") {
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
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC-721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC-721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
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.
*
* _Available since v5.1._
*/
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);
}