From cd02184d35654da673fe7e428d404743e092294c Mon Sep 17 00:00:00 2001 From: bakatrouble Date: Wed, 19 Oct 2022 13:05:23 +0300 Subject: [PATCH] update --- contracts/interfaces/INFT.sol | 14 +- contracts/interfaces/INFTMetadata.sol | 9 + contracts/nft.sol | 192 +++++------------- contracts/nftMetadata.sol | 83 ++++++++ contracts/red_treasury.sol | 4 + {deploy2 => deploy}/00_treasury.ts | 0 {deploy2 => deploy}/01_furnace.ts | 0 {deploy2 => deploy}/03_market.ts | 0 {deploy2 => deploy}/04_token.ts | 0 deploy/05_metadata.ts | 16 ++ deploy/{05_initialize.ts => 06_initialize.ts} | 65 +++--- 11 files changed, 211 insertions(+), 172 deletions(-) create mode 100644 contracts/interfaces/INFTMetadata.sol create mode 100644 contracts/nftMetadata.sol rename {deploy2 => deploy}/00_treasury.ts (100%) rename {deploy2 => deploy}/01_furnace.ts (100%) rename {deploy2 => deploy}/03_market.ts (100%) rename {deploy2 => deploy}/04_token.ts (100%) create mode 100644 deploy/05_metadata.ts rename deploy/{05_initialize.ts => 06_initialize.ts} (54%) diff --git a/contracts/interfaces/INFT.sol b/contracts/interfaces/INFT.sol index 6fca8d7..e2cbab1 100644 --- a/contracts/interfaces/INFT.sol +++ b/contracts/interfaces/INFT.sol @@ -3,5 +3,17 @@ pragma solidity ^0.8.14; interface INFT { + struct TokenInfo { + string image; + address[] tokens; + uint256 rate; + uint256 attack; + uint256 defense; + uint256 health; + uint256 critChance; + uint256 critDmg; + uint256 recover; + } + function raiseRewardPool(uint256 amount) external; -} \ No newline at end of file +} diff --git a/contracts/interfaces/INFTMetadata.sol b/contracts/interfaces/INFTMetadata.sol new file mode 100644 index 0000000..f47dfe2 --- /dev/null +++ b/contracts/interfaces/INFTMetadata.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +pragma solidity ^0.8.14; + +import "./INFT.sol"; + +interface INFTMetadata { + function tokenURI(uint256 tokenId, INFT.TokenInfo memory token) external view returns (string memory); +} diff --git a/contracts/nft.sol b/contracts/nft.sol index 8ff315d..1fe6593 100644 --- a/contracts/nft.sol +++ b/contracts/nft.sol @@ -11,8 +11,10 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./interfaces/ITreasury.sol"; import "./interfaces/IPancakeSwapPair.sol"; import "./mixins/signature-control.sol"; +import "./interfaces/INFT.sol"; +import "./interfaces/INFTMetadata.sol"; -contract NFT is ERC721EnumerableUpgradeable, SignatureControl { +contract NFT is ERC721EnumerableUpgradeable, SignatureControl, INFT { using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; using SafeERC20Upgradeable for IERC20Upgradeable; @@ -42,11 +44,15 @@ contract NFT is ERC721EnumerableUpgradeable, SignatureControl { //mint prices, caps, addresses of reward tokens(shiba,floki,doggy,doge) uint256[4] prices = [ .001 ether, + .0008 ether, + .0006 ether, + .0004 ether // 300 * 10**18, - 250 * 10**18, - 200 * 10**18, - 250 * 10**18 +// 250 * 10**18, +// 200 * 10**18, +// 250 * 10**18 ]; + INFTMetadata metadata; IERC20Upgradeable BUSD; IERC20Upgradeable WETH; mapping(address => uint256) tokenToPrices; @@ -69,7 +75,6 @@ contract NFT is ERC721EnumerableUpgradeable, SignatureControl { mapping(address => mapping(uint256 => uint256)) accountRewards; mapping(address => mapping(uint256 => uint256)) accountRewardsPerTokenPaid; - //whitelist shit bool public isPresale = true; uint256 whitelistPrice = 200 ether; uint256 maxMintPerAddressDuringWhitelist = 5; @@ -82,17 +87,6 @@ contract NFT is ERC721EnumerableUpgradeable, SignatureControl { uint256 statMultiplier = 100000; mapping(uint256 => TokenInfo) tokenIdToInfo; - struct TokenInfo { - string image; - address[] tokens; - uint256 rate; - uint256 attack; - uint256 defense; - uint256 health; - uint256 critChance; - uint256 critDmg; - uint256 recover; - } mapping(address => Tokenomic) addressToToken; struct Tokenomic { @@ -150,7 +144,7 @@ contract NFT is ERC721EnumerableUpgradeable, SignatureControl { } modifier isValidMint(uint256 amount, uint256 tokenType) { - require(!isPresale, "(mint) presale"); + require(!isPresale, "presale"); require(tokenType <= 3, "!tokenType"); require(mintCapOfToken[tokenType] + amount <= nftCaps[tokenType]); _; @@ -485,68 +479,8 @@ contract NFT is ERC721EnumerableUpgradeable, SignatureControl { address(this), block.timestamp ); - // return - // swapRouter.swapTokensForExactETH( - // price, - // amount, - // path, - // address(this), - // block.timestamp - // ); } -// function isValidClaim( -// uint256[] memory tokenAmounts, -// bytes memory signature, -// uint256 userNonce, -// uint256 timestamp -// ) internal returns (bool) { -// bytes memory data = abi.encodePacked( -// _toAsciiString(msg.sender), -// " is authorized to claim ", -// StringsUpgradeable.toString(tokenAmounts[0]), -// " shiba, ", -// StringsUpgradeable.toString(tokenAmounts[1]), -// " floki, ", -// StringsUpgradeable.toString(tokenAmounts[2]), -// " doggy, ", -// StringsUpgradeable.toString(tokenAmounts[3]), -// " doge before ", -// StringsUpgradeable.toString(timestamp), -// ", ", -// StringsUpgradeable.toString(userNonce) -// ); -// bytes32 hash = _toEthSignedMessage(data); -// address signer = ECDSAUpgradeable.recover(hash, signature); -// require(treasury.isOperator(signer), "Mint not verified by operator"); -// require(block.timestamp <= timestamp, "Outdated signed message"); -// require(!usedNonces[userNonce], "Used nonce"); -// return true; -// } -// -// function claimRewards( -// uint256[] memory amounts, -// bytes memory signature, -// uint256 userNonce, -// uint256 timestamp -// ) public { -// require(isValidClaim(amounts, signature, userNonce, timestamp), ""); -// for (uint256 i = 0; i < 4; i++) { -// if (amounts[i] > 0) { -// address[] memory path; -// path[0] = address(WETH); -// path[1] = rewardTokens[i]; -// swapRouter.swapExactTokensForTokens( -// amounts[i], -// 1, -// path, -// msg.sender, -// block.timestamp -// ); -// } -// } -// } - function walletOfOwner(address _owner) public view @@ -566,39 +500,9 @@ contract NFT is ERC721EnumerableUpgradeable, SignatureControl { override returns (string memory) { - require(_exists(tokenId), "Non-existant token"); - TokenInfo storage token = tokenIdToInfo[tokenId]; - return - string( - abi.encodePacked( - "data:application/json;base64,", - Base64Upgradeable.encode( - bytes( - abi.encodePacked( - '{"name": Babies of Mars #', - tokenId, - ', "description": "adasdasdasd", "image": ', - token.image, - ',{ "attributes": [ {"trait_type": "tokens", "value": ', - token.tokens, - '}, { "trait_type": "attack", "value": ', - token.attack, - '}, { "trait_type": "defense", "value": ', - token.defense, - '}, { "trait_type": "health", "value": ', - token.health, - '}, { "trait_type": "critical rate", "value": ', - token.critChance, - '}, { "trait_type": "critical damage", "value": ', - token.critDmg, - '}, { "trait_type": "recovery", "value": ', - token.recover, - "} ] }" - ) - ) - ) - ) - ); + require(_exists(tokenId), "Non-existent token"); + TokenInfo memory token = tokenIdToInfo[tokenId]; + return metadata.tokenURI(tokenId, token); } function raiseRewardPool(uint256 amount) public { @@ -645,38 +549,38 @@ contract NFT is ERC721EnumerableUpgradeable, SignatureControl { emit RewardPoolRaised(swappedFor); } -// function _afterTokenTransfer( -// address from, -// address to, -// uint256 tokenId -// ) internal virtual override { -// super._afterTokenTransfer(from, to, tokenId); -// TokenInfo memory info = tokenIdToInfo[tokenId]; -// if (from != address(0)) { -// if (ERC721Upgradeable.balanceOf(from) == 0) { -// _holders.remove(from); -// } -// if (info.tokens.length > 1) { -// totalShares[4] -= info.rate; -// accountShares[from][4] -= info.rate; -// } else { -// uint256 idx = addressToType[info.tokens[0]]; -// totalShares[idx] -= info.rate; -// accountShares[from][idx] -= info.rate; -// } -// } -// if (to != address(0)) { -// _holders.add(to); -// if (info.tokens.length > 1) { -//// totalShares[4] += info.rate; -//// accountShares[to][4] += info.rate; -// } else { -//// uint256 idx = addressToType[info.tokens[0]]; -//// totalShares[idx] += info.rate; -//// accountShares[to][idx] += info.rate; -// } -// } -// } + function _afterTokenTransfer( + address from, + address to, + uint256 tokenId + ) internal virtual override { + super._afterTokenTransfer(from, to, tokenId); + TokenInfo memory info = tokenIdToInfo[tokenId]; + if (from != address(0)) { + if (balanceOf(from) == 0) { + _holders.remove(from); + } + if (info.tokens.length > 1) { + totalShares[4] -= info.rate; + accountShares[from][4] -= info.rate; + } else { + uint256 idx = addressToType[info.tokens[0]]; + totalShares[idx] -= info.rate; + accountShares[from][idx] -= info.rate; + } + } + if (to != address(0)) { + _holders.add(to); + if (info.tokens.length > 1) { + totalShares[4] += info.rate; + accountShares[to][4] += info.rate; + } else { + uint256 idx = addressToType[info.tokens[0]]; + totalShares[idx] += info.rate; + accountShares[to][idx] += info.rate; + } + } + } function pendingReward(uint256 idx, address account) public view returns (uint256) { return accountShares[account][idx] + (_rewardPerShare(idx) - accountRewardsPerTokenPaid[account][idx]) / 1e18 + accountRewards[account][idx]; @@ -726,4 +630,8 @@ contract NFT is ERC721EnumerableUpgradeable, SignatureControl { require(isPresale, "!presale"); isPresale = false; } + + function updateMetadata(INFTMetadata newValue) external onlyAdmin { + metadata = newValue; + } } diff --git a/contracts/nftMetadata.sol b/contracts/nftMetadata.sol new file mode 100644 index 0000000..707a3fb --- /dev/null +++ b/contracts/nftMetadata.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +pragma solidity ^0.8.14; + +import "@openzeppelin/contracts/utils/Strings.sol"; +import "@openzeppelin/contracts/utils/Base64.sol"; +import "./interfaces/INFT.sol"; + +contract NFTMetadata { + function tokenURI(uint256 tokenId, INFT.TokenInfo calldata token) external view returns (string memory) { + return + string( + abi.encodePacked( + "data:application/json;base64,", + Base64.encode( + bytes( + abi.encodePacked( + '{"name": "Babies of Mars #', + Strings.toString(tokenId), + '", "description": "adasdasdasd", "image": "', + token.image, + '",{ "attributes": [ {"trait_type": "tokens", "value": ', + addressArrayToString(token.tokens), + '}, { "trait_type": "attack", "value": ', + compileStatString(token) + ) + ) + ) + ) + ); + } + + function compileStatString(INFT.TokenInfo calldata token) internal view returns (string memory) { + return string(abi.encodePacked( + '}, { "trait_type": "attack", "value": ', + Strings.toString(token.attack), + '}, { "trait_type": "defense", "value": ', + Strings.toString(token.defense), + '}, { "trait_type": "health", "value": ', + Strings.toString(token.health), + '}, { "trait_type": "critical rate", "value": ', + Strings.toString(token.critChance), + '}, { "trait_type": "critical damage", "value": ', + Strings.toString(token.critDmg), + '}, { "trait_type": "recovery", "value": ', + Strings.toString(token.recover), + "} ] }" + )); + } + + function addressArrayToString(address[] memory addressArray) internal pure returns (string memory) { + string memory result = "["; + for(uint256 i = 0; i < addressArray.length; i++) { + if(i == addressArray.length-1) { + result = string.concat(result,"'0x",toAsciiString(addressArray[i]),"'"); + } else { + + result = string.concat(result,"'0x",toAsciiString(addressArray[i]),"',"); + } + } + result = string.concat(result,"]"); + return result; + } + + function toAsciiString(address x) internal pure returns (string memory) { + bytes memory s = new bytes(40); + for (uint i = 0; i < 20; i++) { + bytes1 b = bytes1(uint8(uint(uint160(x)) / (2**(8*(19 - i))))); + bytes1 hi = bytes1(uint8(b) / 16); + bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi)); + s[2*i] = char(hi); + s[2*i+1] = char(lo); + } + return string(s); + } + + function char(bytes1 b) internal pure returns (bytes1 c) { + if (uint8(b) < 10) + return bytes1(uint8(b) + 0x30); + else + return bytes1(uint8(b) + 0x57); + } +} diff --git a/contracts/red_treasury.sol b/contracts/red_treasury.sol index a693026..55b76a9 100644 --- a/contracts/red_treasury.sol +++ b/contracts/red_treasury.sol @@ -16,6 +16,10 @@ contract Treasury is RoleControl { uint256 amount ); + constructor() { + _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); + } + function initialize(address admin) public initializer { _setupRole(DEFAULT_ADMIN_ROLE, admin); } diff --git a/deploy2/00_treasury.ts b/deploy/00_treasury.ts similarity index 100% rename from deploy2/00_treasury.ts rename to deploy/00_treasury.ts diff --git a/deploy2/01_furnace.ts b/deploy/01_furnace.ts similarity index 100% rename from deploy2/01_furnace.ts rename to deploy/01_furnace.ts diff --git a/deploy2/03_market.ts b/deploy/03_market.ts similarity index 100% rename from deploy2/03_market.ts rename to deploy/03_market.ts diff --git a/deploy2/04_token.ts b/deploy/04_token.ts similarity index 100% rename from deploy2/04_token.ts rename to deploy/04_token.ts diff --git a/deploy/05_metadata.ts b/deploy/05_metadata.ts new file mode 100644 index 0000000..cddd7d9 --- /dev/null +++ b/deploy/05_metadata.ts @@ -0,0 +1,16 @@ +import { DeployFunction } from 'hardhat-deploy/dist/types'; + +const deployFunc: DeployFunction = async ({ getNamedAccounts, deployments, getChainId }) => { + const { deploy } = deployments; + const { deployer } = await getNamedAccounts(); + await deploy('NFTMetadata', { + from: deployer, + args: [], + log: true, + }); +}; + +const tags = ['NFTMetadata']; + +export default deployFunc; +export { tags }; diff --git a/deploy/05_initialize.ts b/deploy/06_initialize.ts similarity index 54% rename from deploy/05_initialize.ts rename to deploy/06_initialize.ts index c7d000c..c1c0371 100644 --- a/deploy/05_initialize.ts +++ b/deploy/06_initialize.ts @@ -10,66 +10,73 @@ const deployFunc: DeployFunction = async ({ getNamedAccounts, deployments, ether const AddressZero = ethers.constants.AddressZero; const PancakeRouter = '0xD99D1c33F9fC3444f8101754aBC46c52416550D1'; - const Owner = deployer; //'0x22f60E6BD7973c226979B6F57BC92C2d66a8c151'; + const Owner = '0x22f60E6BD7973c226979B6F57BC92C2d66a8c151'; const BUSD = '0xB08B32EC7E2Aa60a4C2f4343E7EB638187163415'; const WETH = '0xF1960ee684B173cf6c17fCdA5F1dFC366Aba55d3'; const RedTrustWallet = '0x22f60E6BD7973c226979B6F57BC92C2d66a8c151'; - const treasuryAddress = '0x0Cb09553b2d1Da0459577d7027EdA21d40f2a7Cb'; //(await get('Treasury')).address; - const furnaceAddress = '0xe1cCbDFaBc90F15F96A658a6e96c27D0f1DA9Bd4'; //(await get('RedFurnace')).address; + const treasuryAddress = (await get('Treasury')).address; + const furnaceAddress = (await get('RedFurnace')).address; const nftAddress = (await get('NFT')).address; - const marketAddress = '0x92DE9C3a72d787d16F73e18649139341869f4621'; //(await get('Marketplace')).address; - const tokenAddress = '0x341D28f61ea857C31BAC30302D88a69f5E8c3ed0'; //(await get('BabiesOfMars')).address; + const marketAddress = (await get('Marketplace')).address; + const tokenAddress = (await get('BabiesOfMars')).address; + const nftMetadataAddress = (await get('NFTMetadata')).address; const token = await ethers.getContractAt('BabiesOfMars', tokenAddress); const pancake = await ethers.getContractAt('IPancakeSwapRouter', PancakeRouter); const busd = await ethers.getContractAt('ERC', BUSD); const weth = await ethers.getContractAt('ERC', WETH); const nft = await ethers.getContractAt('NFT', nftAddress); + const nftMetadata = await ethers.getContractAt('NFTMetadata', nftMetadataAddress); - // await execute('Treasury', { - // from: deployer, - // log: true, - // }, 'initialize', Owner) - // - // await execute('BabiesOfMars', { - // from: deployer, - // log: true, - // }, 'initialize', PancakeRouter, Owner, treasuryAddress, RedTrustWallet, nftAddress, furnaceAddress, BUSD); + await execute('Treasury', { + from: deployer, + log: true, + }, 'initialize', Owner) - // await execute('NFT', { - // from: deployer, - // log: true, - // }, 'initialize', 'NFT Name', 'NFTS', treasuryAddress, RedTrustWallet, tokenAddress, 200, 200, 200, await token.pair(), PancakeRouter, BUSD, WETH); + await execute('BabiesOfMars', { + from: deployer, + log: true, + }, 'initialize', PancakeRouter, Owner, treasuryAddress, RedTrustWallet, nftAddress, furnaceAddress, BUSD); - // await execute('Marketplace', { - // from: deployer, - // log: true, - // }, 'initialize', 200, treasuryAddress, nftAddress, tokenAddress); + await execute('NFT', { + from: deployer, + log: true, + }, 'initialize', 'NFT Name', 'NFTS', treasuryAddress, RedTrustWallet, tokenAddress, 200, 200, 200, await token.pair(), PancakeRouter, BUSD, WETH); + + await execute('Marketplace', { + from: deployer, + log: true, + }, 'initialize', 200, treasuryAddress, nftAddress, tokenAddress); + + await execute('NFT', { + from: deployer, + log: true, + }, 'updateMetadata', nftMetadataAddress); // await busd.mint(deployer, parseEther('100000000')); // await weth.mint(deployer, parseEther('100000000')); // await weth.approve(PancakeRouter, parseEther('100000000')); // await weth.approve(PancakeRouter, parseEther('100000000')); - // await token.approve(PancakeRouter, parseEther('1000000000000000000')); - // await new Promise((approve) => setTimeout(() => approve(null), 5000)); - // await pancake.addLiquidity(tokenAddress, WETH, 10000000, parseEther('10'), 0, 0, deployer, getCurrentTimestamp() + 1000); - // await pancake.addLiquidity(tokenAddress, BUSD, 10000000, parseEther('1000'), 0, 0, deployer, getCurrentTimestamp() + 1000); + await token.approve(PancakeRouter, parseEther('1000000000000000000')); + await new Promise((approve) => setTimeout(() => approve(null), 5000)); + await pancake.addLiquidity(tokenAddress, WETH, 10000000, parseEther('10'), 0, 0, deployer, getCurrentTimestamp() + 1000); + await pancake.addLiquidity(tokenAddress, BUSD, 10000000, parseEther('1000'), 0, 0, deployer, getCurrentTimestamp() + 1000); console.log(`Treasury: ${treasuryAddress}\nRedFurnace: ${furnaceAddress}\nNFT: ${nftAddress}\nMarketplace: ${marketAddress}\nBabiesOfMars: ${tokenAddress}`); // await token.approve(nft.address, constants.MaxUint256); // await nft.finishPresale(); // await new Promise((approve) => setTimeout(() => approve(null), 5000)); - // await nft.mint(10, 0); + // await nft.mint(10, 0, { gasLimit: 5000000 }); // await new Promise((approve) => setTimeout(() => approve(null), 5000)); - await nft.combineTokens([1, 2], { value: parseEther('.05'), gasLimit: 5000000 }); + // await nft.combineTokens([1, 2], { value: parseEther('.05'), gasLimit: 5000000 }); }; const tags = ['Initialize']; -const dependencies = ['Treasury', 'Furnace', 'NFT', 'Marketplace', 'Token'] +const dependencies = ['Treasury', 'Furnace', 'NFT', 'Marketplace', 'Token', 'NFTMetadata']; export default deployFunc; export { tags, dependencies };