使用prettier格式化代码
This commit is contained in:
parent
cbf5712881
commit
69681b8106
15
.prettierrc.json
Normal file
15
.prettierrc.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.sol",
|
||||
"options": {
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"singleQuote": false,
|
||||
"bracketSpacing": false,
|
||||
"explicitTypes": "always"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -3,7 +3,7 @@ pragma solidity 0.8.10;
|
||||
|
||||
contract Migrations {
|
||||
address public owner = msg.sender;
|
||||
uint public last_completed_migration;
|
||||
uint256 public last_completed_migration;
|
||||
|
||||
modifier restricted() {
|
||||
require(
|
||||
@ -13,7 +13,7 @@ contract Migrations {
|
||||
_;
|
||||
}
|
||||
|
||||
function setCompleted(uint completed) external restricted {
|
||||
function setCompleted(uint256 completed) external restricted {
|
||||
last_completed_migration = completed;
|
||||
}
|
||||
|
||||
|
@ -7,39 +7,51 @@ contract BETimelockController is TimelockController {
|
||||
uint256 public constant MAX_DELAY = 16 days;
|
||||
uint256 private _minDelay;
|
||||
|
||||
constructor(
|
||||
address[] memory proposers,
|
||||
address[] memory executors)
|
||||
TimelockController(MIN_DELAY, proposers, executors){
|
||||
_minDelay = MIN_DELAY;
|
||||
constructor(address[] memory proposers, address[] memory executors)
|
||||
TimelockController(MIN_DELAY, proposers, executors)
|
||||
{
|
||||
_minDelay = MIN_DELAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the minimum delay for an operation to become valid.
|
||||
*
|
||||
* This value can be changed by executing an operation that calls `updateDelay`.
|
||||
*/
|
||||
function getMinDelay()
|
||||
public
|
||||
view
|
||||
virtual
|
||||
override
|
||||
returns (uint256 duration)
|
||||
{
|
||||
return _minDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the minimum delay for an operation to become valid.
|
||||
*
|
||||
* This value can be changed by executing an operation that calls `updateDelay`.
|
||||
*/
|
||||
function getMinDelay() public view virtual override returns (uint256 duration) {
|
||||
return _minDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Changes the minimum timelock duration for future operations.
|
||||
*
|
||||
* Emits a {MinDelayChange} event.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - the caller must be the timelock itself. This can only be achieved by scheduling and later executing
|
||||
* an operation where the timelock is the target and the data is the ABI-encoded call to this function.
|
||||
*/
|
||||
function updateDelay(uint256 newDelay) external virtual override {
|
||||
require(msg.sender == address(this), "BETimelockController: caller must be timelock");
|
||||
require(newDelay >= MIN_DELAY, "BETimelockController: newDelay must greater than or equal to MIN_DELAY");
|
||||
require(newDelay <= MAX_DELAY, "BETimelockController: newDelay must less than or equal to MAX_DELAY");
|
||||
emit MinDelayChange(_minDelay, newDelay);
|
||||
_minDelay = newDelay;
|
||||
}
|
||||
* @dev Changes the minimum timelock duration for future operations.
|
||||
*
|
||||
* Emits a {MinDelayChange} event.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - the caller must be the timelock itself. This can only be achieved by scheduling and later executing
|
||||
* an operation where the timelock is the target and the data is the ABI-encoded call to this function.
|
||||
*/
|
||||
function updateDelay(uint256 newDelay) external virtual override {
|
||||
require(
|
||||
msg.sender == address(this),
|
||||
"BETimelockController: caller must be timelock"
|
||||
);
|
||||
require(
|
||||
newDelay >= MIN_DELAY,
|
||||
"BETimelockController: newDelay must greater than or equal to MIN_DELAY"
|
||||
);
|
||||
require(
|
||||
newDelay <= MAX_DELAY,
|
||||
"BETimelockController: newDelay must less than or equal to MAX_DELAY"
|
||||
);
|
||||
emit MinDelayChange(_minDelay, newDelay);
|
||||
_minDelay = newDelay;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,136 +4,123 @@ import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import "../utils/Approval.sol";
|
||||
|
||||
contract HasSignature is Ownable, Approval{
|
||||
bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
|
||||
uint256 private immutable _CACHED_CHAIN_ID;
|
||||
address private immutable _CACHED_THIS;
|
||||
contract HasSignature is Ownable, Approval {
|
||||
bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
|
||||
uint256 private immutable _CACHED_CHAIN_ID;
|
||||
address private immutable _CACHED_THIS;
|
||||
|
||||
bytes32 private immutable _HASHED_NAME;
|
||||
bytes32 private immutable _HASHED_VERSION;
|
||||
bytes32 private immutable _TYPE_HASH;
|
||||
mapping(bytes => bool) private _usedSignatures;
|
||||
bytes32 private immutable _HASHED_NAME;
|
||||
bytes32 private immutable _HASHED_VERSION;
|
||||
bytes32 private immutable _TYPE_HASH;
|
||||
mapping(bytes => bool) private _usedSignatures;
|
||||
|
||||
constructor(string memory name, string memory version) {
|
||||
bytes32 hashedName = keccak256(bytes(name));
|
||||
bytes32 hashedVersion = keccak256(bytes(version));
|
||||
bytes32 typeHash = keccak256(
|
||||
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
|
||||
);
|
||||
_HASHED_NAME = hashedName;
|
||||
_HASHED_VERSION = hashedVersion;
|
||||
_CACHED_CHAIN_ID = block.chainid;
|
||||
_CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(
|
||||
typeHash,
|
||||
hashedName,
|
||||
hashedVersion
|
||||
);
|
||||
_CACHED_THIS = address(this);
|
||||
_TYPE_HASH = typeHash;
|
||||
}
|
||||
constructor(string memory name, string memory version) {
|
||||
bytes32 hashedName = keccak256(bytes(name));
|
||||
bytes32 hashedVersion = keccak256(bytes(version));
|
||||
bytes32 typeHash = keccak256(
|
||||
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
|
||||
);
|
||||
_HASHED_NAME = hashedName;
|
||||
_HASHED_VERSION = hashedVersion;
|
||||
_CACHED_CHAIN_ID = block.chainid;
|
||||
_CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(
|
||||
typeHash,
|
||||
hashedName,
|
||||
hashedVersion
|
||||
);
|
||||
_CACHED_THIS = address(this);
|
||||
_TYPE_HASH = typeHash;
|
||||
}
|
||||
|
||||
function _buildDomainSeparator(
|
||||
bytes32 typeHash,
|
||||
bytes32 nameHash,
|
||||
bytes32 versionHash
|
||||
) private view returns (bytes32) {
|
||||
return
|
||||
keccak256(
|
||||
abi.encode(
|
||||
typeHash,
|
||||
nameHash,
|
||||
versionHash,
|
||||
block.chainid,
|
||||
address(this)
|
||||
)
|
||||
);
|
||||
}
|
||||
function _buildDomainSeparator(
|
||||
bytes32 typeHash,
|
||||
bytes32 nameHash,
|
||||
bytes32 versionHash
|
||||
) private view returns (bytes32) {
|
||||
return
|
||||
keccak256(
|
||||
abi.encode(
|
||||
typeHash,
|
||||
nameHash,
|
||||
versionHash,
|
||||
block.chainid,
|
||||
address(this)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the domain separator for the current chain.
|
||||
*/
|
||||
function _domainSeparatorV4() internal view returns (bytes32) {
|
||||
if (
|
||||
address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID
|
||||
) {
|
||||
return _CACHED_DOMAIN_SEPARATOR;
|
||||
} else {
|
||||
return
|
||||
_buildDomainSeparator(
|
||||
_TYPE_HASH,
|
||||
_HASHED_NAME,
|
||||
_HASHED_VERSION
|
||||
);
|
||||
}
|
||||
/**
|
||||
* @dev Returns the domain separator for the current chain.
|
||||
*/
|
||||
function _domainSeparatorV4() internal view returns (bytes32) {
|
||||
if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
|
||||
return _CACHED_DOMAIN_SEPARATOR;
|
||||
} else {
|
||||
return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
|
||||
* function returns the hash of the fully encoded EIP712 message for this domain.
|
||||
*
|
||||
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
|
||||
*
|
||||
* ```solidity
|
||||
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
|
||||
* keccak256("Mail(address to,string contents)"),
|
||||
* mailTo,
|
||||
* keccak256(bytes(mailContents))
|
||||
* )));
|
||||
* address signer = ECDSA.recover(digest, signature);
|
||||
* ```
|
||||
*/
|
||||
function _hashTypedDataV4(bytes32 structHash)
|
||||
internal
|
||||
view
|
||||
virtual
|
||||
returns (bytes32)
|
||||
{
|
||||
return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
|
||||
}
|
||||
/**
|
||||
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
|
||||
* function returns the hash of the fully encoded EIP712 message for this domain.
|
||||
*
|
||||
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
|
||||
*
|
||||
* ```solidity
|
||||
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
|
||||
* keccak256("Mail(address to,string contents)"),
|
||||
* mailTo,
|
||||
* keccak256(bytes(mailContents))
|
||||
* )));
|
||||
* address signer = ECDSA.recover(digest, signature);
|
||||
* ```
|
||||
*/
|
||||
function _hashTypedDataV4(bytes32 structHash)
|
||||
internal
|
||||
view
|
||||
virtual
|
||||
returns (bytes32)
|
||||
{
|
||||
return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
|
||||
}
|
||||
|
||||
function checkSigner712(
|
||||
address signer,
|
||||
bytes32 structHash,
|
||||
bytes memory signature
|
||||
) public view{
|
||||
bytes32 digest = _hashTypedDataV4(structHash);
|
||||
address recovered = ECDSA.recover(digest, signature);
|
||||
require(recovered == signer, "[BE] invalid signature");
|
||||
require(signer != address(0), "ECDSA: invalid signature");
|
||||
}
|
||||
function checkSigner712(
|
||||
address signer,
|
||||
bytes32 structHash,
|
||||
bytes memory signature
|
||||
) public view {
|
||||
bytes32 digest = _hashTypedDataV4(structHash);
|
||||
address recovered = ECDSA.recover(digest, signature);
|
||||
require(recovered == signer, "[BE] invalid signature");
|
||||
require(signer != address(0), "ECDSA: invalid signature");
|
||||
}
|
||||
|
||||
function checkSigner(
|
||||
address signer,
|
||||
bytes32 hash,
|
||||
bytes memory signature
|
||||
) public pure {
|
||||
require(signature.length == 65, "[BE] invalid signature length");
|
||||
bytes32 ethSignedMessageHash = ECDSA.toEthSignedMessageHash(
|
||||
hash
|
||||
);
|
||||
|
||||
address recovered = ECDSA.recover(ethSignedMessageHash, signature);
|
||||
require(recovered == signer, "[BE] invalid signature");
|
||||
function checkSigner(
|
||||
address signer,
|
||||
bytes32 hash,
|
||||
bytes memory signature
|
||||
) public pure {
|
||||
require(signature.length == 65, "[BE] invalid signature length");
|
||||
bytes32 ethSignedMessageHash = ECDSA.toEthSignedMessageHash(hash);
|
||||
|
||||
address recovered = ECDSA.recover(ethSignedMessageHash, signature);
|
||||
require(recovered == signer, "[BE] invalid signature");
|
||||
}
|
||||
|
||||
modifier signatureValid(bytes calldata signature) {
|
||||
require(
|
||||
!_usedSignatures[signature],
|
||||
"signature used. please send another transaction with new signature"
|
||||
);
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev mark signature used
|
||||
*/
|
||||
function useSignature(bytes calldata signature) public approvaled {
|
||||
if (!_usedSignatures[signature]) {
|
||||
_usedSignatures[signature] = true;
|
||||
}
|
||||
|
||||
modifier signatureValid(
|
||||
bytes calldata signature
|
||||
) {
|
||||
require(
|
||||
!_usedSignatures[signature],
|
||||
"signature used. please send another transaction with new signature"
|
||||
);
|
||||
_;
|
||||
}
|
||||
/**
|
||||
* @dev mark signature used
|
||||
*/
|
||||
function useSignature(
|
||||
bytes calldata signature
|
||||
) public approvaled {
|
||||
if (!_usedSignatures[signature]) {
|
||||
_usedSignatures[signature] = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -4,20 +4,23 @@ pragma solidity 0.8.10;
|
||||
import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
|
||||
|
||||
interface IBEERC1155 is IERC1155 {
|
||||
function mintBatch(
|
||||
address to,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory amounts,
|
||||
bytes memory data
|
||||
) external;
|
||||
function mintBatch(
|
||||
address to,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory amounts,
|
||||
bytes memory data
|
||||
) external;
|
||||
|
||||
function burnBatch(
|
||||
address owner,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory values
|
||||
) external;
|
||||
function burnBatch(
|
||||
address owner,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory values
|
||||
) external;
|
||||
|
||||
function balanceOf(address account, uint256 id) external view returns (uint256);
|
||||
function balanceOf(address account, uint256 id)
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
|
||||
function canMint(uint256 id) external view returns (bool);
|
||||
function canMint(uint256 id) external view returns (bool);
|
||||
}
|
||||
|
@ -3,10 +3,10 @@ pragma solidity 0.8.10;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
|
||||
|
||||
|
||||
|
||||
interface IBEERC721 is IERC721 {
|
||||
function mint(address to, uint256 tokenId) external;
|
||||
function burn(address owner, uint256 tokenId) external;
|
||||
function ownerOf(uint256 tokenId) external view returns (address owner);
|
||||
function mint(address to, uint256 tokenId) external;
|
||||
|
||||
function burn(address owner, uint256 tokenId) external;
|
||||
|
||||
function ownerOf(uint256 tokenId) external view returns (address owner);
|
||||
}
|
||||
|
@ -19,9 +19,7 @@ interface IEvolveFactory {
|
||||
uint256 startTime,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature
|
||||
) external;
|
||||
|
||||
function useSignature(
|
||||
bytes calldata signature
|
||||
) external;
|
||||
|
||||
function useSignature(bytes calldata signature) external;
|
||||
}
|
||||
|
@ -18,14 +18,9 @@ contract EvolveFactory is Ownable, TimeChecker, Initializable, HasSignature {
|
||||
|
||||
address public executor;
|
||||
|
||||
event TokenEvolved(
|
||||
address indexed owner,
|
||||
uint256[] tokenIds
|
||||
);
|
||||
event TokenEvolved(address indexed owner, uint256[] tokenIds);
|
||||
|
||||
constructor()
|
||||
HasSignature("EvolveFactory", "1"){
|
||||
}
|
||||
constructor() HasSignature("EvolveFactory", "1") {}
|
||||
|
||||
function init(address chipAddress) external initializer onlyOwner {
|
||||
chip = IBEERC1155(chipAddress);
|
||||
@ -35,7 +30,7 @@ contract EvolveFactory is Ownable, TimeChecker, Initializable, HasSignature {
|
||||
* @dev update executor
|
||||
*/
|
||||
function updateExecutor(address account) external onlyOwner {
|
||||
require(account != address(0), 'address can not be zero');
|
||||
require(account != address(0), "address can not be zero");
|
||||
executor = account;
|
||||
}
|
||||
|
||||
@ -46,9 +41,9 @@ contract EvolveFactory is Ownable, TimeChecker, Initializable, HasSignature {
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature,
|
||||
IBEERC721 nft
|
||||
) external signatureValid(signature) timeValid(startTime){
|
||||
) external signatureValid(signature) timeValid(startTime) {
|
||||
require(
|
||||
tokenIds[0] > 0 && tokenIds[1] > 0,
|
||||
tokenIds[0] > 0 && tokenIds[1] > 0,
|
||||
"EvolveFactory: token to evolve and burn can not be 0"
|
||||
);
|
||||
|
||||
@ -69,7 +64,7 @@ contract EvolveFactory is Ownable, TimeChecker, Initializable, HasSignature {
|
||||
}
|
||||
|
||||
uint256[] memory signArray = new uint256[](3);
|
||||
for (uint256 i = 0; i < tokenIds.length; ++ i) {
|
||||
for (uint256 i = 0; i < tokenIds.length; ++i) {
|
||||
uint256 _id = tokenIds[i];
|
||||
signArray[i] = _id;
|
||||
}
|
||||
@ -83,15 +78,14 @@ contract EvolveFactory is Ownable, TimeChecker, Initializable, HasSignature {
|
||||
nft.burn(to, tokenIds[1]);
|
||||
if (tokenIds[2] > 0) {
|
||||
uint256 amount = 1;
|
||||
chip.burnBatch(to,
|
||||
tokenIds[2].asSingletonArray(),
|
||||
amount.asSingletonArray());
|
||||
chip.burnBatch(
|
||||
to,
|
||||
tokenIds[2].asSingletonArray(),
|
||||
amount.asSingletonArray()
|
||||
);
|
||||
}
|
||||
useSignature(signature);
|
||||
emit TokenEvolved(
|
||||
to,
|
||||
signArray
|
||||
);
|
||||
emit TokenEvolved(to, signArray);
|
||||
}
|
||||
|
||||
function evolveChip(
|
||||
@ -100,14 +94,14 @@ contract EvolveFactory is Ownable, TimeChecker, Initializable, HasSignature {
|
||||
uint256 startTime,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature
|
||||
) external signatureValid(signature) timeValid(startTime){
|
||||
) external signatureValid(signature) timeValid(startTime) {
|
||||
require(to != address(0), "EvolveFacrory: address is zero address");
|
||||
uint256 len = tokenIds.length;
|
||||
uint256[] memory amounts = new uint256[](len -1);
|
||||
uint256[] memory amounts = new uint256[](len - 1);
|
||||
uint256[] memory idsForBurn = new uint256[](len - 1);
|
||||
for (uint256 i = 0; i < len; ++ i) {
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
require(
|
||||
chip.balanceOf(to, tokenIds[i]) > 0,
|
||||
chip.balanceOf(to, tokenIds[i]) > 0,
|
||||
"EvolveFacrory: Chip specified not exists"
|
||||
);
|
||||
if (i > 0) {
|
||||
@ -115,15 +109,17 @@ contract EvolveFactory is Ownable, TimeChecker, Initializable, HasSignature {
|
||||
amounts[i - 1] = 1;
|
||||
}
|
||||
}
|
||||
bytes32 criteriaMessageHash = getMessageHash(to, startTime, saltNonce, tokenIds);
|
||||
bytes32 criteriaMessageHash = getMessageHash(
|
||||
to,
|
||||
startTime,
|
||||
saltNonce,
|
||||
tokenIds
|
||||
);
|
||||
checkSigner(executor, criteriaMessageHash, signature);
|
||||
chip.burnBatch(to, idsForBurn, amounts);
|
||||
useSignature(signature);
|
||||
emit TokenEvolved(
|
||||
to,
|
||||
tokenIds
|
||||
);
|
||||
}
|
||||
emit TokenEvolved(to, tokenIds);
|
||||
}
|
||||
|
||||
function getMessageHash(
|
||||
address _to,
|
||||
@ -138,5 +134,4 @@ contract EvolveFactory is Ownable, TimeChecker, Initializable, HasSignature {
|
||||
}
|
||||
return keccak256(encoded);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,285 +12,299 @@ import "../utils/TimeChecker.sol";
|
||||
import "../core/HasSignature.sol";
|
||||
import "./FactoryBase.sol";
|
||||
|
||||
contract MinterFactory is Ownable, FactoryBase, TimeChecker, Initializable, HasSignature {
|
||||
using UInt for uint256;
|
||||
using SafeERC20 for IERC20;
|
||||
address public executor;
|
||||
// NFT contract
|
||||
IBEERC721 public hero;
|
||||
IBEERC721 public equip;
|
||||
IBEERC1155 public chip;
|
||||
IBEERC1155 public shard;
|
||||
contract MinterFactory is
|
||||
Ownable,
|
||||
FactoryBase,
|
||||
TimeChecker,
|
||||
Initializable,
|
||||
HasSignature
|
||||
{
|
||||
using UInt for uint256;
|
||||
using SafeERC20 for IERC20;
|
||||
address public executor;
|
||||
// NFT contract
|
||||
IBEERC721 public hero;
|
||||
IBEERC721 public equip;
|
||||
IBEERC1155 public chip;
|
||||
IBEERC1155 public shard;
|
||||
|
||||
address public feeToAddress;
|
||||
address public feeToAddress;
|
||||
|
||||
event TokenMinted(
|
||||
address contractAddress,
|
||||
address to,
|
||||
uint256 indexed tokenId
|
||||
event TokenMinted(
|
||||
address contractAddress,
|
||||
address to,
|
||||
uint256 indexed tokenId
|
||||
);
|
||||
|
||||
event TokenMintedBatch(
|
||||
address contractAddress,
|
||||
address indexed to,
|
||||
uint256[] ids,
|
||||
uint256[] amounts
|
||||
);
|
||||
|
||||
constructor() HasSignature("MinterFactory", "1") {}
|
||||
|
||||
function init(address[4] calldata _erc721s) external initializer onlyOwner {
|
||||
hero = IBEERC721(_erc721s[0]);
|
||||
equip = IBEERC721(_erc721s[1]);
|
||||
chip = IBEERC1155(_erc721s[2]);
|
||||
shard = IBEERC1155(_erc721s[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev update executor
|
||||
*/
|
||||
function updateExecutor(address account) external onlyOwner {
|
||||
require(account != address(0), "address can not be zero");
|
||||
executor = account;
|
||||
}
|
||||
|
||||
function setFeeToAddress(address _feeToAddress) external onlyOwner {
|
||||
require(
|
||||
_feeToAddress != address(0),
|
||||
"fee received address can not be zero"
|
||||
);
|
||||
feeToAddress = _feeToAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev mint function to distribute Hero NFT to user
|
||||
*/
|
||||
function mintHeroTo(address to, uint256 tokenId) external onlyOwner {
|
||||
mint721NFT(to, tokenId, hero);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev mint function to distribute Equipment NFT to user
|
||||
*/
|
||||
function mintEquipTo(address to, uint256 tokenId) external onlyOwner {
|
||||
mint721NFT(to, tokenId, equip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev mint function to distribute Chip NFT to user
|
||||
*/
|
||||
function mintChipTo(address to, uint256 tokenId) external onlyOwner {
|
||||
mint1155NFT(to, tokenId, 1, chip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev mint function to distribute Shard NFT to user
|
||||
*/
|
||||
function mintShardTo(address to, uint256 tokenId) external onlyOwner {
|
||||
mint1155NFT(to, tokenId, 1, shard);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev batch mint 1155 Chip to user
|
||||
*/
|
||||
function mintChipBatch(address to, uint256[] memory ids) external onlyOwner {
|
||||
require(
|
||||
to != address(0),
|
||||
"MinterFactory::mintChipBatch: to address can not be zero"
|
||||
);
|
||||
require(
|
||||
ids.length > 0,
|
||||
"MinterFactory::mintChipBatch: ids cannot be empty"
|
||||
);
|
||||
uint256[] memory amounts = new uint256[](ids.length);
|
||||
uint256 len = ids.length;
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
amounts[i] = 1;
|
||||
}
|
||||
mint1155NFTBatch(to, ids, amounts, chip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev batch mint 1155 Shard to user
|
||||
*/
|
||||
function mintShardBatch(
|
||||
address to,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory amounts
|
||||
) external onlyOwner {
|
||||
require(to != address(0), "MinterFactory: to address can not be zero");
|
||||
require(ids.length > 0, "MinterFactory: ids cannot be empty");
|
||||
require(
|
||||
ids.length == amounts.length,
|
||||
"MinterFactory: ids and amounts length mismatch"
|
||||
);
|
||||
mint1155NFTBatch(to, ids, amounts, shard);
|
||||
}
|
||||
|
||||
function mint721ByUser(
|
||||
address to,
|
||||
uint256 id,
|
||||
uint256 startTime,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature,
|
||||
IBEERC721 nft
|
||||
) external signatureValid(signature) timeValid(startTime) {
|
||||
uint256[] memory signArray = new uint256[](1);
|
||||
signArray[0] = id;
|
||||
bytes32 criteriaMessageHash = getMessageHash(
|
||||
to,
|
||||
address(nft),
|
||||
startTime,
|
||||
saltNonce,
|
||||
signArray
|
||||
);
|
||||
checkSigner(executor, criteriaMessageHash, signature);
|
||||
mint721NFT(to, id, nft);
|
||||
useSignature(signature);
|
||||
}
|
||||
|
||||
function mint1155BatchByUser(
|
||||
address to,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory amounts,
|
||||
uint256 startTime,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature,
|
||||
IBEERC1155 nft
|
||||
) external signatureValid(signature) timeValid(startTime) {
|
||||
uint256 len = ids.length;
|
||||
require(len > 0, "MinterFactory: ids cannot be empty");
|
||||
require(
|
||||
len == amounts.length,
|
||||
"MinterFactory: ids and amounts length mismatch"
|
||||
);
|
||||
uint256[] memory signArray = new uint256[](len * 2);
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
require(
|
||||
nft.canMint(ids[i]),
|
||||
"MinterFactory: can not mint for current nft rule setting"
|
||||
);
|
||||
signArray[i * 2] = ids[i];
|
||||
signArray[i * 2 + 1] = amounts[i];
|
||||
}
|
||||
bytes32 criteriaMessageHash = getMessageHash(
|
||||
to,
|
||||
address(nft),
|
||||
startTime,
|
||||
saltNonce,
|
||||
signArray
|
||||
);
|
||||
checkSigner(executor, criteriaMessageHash, signature);
|
||||
mint1155NFTBatch(to, ids, amounts, nft);
|
||||
useSignature(signature);
|
||||
}
|
||||
|
||||
function shardMixByUser(
|
||||
ShardParam memory param,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory amounts,
|
||||
bytes calldata signature,
|
||||
IBEERC721 nft
|
||||
) external signatureValid(signature) timeValid(param.startTime) {
|
||||
require(ids.length > 0, "MinterFactory: ids cannot be empty");
|
||||
require(
|
||||
ids.length == amounts.length,
|
||||
"MinterFactory: ids and amounts length mismatch"
|
||||
);
|
||||
|
||||
|
||||
event TokenMintedBatch(
|
||||
address contractAddress,
|
||||
address indexed to,
|
||||
uint256[] ids,
|
||||
uint256[] amounts
|
||||
uint256[] memory signArray = new uint256[](ids.length * 2);
|
||||
for (uint256 i = 0; i < ids.length; ++i) {
|
||||
require(
|
||||
shard.balanceOf(param.to, ids[i]) > 0,
|
||||
"MinterFactory: not enough shard"
|
||||
);
|
||||
signArray[i * 2] = ids[i];
|
||||
signArray[i * 2 + 1] = amounts[i];
|
||||
}
|
||||
bytes32 criteriaMessageHash = getShardMixHash(
|
||||
param,
|
||||
address(nft),
|
||||
signArray
|
||||
);
|
||||
checkSigner(executor, criteriaMessageHash, signature);
|
||||
// Check payment approval and buyer balance
|
||||
IERC20 paymentContract = IERC20(param.payToken);
|
||||
require(
|
||||
paymentContract.balanceOf(param.to) >= param.payAmount,
|
||||
"MinterFactory: doesn't have enough token to mix shard"
|
||||
);
|
||||
require(
|
||||
paymentContract.allowance(param.to, address(this)) >= param.payAmount,
|
||||
"MinterFactory: doesn't approve MinterFactory to spend payment amount"
|
||||
);
|
||||
// transfer money to address
|
||||
paymentContract.safeTransferFrom(param.to, feeToAddress, param.payAmount);
|
||||
shard.burnBatch(param.to, ids, amounts);
|
||||
mint721NFT(param.to, param.nftId, nft);
|
||||
useSignature(signature);
|
||||
}
|
||||
|
||||
function mint721NFT(
|
||||
address to,
|
||||
uint256 tokenId,
|
||||
IBEERC721 nft
|
||||
) internal {
|
||||
require(to != address(0), "MinterFactory: to address can not be zero");
|
||||
nft.mint(to, tokenId);
|
||||
emit TokenMinted(address(nft), to, tokenId);
|
||||
}
|
||||
|
||||
constructor() HasSignature("MinterFactory", "1") {}
|
||||
function mint1155NFT(
|
||||
address to,
|
||||
uint256 id,
|
||||
uint256 amount,
|
||||
IBEERC1155 nft
|
||||
) internal {
|
||||
require(to != address(0), "MinterFactory: to address can not be zero");
|
||||
nft.mintBatch(to, id.asSingletonArray(), amount.asSingletonArray(), "");
|
||||
emit TokenMinted(address(chip), to, id);
|
||||
}
|
||||
|
||||
function init(address[4] calldata _erc721s) external initializer onlyOwner {
|
||||
hero = IBEERC721(_erc721s[0]);
|
||||
equip = IBEERC721(_erc721s[1]);
|
||||
chip = IBEERC1155(_erc721s[2]);
|
||||
shard = IBEERC1155(_erc721s[3]);
|
||||
function mint1155NFTBatch(
|
||||
address to,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory amounts,
|
||||
IBEERC1155 nft
|
||||
) internal {
|
||||
nft.mintBatch(to, ids, amounts, "");
|
||||
emit TokenMintedBatch(address(nft), to, ids, amounts);
|
||||
}
|
||||
|
||||
function getMessageHash(
|
||||
address _to,
|
||||
address _nftAddress,
|
||||
uint256 _startTime,
|
||||
uint256 _saltNonce,
|
||||
uint256[] memory _ids
|
||||
) public pure returns (bytes32) {
|
||||
bytes memory encoded = abi.encodePacked(
|
||||
_to,
|
||||
_nftAddress,
|
||||
_startTime,
|
||||
_saltNonce
|
||||
);
|
||||
uint256 len = _ids.length;
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
encoded = bytes.concat(encoded, abi.encodePacked(_ids[i]));
|
||||
}
|
||||
|
||||
return keccak256(encoded);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev update executor
|
||||
*/
|
||||
function updateExecutor(address account) external onlyOwner {
|
||||
require(account != address(0), "address can not be zero");
|
||||
executor = account;
|
||||
}
|
||||
|
||||
function setFeeToAddress(address _feeToAddress) external onlyOwner {
|
||||
require(_feeToAddress != address(0), 'fee received address can not be zero');
|
||||
feeToAddress = _feeToAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev mint function to distribute Hero NFT to user
|
||||
*/
|
||||
function mintHeroTo(address to, uint256 tokenId) external onlyOwner {
|
||||
mint721NFT(to, tokenId, hero);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev mint function to distribute Equipment NFT to user
|
||||
*/
|
||||
function mintEquipTo(address to, uint256 tokenId) external onlyOwner {
|
||||
mint721NFT(to, tokenId, equip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev mint function to distribute Chip NFT to user
|
||||
*/
|
||||
function mintChipTo(address to, uint256 tokenId) external onlyOwner {
|
||||
mint1155NFT(to, tokenId, 1, chip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev mint function to distribute Shard NFT to user
|
||||
*/
|
||||
function mintShardTo(address to, uint256 tokenId) external onlyOwner {
|
||||
mint1155NFT(to, tokenId, 1, shard);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev batch mint 1155 Chip to user
|
||||
*/
|
||||
function mintChipBatch(address to, uint256[] memory ids)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
require(
|
||||
to != address(0),
|
||||
"MinterFactory::mintChipBatch: to address can not be zero"
|
||||
);
|
||||
require(
|
||||
ids.length > 0,
|
||||
"MinterFactory::mintChipBatch: ids cannot be empty"
|
||||
);
|
||||
uint256[] memory amounts = new uint256[](ids.length);
|
||||
uint256 len = ids.length;
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
amounts[i] = 1;
|
||||
}
|
||||
mint1155NFTBatch(to, ids, amounts, chip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev batch mint 1155 Shard to user
|
||||
*/
|
||||
function mintShardBatch(
|
||||
address to,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory amounts
|
||||
) external onlyOwner {
|
||||
require(to != address(0), "MinterFactory: to address can not be zero");
|
||||
require(ids.length > 0, "MinterFactory: ids cannot be empty");
|
||||
require(
|
||||
ids.length == amounts.length,
|
||||
"MinterFactory: ids and amounts length mismatch"
|
||||
);
|
||||
mint1155NFTBatch(to, ids, amounts, shard);
|
||||
}
|
||||
|
||||
function mint721ByUser(
|
||||
address to,
|
||||
uint256 id,
|
||||
uint256 startTime,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature,
|
||||
IBEERC721 nft
|
||||
) external signatureValid(signature) timeValid(startTime){
|
||||
uint256[] memory signArray = new uint256[](1);
|
||||
signArray[0] = id;
|
||||
bytes32 criteriaMessageHash =
|
||||
getMessageHash(to, address(nft), startTime, saltNonce, signArray);
|
||||
checkSigner(executor, criteriaMessageHash, signature);
|
||||
mint721NFT(to, id, nft);
|
||||
useSignature(signature);
|
||||
}
|
||||
|
||||
function mint1155BatchByUser(
|
||||
address to,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory amounts,
|
||||
uint256 startTime,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature,
|
||||
IBEERC1155 nft
|
||||
) external signatureValid(signature) timeValid(startTime){
|
||||
uint256 len = ids.length;
|
||||
require(len > 0, "MinterFactory: ids cannot be empty");
|
||||
require(
|
||||
len == amounts.length,
|
||||
"MinterFactory: ids and amounts length mismatch"
|
||||
);
|
||||
uint256[] memory signArray = new uint256[](len * 2);
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
require(
|
||||
nft.canMint(ids[i]),
|
||||
"MinterFactory: can not mint for current nft rule setting"
|
||||
);
|
||||
signArray[i * 2] = ids[i];
|
||||
signArray[i * 2 + 1] = amounts[i];
|
||||
}
|
||||
bytes32 criteriaMessageHash =
|
||||
getMessageHash(to, address(nft), startTime, saltNonce, signArray);
|
||||
checkSigner(executor, criteriaMessageHash, signature);
|
||||
mint1155NFTBatch(to, ids, amounts, nft);
|
||||
useSignature(signature);
|
||||
}
|
||||
|
||||
function shardMixByUser(
|
||||
ShardParam memory param,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory amounts,
|
||||
bytes calldata signature,
|
||||
IBEERC721 nft
|
||||
) external signatureValid(signature) timeValid(param.startTime) {
|
||||
require(ids.length > 0, "MinterFactory: ids cannot be empty");
|
||||
require(
|
||||
ids.length == amounts.length,
|
||||
"MinterFactory: ids and amounts length mismatch"
|
||||
);
|
||||
|
||||
uint256[] memory signArray = new uint256[](ids.length * 2);
|
||||
for (uint256 i = 0; i < ids.length; ++i) {
|
||||
require(
|
||||
shard.balanceOf(param.to, ids[i]) > 0,
|
||||
"MinterFactory: not enough shard"
|
||||
);
|
||||
signArray[i * 2] = ids[i];
|
||||
signArray[i * 2 + 1] = amounts[i];
|
||||
}
|
||||
bytes32 criteriaMessageHash =
|
||||
getShardMixHash(param, address(nft), signArray);
|
||||
checkSigner(executor, criteriaMessageHash, signature);
|
||||
// Check payment approval and buyer balance
|
||||
IERC20 paymentContract = IERC20(param.payToken);
|
||||
require(
|
||||
paymentContract.balanceOf(param.to) >= param.payAmount,
|
||||
"MinterFactory: doesn't have enough token to mix shard"
|
||||
);
|
||||
require(
|
||||
paymentContract.allowance(param.to, address(this)) >= param.payAmount,
|
||||
"MinterFactory: doesn't approve MinterFactory to spend payment amount"
|
||||
);
|
||||
// transfer money to address
|
||||
paymentContract.safeTransferFrom(
|
||||
param.to,
|
||||
feeToAddress,
|
||||
param.payAmount
|
||||
);
|
||||
shard.burnBatch(param.to, ids, amounts);
|
||||
mint721NFT(param.to, param.nftId, nft);
|
||||
useSignature(signature);
|
||||
}
|
||||
|
||||
function mint721NFT(
|
||||
address to,
|
||||
uint256 tokenId,
|
||||
IBEERC721 nft
|
||||
) internal {
|
||||
require(to != address(0), "MinterFactory: to address can not be zero");
|
||||
nft.mint(to, tokenId);
|
||||
emit TokenMinted(address(nft), to, tokenId);
|
||||
}
|
||||
|
||||
function mint1155NFT(
|
||||
address to,
|
||||
uint256 id,
|
||||
uint256 amount,
|
||||
IBEERC1155 nft
|
||||
) internal {
|
||||
require(to != address(0), "MinterFactory: to address can not be zero");
|
||||
nft.mintBatch(to, id.asSingletonArray(), amount.asSingletonArray(), "");
|
||||
emit TokenMinted(address(chip), to, id);
|
||||
}
|
||||
|
||||
function mint1155NFTBatch(
|
||||
address to,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory amounts,
|
||||
IBEERC1155 nft
|
||||
) internal {
|
||||
nft.mintBatch(to, ids, amounts, "");
|
||||
emit TokenMintedBatch(address(nft), to, ids, amounts);
|
||||
}
|
||||
|
||||
|
||||
function getMessageHash(
|
||||
address _to,
|
||||
address _nftAddress,
|
||||
uint256 _startTime,
|
||||
uint256 _saltNonce,
|
||||
uint256[] memory _ids
|
||||
) public pure returns (bytes32) {
|
||||
bytes memory encoded =
|
||||
abi.encodePacked(_to, _nftAddress, _startTime, _saltNonce);
|
||||
uint256 len = _ids.length;
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
encoded = bytes.concat(encoded, abi.encodePacked(_ids[i]));
|
||||
}
|
||||
return keccak256(encoded);
|
||||
}
|
||||
|
||||
function getShardMixHash(
|
||||
ShardParam memory param,
|
||||
address nftAddress,
|
||||
uint256[] memory _ids
|
||||
) internal pure returns (bytes32) {
|
||||
bytes memory encoded =
|
||||
abi.encodePacked(
|
||||
param.to,
|
||||
nftAddress,
|
||||
param.nftId,
|
||||
param.payToken,
|
||||
param.payAmount,
|
||||
param.startTime,
|
||||
param.saltNonce
|
||||
);
|
||||
uint256 len = _ids.length;
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
encoded = bytes.concat(encoded, abi.encodePacked(_ids[i]));
|
||||
}
|
||||
return keccak256(encoded);
|
||||
function getShardMixHash(
|
||||
ShardParam memory param,
|
||||
address nftAddress,
|
||||
uint256[] memory _ids
|
||||
) internal pure returns (bytes32) {
|
||||
bytes memory encoded = abi.encodePacked(
|
||||
param.to,
|
||||
nftAddress,
|
||||
param.nftId,
|
||||
param.payToken,
|
||||
param.payAmount,
|
||||
param.startTime,
|
||||
param.saltNonce
|
||||
);
|
||||
uint256 len = _ids.length;
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
encoded = bytes.concat(encoded, abi.encodePacked(_ids[i]));
|
||||
}
|
||||
return keccak256(encoded);
|
||||
}
|
||||
}
|
||||
|
@ -8,165 +8,153 @@ import "../interfaces/IBEERC721.sol";
|
||||
import "../core/HasSignature.sol";
|
||||
|
||||
contract MysteryBoxProxy is Ownable, Initializable, HasSignature {
|
||||
IBEERC721 public box;
|
||||
IBEERC721 public hero;
|
||||
IBEERC721 public equip;
|
||||
IBEERC721 public chip;
|
||||
IBEERC721 public box;
|
||||
IBEERC721 public hero;
|
||||
IBEERC721 public equip;
|
||||
IBEERC721 public chip;
|
||||
|
||||
uint8 public constant TYPE_NONE = 0;
|
||||
uint8 public constant TYPE_HERO = 1;
|
||||
uint8 public constant TYPE_EQUIP = 2;
|
||||
uint8 public constant TYPE_CHIP = 3;
|
||||
uint8 public constant TYPE_NONE = 0;
|
||||
uint8 public constant TYPE_HERO = 1;
|
||||
uint8 public constant TYPE_EQUIP = 2;
|
||||
uint8 public constant TYPE_CHIP = 3;
|
||||
|
||||
address public executor;
|
||||
address public executor;
|
||||
|
||||
mapping(bytes => bool) public usedSignatures;
|
||||
mapping(bytes => bool) public usedSignatures;
|
||||
|
||||
event TokenMinted(
|
||||
address contractAddress,
|
||||
address to,
|
||||
uint256 indexed tokenId
|
||||
event TokenMinted(
|
||||
address contractAddress,
|
||||
address to,
|
||||
uint256 indexed tokenId
|
||||
);
|
||||
|
||||
event BoxOpened(
|
||||
address indexed to,
|
||||
uint256 indexed boxId,
|
||||
uint256 val,
|
||||
uint256[3] ids,
|
||||
uint8[3] types
|
||||
);
|
||||
|
||||
constructor() HasSignature("MysteryBoxProxy", "1") {}
|
||||
|
||||
function init(address[4] calldata _erc721s) external initializer onlyOwner {
|
||||
hero = IBEERC721(_erc721s[0]);
|
||||
equip = IBEERC721(_erc721s[1]);
|
||||
chip = IBEERC721(_erc721s[2]);
|
||||
box = IBEERC721(_erc721s[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev update executor
|
||||
*/
|
||||
function updateExecutor(address account) external onlyOwner {
|
||||
require(account != address(0), "address can not be zero");
|
||||
executor = account;
|
||||
}
|
||||
|
||||
function mintBoxTo(address to, uint256 tokenId) external onlyOwner {
|
||||
require(to != address(0), "to address can not be zero");
|
||||
box.mint(to, tokenId);
|
||||
emit TokenMinted(address(box), to, tokenId);
|
||||
}
|
||||
|
||||
function openBox(
|
||||
uint256 boxId,
|
||||
uint256[3] calldata ids,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature
|
||||
) external {
|
||||
require(ids.length == 3, "MysteryBoxProxy: amount of token id mismatch");
|
||||
require(
|
||||
!usedSignatures[signature],
|
||||
"MysteryBoxProxy: signature used. please send another transaction with new signature"
|
||||
);
|
||||
|
||||
event BoxOpened(
|
||||
address indexed to,
|
||||
uint256 indexed boxId,
|
||||
uint256 val,
|
||||
uint256[3] ids,
|
||||
uint8[3] types
|
||||
address owner = msg.sender;
|
||||
require(
|
||||
box.ownerOf(boxId) == owner,
|
||||
"MysteryBoxProxy: only owner can open this box"
|
||||
);
|
||||
bytes32 criteriaMessageHash = getMessageHash(
|
||||
boxId,
|
||||
ids[0],
|
||||
ids[1],
|
||||
ids[2],
|
||||
saltNonce
|
||||
);
|
||||
checkSigner(executor, criteriaMessageHash, signature);
|
||||
// open box
|
||||
box.burn(owner, boxId);
|
||||
usedSignatures[signature] = true;
|
||||
|
||||
constructor() HasSignature("MysteryBoxProxy", "1") {}
|
||||
uint256[3] memory results = [ids[0], 0, 0];
|
||||
uint8[3] memory types = [TYPE_HERO, TYPE_NONE, TYPE_NONE];
|
||||
mint721WithType(owner, ids[0], types[0]);
|
||||
results[0] = ids[0];
|
||||
|
||||
function init(address[4] calldata _erc721s)
|
||||
external
|
||||
initializer
|
||||
onlyOwner
|
||||
{
|
||||
hero = IBEERC721(_erc721s[0]);
|
||||
equip = IBEERC721(_erc721s[1]);
|
||||
chip = IBEERC721(_erc721s[2]);
|
||||
box = IBEERC721(_erc721s[3]);
|
||||
uint256 val = rand(owner, saltNonce, 100);
|
||||
if (val >= 70 && val < 90) {
|
||||
types[1] = TYPE_CHIP;
|
||||
mint721WithType(owner, ids[1], types[1]);
|
||||
results[1] = ids[1];
|
||||
} else if (val >= 90 && val < 98) {
|
||||
types[1] = TYPE_EQUIP;
|
||||
mint721WithType(owner, ids[1], types[1]);
|
||||
results[1] = ids[1];
|
||||
} else if (val >= 98) {
|
||||
types[1] = TYPE_EQUIP;
|
||||
mint721WithType(owner, ids[1], types[1]);
|
||||
results[1] = ids[1];
|
||||
types[2] = TYPE_CHIP;
|
||||
mint721WithType(owner, ids[2], types[2]);
|
||||
results[2] = ids[2];
|
||||
}
|
||||
emit BoxOpened(owner, boxId, val, results, types);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev update executor
|
||||
*/
|
||||
function updateExecutor(address account) external onlyOwner {
|
||||
require(account != address(0), "address can not be zero");
|
||||
executor = account;
|
||||
function mint721WithType(
|
||||
address to,
|
||||
uint256 tokenId,
|
||||
uint256 typeNum
|
||||
) private {
|
||||
if (typeNum == 1) {
|
||||
hero.mint(to, tokenId);
|
||||
} else if (typeNum == 2) {
|
||||
equip.mint(to, tokenId);
|
||||
} else if (typeNum == 3) {
|
||||
chip.mint(to, tokenId);
|
||||
}
|
||||
}
|
||||
|
||||
function mintBoxTo(address to, uint256 tokenId) external onlyOwner{
|
||||
require(to != address(0), 'to address can not be zero');
|
||||
box.mint(to, tokenId);
|
||||
emit TokenMinted(address(box), to, tokenId);
|
||||
}
|
||||
function rand(
|
||||
address owner,
|
||||
uint256 nonce,
|
||||
uint256 _length
|
||||
) internal view returns (uint256) {
|
||||
uint256 random = uint256(
|
||||
keccak256(
|
||||
abi.encodePacked(owner, nonce, block.difficulty, block.timestamp)
|
||||
)
|
||||
);
|
||||
return random % _length;
|
||||
}
|
||||
|
||||
function openBox(
|
||||
uint256 boxId,
|
||||
uint256[3] calldata ids,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature
|
||||
) external {
|
||||
require(
|
||||
ids.length == 3,
|
||||
"MysteryBoxProxy: amount of token id mismatch"
|
||||
);
|
||||
require(
|
||||
!usedSignatures[signature],
|
||||
"MysteryBoxProxy: signature used. please send another transaction with new signature"
|
||||
);
|
||||
address owner = msg.sender;
|
||||
require(
|
||||
box.ownerOf(boxId) == owner,
|
||||
"MysteryBoxProxy: only owner can open this box"
|
||||
);
|
||||
bytes32 criteriaMessageHash = getMessageHash(
|
||||
boxId,
|
||||
ids[0],
|
||||
ids[1],
|
||||
ids[2],
|
||||
saltNonce
|
||||
);
|
||||
checkSigner(executor, criteriaMessageHash, signature);
|
||||
// open box
|
||||
box.burn(owner, boxId);
|
||||
usedSignatures[signature] = true;
|
||||
|
||||
uint256[3] memory results = [ids[0], 0, 0];
|
||||
uint8[3] memory types = [TYPE_HERO, TYPE_NONE, TYPE_NONE];
|
||||
mint721WithType(owner, ids[0], types[0]);
|
||||
results[0] = ids[0];
|
||||
|
||||
uint256 val = rand(owner, saltNonce, 100);
|
||||
if (val >= 70 && val < 90) {
|
||||
types[1] = TYPE_CHIP;
|
||||
mint721WithType(owner, ids[1], types[1]);
|
||||
results[1] = ids[1];
|
||||
} else if (val >= 90 && val < 98) {
|
||||
types[1] = TYPE_EQUIP;
|
||||
mint721WithType(owner, ids[1], types[1]);
|
||||
results[1] = ids[1];
|
||||
} else if (val >= 98) {
|
||||
types[1] = TYPE_EQUIP;
|
||||
mint721WithType(owner, ids[1], types[1]);
|
||||
results[1] = ids[1];
|
||||
types[2] = TYPE_CHIP;
|
||||
mint721WithType(owner, ids[2], types[2]);
|
||||
results[2] = ids[2];
|
||||
}
|
||||
emit BoxOpened(owner, boxId, val, results, types);
|
||||
}
|
||||
|
||||
function mint721WithType(
|
||||
address to,
|
||||
uint256 tokenId,
|
||||
uint256 typeNum
|
||||
) private {
|
||||
if (typeNum == 1) {
|
||||
hero.mint(to, tokenId);
|
||||
} else if (typeNum == 2) {
|
||||
equip.mint(to, tokenId);
|
||||
} else if (typeNum == 3) {
|
||||
chip.mint(to, tokenId);
|
||||
}
|
||||
}
|
||||
|
||||
function rand(
|
||||
address owner,
|
||||
uint256 nonce,
|
||||
uint256 _length
|
||||
) internal view returns (uint256) {
|
||||
uint256 random = uint256(
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
owner,
|
||||
nonce,
|
||||
block.difficulty,
|
||||
block.timestamp
|
||||
)
|
||||
)
|
||||
);
|
||||
return random % _length;
|
||||
}
|
||||
|
||||
function getMessageHash(
|
||||
uint256 _boxId,
|
||||
uint256 _firstToken,
|
||||
uint256 _secondToken,
|
||||
uint256 _thirdToken,
|
||||
uint256 _saltNonce
|
||||
) public pure returns (bytes32) {
|
||||
return
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
_boxId,
|
||||
_firstToken,
|
||||
_secondToken,
|
||||
_thirdToken,
|
||||
_saltNonce
|
||||
)
|
||||
);
|
||||
}
|
||||
function getMessageHash(
|
||||
uint256 _boxId,
|
||||
uint256 _firstToken,
|
||||
uint256 _secondToken,
|
||||
uint256 _thirdToken,
|
||||
uint256 _saltNonce
|
||||
) public pure returns (bytes32) {
|
||||
return
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
_boxId,
|
||||
_firstToken,
|
||||
_secondToken,
|
||||
_thirdToken,
|
||||
_saltNonce
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ contract UserEvolveFactory is Ownable, Initializable {
|
||||
IBEERC721 public hero;
|
||||
IBEERC721 public equip;
|
||||
|
||||
event TokenEvolveFail (
|
||||
event TokenEvolveFail(
|
||||
address indexed to,
|
||||
bytes signature,
|
||||
string reason,
|
||||
@ -23,114 +23,83 @@ contract UserEvolveFactory is Ownable, Initializable {
|
||||
factory = IEvolveFactory(_nfts[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @dev evolve function Hero NFT
|
||||
* tokenIds: [hero_to_evolve, hero_for_burn, chip]
|
||||
*/
|
||||
function evolveHero(
|
||||
uint256[3] calldata tokenIds,
|
||||
uint256 startTime,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature
|
||||
) external returns (bool success){
|
||||
uint256[3] calldata tokenIds,
|
||||
uint256 startTime,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature
|
||||
) external returns (bool success) {
|
||||
address to = _msgSender();
|
||||
try factory.evolve721NFT(to, tokenIds, startTime, saltNonce, signature, hero) {
|
||||
try
|
||||
factory.evolve721NFT(to, tokenIds, startTime, saltNonce, signature, hero)
|
||||
{
|
||||
return true;
|
||||
} catch Error(string memory reason) {
|
||||
bytes memory by;
|
||||
factory.useSignature(signature);
|
||||
emit TokenEvolveFail(
|
||||
to,
|
||||
signature,
|
||||
reason,
|
||||
by
|
||||
);
|
||||
emit TokenEvolveFail(to, signature, reason, by);
|
||||
return false;
|
||||
} catch (bytes memory lowLevelData) {
|
||||
factory.useSignature(signature);
|
||||
string memory reason;
|
||||
emit TokenEvolveFail(
|
||||
to,
|
||||
signature,
|
||||
reason,
|
||||
lowLevelData
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @dev evolve function for equip NFT
|
||||
* tokenIds: [equip_to_evolve, equip_for_burn, chip]
|
||||
*/
|
||||
function evolveEquip(
|
||||
uint256[3] calldata tokenIds,
|
||||
uint256 startTime,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature
|
||||
) external returns (bool success){
|
||||
address to = _msgSender();
|
||||
try factory.evolve721NFT(to, tokenIds, startTime, saltNonce, signature, equip) {
|
||||
return true;
|
||||
} catch Error(string memory reason) {
|
||||
bytes memory by;
|
||||
factory.useSignature(signature);
|
||||
emit TokenEvolveFail(
|
||||
to,
|
||||
signature,
|
||||
reason,
|
||||
by
|
||||
);
|
||||
return false;
|
||||
} catch (bytes memory lowLevelData) {
|
||||
factory.useSignature(signature);
|
||||
string memory reason;
|
||||
emit TokenEvolveFail(
|
||||
to,
|
||||
signature,
|
||||
reason,
|
||||
lowLevelData
|
||||
);
|
||||
emit TokenEvolveFail(to, signature, reason, lowLevelData);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev evolve function for chip
|
||||
* @dev evolve function for equip NFT
|
||||
* tokenIds: [equip_to_evolve, equip_for_burn, chip]
|
||||
*/
|
||||
function evolveChip(
|
||||
uint256[] memory ids,
|
||||
uint256 startTime,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature
|
||||
) external returns (bool success){
|
||||
function evolveEquip(
|
||||
uint256[3] calldata tokenIds,
|
||||
uint256 startTime,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature
|
||||
) external returns (bool success) {
|
||||
address to = _msgSender();
|
||||
try factory.evolveChip(
|
||||
to,
|
||||
ids,
|
||||
startTime,
|
||||
saltNonce,
|
||||
signature
|
||||
) {
|
||||
try
|
||||
factory.evolve721NFT(to, tokenIds, startTime, saltNonce, signature, equip)
|
||||
{
|
||||
return true;
|
||||
} catch Error(string memory reason) {
|
||||
bytes memory by;
|
||||
factory.useSignature(signature);
|
||||
emit TokenEvolveFail(
|
||||
to,
|
||||
signature,
|
||||
reason,
|
||||
by
|
||||
);
|
||||
emit TokenEvolveFail(to, signature, reason, by);
|
||||
return false;
|
||||
} catch (bytes memory lowLevelData) {
|
||||
factory.useSignature(signature);
|
||||
string memory reason;
|
||||
emit TokenEvolveFail(
|
||||
to,
|
||||
signature,
|
||||
reason,
|
||||
lowLevelData
|
||||
);
|
||||
emit TokenEvolveFail(to, signature, reason, lowLevelData);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev evolve function for chip
|
||||
*/
|
||||
function evolveChip(
|
||||
uint256[] memory ids,
|
||||
uint256 startTime,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature
|
||||
) external returns (bool success) {
|
||||
address to = _msgSender();
|
||||
try factory.evolveChip(to, ids, startTime, saltNonce, signature) {
|
||||
return true;
|
||||
} catch Error(string memory reason) {
|
||||
bytes memory by;
|
||||
factory.useSignature(signature);
|
||||
emit TokenEvolveFail(to, signature, reason, by);
|
||||
return false;
|
||||
} catch (bytes memory lowLevelData) {
|
||||
factory.useSignature(signature);
|
||||
string memory reason;
|
||||
emit TokenEvolveFail(to, signature, reason, lowLevelData);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ contract UserMinterFactory is Ownable, FactoryBase, Initializable {
|
||||
shard = IBEERC1155(addressArr[3]);
|
||||
factory = MinterFactory(addressArr[4]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev mint hero by user
|
||||
*/
|
||||
@ -36,32 +37,23 @@ contract UserMinterFactory is Ownable, FactoryBase, Initializable {
|
||||
uint256 startTime,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature
|
||||
) external returns (bool success){
|
||||
) external returns (bool success) {
|
||||
address to = _msgSender();
|
||||
try factory.mint721ByUser(to, tokenId, startTime, saltNonce, signature, hero) {
|
||||
try
|
||||
factory.mint721ByUser(to, tokenId, startTime, saltNonce, signature, hero)
|
||||
{
|
||||
return true;
|
||||
} catch Error(string memory reason) {
|
||||
bytes memory by;
|
||||
factory.useSignature(signature);
|
||||
emit TokenMintFail(
|
||||
to,
|
||||
signature,
|
||||
reason,
|
||||
by
|
||||
);
|
||||
emit TokenMintFail(to, signature, reason, by);
|
||||
return false;
|
||||
} catch (bytes memory lowLevelData) {
|
||||
factory.useSignature(signature);
|
||||
string memory reason;
|
||||
emit TokenMintFail(
|
||||
to,
|
||||
signature,
|
||||
reason,
|
||||
lowLevelData
|
||||
);
|
||||
emit TokenMintFail(to, signature, reason, lowLevelData);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,112 +64,104 @@ contract UserMinterFactory is Ownable, FactoryBase, Initializable {
|
||||
uint256 startTime,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature
|
||||
) external returns (bool success){
|
||||
) external returns (bool success) {
|
||||
address to = _msgSender();
|
||||
try factory.mint721ByUser(to, tokenId, startTime, saltNonce, signature, equip) {
|
||||
try
|
||||
factory.mint721ByUser(to, tokenId, startTime, saltNonce, signature, equip)
|
||||
{
|
||||
return true;
|
||||
} catch Error(string memory reason) {
|
||||
bytes memory by;
|
||||
factory.useSignature(signature);
|
||||
emit TokenMintFail(
|
||||
to,
|
||||
signature,
|
||||
reason,
|
||||
by
|
||||
);
|
||||
emit TokenMintFail(to, signature, reason, by);
|
||||
return false;
|
||||
} catch (bytes memory lowLevelData) {
|
||||
factory.useSignature(signature);
|
||||
string memory reason;
|
||||
emit TokenMintFail(
|
||||
to,
|
||||
signature,
|
||||
reason,
|
||||
lowLevelData
|
||||
);
|
||||
emit TokenMintFail(to, signature, reason, lowLevelData);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev user batch mint 1155 chip
|
||||
*/
|
||||
* @dev user batch mint 1155 chip
|
||||
*/
|
||||
function mintChipBatchUser(
|
||||
uint256[] memory ids,
|
||||
uint256 startTime,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature
|
||||
) external returns (bool success){
|
||||
) external returns (bool success) {
|
||||
uint256 len = ids.length;
|
||||
uint256[] memory amounts = new uint256[](len);
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
amounts[i] = 1;
|
||||
}
|
||||
address to = _msgSender();
|
||||
try factory.mint1155BatchByUser(to, ids, amounts, startTime, saltNonce, signature, chip) {
|
||||
try
|
||||
factory.mint1155BatchByUser(
|
||||
to,
|
||||
ids,
|
||||
amounts,
|
||||
startTime,
|
||||
saltNonce,
|
||||
signature,
|
||||
chip
|
||||
)
|
||||
{
|
||||
return true;
|
||||
} catch Error(string memory reason) {
|
||||
} catch Error(string memory reason) {
|
||||
bytes memory by;
|
||||
factory.useSignature(signature);
|
||||
emit TokenMintFail(
|
||||
to,
|
||||
signature,
|
||||
reason,
|
||||
by
|
||||
);
|
||||
emit TokenMintFail(to, signature, reason, by);
|
||||
return false;
|
||||
} catch (bytes memory lowLevelData) {
|
||||
factory.useSignature(signature);
|
||||
string memory reason;
|
||||
emit TokenMintFail(
|
||||
to,
|
||||
signature,
|
||||
reason,
|
||||
lowLevelData
|
||||
);
|
||||
emit TokenMintFail(to, signature, reason, lowLevelData);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev user batch mint 1155 shard
|
||||
*/
|
||||
* @dev user batch mint 1155 shard
|
||||
*/
|
||||
function mintShardBatchUser(
|
||||
uint256[] memory ids,
|
||||
uint256[] memory amounts,
|
||||
uint256 startTime,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature
|
||||
) external returns (bool success){
|
||||
) external returns (bool success) {
|
||||
address to = _msgSender();
|
||||
try factory.mint1155BatchByUser(to, ids, amounts, startTime, saltNonce, signature, shard) {
|
||||
try
|
||||
factory.mint1155BatchByUser(
|
||||
to,
|
||||
ids,
|
||||
amounts,
|
||||
startTime,
|
||||
saltNonce,
|
||||
signature,
|
||||
shard
|
||||
)
|
||||
{
|
||||
return true;
|
||||
} catch Error(string memory reason) {
|
||||
bytes memory by;
|
||||
factory.useSignature(signature);
|
||||
emit TokenMintFail(
|
||||
to,
|
||||
signature,
|
||||
reason,
|
||||
by
|
||||
);
|
||||
emit TokenMintFail(to, signature, reason, by);
|
||||
return false;
|
||||
} catch (bytes memory lowLevelData) {
|
||||
factory.useSignature(signature);
|
||||
string memory reason;
|
||||
emit TokenMintFail(
|
||||
to,
|
||||
signature,
|
||||
reason,
|
||||
lowLevelData
|
||||
);
|
||||
emit TokenMintFail(to, signature, reason, lowLevelData);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev mint hero or equip with shard
|
||||
*/
|
||||
* @dev mint hero or equip with shard
|
||||
*/
|
||||
function shardMixByUser(
|
||||
uint256 nftId,
|
||||
uint8 nftType,
|
||||
@ -197,36 +181,24 @@ contract UserMinterFactory is Ownable, FactoryBase, Initializable {
|
||||
nft = equip;
|
||||
}
|
||||
ShardParam memory param = ShardParam(
|
||||
to, nftId, payToken, payAmount, startTime, saltNonce);
|
||||
try factory.shardMixByUser(
|
||||
param,
|
||||
ids,
|
||||
amounts,
|
||||
signature,
|
||||
nft
|
||||
) {
|
||||
to,
|
||||
nftId,
|
||||
payToken,
|
||||
payAmount,
|
||||
startTime,
|
||||
saltNonce
|
||||
);
|
||||
try factory.shardMixByUser(param, ids, amounts, signature, nft) {
|
||||
return true;
|
||||
} catch Error(string memory reason) {
|
||||
bytes memory by;
|
||||
factory.useSignature(signature);
|
||||
emit TokenMintFail(
|
||||
to,
|
||||
signature,
|
||||
reason,
|
||||
by
|
||||
);
|
||||
emit TokenMintFail(to, signature, reason, by);
|
||||
return false;
|
||||
} catch (bytes memory lowLevelData) {
|
||||
factory.useSignature(signature);
|
||||
string memory reason;
|
||||
emit TokenMintFail(
|
||||
to,
|
||||
signature,
|
||||
reason,
|
||||
lowLevelData
|
||||
);
|
||||
emit TokenMintFail(to, signature, reason, lowLevelData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -8,137 +8,150 @@ import "@openzeppelin/contracts/utils/Address.sol";
|
||||
import "@openzeppelin/contracts/governance/TimelockController.sol";
|
||||
import "../core/HasSignature.sol";
|
||||
|
||||
contract BEBoxMall is Ownable, HasSignature, TimelockController{
|
||||
using SafeERC20 for IERC20;
|
||||
using Address for address;
|
||||
contract BEBoxMall is Ownable, HasSignature, TimelockController {
|
||||
using SafeERC20 for IERC20;
|
||||
using Address for address;
|
||||
|
||||
uint256 public constant MIN_DELAY = 2 days;
|
||||
uint256 public constant MAX_DELAY = 16 days;
|
||||
uint256 private _minDelay;
|
||||
uint256 public constant MIN_DELAY = 2 days;
|
||||
uint256 public constant MAX_DELAY = 16 days;
|
||||
uint256 private _minDelay;
|
||||
|
||||
bool public address_initialized;
|
||||
bool public address_initialized;
|
||||
|
||||
constructor(
|
||||
address[] memory proposers,
|
||||
address[] memory executors)
|
||||
TimelockController(MIN_DELAY, proposers, executors)
|
||||
HasSignature("BEBoxMall", "1"){
|
||||
_minDelay = MIN_DELAY;
|
||||
address_initialized = false;
|
||||
constructor(address[] memory proposers, address[] memory executors)
|
||||
TimelockController(MIN_DELAY, proposers, executors)
|
||||
HasSignature("BEBoxMall", "1")
|
||||
{
|
||||
_minDelay = MIN_DELAY;
|
||||
address_initialized = false;
|
||||
}
|
||||
|
||||
event BEBoxPaid(
|
||||
uint256 indexed boxId,
|
||||
address indexed buyer,
|
||||
uint256 boxType,
|
||||
uint256 price,
|
||||
address paymentToken
|
||||
);
|
||||
|
||||
address public paymentReceivedAddress;
|
||||
|
||||
function setPaymentReceivedAddress(address _paymentReceivedAddress) public {
|
||||
require(
|
||||
_paymentReceivedAddress != address(0),
|
||||
"BEBoxMall::setPaymentReceivedAddress: payment received address can not be zero"
|
||||
);
|
||||
if (address_initialized) {
|
||||
require(
|
||||
msg.sender == address(this),
|
||||
"BEBoxMall::setPaymentReceivedAddress: Call must come from BEBoxMall."
|
||||
);
|
||||
} else {
|
||||
require(
|
||||
msg.sender == owner(),
|
||||
"BEBoxMall::setPaymentReceivedAddress: First call must come from owner."
|
||||
);
|
||||
address_initialized = true;
|
||||
}
|
||||
paymentReceivedAddress = _paymentReceivedAddress;
|
||||
}
|
||||
|
||||
event BEBoxPaid(
|
||||
uint256 indexed boxId,
|
||||
address indexed buyer,
|
||||
uint256 boxType,
|
||||
uint256 price,
|
||||
address paymentToken
|
||||
/**
|
||||
* @dev BE box payment function
|
||||
*/
|
||||
function payForBoxWithSignature(
|
||||
uint256 boxId,
|
||||
uint256 _type,
|
||||
address userAddress,
|
||||
uint256 price,
|
||||
address paymentErc20,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature
|
||||
) external onlyOwner signatureValid(signature) {
|
||||
require(
|
||||
!userAddress.isContract(),
|
||||
"BEBoxPayment: Only user address is allowed to buy box"
|
||||
);
|
||||
require(_type > 0, "BEBoxPayment: Invalid box type");
|
||||
require(price > 0, "BEBoxPayment: Invalid payment amount");
|
||||
bytes32 criteriaMessageHash = getMessageHash(
|
||||
_type,
|
||||
paymentErc20,
|
||||
price,
|
||||
saltNonce
|
||||
);
|
||||
|
||||
address public paymentReceivedAddress;
|
||||
checkSigner712(userAddress, criteriaMessageHash, signature);
|
||||
|
||||
function setPaymentReceivedAddress(address _paymentReceivedAddress)
|
||||
public
|
||||
{
|
||||
require(_paymentReceivedAddress != address(0), 'BEBoxMall::setPaymentReceivedAddress: payment received address can not be zero');
|
||||
if (address_initialized) {
|
||||
require(msg.sender == address(this), "BEBoxMall::setPaymentReceivedAddress: Call must come from BEBoxMall.");
|
||||
} else {
|
||||
require(msg.sender == owner(), "BEBoxMall::setPaymentReceivedAddress: First call must come from owner.");
|
||||
address_initialized = true;
|
||||
}
|
||||
paymentReceivedAddress = _paymentReceivedAddress;
|
||||
}
|
||||
IERC20 paymentToken = IERC20(paymentErc20);
|
||||
uint256 allowToPayAmount = paymentToken.allowance(
|
||||
userAddress,
|
||||
address(this)
|
||||
);
|
||||
require(allowToPayAmount >= price, "BEBoxPayment: Invalid token allowance");
|
||||
// Transfer payment
|
||||
paymentToken.safeTransferFrom(userAddress, paymentReceivedAddress, price);
|
||||
useSignature(signature);
|
||||
// Emit payment event
|
||||
emit BEBoxPaid(boxId, userAddress, _type, price, paymentErc20);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev BE box payment function
|
||||
*/
|
||||
function payForBoxWithSignature(
|
||||
uint256 boxId,
|
||||
uint256 _type,
|
||||
address userAddress,
|
||||
uint256 price,
|
||||
address paymentErc20,
|
||||
uint256 saltNonce,
|
||||
bytes calldata signature
|
||||
) external onlyOwner signatureValid(signature){
|
||||
require(
|
||||
!userAddress.isContract(),
|
||||
"BEBoxPayment: Only user address is allowed to buy box"
|
||||
);
|
||||
require(_type > 0, "BEBoxPayment: Invalid box type");
|
||||
require(price > 0, "BEBoxPayment: Invalid payment amount");
|
||||
bytes32 criteriaMessageHash = getMessageHash(
|
||||
_type,
|
||||
paymentErc20,
|
||||
price,
|
||||
saltNonce
|
||||
);
|
||||
function getMessageHash(
|
||||
uint256 _boxType,
|
||||
address _paymentErc20,
|
||||
uint256 _price,
|
||||
uint256 _saltNonce
|
||||
) public pure returns (bytes32) {
|
||||
return
|
||||
keccak256(
|
||||
abi.encode(
|
||||
keccak256(
|
||||
"set(uint256 item,address token,uint256 price,uint256 salt)"
|
||||
),
|
||||
_boxType,
|
||||
_paymentErc20,
|
||||
_price,
|
||||
_saltNonce
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
checkSigner712(userAddress, criteriaMessageHash, signature);
|
||||
/**
|
||||
* @dev Returns the minimum delay for an operation to become valid.
|
||||
*
|
||||
* This value can be changed by executing an operation that calls `updateDelay`.
|
||||
*/
|
||||
function getMinDelay()
|
||||
public
|
||||
view
|
||||
virtual
|
||||
override
|
||||
returns (uint256 duration)
|
||||
{
|
||||
return _minDelay;
|
||||
}
|
||||
|
||||
IERC20 paymentToken = IERC20(paymentErc20);
|
||||
uint256 allowToPayAmount = paymentToken.allowance(
|
||||
userAddress,
|
||||
address(this)
|
||||
);
|
||||
require(
|
||||
allowToPayAmount >= price,
|
||||
"BEBoxPayment: Invalid token allowance"
|
||||
);
|
||||
// Transfer payment
|
||||
paymentToken.safeTransferFrom(
|
||||
userAddress,
|
||||
paymentReceivedAddress,
|
||||
price
|
||||
);
|
||||
useSignature(signature);
|
||||
// Emit payment event
|
||||
emit BEBoxPaid(boxId, userAddress, _type, price, paymentErc20);
|
||||
}
|
||||
|
||||
function getMessageHash(
|
||||
uint256 _boxType,
|
||||
address _paymentErc20,
|
||||
uint256 _price,
|
||||
uint256 _saltNonce
|
||||
) public pure returns (bytes32) {
|
||||
return
|
||||
keccak256(
|
||||
abi.encode(
|
||||
keccak256("set(uint256 item,address token,uint256 price,uint256 salt)"),
|
||||
_boxType,
|
||||
_paymentErc20,
|
||||
_price,
|
||||
_saltNonce
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the minimum delay for an operation to become valid.
|
||||
*
|
||||
* This value can be changed by executing an operation that calls `updateDelay`.
|
||||
*/
|
||||
function getMinDelay() public view virtual override returns (uint256 duration) {
|
||||
return _minDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Changes the minimum timelock duration for future operations.
|
||||
*
|
||||
* Emits a {MinDelayChange} event.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - the caller must be the timelock itself. This can only be achieved by scheduling and later executing
|
||||
* an operation where the timelock is the target and the data is the ABI-encoded call to this function.
|
||||
*/
|
||||
function updateDelay(uint256 newDelay) external virtual override {
|
||||
require(msg.sender == address(this), "BEBoxMall: caller must be timelock");
|
||||
require(newDelay >= MIN_DELAY, "BEBoxMall: newDelay must greater than or equal to MIN_DELAY");
|
||||
require(newDelay <= MAX_DELAY, "BEBoxMall: newDelay must less than or equal to MAX_DELAY");
|
||||
emit MinDelayChange(_minDelay, newDelay);
|
||||
_minDelay = newDelay;
|
||||
}
|
||||
/**
|
||||
* @dev Changes the minimum timelock duration for future operations.
|
||||
*
|
||||
* Emits a {MinDelayChange} event.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - the caller must be the timelock itself. This can only be achieved by scheduling and later executing
|
||||
* an operation where the timelock is the target and the data is the ABI-encoded call to this function.
|
||||
*/
|
||||
function updateDelay(uint256 newDelay) external virtual override {
|
||||
require(msg.sender == address(this), "BEBoxMall: caller must be timelock");
|
||||
require(
|
||||
newDelay >= MIN_DELAY,
|
||||
"BEBoxMall: newDelay must greater than or equal to MIN_DELAY"
|
||||
);
|
||||
require(
|
||||
newDelay <= MAX_DELAY,
|
||||
"BEBoxMall: newDelay must less than or equal to MAX_DELAY"
|
||||
);
|
||||
emit MinDelayChange(_minDelay, newDelay);
|
||||
_minDelay = newDelay;
|
||||
}
|
||||
}
|
||||
|
@ -13,191 +13,191 @@ import "../core/HasSignature.sol";
|
||||
// this contract will transfer ownership to BETimelockController after deployed
|
||||
// all onlyowner method would add timelock
|
||||
contract MarketPlace is Ownable, HasSignature {
|
||||
using SafeERC20 for IERC20;
|
||||
using SafeMath for uint256;
|
||||
using SafeERC20 for IERC20;
|
||||
using SafeMath for uint256;
|
||||
|
||||
// Supported payment token WETH & list of authorized ERC20
|
||||
mapping(address => bool) public paymentTokens;
|
||||
mapping(bytes => bool) public usedSignatures;
|
||||
// Supported payment token WETH & list of authorized ERC20
|
||||
mapping(address => bool) public paymentTokens;
|
||||
mapping(bytes => bool) public usedSignatures;
|
||||
|
||||
// Address to receive transaction fee
|
||||
address public feeToAddress;
|
||||
uint256 public transactionFee;
|
||||
// Address to receive transaction fee
|
||||
address public feeToAddress;
|
||||
uint256 public transactionFee;
|
||||
|
||||
uint256 public constant MIN_TRANSACTION_FEE = 50;
|
||||
uint256 public constant MAX_TRANSACTION_FEE = 1000;
|
||||
uint256 public constant MIN_TRANSACTION_FEE = 50;
|
||||
uint256 public constant MAX_TRANSACTION_FEE = 1000;
|
||||
|
||||
// Events
|
||||
event MatchTransaction(
|
||||
uint256 indexed tokenId,
|
||||
address contractAddress,
|
||||
uint256 price,
|
||||
address paymentToken,
|
||||
address seller,
|
||||
address buyer,
|
||||
uint256 fee
|
||||
// Events
|
||||
event MatchTransaction(
|
||||
uint256 indexed tokenId,
|
||||
address contractAddress,
|
||||
uint256 price,
|
||||
address paymentToken,
|
||||
address seller,
|
||||
address buyer,
|
||||
uint256 fee
|
||||
);
|
||||
|
||||
constructor() HasSignature("MarketPlace", "1") {}
|
||||
|
||||
function setFeeToAddress(address _feeToAddress) external onlyOwner {
|
||||
require(
|
||||
_feeToAddress != address(0),
|
||||
"fee received address can not be zero"
|
||||
);
|
||||
feeToAddress = _feeToAddress;
|
||||
}
|
||||
|
||||
function setTransactionFee(uint256 _transactionFee) external onlyOwner {
|
||||
require(
|
||||
_transactionFee >= MIN_TRANSACTION_FEE &&
|
||||
_transactionFee <= MAX_TRANSACTION_FEE,
|
||||
"Marketplace: _transactionFee must >= 50 and <= 1000"
|
||||
);
|
||||
transactionFee = _transactionFee;
|
||||
}
|
||||
|
||||
function setPaymentTokens(address[] calldata _paymentTokens)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
for (uint256 i = 0; i < _paymentTokens.length; i++) {
|
||||
if (paymentTokens[_paymentTokens[i]]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
paymentTokens[_paymentTokens[i]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
function removePaymentTokens(address[] calldata _removedPaymentTokens)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
for (uint256 i = 0; i < _removedPaymentTokens.length; i++) {
|
||||
paymentTokens[_removedPaymentTokens[i]] = false;
|
||||
}
|
||||
}
|
||||
|
||||
function ignoreSignature(
|
||||
address[2] calldata addresses,
|
||||
uint256[3] calldata values,
|
||||
bytes calldata signature
|
||||
) external signatureValid(signature) {
|
||||
bytes32 criteriaMessageHash = getMessageHash(
|
||||
addresses[0],
|
||||
values[0],
|
||||
addresses[1],
|
||||
values[1],
|
||||
values[2]
|
||||
);
|
||||
|
||||
constructor()
|
||||
HasSignature("MarketPlace", "1"){
|
||||
checkSigner(_msgSender(), criteriaMessageHash, signature);
|
||||
useSignature(signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Function matched transaction with user signatures
|
||||
*/
|
||||
function matchTransaction(
|
||||
address[3] calldata addresses,
|
||||
uint256[3] calldata values,
|
||||
bytes calldata signature
|
||||
) external signatureValid(signature) 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]], "Marketplace: invalid payment method");
|
||||
|
||||
bytes32 criteriaMessageHash = getMessageHash(
|
||||
addresses[1],
|
||||
values[0],
|
||||
addresses[2],
|
||||
values[1],
|
||||
values[2]
|
||||
);
|
||||
|
||||
checkSigner712(addresses[0], criteriaMessageHash, signature);
|
||||
|
||||
// check current ownership
|
||||
IERC721 nft = IERC721(addresses[1]);
|
||||
require(
|
||||
nft.ownerOf(values[0]) == addresses[0],
|
||||
"Marketplace: seller is not owner of this item now"
|
||||
);
|
||||
|
||||
// Check payment approval and buyer balance
|
||||
IERC20 paymentContract = IERC20(addresses[2]);
|
||||
require(
|
||||
paymentContract.balanceOf(_msgSender()) >= values[1],
|
||||
"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);
|
||||
}
|
||||
|
||||
function setFeeToAddress(address _feeToAddress) external onlyOwner {
|
||||
require(_feeToAddress != address(0), 'fee received address can not be zero');
|
||||
feeToAddress = _feeToAddress;
|
||||
}
|
||||
// transfer item to buyer
|
||||
nft.safeTransferFrom(addresses[0], _msgSender(), values[0]);
|
||||
|
||||
function setTransactionFee(uint256 _transactionFee) external onlyOwner {
|
||||
require(
|
||||
_transactionFee >= MIN_TRANSACTION_FEE && _transactionFee <= MAX_TRANSACTION_FEE,
|
||||
"Marketplace: _transactionFee must >= 50 and <= 1000"
|
||||
);
|
||||
transactionFee = _transactionFee;
|
||||
}
|
||||
useSignature(signature);
|
||||
// emit sale event
|
||||
emitEvent(addresses, values);
|
||||
return true;
|
||||
}
|
||||
|
||||
function setPaymentTokens(address[] calldata _paymentTokens)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
for (uint256 i = 0; i < _paymentTokens.length; i++) {
|
||||
if (paymentTokens[_paymentTokens[i]]) {
|
||||
continue;
|
||||
}
|
||||
/**
|
||||
* @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
|
||||
);
|
||||
}
|
||||
|
||||
paymentTokens[_paymentTokens[i]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
function removePaymentTokens(address[] calldata _removedPaymentTokens)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
for (uint256 i = 0; i < _removedPaymentTokens.length; i++) {
|
||||
paymentTokens[_removedPaymentTokens[i]] = false;
|
||||
}
|
||||
}
|
||||
|
||||
function ignoreSignature(
|
||||
address[2] calldata addresses,
|
||||
uint256[3] calldata values,
|
||||
bytes calldata signature
|
||||
) external signatureValid(signature){
|
||||
bytes32 criteriaMessageHash = getMessageHash(
|
||||
addresses[0],
|
||||
values[0],
|
||||
addresses[1],
|
||||
values[1],
|
||||
values[2]
|
||||
);
|
||||
|
||||
checkSigner(_msgSender(), criteriaMessageHash, signature);
|
||||
useSignature(signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Function matched transaction with user signatures
|
||||
*/
|
||||
function matchTransaction(
|
||||
address[3] calldata addresses,
|
||||
uint256[3] calldata values,
|
||||
bytes calldata signature
|
||||
) external signatureValid(signature) 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]],
|
||||
"Marketplace: invalid payment method"
|
||||
);
|
||||
|
||||
bytes32 criteriaMessageHash = getMessageHash(
|
||||
addresses[1],
|
||||
values[0],
|
||||
addresses[2],
|
||||
values[1],
|
||||
values[2]
|
||||
);
|
||||
|
||||
checkSigner712(addresses[0], criteriaMessageHash, signature);
|
||||
|
||||
// check current ownership
|
||||
IERC721 nft = IERC721(addresses[1]);
|
||||
require(
|
||||
nft.ownerOf(values[0]) == addresses[0],
|
||||
"Marketplace: seller is not owner of this item now"
|
||||
);
|
||||
|
||||
// Check payment approval and buyer balance
|
||||
IERC20 paymentContract = IERC20(addresses[2]);
|
||||
require(
|
||||
paymentContract.balanceOf(_msgSender()) >= values[1],
|
||||
"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);
|
||||
}
|
||||
|
||||
// transfer item to buyer
|
||||
nft.safeTransferFrom(addresses[0], _msgSender(), values[0]);
|
||||
|
||||
useSignature(signature);
|
||||
// emit sale event
|
||||
emitEvent(addresses, values);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 getMessageHash(
|
||||
address _nftAddress,
|
||||
uint256 _tokenId,
|
||||
address _paymentErc20,
|
||||
uint256 _price,
|
||||
uint256 _saltNonce
|
||||
) public pure returns (bytes32) {
|
||||
return
|
||||
keccak256(
|
||||
abi.encode(
|
||||
keccak256("set(address nft,uint256 tokenId,address payToken,uint256 price,uint256 salt)"),
|
||||
_nftAddress,
|
||||
_tokenId,
|
||||
_paymentErc20,
|
||||
_price,
|
||||
_saltNonce
|
||||
)
|
||||
);
|
||||
}
|
||||
function getMessageHash(
|
||||
address _nftAddress,
|
||||
uint256 _tokenId,
|
||||
address _paymentErc20,
|
||||
uint256 _price,
|
||||
uint256 _saltNonce
|
||||
) public pure returns (bytes32) {
|
||||
return
|
||||
keccak256(
|
||||
abi.encode(
|
||||
keccak256(
|
||||
"set(address nft,uint256 tokenId,address payToken,uint256 price,uint256 salt)"
|
||||
),
|
||||
_nftAddress,
|
||||
_tokenId,
|
||||
_paymentErc20,
|
||||
_price,
|
||||
_saltNonce
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -7,301 +7,285 @@ import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import "@openzeppelin/contracts/access/AccessControl.sol";
|
||||
|
||||
abstract contract BEBase1155 is ERC1155, Ownable, AccessControl {
|
||||
using Strings for uint256;
|
||||
using Strings for uint256;
|
||||
|
||||
bytes32 public constant URI_SETTER_ROLE = keccak256("URI_SETTER_ROLE");
|
||||
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
|
||||
bytes32 public constant BURN_ROLE = keccak256("BURN_ROLE");
|
||||
bytes32 public constant LOCK_ROLE = keccak256("LOCK_ROLE");
|
||||
bytes32 public constant URI_SETTER_ROLE = keccak256("URI_SETTER_ROLE");
|
||||
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
|
||||
bytes32 public constant BURN_ROLE = keccak256("BURN_ROLE");
|
||||
bytes32 public constant LOCK_ROLE = keccak256("LOCK_ROLE");
|
||||
|
||||
mapping(uint256 => bool) public lockedTokens;
|
||||
mapping(uint256 => bool) public lockedTokens;
|
||||
|
||||
mapping(address => uint256[]) private _ownedTokens;
|
||||
// for 0 means not exists, value stored = index + 1;
|
||||
mapping(address => mapping(uint256 => uint256)) private _ownedTokensIndex;
|
||||
mapping(address => uint256[]) private _ownedTokens;
|
||||
// for 0 means not exists, value stored = index + 1;
|
||||
mapping(address => mapping(uint256 => uint256)) private _ownedTokensIndex;
|
||||
|
||||
mapping(uint256 => uint256) private _totalSupply;
|
||||
mapping(uint256 => uint256) private _totalSupply;
|
||||
|
||||
uint256 public maxSupply = 0;
|
||||
uint256 public maxSupply = 0;
|
||||
|
||||
struct TokenStruct {
|
||||
uint256 tokenId;
|
||||
uint256 amount;
|
||||
struct TokenStruct {
|
||||
uint256 tokenId;
|
||||
uint256 amount;
|
||||
}
|
||||
|
||||
constructor() ERC1155("https://market.cebg.games/api/nft/info/{id}") {
|
||||
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
|
||||
_grantRole(URI_SETTER_ROLE, msg.sender);
|
||||
}
|
||||
|
||||
function setURI(string memory newuri) external onlyRole(URI_SETTER_ROLE) {
|
||||
_setURI(newuri);
|
||||
}
|
||||
|
||||
function mintBatch(
|
||||
address to,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory amounts,
|
||||
bytes memory data
|
||||
) external onlyRole(MINTER_ROLE) {
|
||||
_mintBatch(to, ids, amounts, data);
|
||||
}
|
||||
|
||||
function burnBatch(
|
||||
address account,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory values
|
||||
) external onlyRole(BURN_ROLE) {
|
||||
_burnBatch(account, ids, values);
|
||||
}
|
||||
|
||||
// The following functions are overrides required by Solidity.
|
||||
|
||||
function supportsInterface(bytes4 interfaceId)
|
||||
public
|
||||
view
|
||||
override(ERC1155, AccessControl)
|
||||
returns (bool)
|
||||
{
|
||||
return super.supportsInterface(interfaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Add factory to mint/burn item
|
||||
*/
|
||||
function setMintFactory(address factory) external onlyOwner {
|
||||
_grantRole(MINTER_ROLE, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Remove factory
|
||||
*/
|
||||
function removeMintFactory(address factory) external onlyOwner {
|
||||
_revokeRole(MINTER_ROLE, factory);
|
||||
}
|
||||
|
||||
function grantLockRole(address account) external onlyOwner {
|
||||
_grantRole(LOCK_ROLE, account);
|
||||
}
|
||||
|
||||
function revokeLockRole(address account) external onlyOwner {
|
||||
_revokeRole(LOCK_ROLE, account);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Add factory to burn item
|
||||
*/
|
||||
function grantBurnRole(address proxy) external onlyOwner {
|
||||
_grantRole(BURN_ROLE, proxy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Remove proxy
|
||||
*/
|
||||
function revokeBurnRole(address proxy) external onlyOwner {
|
||||
_revokeRole(BURN_ROLE, proxy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Total amount of tokens in with a given id.
|
||||
*/
|
||||
function totalSupply(uint256 id) external view virtual returns (uint256) {
|
||||
return _totalSupply[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Indicates whether any token exist with a given id, or not.
|
||||
*/
|
||||
function exists(uint256 id) public view virtual returns (bool) {
|
||||
return _totalSupply[id] > 0;
|
||||
}
|
||||
|
||||
function canMint(
|
||||
uint256 /*id*/
|
||||
) external view virtual returns (bool) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Lock token to use in game or for rental
|
||||
*/
|
||||
function lock(uint256 id) external onlyRole(LOCK_ROLE) {
|
||||
require(_totalSupply[id] > 0, "Must be valid tokenId");
|
||||
require(!lockedTokens[id], "Token has already locked");
|
||||
lockedTokens[id] = true;
|
||||
}
|
||||
|
||||
function lockBatch(uint256[] memory ids) external onlyRole(LOCK_ROLE) {
|
||||
uint256 len = ids.length;
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
uint256 id = ids[i];
|
||||
require(_totalSupply[id] > 0, "Must be valid tokenId");
|
||||
require(!lockedTokens[id], "Token has already locked");
|
||||
lockedTokens[id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
constructor() ERC1155("https://market.cebg.games/api/nft/info/{id}") {
|
||||
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
|
||||
_grantRole(URI_SETTER_ROLE, msg.sender);
|
||||
/**
|
||||
* @dev Unlock token to use blockchain or sale on marketplace
|
||||
*/
|
||||
function unlock(uint256 id) external onlyRole(LOCK_ROLE) {
|
||||
require(_totalSupply[id] > 0, "Must be valid tokenId");
|
||||
require(lockedTokens[id], "Token has already unlocked");
|
||||
lockedTokens[id] = false;
|
||||
}
|
||||
|
||||
function unlockBatch(uint256[] memory ids) external onlyRole(LOCK_ROLE) {
|
||||
uint256 len = ids.length;
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
uint256 id = ids[i];
|
||||
require(_totalSupply[id] > 0, "Must be valid tokenId");
|
||||
require(lockedTokens[id], "Token has already unlocked");
|
||||
lockedTokens[id] = false;
|
||||
}
|
||||
}
|
||||
|
||||
function setURI(string memory newuri) external onlyRole(URI_SETTER_ROLE) {
|
||||
_setURI(newuri);
|
||||
/**
|
||||
* @dev Get lock status
|
||||
*/
|
||||
function isLocked(uint256 id) external view returns (bool) {
|
||||
return lockedTokens[id];
|
||||
}
|
||||
|
||||
function isLockedBatch(uint256[] memory ids)
|
||||
external
|
||||
view
|
||||
returns (bool[] memory)
|
||||
{
|
||||
uint256 len = ids.length;
|
||||
bool[] memory results = new bool[](len);
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
results[i] = lockedTokens[ids[i]];
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function mintBatch(
|
||||
address to,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory amounts,
|
||||
bytes memory data
|
||||
) external onlyRole(MINTER_ROLE) {
|
||||
_mintBatch(to, ids, amounts, data);
|
||||
/**
|
||||
* @dev See {ERC1155-_beforeTokenTransfer}.
|
||||
*/
|
||||
function _beforeTokenTransfer(
|
||||
address operator,
|
||||
address from,
|
||||
address to,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory amounts,
|
||||
bytes memory data
|
||||
) internal virtual override {
|
||||
super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
|
||||
uint256 len = ids.length;
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
require(!lockedTokens[ids[i]], "Can not transfer locked token");
|
||||
}
|
||||
|
||||
|
||||
function burnBatch(
|
||||
address account,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory values
|
||||
) external onlyRole(BURN_ROLE) {
|
||||
_burnBatch(account, ids, values);
|
||||
}
|
||||
|
||||
// The following functions are overrides required by Solidity.
|
||||
|
||||
function supportsInterface(bytes4 interfaceId)
|
||||
public
|
||||
view
|
||||
override(ERC1155, AccessControl)
|
||||
returns (bool)
|
||||
{
|
||||
return super.supportsInterface(interfaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Add factory to mint/burn item
|
||||
*/
|
||||
function setMintFactory(address factory)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
_grantRole(MINTER_ROLE, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Remove factory
|
||||
*/
|
||||
function removeMintFactory(address factory)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
_revokeRole(MINTER_ROLE, factory);
|
||||
}
|
||||
|
||||
function grantLockRole(address account)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
_grantRole(LOCK_ROLE, account);
|
||||
}
|
||||
|
||||
function revokeLockRole(address account)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
_revokeRole(LOCK_ROLE, account);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Add factory to burn item
|
||||
*/
|
||||
function grantBurnRole(address proxy) external onlyOwner {
|
||||
_grantRole(BURN_ROLE, proxy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Remove proxy
|
||||
*/
|
||||
function revokeBurnRole(address proxy) external onlyOwner {
|
||||
_revokeRole(BURN_ROLE, proxy);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dev Total amount of tokens in with a given id.
|
||||
*/
|
||||
function totalSupply(uint256 id) external view virtual returns (uint256) {
|
||||
return _totalSupply[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Indicates whether any token exist with a given id, or not.
|
||||
*/
|
||||
function exists(uint256 id) public view virtual returns (bool) {
|
||||
return _totalSupply[id] > 0;
|
||||
}
|
||||
|
||||
function canMint(uint256 /*id*/) external view virtual returns (bool) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @dev Lock token to use in game or for rental
|
||||
*/
|
||||
function lock(uint256 id) external onlyRole(LOCK_ROLE) {
|
||||
require(_totalSupply[id] > 0, "Must be valid tokenId");
|
||||
require(!lockedTokens[id], "Token has already locked");
|
||||
lockedTokens[id] = true;
|
||||
}
|
||||
|
||||
function lockBatch(uint256[] memory ids) external onlyRole(LOCK_ROLE) {
|
||||
uint256 len = ids.length;
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
uint256 id = ids[i];
|
||||
require(_totalSupply[id] > 0, "Must be valid tokenId");
|
||||
require(!lockedTokens[id], "Token has already locked");
|
||||
lockedTokens[id] = true;
|
||||
if (from == address(0)) {
|
||||
// mint nft
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
_totalSupply[ids[i]] += amounts[i];
|
||||
if (maxSupply > 0) {
|
||||
require(
|
||||
_totalSupply[ids[i]] <= maxSupply,
|
||||
"Can not mint for exceeds max supply"
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (from != to) {
|
||||
// transfer from -> to
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
_removeTokenFromOwnerEnumeration(from, ids[i], amounts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Unlock token to use blockchain or sale on marketplace
|
||||
*/
|
||||
function unlock(uint256 id) external onlyRole(LOCK_ROLE) {
|
||||
require(_totalSupply[id] > 0, "Must be valid tokenId");
|
||||
require(lockedTokens[id], "Token has already unlocked");
|
||||
lockedTokens[id] = false;
|
||||
if (to == address(0)) {
|
||||
// burn nft
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
_totalSupply[ids[i]] -= amounts[i];
|
||||
}
|
||||
} else if (to != from) {
|
||||
// mint or transfer from -> to
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
_addTokenToOwnerEnumeration(to, ids[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function unlockBatch(uint256[] memory ids) external onlyRole(LOCK_ROLE) {
|
||||
uint256 len = ids.length;
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
uint256 id = ids[i];
|
||||
require(_totalSupply[id] > 0, "Must be valid tokenId");
|
||||
require(lockedTokens[id], "Token has already unlocked");
|
||||
lockedTokens[id] = false;
|
||||
}
|
||||
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
|
||||
if (_ownedTokensIndex[to][tokenId] == 0 && balanceOf(to, tokenId) == 0) {
|
||||
_ownedTokensIndex[to][tokenId] = _ownedTokens[to].length + 1;
|
||||
_ownedTokens[to].push(tokenId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get lock status
|
||||
*/
|
||||
function isLocked(uint256 id) external view returns (bool) {
|
||||
return lockedTokens[id];
|
||||
function _removeTokenFromOwnerEnumeration(
|
||||
address from,
|
||||
uint256 tokenId,
|
||||
uint256 amount
|
||||
) private {
|
||||
uint256 balance = balanceOf(from, tokenId);
|
||||
if (balance == amount) {
|
||||
uint256 lastTokenIndex = _ownedTokens[from].length - 1;
|
||||
uint256 tokenIndex = _ownedTokensIndex[from][tokenId] - 1;
|
||||
|
||||
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
|
||||
// Move the last token to the slot of the to-delete token
|
||||
_ownedTokens[from][tokenIndex] = lastTokenId;
|
||||
// Update the moved token's index
|
||||
_ownedTokensIndex[from][lastTokenId] = tokenIndex + 1;
|
||||
|
||||
// This also deletes the contents at the last position of the array
|
||||
delete _ownedTokensIndex[from][tokenId];
|
||||
_ownedTokens[from].pop();
|
||||
}
|
||||
}
|
||||
|
||||
function isLockedBatch(uint256[] memory ids)
|
||||
external
|
||||
view
|
||||
returns (bool[] memory)
|
||||
{
|
||||
uint256 len = ids.length;
|
||||
bool[] memory results = new bool[](len);
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
results[i] = lockedTokens[ids[i]];
|
||||
}
|
||||
return results;
|
||||
function userTokens(
|
||||
address user,
|
||||
uint256 start,
|
||||
uint256 page
|
||||
) external view returns (TokenStruct[] memory) {
|
||||
uint256 size = _ownedTokens[user].length;
|
||||
uint256 max = size;
|
||||
uint256 pageSize = 0;
|
||||
if (start < size) {
|
||||
if (start + page < size) {
|
||||
pageSize = page;
|
||||
max = start + page;
|
||||
} else {
|
||||
pageSize = size - start;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {ERC1155-_beforeTokenTransfer}.
|
||||
*/
|
||||
function _beforeTokenTransfer(
|
||||
address operator,
|
||||
address from,
|
||||
address to,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory amounts,
|
||||
bytes memory data
|
||||
) internal virtual override {
|
||||
super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
|
||||
uint256 len = ids.length;
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
require(!lockedTokens[ids[i]], "Can not transfer locked token");
|
||||
}
|
||||
if (from == address(0)) {
|
||||
// mint nft
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
_totalSupply[ids[i]] += amounts[i];
|
||||
if (maxSupply > 0) {
|
||||
require(
|
||||
_totalSupply[ids[i]] <= maxSupply,
|
||||
"Can not mint for exceeds max supply"
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (from != to) {
|
||||
// transfer from -> to
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
_removeTokenFromOwnerEnumeration(from, ids[i], amounts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (to == address(0)) {
|
||||
// burn nft
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
_totalSupply[ids[i]] -= amounts[i];
|
||||
}
|
||||
} else if (to != from) {
|
||||
// mint or transfer from -> to
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
_addTokenToOwnerEnumeration(to, ids[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
|
||||
if (
|
||||
_ownedTokensIndex[to][tokenId] == 0 && balanceOf(to, tokenId) == 0
|
||||
) {
|
||||
_ownedTokensIndex[to][tokenId] = _ownedTokens[to].length + 1;
|
||||
_ownedTokens[to].push(tokenId);
|
||||
}
|
||||
TokenStruct[] memory results = new TokenStruct[](pageSize);
|
||||
if (pageSize > 0) {
|
||||
for (uint256 i = start; i < max; ++i) {
|
||||
TokenStruct memory dataObj;
|
||||
uint256 tokenId = _ownedTokens[user][i];
|
||||
dataObj.tokenId = tokenId;
|
||||
dataObj.amount = balanceOf(user, tokenId);
|
||||
results[i - start] = dataObj;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function _removeTokenFromOwnerEnumeration(
|
||||
address from,
|
||||
uint256 tokenId,
|
||||
uint256 amount
|
||||
) private {
|
||||
uint256 balance = balanceOf(from, tokenId);
|
||||
if (balance == amount) {
|
||||
uint256 lastTokenIndex = _ownedTokens[from].length - 1;
|
||||
uint256 tokenIndex = _ownedTokensIndex[from][tokenId] - 1;
|
||||
|
||||
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
|
||||
// Move the last token to the slot of the to-delete token
|
||||
_ownedTokens[from][tokenIndex] = lastTokenId;
|
||||
// Update the moved token's index
|
||||
_ownedTokensIndex[from][lastTokenId] = tokenIndex + 1;
|
||||
|
||||
// This also deletes the contents at the last position of the array
|
||||
delete _ownedTokensIndex[from][tokenId];
|
||||
_ownedTokens[from].pop();
|
||||
}
|
||||
}
|
||||
|
||||
function userTokens(
|
||||
address user,
|
||||
uint256 start,
|
||||
uint256 page
|
||||
) external view returns (TokenStruct[] memory) {
|
||||
uint256 size = _ownedTokens[user].length;
|
||||
uint256 max = size;
|
||||
uint256 pageSize = 0;
|
||||
if (start < size) {
|
||||
if (start + page < size) {
|
||||
pageSize = page;
|
||||
max = start + page;
|
||||
} else {
|
||||
pageSize = size - start;
|
||||
}
|
||||
}
|
||||
TokenStruct[] memory results = new TokenStruct[](pageSize);
|
||||
if (pageSize > 0) {
|
||||
for (uint256 i = start; i < max; ++i) {
|
||||
TokenStruct memory dataObj;
|
||||
uint256 tokenId = _ownedTokens[user][i];
|
||||
dataObj.tokenId = tokenId;
|
||||
dataObj.amount = balanceOf(user, tokenId);
|
||||
results[i - start] = dataObj;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function tokenTypes(address user) external view returns (uint256) {
|
||||
return _ownedTokens[user].length;
|
||||
}
|
||||
function tokenTypes(address user) external view returns (uint256) {
|
||||
return _ownedTokens[user].length;
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,13 @@ pragma solidity 0.8.10;
|
||||
import "./BEBase1155.sol";
|
||||
|
||||
contract BEChip1155 is BEBase1155 {
|
||||
mapping(uint256 => uint256) public tokenLevels;
|
||||
mapping(uint256 => uint256) public tokenLevels;
|
||||
|
||||
constructor() {
|
||||
maxSupply = 1;
|
||||
}
|
||||
constructor() {
|
||||
maxSupply = 1;
|
||||
}
|
||||
|
||||
function canMint(uint256 id) external view override returns (bool) {
|
||||
return !exists(id);
|
||||
}
|
||||
function canMint(uint256 id) external view override returns (bool) {
|
||||
return !exists(id);
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,10 @@
|
||||
pragma solidity 0.8.10;
|
||||
|
||||
import "./BEBase1155.sol";
|
||||
|
||||
/**
|
||||
* for Hero and Weapon shard
|
||||
*/
|
||||
contract BEShard is BEBase1155 {
|
||||
|
||||
|
||||
}
|
||||
|
@ -2,11 +2,10 @@
|
||||
pragma solidity 0.8.10;
|
||||
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
|
||||
|
||||
|
||||
contract BECoin is ERC20Burnable {
|
||||
uint256 public constant INITIALIZED_CAP = 100000000 * 1e18;
|
||||
uint256 public constant INITIALIZED_CAP = 100000000 * 1e18;
|
||||
|
||||
constructor() ERC20("CRYPTO ELITE'S COIN", "CEC") {
|
||||
_mint(_msgSender(), INITIALIZED_CAP);
|
||||
}
|
||||
}
|
||||
constructor() ERC20("CRYPTO ELITE'S COIN", "CEC") {
|
||||
_mint(_msgSender(), INITIALIZED_CAP);
|
||||
}
|
||||
}
|
||||
|
@ -4,33 +4,31 @@ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
|
||||
import "@openzeppelin/contracts/security/Pausable.sol";
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
|
||||
/**
|
||||
* this contract will transfer ownership to BETimelockController after deployed
|
||||
* all onlyowner method would add timelock
|
||||
*/
|
||||
contract BEGold is ERC20, ERC20Burnable, Pausable, Ownable {
|
||||
constructor() ERC20("CRYPTO ELITE'S GOLD", "CEG") {}
|
||||
|
||||
function pause() external onlyOwner {
|
||||
_pause();
|
||||
}
|
||||
|
||||
constructor() ERC20("CRYPTO ELITE'S GOLD", "CEG") {
|
||||
}
|
||||
function unpause() external onlyOwner {
|
||||
_unpause();
|
||||
}
|
||||
|
||||
function pause() external onlyOwner {
|
||||
_pause();
|
||||
}
|
||||
function mint(address to, uint256 amount) external onlyOwner {
|
||||
_mint(to, amount);
|
||||
}
|
||||
|
||||
function unpause() external onlyOwner {
|
||||
_unpause();
|
||||
}
|
||||
|
||||
function mint(address to, uint256 amount) external onlyOwner {
|
||||
_mint(to, amount);
|
||||
}
|
||||
|
||||
function _beforeTokenTransfer(address from, address to, uint256 amount)
|
||||
internal
|
||||
whenNotPaused
|
||||
override
|
||||
{
|
||||
super._beforeTokenTransfer(from, to, amount);
|
||||
}
|
||||
}
|
||||
function _beforeTokenTransfer(
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
) internal override whenNotPaused {
|
||||
super._beforeTokenTransfer(from, to, amount);
|
||||
}
|
||||
}
|
||||
|
@ -7,164 +7,157 @@ import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
|
||||
import "@openzeppelin/contracts/access/AccessControl.sol";
|
||||
|
||||
abstract contract BEBase is ERC721, AccessControl, ERC721Enumerable, Ownable {
|
||||
using Strings for uint256;
|
||||
using Strings for uint256;
|
||||
|
||||
mapping(uint256 => bool) public lockedTokens;
|
||||
string private _baseTokenURI = "https://market.cebg.games/api/nft/info/";
|
||||
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
|
||||
bytes32 public constant BURN_ROLE = keccak256("BURN_ROLE");
|
||||
bytes32 public constant LOCK_ROLE = keccak256("LOCK_ROLE");
|
||||
mapping(uint256 => bool) public lockedTokens;
|
||||
string private _baseTokenURI = "https://market.cebg.games/api/nft/info/";
|
||||
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
|
||||
bytes32 public constant BURN_ROLE = keccak256("BURN_ROLE");
|
||||
bytes32 public constant LOCK_ROLE = keccak256("LOCK_ROLE");
|
||||
|
||||
function _baseURI() internal view virtual override returns (string memory) {
|
||||
return _baseTokenURI;
|
||||
}
|
||||
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)
|
||||
external
|
||||
onlyRole(MINTER_ROLE)
|
||||
virtual {
|
||||
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 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)
|
||||
external
|
||||
virtual
|
||||
onlyRole(MINTER_ROLE)
|
||||
{
|
||||
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 Add factory to mint item
|
||||
*/
|
||||
function setMintFactory(address factory) external onlyOwner {
|
||||
_grantRole(MINTER_ROLE, factory);
|
||||
}
|
||||
/**
|
||||
* @dev Add factory to mint item
|
||||
*/
|
||||
function setMintFactory(address factory) external onlyOwner {
|
||||
_grantRole(MINTER_ROLE, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Remove factory
|
||||
*/
|
||||
function removeMintFactory(address factory) external onlyOwner {
|
||||
_revokeRole(MINTER_ROLE, factory);
|
||||
}
|
||||
/**
|
||||
* @dev Remove factory
|
||||
*/
|
||||
function removeMintFactory(address factory) external onlyOwner {
|
||||
_revokeRole(MINTER_ROLE, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Add factory to burn item
|
||||
*/
|
||||
function grantBurnRole(address proxy) external onlyOwner {
|
||||
_grantRole(BURN_ROLE, proxy);
|
||||
}
|
||||
/**
|
||||
* @dev Add factory to burn item
|
||||
*/
|
||||
function grantBurnRole(address proxy) external onlyOwner {
|
||||
_grantRole(BURN_ROLE, proxy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Remove proxy
|
||||
*/
|
||||
function revokeBurnProxy(address proxy) external onlyOwner {
|
||||
_revokeRole(BURN_ROLE, proxy);
|
||||
}
|
||||
/**
|
||||
* @dev Add address to lock item
|
||||
*/
|
||||
function grantLockRole(address account)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
_grantRole(LOCK_ROLE, account);
|
||||
}
|
||||
/**
|
||||
* @dev Remove address for lock item
|
||||
*/
|
||||
function revokeLockRole(address account)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
_revokeRole(LOCK_ROLE, account);
|
||||
}
|
||||
/**
|
||||
* @dev Remove proxy
|
||||
*/
|
||||
function revokeBurnProxy(address proxy) external onlyOwner {
|
||||
_revokeRole(BURN_ROLE, proxy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Add address to lock item
|
||||
*/
|
||||
function grantLockRole(address account) external onlyOwner {
|
||||
_grantRole(LOCK_ROLE, account);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Lock token to use in game or for rental
|
||||
*/
|
||||
function lock(uint256 tokenId) external onlyRole(LOCK_ROLE) {
|
||||
require(_exists(tokenId), "Must be valid tokenId");
|
||||
require(!lockedTokens[tokenId], "Token has already locked");
|
||||
lockedTokens[tokenId] = true;
|
||||
}
|
||||
/**
|
||||
* @dev Remove address for lock item
|
||||
*/
|
||||
function revokeLockRole(address account) external onlyOwner {
|
||||
_revokeRole(LOCK_ROLE, account);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Unlock token to use blockchain or sale on marketplace
|
||||
*/
|
||||
function unlock(uint256 tokenId) external onlyRole(LOCK_ROLE){
|
||||
require(_exists(tokenId), "Must be valid tokenId");
|
||||
require(lockedTokens[tokenId], "Token has already unlocked");
|
||||
lockedTokens[tokenId] = false;
|
||||
}
|
||||
/**
|
||||
* @dev Lock token to use in game or for rental
|
||||
*/
|
||||
function lock(uint256 tokenId) external onlyRole(LOCK_ROLE) {
|
||||
require(_exists(tokenId), "Must be valid tokenId");
|
||||
require(!lockedTokens[tokenId], "Token has already locked");
|
||||
lockedTokens[tokenId] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get lock status
|
||||
*/
|
||||
function isLocked(uint256 tokenId) external view returns (bool) {
|
||||
return lockedTokens[tokenId];
|
||||
}
|
||||
/**
|
||||
* @dev Unlock token to use blockchain or sale on marketplace
|
||||
*/
|
||||
function unlock(uint256 tokenId) external onlyRole(LOCK_ROLE) {
|
||||
require(_exists(tokenId), "Must be valid tokenId");
|
||||
require(lockedTokens[tokenId], "Token has already unlocked");
|
||||
lockedTokens[tokenId] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Set token URI
|
||||
*/
|
||||
function updateBaseURI(string calldata baseTokenURI) external onlyOwner {
|
||||
_baseTokenURI = baseTokenURI;
|
||||
}
|
||||
/**
|
||||
* @dev Get lock status
|
||||
*/
|
||||
function isLocked(uint256 tokenId) external view returns (bool) {
|
||||
return lockedTokens[tokenId];
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 Set token URI
|
||||
*/
|
||||
function updateBaseURI(string calldata baseTokenURI) external onlyOwner {
|
||||
_baseTokenURI = baseTokenURI;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC165-supportsInterface}.
|
||||
*/
|
||||
function supportsInterface(bytes4 interfaceId)
|
||||
public
|
||||
view
|
||||
virtual
|
||||
override(AccessControl, ERC721, ERC721Enumerable)
|
||||
returns (bool)
|
||||
{
|
||||
return super.supportsInterface(interfaceId);
|
||||
}
|
||||
/**
|
||||
* @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 Burns `tokenId`.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - The caller must own `tokenId` or be an approved operator.
|
||||
*/
|
||||
function burn(address owner, uint256 tokenId)
|
||||
external
|
||||
onlyRole(BURN_ROLE)
|
||||
virtual
|
||||
{
|
||||
require(_exists(tokenId), "TokenId not exists");
|
||||
require(!lockedTokens[tokenId], "Can not burn locked token");
|
||||
require(
|
||||
ownerOf(tokenId) == owner,
|
||||
"current address is not owner of this item now"
|
||||
);
|
||||
_burn(tokenId);
|
||||
}
|
||||
/**
|
||||
* @dev See {IERC165-supportsInterface}.
|
||||
*/
|
||||
function supportsInterface(bytes4 interfaceId)
|
||||
public
|
||||
view
|
||||
virtual
|
||||
override(AccessControl, ERC721, ERC721Enumerable)
|
||||
returns (bool)
|
||||
{
|
||||
return super.supportsInterface(interfaceId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dev Burns `tokenId`.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - The caller must own `tokenId` or be an approved operator.
|
||||
*/
|
||||
function burn(address owner, uint256 tokenId)
|
||||
external
|
||||
virtual
|
||||
onlyRole(BURN_ROLE)
|
||||
{
|
||||
require(_exists(tokenId), "TokenId not exists");
|
||||
require(!lockedTokens[tokenId], "Can not burn locked token");
|
||||
require(
|
||||
ownerOf(tokenId) == owner,
|
||||
"current address is not owner of this item now"
|
||||
);
|
||||
_burn(tokenId);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,6 @@ import "./BEBase.sol";
|
||||
|
||||
// this contract will transfer ownership to BETimelockController after deployed
|
||||
// all onlyowner method would add timelock
|
||||
contract BEChip is BEBase{
|
||||
constructor() ERC721("CRYPTO ELITE'S CHIP", "CHIP") {}
|
||||
contract BEChip is BEBase {
|
||||
constructor() ERC721("CRYPTO ELITE'S CHIP", "CHIP") {}
|
||||
}
|
||||
|
@ -4,6 +4,6 @@ import "./BEBase.sol";
|
||||
|
||||
// this contract will transfer ownership to BETimelockController after deployed
|
||||
// all onlyowner method would add timelock
|
||||
contract BEEquipment is BEBase{
|
||||
constructor() ERC721("CRYPTO ELITE'S WEAPON", "WEAPON") {}
|
||||
contract BEEquipment is BEBase {
|
||||
constructor() ERC721("CRYPTO ELITE'S WEAPON", "WEAPON") {}
|
||||
}
|
||||
|
@ -4,6 +4,6 @@ import "./BEBase.sol";
|
||||
|
||||
// this contract will transfer ownership to BETimelockController after deployed
|
||||
// all onlyowner method would add timelock
|
||||
contract BEHero is BEBase{
|
||||
constructor() ERC721("CRYPTO ELITE'S HERO", "HERO") {}
|
||||
}
|
||||
contract BEHero is BEBase {
|
||||
constructor() ERC721("CRYPTO ELITE'S HERO", "HERO") {}
|
||||
}
|
||||
|
@ -4,23 +4,26 @@ import "./BEBase.sol";
|
||||
|
||||
// this contract will transfer ownership to BETimelockController after deployed
|
||||
// all onlyowner method would add timelock
|
||||
contract BEMysteryBox is BEBase{
|
||||
constructor() ERC721("CRYPTO ELITE'S MYSTERY BOXES", "BOX") {}
|
||||
|
||||
function userTokens(address user, uint256 start, uint256 page)
|
||||
external view returns (uint256 [] memory){
|
||||
uint256 size = balanceOf(user);
|
||||
uint256[] memory results = new uint256[](page);
|
||||
if (start < size) {
|
||||
uint256 max = size;
|
||||
if (start + page < size) {
|
||||
max = start + page;
|
||||
}
|
||||
for (uint256 i = start; i < max; ++i) {
|
||||
uint256 tokenId = tokenOfOwnerByIndex(user, i);
|
||||
results[i-start] = tokenId;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
contract BEMysteryBox is BEBase {
|
||||
constructor() ERC721("CRYPTO ELITE'S MYSTERY BOXES", "BOX") {}
|
||||
|
||||
function userTokens(
|
||||
address user,
|
||||
uint256 start,
|
||||
uint256 page
|
||||
) external view returns (uint256[] memory) {
|
||||
uint256 size = balanceOf(user);
|
||||
uint256[] memory results = new uint256[](page);
|
||||
if (start < size) {
|
||||
uint256 max = size;
|
||||
if (start + page < size) {
|
||||
max = start + page;
|
||||
}
|
||||
for (uint256 i = start; i < max; ++i) {
|
||||
uint256 tokenId = tokenOfOwnerByIndex(user, i);
|
||||
results[i - start] = tokenId;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
@ -5,13 +5,14 @@ import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
contract TimeChecker is Ownable {
|
||||
mapping(address => bool) private approvalLists;
|
||||
uint256 private _duration;
|
||||
|
||||
|
||||
constructor() {
|
||||
_duration = 1 days;
|
||||
}
|
||||
|
||||
modifier timeValid(uint256 time) {
|
||||
require(
|
||||
time + _duration >= block.timestamp,
|
||||
time + _duration >= block.timestamp,
|
||||
"expired, please send another transaction with new signature"
|
||||
);
|
||||
_;
|
||||
@ -20,10 +21,9 @@ contract TimeChecker is Ownable {
|
||||
/**
|
||||
* @dev Returns the max duration for function called by user
|
||||
*/
|
||||
function getDuration() external view
|
||||
returns (uint256 duration) {
|
||||
return _duration;
|
||||
}
|
||||
function getDuration() external view returns (uint256 duration) {
|
||||
return _duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Change duration value
|
||||
@ -31,6 +31,4 @@ contract TimeChecker is Ownable {
|
||||
function updateDuation(uint256 valNew) external onlyOwner {
|
||||
_duration = valNew;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,12 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.10;
|
||||
|
||||
library UInt{
|
||||
|
||||
function asSingletonArray(uint256 element) internal pure returns (uint256[] memory) {
|
||||
library UInt {
|
||||
function asSingletonArray(uint256 element)
|
||||
internal
|
||||
pure
|
||||
returns (uint256[] memory)
|
||||
{
|
||||
uint256[] memory array = new uint256[](1);
|
||||
array[0] = element;
|
||||
return array;
|
||||
|
104
package-lock.json
generated
104
package-lock.json
generated
@ -1426,6 +1426,15 @@
|
||||
"integrity": "sha1-n7OjzzEyMoFR81PeRjLgHlIQK+o=",
|
||||
"dev": true
|
||||
},
|
||||
"@solidity-parser/parser": {
|
||||
"version": "0.14.3",
|
||||
"resolved": "https://registry.npmmirror.com/@solidity-parser/parser/-/parser-0.14.3.tgz",
|
||||
"integrity": "sha512-29g2SZ29HtsqA58pLCtopI1P/cPy5/UAzlcAXO6T/CNJimG6yA8kx4NaseMyJULiC+TEs02Y9/yeHzClqoA0hw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"antlr4ts": "^0.5.0-alpha.4"
|
||||
}
|
||||
},
|
||||
"@szmarczak/http-timer": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/@szmarczak/http-timer/download/@szmarczak/http-timer-1.1.2.tgz",
|
||||
@ -3061,6 +3070,12 @@
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"antlr4ts": {
|
||||
"version": "0.5.0-alpha.4",
|
||||
"resolved": "https://registry.npmmirror.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz",
|
||||
"integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==",
|
||||
"dev": true
|
||||
},
|
||||
"any-signal": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/any-signal/download/any-signal-2.1.2.tgz",
|
||||
@ -15296,6 +15311,89 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"prettier": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.7.1.tgz",
|
||||
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
|
||||
"dev": true
|
||||
},
|
||||
"prettier-plugin-solidity": {
|
||||
"version": "1.0.0-beta.24",
|
||||
"resolved": "https://registry.npmmirror.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.24.tgz",
|
||||
"integrity": "sha512-6JlV5BBTWzmDSq4kZ9PTXc3eLOX7DF5HpbqmmaF+kloyUwOZbJ12hIYsUaZh2fVgZdV2t0vWcvY6qhILhlzgqg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@solidity-parser/parser": "^0.14.3",
|
||||
"emoji-regex": "^10.1.0",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"semver": "^7.3.7",
|
||||
"solidity-comments-extractor": "^0.0.7",
|
||||
"string-width": "^4.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.1.0.tgz",
|
||||
"integrity": "sha512-xAEnNCT3w2Tg6MA7ly6QqYJvEoY1tm9iIjJ3yMKK9JPlWuRHAMoe5iETwQnx3M9TVbFMfsrBgWKR+IsmswwNjg==",
|
||||
"dev": true
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"dev": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.3.7.tgz",
|
||||
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"printj": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/printj/download/printj-1.1.2.tgz",
|
||||
@ -16708,6 +16806,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"solidity-comments-extractor": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz",
|
||||
"integrity": "sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==",
|
||||
"dev": true
|
||||
},
|
||||
"source-list-map": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/source-list-map/download/source-list-map-2.0.1.tgz",
|
||||
|
@ -6,6 +6,8 @@
|
||||
"scripts": {
|
||||
"test": "npx truffle test",
|
||||
"build": "truffle compile",
|
||||
"prettier": "prettier --write 'contracts/**/*.sol'",
|
||||
"lint": "prettier --list-different 'contracts/**/*.sol'",
|
||||
"deploy:dev": "truffle migrate --network development",
|
||||
"deploy:20": "truffle migrate --network lan20 --compile-none",
|
||||
"deploy:22": "truffle migrate --network lan22 --compile-none",
|
||||
@ -20,6 +22,8 @@
|
||||
"@openzeppelin/test-helpers": "^0.5.15",
|
||||
"@truffle/hdwallet-provider": "^2.0.0",
|
||||
"chai": "^4.3.4",
|
||||
"prettier": "^2.7.1",
|
||||
"prettier-plugin-solidity": "^1.0.0-beta.24",
|
||||
"truffle": "^5.4.23",
|
||||
"truffle-plugin-stdjsonin": "git+https://github.com/mhrsalehi/truffle-plugin-stdjsonin.git",
|
||||
"truffle-plugin-verify": "^0.5.25"
|
||||
|
Loading…
x
Reference in New Issue
Block a user