优化contracts代码
This commit is contained in:
parent
566f1f379e
commit
c08707047d
33
contracts/BEChip.sol
Normal file
33
contracts/BEChip.sol
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
|
||||||
|
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
|
||||||
|
|
||||||
|
contract BEChip is ERC721, AccessControlEnumerable, ERC721Enumerable, Ownable {
|
||||||
|
/**
|
||||||
|
* @dev See {IERC165-_beforeTokenTransfer}.
|
||||||
|
* TODO::
|
||||||
|
*/
|
||||||
|
function _beforeTokenTransfer(
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 tokenId
|
||||||
|
) internal virtual override(ERC721, ERC721Enumerable) {
|
||||||
|
super._beforeTokenTransfer(from, to, tokenId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev See {IERC165-supportsInterface}.
|
||||||
|
*/
|
||||||
|
function supportsInterface(bytes4 interfaceId)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
virtual
|
||||||
|
override(AccessControlEnumerable, ERC721, ERC721Enumerable)
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
return super.supportsInterface(interfaceId);
|
||||||
|
}
|
||||||
|
}
|
12
contracts/BECoin.sol
Normal file
12
contracts/BECoin.sol
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
|
||||||
|
|
||||||
|
contract BECoin is ERC20PresetMinterPauser, Ownable {
|
||||||
|
uint256 public initializedCap = 20000000 * 1e18;
|
||||||
|
|
||||||
|
constructor() ERC20PresetMinterPauser("Blissful Elites Coin", "BEC") {
|
||||||
|
_mint(_msgSender(), initializedCap);
|
||||||
|
}
|
||||||
|
}
|
152
contracts/BEHero.sol
Normal file
152
contracts/BEHero.sol
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
|
||||||
|
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
|
||||||
|
|
||||||
|
contract BEHero is ERC721, AccessControlEnumerable, ERC721Enumerable, Ownable {
|
||||||
|
mapping(address => bool) public approvalWhitelists;
|
||||||
|
mapping(uint256 => bool) public lockedTokens;
|
||||||
|
string private _baseTokenURI;
|
||||||
|
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
|
||||||
|
|
||||||
|
constructor() ERC721("Blissful Elites Hero", "BEH") {}
|
||||||
|
|
||||||
|
function _baseURI() internal view virtual override returns (string memory) {
|
||||||
|
return _baseTokenURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Creates a new token for `to`. Its token ID will be automatically
|
||||||
|
* assigned (and available on the emitted {IERC721-Transfer} event), and the token
|
||||||
|
* URI autogenerated based on the base URI passed at construction.
|
||||||
|
*
|
||||||
|
* See {ERC721-_mint}.
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
*
|
||||||
|
* - the caller must have the `MINTER_ROLE`.
|
||||||
|
*/
|
||||||
|
function mint(address to, uint256 tokenId) public virtual {
|
||||||
|
require(
|
||||||
|
hasRole(MINTER_ROLE, _msgSender()),
|
||||||
|
"Must have minter role to mint"
|
||||||
|
);
|
||||||
|
require(!_exists(tokenId), "Must have unique tokenId");
|
||||||
|
// We cannot just use balanceOf to create the new tokenId because tokens
|
||||||
|
// can be burned (destroyed), so we need a separate counter.
|
||||||
|
_mint(to, tokenId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev See {IERC721-isApprovedForAll}.
|
||||||
|
*/
|
||||||
|
function isApprovedForAll(address owner, address operator)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
override
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
if (approvalWhitelists[operator] == true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.isApprovedForAll(owner, operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Allow operation to reduce gas fee.
|
||||||
|
*/
|
||||||
|
function addApprovalWhitelist(address proxy) public onlyOwner {
|
||||||
|
require(approvalWhitelists[proxy] == false, "Invalid proxy address");
|
||||||
|
|
||||||
|
approvalWhitelists[proxy] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Remove operation from approval list.
|
||||||
|
*/
|
||||||
|
function removeApprovalWhitelist(address proxy) public onlyOwner {
|
||||||
|
approvalWhitelists[proxy] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Add factory to mint item
|
||||||
|
*/
|
||||||
|
function setMintFactory(address factory) public onlyOwner {
|
||||||
|
_setupRole(MINTER_ROLE, factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Remove factory
|
||||||
|
*/
|
||||||
|
function removeMintFactory(address factory) public onlyOwner {
|
||||||
|
revokeRole(MINTER_ROLE, factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Lock token to use in game or for rental
|
||||||
|
*/
|
||||||
|
function lock(uint256 tokenId) public {
|
||||||
|
require(
|
||||||
|
approvalWhitelists[_msgSender()],
|
||||||
|
"Must be valid approval whitelist"
|
||||||
|
);
|
||||||
|
require(_exists(tokenId), "Must be valid tokenId");
|
||||||
|
require(!lockedTokens[tokenId], "Token has already locked");
|
||||||
|
lockedTokens[tokenId] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Unlock token to use blockchain or sale on marketplace
|
||||||
|
*/
|
||||||
|
function unlock(uint256 tokenId) public {
|
||||||
|
require(
|
||||||
|
approvalWhitelists[_msgSender()],
|
||||||
|
"Must be valid approval whitelist"
|
||||||
|
);
|
||||||
|
require(_exists(tokenId), "Must be valid tokenId");
|
||||||
|
require(lockedTokens[tokenId], "Token has already unlocked");
|
||||||
|
lockedTokens[tokenId] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Get lock status
|
||||||
|
*/
|
||||||
|
function isLocked(uint256 tokenId) public view returns (bool) {
|
||||||
|
return lockedTokens[tokenId];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Set token URI
|
||||||
|
*/
|
||||||
|
function updateBaseURI(string calldata baseTokenURI) public onlyOwner {
|
||||||
|
_baseTokenURI = baseTokenURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev See {IERC165-_beforeTokenTransfer}.
|
||||||
|
*/
|
||||||
|
function _beforeTokenTransfer(
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 tokenId
|
||||||
|
) internal virtual override(ERC721, ERC721Enumerable) {
|
||||||
|
require(!lockedTokens[tokenId], "Can not transfer locked token");
|
||||||
|
super._beforeTokenTransfer(from, to, tokenId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev See {IERC165-supportsInterface}.
|
||||||
|
*/
|
||||||
|
function supportsInterface(bytes4 interfaceId)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
virtual
|
||||||
|
override(AccessControlEnumerable, ERC721, ERC721Enumerable)
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
return super.supportsInterface(interfaceId);
|
||||||
|
}
|
||||||
|
}
|
@ -1,302 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
pragma solidity ^0.8.0;
|
|
||||||
|
|
||||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
||||||
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
|
|
||||||
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
|
|
||||||
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
|
||||||
|
|
||||||
contract CryptoHero is Ownable, IERC721 {
|
|
||||||
using SafeMath for uint256;
|
|
||||||
|
|
||||||
uint256 public constant maxGen0Hero = 10;
|
|
||||||
|
|
||||||
uint256 public gen0Counter = 0;
|
|
||||||
|
|
||||||
uint256 initHeroPrice = 0.01 ether;
|
|
||||||
|
|
||||||
bytes4 internal constant _ERC721Checksum = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
|
|
||||||
|
|
||||||
bytes4 private constant _InterfaceIdERC721 = 0x80ac58cd;
|
|
||||||
bytes4 private constant _InterfaceIdERC165 = 0x01ffc9a7;
|
|
||||||
|
|
||||||
string private _name;
|
|
||||||
string private _symbol;
|
|
||||||
|
|
||||||
struct Hero {
|
|
||||||
uint256 genes;
|
|
||||||
uint64 birthTime;
|
|
||||||
uint32 mumId;
|
|
||||||
uint32 dadId;
|
|
||||||
uint16 generation;
|
|
||||||
}
|
|
||||||
Hero[] heros;
|
|
||||||
// map of hero and owner
|
|
||||||
mapping(uint256 => address) public heroOwner;
|
|
||||||
// hero count of owner
|
|
||||||
mapping(address => uint256) ownsNumberOfTokens;
|
|
||||||
// hero in trading
|
|
||||||
mapping(uint256 => address) public approvalOneHero;
|
|
||||||
|
|
||||||
mapping(address => mapping (address => bool)) private _operatorApprovals;
|
|
||||||
|
|
||||||
event HeroBirth(address owner, uint256 _heroId, uint256 mumId, uint256 dadId, uint256 genes);
|
|
||||||
event HeroEvolved(uint256 indexed _heroId, uint256 _oldGenes, uint256 _newGenes);
|
|
||||||
event HeroRetired(uint256 indexed _heroId);
|
|
||||||
|
|
||||||
constructor(string memory dname, string memory dsymbol) {
|
|
||||||
_name = dname;
|
|
||||||
_symbol = dsymbol;
|
|
||||||
_createHero(0, 0, 0, uint256(0), address(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
function getContractOwner() external view returns (address contractowner) {
|
|
||||||
return owner();
|
|
||||||
}
|
|
||||||
|
|
||||||
function supportsInterface(bytes4 _interfaceId) external pure returns (bool) {
|
|
||||||
return (_interfaceId == _InterfaceIdERC721 || _interfaceId == _InterfaceIdERC165);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* for player buy init heros
|
|
||||||
*/
|
|
||||||
function buyInitHero(uint256 genes) public payable returns (uint256) {
|
|
||||||
require(gen0Counter < maxGen0Hero, "Maximum number of Heros is reached. No new hero allowed!");
|
|
||||||
require(msg.value >= initHeroPrice, "not enough money!");
|
|
||||||
gen0Counter = SafeMath.add(gen0Counter, 1);
|
|
||||||
return _createHero(0, 0, 0, genes, msg.sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setInitHeroPrice(uint _price) external onlyOwner {
|
|
||||||
initHeroPrice = _price;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _createHero(
|
|
||||||
uint256 _mumId,
|
|
||||||
uint256 _dadId,
|
|
||||||
uint256 _generation,
|
|
||||||
uint256 _genes,
|
|
||||||
address _owner
|
|
||||||
) internal returns (uint256) {
|
|
||||||
Hero memory _hero = Hero({
|
|
||||||
genes: _genes,
|
|
||||||
birthTime: uint64(block.timestamp),
|
|
||||||
mumId: uint32(_mumId), //easier to input 256 and later convert to 32.
|
|
||||||
dadId: uint32(_dadId),
|
|
||||||
generation: uint16(_generation)
|
|
||||||
});
|
|
||||||
heros.push(_hero);
|
|
||||||
uint256 newHeroId = SafeMath.sub(heros.length, 1);//want to start with zero.
|
|
||||||
_transfer(address(0), _owner, newHeroId);//transfer from nowhere. Creation event.
|
|
||||||
emit HeroBirth(_owner, newHeroId, _mumId, _dadId, _genes);
|
|
||||||
return newHeroId;
|
|
||||||
}
|
|
||||||
|
|
||||||
function retireHero(uint256 heroId) internal {
|
|
||||||
delete heros[heroId];
|
|
||||||
emit HeroRetired(heroId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function evolveHero(uint256 _heroId, uint256 _newGenes) internal {
|
|
||||||
uint256 _oldGenes = heros[_heroId].genes;
|
|
||||||
heros[_heroId].genes = _newGenes;
|
|
||||||
emit HeroEvolved(_heroId, _oldGenes, _newGenes);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHero(uint256 tokenId) public view returns (
|
|
||||||
uint256 genes,
|
|
||||||
uint256 birthTime,
|
|
||||||
uint256 mumId,
|
|
||||||
uint256 dadId,
|
|
||||||
uint256 generation) //code looks cleaner when the params appear here vs. in the return statement.
|
|
||||||
{
|
|
||||||
require(tokenId < heros.length, "Token ID doesn't exist.");
|
|
||||||
Hero storage hero = heros[tokenId];//saves space over using memory, which would make a copy
|
|
||||||
|
|
||||||
genes = hero.genes;
|
|
||||||
birthTime = uint256(hero.birthTime);
|
|
||||||
mumId = uint256(hero.mumId);
|
|
||||||
dadId = uint256(hero.dadId);
|
|
||||||
generation = uint256(hero.generation);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAllHerosOfOwner(address owner) external view returns(uint256[] memory) {
|
|
||||||
uint256[] memory allHerosOfOwner = new uint[](ownsNumberOfTokens[owner]);
|
|
||||||
uint256 j = 0;
|
|
||||||
for (uint256 i = 0; i < heros.length; i++) {
|
|
||||||
if (heroOwner[i] == owner) {
|
|
||||||
allHerosOfOwner[j] = i;
|
|
||||||
j = SafeMath.add(j, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return allHerosOfOwner;
|
|
||||||
}
|
|
||||||
|
|
||||||
function balanceOf(address owner) external view returns (uint256 balance) {
|
|
||||||
return ownsNumberOfTokens[owner];
|
|
||||||
}
|
|
||||||
|
|
||||||
function totalSupply() external view returns (uint256 total) {
|
|
||||||
return heros.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
function name() external view returns (string memory tokenName){
|
|
||||||
return _name;
|
|
||||||
}
|
|
||||||
|
|
||||||
function symbol() external view returns (string memory tokenSymbol){
|
|
||||||
return _symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ownerOf(uint256 tokenId) external view returns (address owner) {
|
|
||||||
require(tokenId < heros.length, "Token ID doesn't exist.");
|
|
||||||
return heroOwner[tokenId];
|
|
||||||
}
|
|
||||||
|
|
||||||
function transfer(address to, uint256 tokenId) external {
|
|
||||||
require(to != address(0), "Use the burn function to burn tokens!");
|
|
||||||
require(to != address(this), "Wrong address, try again!");
|
|
||||||
require(heroOwner[tokenId] == msg.sender);
|
|
||||||
_transfer(msg.sender, to, tokenId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _transfer(address _from, address _to, uint256 _tokenId) internal {
|
|
||||||
require(_to != address(this));
|
|
||||||
ownsNumberOfTokens[_to] = SafeMath.add(ownsNumberOfTokens[_to], 1);
|
|
||||||
heroOwner[_tokenId] = _to;
|
|
||||||
|
|
||||||
if (_from != address(0)) {
|
|
||||||
ownsNumberOfTokens[_from] = SafeMath.sub(ownsNumberOfTokens[_from], 1);
|
|
||||||
delete approvalOneHero[_tokenId];//when owner changes, approval must be removed.
|
|
||||||
}
|
|
||||||
|
|
||||||
emit Transfer(_from, _to, _tokenId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function approve(address _approved, uint256 _tokenId) external {
|
|
||||||
require(heroOwner[_tokenId] == msg.sender || _operatorApprovals[heroOwner[_tokenId]][msg.sender] == true,
|
|
||||||
"You are not authorized to access this function.");
|
|
||||||
approvalOneHero[_tokenId] = _approved;
|
|
||||||
emit Approval(msg.sender, _approved, _tokenId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setApprovalForAll(address _operator, bool _approved) external {
|
|
||||||
require(_operator != msg.sender);
|
|
||||||
_operatorApprovals[msg.sender][_operator] = _approved;
|
|
||||||
emit ApprovalForAll(msg.sender, _operator, _approved);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getApproved(uint256 _tokenId) external view returns (address) {
|
|
||||||
require(_tokenId < heros.length, "Token doesn't exist");
|
|
||||||
return approvalOneHero[_tokenId];
|
|
||||||
}
|
|
||||||
|
|
||||||
function isApprovedForAll(address _owner, address _operator) external view returns (bool) {
|
|
||||||
return _operatorApprovals[_owner][_operator];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function _safeTransfer(address _from, address _to, uint256 _tokenId, bytes memory _data) internal {
|
|
||||||
require(_checkERC721Support(_from, _to, _tokenId, _data));
|
|
||||||
_transfer(_from, _to, _tokenId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _checkERC721Support(address _from, address _to, uint256 _tokenId, bytes memory _data)
|
|
||||||
internal returns(bool) {
|
|
||||||
if(!_isContract(_to)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bytes4 returnData = IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
|
|
||||||
//Call onERC721Received in the _to contract
|
|
||||||
return returnData == _ERC721Checksum;
|
|
||||||
//Check return value
|
|
||||||
}
|
|
||||||
|
|
||||||
function _isContract(address _to) internal view returns (bool) {
|
|
||||||
uint32 size;
|
|
||||||
assembly{
|
|
||||||
size := extcodesize(_to)
|
|
||||||
}
|
|
||||||
return size > 0;
|
|
||||||
//check if code size > 0; wallets have 0 size.
|
|
||||||
}
|
|
||||||
|
|
||||||
function _isOwnerOrApproved(address _from, address _to, uint256 _tokenId) internal view returns (bool) {
|
|
||||||
require(_from == msg.sender ||
|
|
||||||
approvalOneHero[_tokenId] == msg.sender ||
|
|
||||||
_operatorApprovals[_from][msg.sender],
|
|
||||||
"You are not authorized to use this function");
|
|
||||||
require(heroOwner[_tokenId] == _from, "Owner incorrect");
|
|
||||||
require(_to != address(0), "Error: Operation would delete this token permanently");
|
|
||||||
require(_tokenId < heros.length, "Token doesn't exist");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external {
|
|
||||||
_isOwnerOrApproved(_from, _to, _tokenId);
|
|
||||||
_safeTransfer(_from, _to, _tokenId, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external {
|
|
||||||
_isOwnerOrApproved(_from, _to, _tokenId);
|
|
||||||
_safeTransfer(_from, _to, _tokenId, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
function transferFrom(address _from, address _to, uint256 _tokenId) external {
|
|
||||||
_isOwnerOrApproved(_from, _to, _tokenId);
|
|
||||||
_transfer(_from, _to, _tokenId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _mixDna(
|
|
||||||
uint256 _dadDna,
|
|
||||||
uint256 _mumDna,
|
|
||||||
uint8 random,
|
|
||||||
uint8 randomSeventeenthDigit,
|
|
||||||
uint8 randomPair,
|
|
||||||
uint8 randomNumberForRandomPair
|
|
||||||
) internal pure returns (uint256){
|
|
||||||
|
|
||||||
uint256[9] memory geneArray;
|
|
||||||
uint256 i;
|
|
||||||
uint256 counter = 7; // start on the right end
|
|
||||||
|
|
||||||
//DNA example: 11 22 33 44 55 66 77 88 9
|
|
||||||
|
|
||||||
if(randomSeventeenthDigit == 0){
|
|
||||||
geneArray[8] = uint8(_mumDna % 10); //this takes the 17th gene from mum.
|
|
||||||
} else {
|
|
||||||
geneArray[8] = uint8(_dadDna % 10); //this takes the 17th gene from dad.
|
|
||||||
}
|
|
||||||
|
|
||||||
_mumDna = SafeMath.div(_mumDna, 10); // division by 10 removes the last digit
|
|
||||||
_dadDna = SafeMath.div(_dadDna, 10); // division by 10 removes the last digit
|
|
||||||
|
|
||||||
for (i = 1; i <= 128; i=i*2) { //1, 2 , 4, 8, 16, 32, 64 ,128
|
|
||||||
if(random & i == 0){ //00000001
|
|
||||||
geneArray[counter] = uint8(_mumDna % 100); //00000010 etc.
|
|
||||||
} else { //11001011 &
|
|
||||||
geneArray[counter] = uint8(_dadDna % 100); //00000001 will go through random number bitwise
|
|
||||||
} //if(1) - dad gene
|
|
||||||
_mumDna = SafeMath.div(_mumDna, 100); //if(0) - mum gene
|
|
||||||
_dadDna = SafeMath.div(_dadDna, 100); //division by 100 removes last two digits from genes
|
|
||||||
if(counter > 0) {
|
|
||||||
counter = SafeMath.sub(counter, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
geneArray[randomPair] = randomNumberForRandomPair; //extra randomness for random pair.
|
|
||||||
|
|
||||||
uint256 newGene = 0;
|
|
||||||
|
|
||||||
//geneArray example: [11, 22, 33, 44, 55, 66, 77, 88, 9]
|
|
||||||
|
|
||||||
for (i = 0; i < 8; i++) { //8 is number of pairs in array
|
|
||||||
newGene = SafeMath.mul(newGene, 100); //adds two digits to newGene; no digits the first time
|
|
||||||
newGene = SafeMath.add(newGene, geneArray[i]); //adds a pair of genes
|
|
||||||
}
|
|
||||||
newGene = SafeMath.mul(newGene, 10); //add seventeenth digit
|
|
||||||
newGene = SafeMath.add(newGene, geneArray[8]);
|
|
||||||
return newGene;
|
|
||||||
}
|
|
||||||
}
|
|
63
contracts/HasSignature.sol
Normal file
63
contracts/HasSignature.sol
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
contract HasSignature {
|
||||||
|
function getEthSignedMessageHash(bytes32 _messageHash)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (bytes32)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Signature is produced by signing a keccak256 hash with the following format:
|
||||||
|
"\x19Ethereum Signed Message\n" + len(msg) + msg
|
||||||
|
*/
|
||||||
|
return
|
||||||
|
keccak256(
|
||||||
|
abi.encodePacked(
|
||||||
|
"\x19Ethereum Signed Message:\n32",
|
||||||
|
_messageHash
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function recoverSigner(
|
||||||
|
bytes32 _ethSignedMessageHash,
|
||||||
|
bytes memory _signature
|
||||||
|
) public pure returns (address) {
|
||||||
|
(bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);
|
||||||
|
|
||||||
|
return ecrecover(_ethSignedMessageHash, v, r, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitSignature(bytes memory sig)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (
|
||||||
|
bytes32 r,
|
||||||
|
bytes32 s,
|
||||||
|
uint8 v
|
||||||
|
)
|
||||||
|
{
|
||||||
|
require(sig.length == 65, "invalid signature length");
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
/*
|
||||||
|
First 32 bytes stores the length of the signature
|
||||||
|
|
||||||
|
add(sig, 32) = pointer of sig + 32
|
||||||
|
effectively, skips first 32 bytes of signature
|
||||||
|
|
||||||
|
mload(p) loads next 32 bytes starting at the memory address p into memory
|
||||||
|
*/
|
||||||
|
|
||||||
|
// first 32 bytes, after the length prefix
|
||||||
|
r := mload(add(sig, 32))
|
||||||
|
// second 32 bytes
|
||||||
|
s := mload(add(sig, 64))
|
||||||
|
// final byte (first byte of the next 32 bytes)
|
||||||
|
v := byte(0, mload(add(sig, 96)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// implicitly return (r, s, v)
|
||||||
|
}
|
||||||
|
}
|
@ -1,83 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
pragma solidity ^0.8.0;
|
|
||||||
|
|
||||||
import "./CryptoHero.sol";
|
|
||||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Market place to trade heros (should **in theory** be used for any ERC721 token)
|
|
||||||
* It needs an existing hero contract to interact with
|
|
||||||
* Note: it does not inherit from the contract
|
|
||||||
* Note: It takes ownership of the hero for the duration that is is on the marketplace.
|
|
||||||
*/
|
|
||||||
interface IMarketPlace {
|
|
||||||
|
|
||||||
event MarketTransaction(string TxType, address owner, uint256 tokenId);
|
|
||||||
event MonetaryTransaction(string message, address recipient, uint256 amount);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the current contract address and initialize the instance of the contract.
|
|
||||||
* Requirement: Only the contract owner can call.
|
|
||||||
*/
|
|
||||||
function setContract(address _contractAddress) external;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets status of _paused to true which affects all functions that have whenNotPaused modifiers.
|
|
||||||
*/
|
|
||||||
function pause() external;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets status of _paused to false which affects all functions that have whenNotPaused modifiers.
|
|
||||||
*/
|
|
||||||
function resume() external;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the details about a offer for _tokenId. Throws an error if there is no active offer for _tokenId.
|
|
||||||
*/
|
|
||||||
function getOffer(uint256 _tokenId) external view returns (address seller, uint256 price, uint256 index, uint256 tokenId, bool active);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all tokenId's that are currently for sale. Returns an empty array if no offer exists.
|
|
||||||
*/
|
|
||||||
function getAllTokensOnSale() external view returns (uint256[] memory listOfOffers);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new offer for _tokenId for the price _price.
|
|
||||||
* Emits the MarketTransaction event with txType "Create offer"
|
|
||||||
* Requirement: Only the owner of _tokenId can create an offer.
|
|
||||||
* Requirement: There can only be one active offer for a token at a time.
|
|
||||||
* Requirement: Marketplace contract (this) needs to be an approved operator when the offer is created.
|
|
||||||
*/
|
|
||||||
function setOffer(uint256 _price, uint256 _tokenId) external;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes an existing offer.
|
|
||||||
* Emits the MarketTransaction event with txType "Remove offer"
|
|
||||||
* Requirement: Only the seller of _tokenId can remove an offer.
|
|
||||||
*/
|
|
||||||
function removeOffer(uint256 _tokenId) external;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the purchase of _tokenId.
|
|
||||||
* Transfers the token using transferFrom in CryptoHero.
|
|
||||||
* Transfers funds to the _fundsToBeCollected mapping.
|
|
||||||
* Removes the offer from the mapping.
|
|
||||||
* Sets the offer in the array to inactive.
|
|
||||||
* Emits the MarketTransaction event with txType "Buy".
|
|
||||||
* Requirement: The msg.value needs to equal the price of _tokenId
|
|
||||||
* Requirement: There must be an active offer for _tokenId
|
|
||||||
*/
|
|
||||||
function buyHero(uint256 _tokenId) external payable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns current balance of msg.sender
|
|
||||||
*/
|
|
||||||
function getBalance() external view returns (uint256);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send funds to msg.sender.
|
|
||||||
* Emits a MonetaryTransaction event "Successful Transfer".
|
|
||||||
* Requirement: msg.sender must have funds in the mapping.
|
|
||||||
*/
|
|
||||||
function withdrawFunds() external payable;
|
|
||||||
}
|
|
@ -1,194 +1,211 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
pragma solidity ^0.8.0;
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
|
||||||
|
import "@openzeppelin/contracts/utils/math/Math.sol";
|
||||||
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
|
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
|
||||||
import "./CryptoHero.sol";
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
import "./IMarketplace.sol";
|
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||||
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||||
/*
|
import "./HasSignature.sol";
|
||||||
* Market place to trade heros (should **in theory** be used for any ERC721 token)
|
|
||||||
* It needs an existing hero contract to interact with
|
|
||||||
* Note: it does not inherit from the contract
|
|
||||||
* Note: It takes ownership of the hero for the duration that is is on the marketplace.
|
|
||||||
*/
|
|
||||||
|
|
||||||
contract MarketPlace is Ownable, IMarketPlace {
|
|
||||||
CryptoHero private _cryptoHero;
|
|
||||||
|
|
||||||
|
contract Marketplace is Ownable, HasSignature {
|
||||||
|
using SafeERC20 for IERC20;
|
||||||
using SafeMath for uint256;
|
using SafeMath for uint256;
|
||||||
|
|
||||||
struct Offer {
|
// Supported payment token WETH & list of authorized ERC20
|
||||||
address payable seller;
|
mapping(address => bool) public paymentTokens;
|
||||||
uint256 price;
|
mapping(bytes => bool) public usedSignatures;
|
||||||
uint256 index;
|
|
||||||
uint256 tokenId;
|
// Address to receive transaction fee
|
||||||
bool active;
|
address public feeToAddress;
|
||||||
|
uint256 public transactionFee;
|
||||||
|
|
||||||
|
// Events
|
||||||
|
event MatchTransaction(
|
||||||
|
uint256 indexed tokenId,
|
||||||
|
address contractAddress,
|
||||||
|
uint256 price,
|
||||||
|
address paymentToken,
|
||||||
|
address seller,
|
||||||
|
address buyer,
|
||||||
|
uint256 fee
|
||||||
|
);
|
||||||
|
|
||||||
|
function setFeeToAddress(address _feeToAddress) public onlyOwner {
|
||||||
|
feeToAddress = _feeToAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool internal _paused;
|
function setTransactionFee(uint256 _transactionFee) public onlyOwner {
|
||||||
|
transactionFee = _transactionFee;
|
||||||
Offer[] offers;
|
|
||||||
|
|
||||||
mapping(uint256 => Offer) tokenIdToOffer;
|
|
||||||
mapping(address => uint256) internal _fundsToBeCollected;
|
|
||||||
|
|
||||||
//Contract has one event that is already declared in the interface.
|
|
||||||
|
|
||||||
//Contract can be paused by owner to ensure bugs can be fixed after deployment
|
|
||||||
modifier whenNotPaused() {
|
|
||||||
require(!_paused);
|
|
||||||
_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
modifier whenPaused() {
|
function setPaymentTokens(address[] calldata _paymentTokens)
|
||||||
require(_paused);
|
public
|
||||||
_;
|
onlyOwner
|
||||||
}
|
{
|
||||||
|
for (uint256 i = 0; i < _paymentTokens.length; i++) {
|
||||||
function setContract(address _contractAddress) onlyOwner public {
|
if (paymentTokens[_paymentTokens[i]] == true) {
|
||||||
_cryptoHero = CryptoHero(_contractAddress);
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
constructor(address _contractAddress) {
|
|
||||||
setContract(_contractAddress);
|
|
||||||
_paused = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function pause() public onlyOwner whenNotPaused {
|
|
||||||
_paused = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resume() public onlyOwner whenPaused {
|
|
||||||
_paused = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isPaused() public view returns (bool) {
|
|
||||||
return _paused;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getOffer(uint256 _tokenId) public view returns (
|
|
||||||
address seller,
|
|
||||||
uint256 price,
|
|
||||||
uint256 index,
|
|
||||||
uint256 tokenId,
|
|
||||||
bool active) {
|
|
||||||
|
|
||||||
require(tokenIdToOffer[_tokenId].active == true, "No active offer at this time");
|
|
||||||
|
|
||||||
return (tokenIdToOffer[_tokenId].seller,
|
|
||||||
tokenIdToOffer[_tokenId].price,
|
|
||||||
tokenIdToOffer[_tokenId].index,
|
|
||||||
tokenIdToOffer[_tokenId].tokenId,
|
|
||||||
tokenIdToOffer[_tokenId].active);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAllTokensOnSale() public view returns (uint256[] memory listOfOffers) {
|
|
||||||
uint256 resultId = 0;//index for all heros with active offer status (true)
|
|
||||||
|
|
||||||
for (uint256 index = 0; index < offers.length; index++) {
|
|
||||||
if (offers[index].active == true) {
|
|
||||||
resultId = SafeMath.add(resultId, 1);//determine length of array to return
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
paymentTokens[_paymentTokens[i]] = true;
|
||||||
if (offers.length == 0) {
|
|
||||||
return new uint256[](0);//returns empty array
|
|
||||||
} else {
|
|
||||||
uint256[] memory allTokensOnSale = new uint256[](resultId);
|
|
||||||
//initialize new array with correct length
|
|
||||||
resultId = 0;//reset index of new array
|
|
||||||
for (uint256 index = 0; index < offers.length; index++) {//iterate through entire offers array
|
|
||||||
if (offers[index].active == true) {
|
|
||||||
allTokensOnSale[resultId] = offers[index].tokenId;
|
|
||||||
resultId = SafeMath.add(resultId, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return allTokensOnSale;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _ownsHero(address _address, uint256 _tokenId) internal view returns (bool) {
|
function removePaymentTokens(address[] calldata _removedPaymentTokens)
|
||||||
return (_cryptoHero.ownerOf(_tokenId) == _address);
|
public
|
||||||
|
onlyOwner
|
||||||
|
{
|
||||||
|
for (uint256 i = 0; i < _removedPaymentTokens.length; i++) {
|
||||||
|
paymentTokens[_removedPaymentTokens[i]] = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setOffer(uint256 _price, uint256 _tokenId) public {
|
function ignoreSignature(
|
||||||
require(_ownsHero(msg.sender, _tokenId),
|
address[2] calldata addresses,
|
||||||
"Only the owner of the hero can initialize an offer");
|
uint256[3] calldata values,
|
||||||
require(tokenIdToOffer[_tokenId].active == false,
|
bytes calldata signature
|
||||||
"You already created an offer for this hero. Please remove it first before creating a new one.");
|
) external {
|
||||||
require(_cryptoHero.isApprovedForAll(msg.sender, address(this)),
|
require(
|
||||||
"MarketPlace contract must first be an approved operator for your heros");
|
!usedSignatures[signature],
|
||||||
|
"Marketplace: this signature has added to ignore list already"
|
||||||
|
);
|
||||||
|
bytes32 criteriaMessageHash = getMessageHash(
|
||||||
|
addresses[0],
|
||||||
|
values[0],
|
||||||
|
addresses[1],
|
||||||
|
values[1],
|
||||||
|
values[2]
|
||||||
|
);
|
||||||
|
|
||||||
Offer memory _currentOffer = Offer({//set offer
|
bytes32 ethSignedMessageHash = getEthSignedMessageHash(
|
||||||
seller: payable(msg.sender),
|
criteriaMessageHash
|
||||||
price: _price,
|
);
|
||||||
index: offers.length,
|
|
||||||
tokenId: _tokenId,
|
|
||||||
active: true
|
|
||||||
});
|
|
||||||
|
|
||||||
tokenIdToOffer[_tokenId] = _currentOffer;//update mapping
|
require(
|
||||||
offers.push(_currentOffer);//update array
|
recoverSigner(ethSignedMessageHash, signature) == _msgSender(),
|
||||||
|
"Marketplace: invalid signature"
|
||||||
|
);
|
||||||
|
|
||||||
emit MarketTransaction("Offer created", msg.sender, _tokenId);
|
usedSignatures[signature] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeOffer(uint256 _tokenId) public {
|
/**
|
||||||
require(tokenIdToOffer[_tokenId].seller == msg.sender,
|
* @dev Function matched transaction with user signatures
|
||||||
"Only the owner of the hero can withdraw the offer.");
|
*/
|
||||||
|
function matchTransaction(
|
||||||
|
address[3] calldata addresses,
|
||||||
|
uint256[3] calldata values,
|
||||||
|
bytes calldata signature
|
||||||
|
) external returns (bool) {
|
||||||
|
// address[3] [seller_address,nft_address,payment_token_address]
|
||||||
|
// uint256[3] [token_id,price,salt_nonce]
|
||||||
|
// bytes seller_signature
|
||||||
|
require(
|
||||||
|
paymentTokens[addresses[2]] == true,
|
||||||
|
"Marketplace: invalid payment method"
|
||||||
|
);
|
||||||
|
|
||||||
offers[tokenIdToOffer[_tokenId].index].active = false;
|
require(
|
||||||
//don't iterate through array, simply set active to false.
|
!usedSignatures[signature],
|
||||||
delete tokenIdToOffer[_tokenId];//delete entry in mapping
|
"Marketplace: signature used. please send another transaction with new signature"
|
||||||
|
);
|
||||||
|
|
||||||
emit MarketTransaction("Offer removed", msg.sender, _tokenId);
|
bytes32 criteriaMessageHash = getMessageHash(
|
||||||
}
|
addresses[1],
|
||||||
|
values[0],
|
||||||
|
addresses[2],
|
||||||
|
values[1],
|
||||||
|
values[2]
|
||||||
|
);
|
||||||
|
|
||||||
function buyHero(uint256 _tokenId) public payable whenNotPaused{
|
bytes32 ethSignedMessageHash = getEthSignedMessageHash(
|
||||||
Offer memory _currentOffer = tokenIdToOffer[_tokenId];
|
criteriaMessageHash
|
||||||
|
);
|
||||||
|
|
||||||
//checks
|
require(
|
||||||
require(_currentOffer.active, "There is no active offer for this hero");
|
recoverSigner(ethSignedMessageHash, signature) == addresses[0],
|
||||||
require(msg.value == _currentOffer.price, "The amount offered is not equal to the requested amount");
|
"Marketplace: invalid seller signature"
|
||||||
|
);
|
||||||
|
|
||||||
//effects
|
// check current ownership
|
||||||
delete tokenIdToOffer[_tokenId];//delete entry in mapping
|
IERC721 nft = IERC721(addresses[1]);
|
||||||
offers[_currentOffer.index].active = false;//don't iterate through array, but simply set active to false.
|
require(
|
||||||
|
nft.ownerOf(values[0]) == addresses[0],
|
||||||
|
"Marketplace: seller is not owner of this item now"
|
||||||
|
);
|
||||||
|
|
||||||
//interactions
|
// Check payment approval and buyer balance
|
||||||
if (_currentOffer.price > 0) {
|
IERC20 paymentContract = IERC20(addresses[2]);
|
||||||
_fundsToBeCollected[_currentOffer.seller] =
|
require(
|
||||||
SafeMath.add(_fundsToBeCollected[_currentOffer.seller], _currentOffer.price);
|
paymentContract.balanceOf(_msgSender()) >= values[1],
|
||||||
//instead of sending money to seller it is deposited in a mapping waiting for seller to pull.
|
"Marketplace: buyer doesn't have enough token to buy this item"
|
||||||
|
);
|
||||||
|
require(
|
||||||
|
paymentContract.allowance(_msgSender(), address(this)) >= values[1],
|
||||||
|
"Marketplace: buyer doesn't approve marketplace to spend payment amount"
|
||||||
|
);
|
||||||
|
|
||||||
|
// We divide by 10000 to support decimal value such as 4.25% => 425 / 10000
|
||||||
|
uint256 fee = transactionFee.mul(values[1]).div(10000);
|
||||||
|
uint256 payToSellerAmount = values[1].sub(fee);
|
||||||
|
|
||||||
|
// transfer money to seller
|
||||||
|
paymentContract.safeTransferFrom(
|
||||||
|
_msgSender(),
|
||||||
|
addresses[0],
|
||||||
|
payToSellerAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
// transfer fee to address
|
||||||
|
if (fee > 0) {
|
||||||
|
paymentContract.safeTransferFrom(_msgSender(), feeToAddress, fee);
|
||||||
}
|
}
|
||||||
|
|
||||||
_cryptoHero.transferFrom(_currentOffer.seller, msg.sender, _tokenId);//ERC721 ownership transferred
|
// transfer item to buyer
|
||||||
|
nft.safeTransferFrom(addresses[0], _msgSender(), values[0]);
|
||||||
|
|
||||||
emit MarketTransaction("Hero successfully purchased", msg.sender, _tokenId);
|
usedSignatures[signature] = true;
|
||||||
|
// emit sale event
|
||||||
|
emitEvent(addresses, values);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBalance() public view returns (uint256) {
|
/**
|
||||||
return _fundsToBeCollected[msg.sender];
|
* @dev Function to emit transaction matched event
|
||||||
|
*/
|
||||||
|
function emitEvent(
|
||||||
|
address[3] calldata addresses,
|
||||||
|
uint256[3] calldata values
|
||||||
|
) internal {
|
||||||
|
emit MatchTransaction(
|
||||||
|
values[0],
|
||||||
|
addresses[1],
|
||||||
|
values[1],
|
||||||
|
addresses[2],
|
||||||
|
addresses[0],
|
||||||
|
_msgSender(),
|
||||||
|
transactionFee
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function withdrawFunds() public payable whenNotPaused{
|
function getMessageHash(
|
||||||
|
address _nftAddress,
|
||||||
//check
|
uint256 _tokenId,
|
||||||
require(_fundsToBeCollected[msg.sender] > 0, "No funds available at this time");
|
address _paymentErc20,
|
||||||
|
uint256 _price,
|
||||||
uint256 toWithdraw = _fundsToBeCollected[msg.sender];
|
uint256 _saltNonce
|
||||||
|
) public pure returns (bytes32) {
|
||||||
//effect
|
return
|
||||||
_fundsToBeCollected[msg.sender] = 0;
|
keccak256(
|
||||||
|
abi.encodePacked(
|
||||||
//interaction
|
_nftAddress,
|
||||||
payable(msg.sender).transfer(toWithdraw);
|
_tokenId,
|
||||||
|
_paymentErc20,
|
||||||
//making sure transfer executed correctly
|
_price,
|
||||||
assert(_fundsToBeCollected[msg.sender] == 0);
|
_saltNonce
|
||||||
|
)
|
||||||
//emit event
|
);
|
||||||
emit MonetaryTransaction("Funds successfully received", msg.sender, toWithdraw);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
41
contracts/MinterFactory.sol
Normal file
41
contracts/MinterFactory.sol
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||||
|
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
|
||||||
|
|
||||||
|
interface IMintableERC721 is IERC721 {
|
||||||
|
function mint(address to, uint256 tokenId) external;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract MinterFactory is Ownable, Initializable {
|
||||||
|
// NFT contract
|
||||||
|
IMintableERC721 public erc721;
|
||||||
|
bool public publicMintAllowed;
|
||||||
|
|
||||||
|
event TokenMinted(
|
||||||
|
address contractAddress,
|
||||||
|
address to,
|
||||||
|
uint256 indexed tokenId
|
||||||
|
);
|
||||||
|
|
||||||
|
function init(address _erc721) external initializer onlyOwner {
|
||||||
|
erc721 = IMintableERC721(_erc721);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev mint function to distribute Blissful Elites NFT to user
|
||||||
|
*/
|
||||||
|
function mintTo(address to, uint256 tokenId) external {
|
||||||
|
require(publicMintAllowed || _msgSender() == owner());
|
||||||
|
erc721.mint(to, tokenId);
|
||||||
|
emit TokenMinted(address(erc721), to, tokenId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev function to allow user mint items
|
||||||
|
*/
|
||||||
|
function allowPublicMint() public onlyOwner {
|
||||||
|
publicMintAllowed = true;
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
const Hero = artifacts.require('CryptoHero');
|
const Hero = artifacts.require('BEHero');
|
||||||
|
|
||||||
module.exports = async function (deployer) {
|
module.exports = async function (deployer) {
|
||||||
await deployer.deploy(Hero, "CryptoHero", "JC");
|
await deployer.deploy(Hero, "BEHero", "JC");
|
||||||
const instance = await Hero.deployed();
|
const instance = await Hero.deployed();
|
||||||
if(instance) {
|
if(instance) {
|
||||||
console.log("CryptoHero successfully deployed.")
|
console.log("BEHero successfully deployed.")
|
||||||
}
|
}
|
||||||
};
|
};
|
Loading…
x
Reference in New Issue
Block a user