From 1858260d003a581f5bec78a422fb1eafbf0fbd21 Mon Sep 17 00:00:00 2001 From: cebgcontract <99630598+cebgcontract@users.noreply.github.com> Date: Tue, 16 Aug 2022 16:00:49 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=BB=A3=E7=A0=81,=20?= =?UTF-8?q?=E5=B0=86=E4=B8=80=E4=BA=9B=E9=87=8D=E5=A4=8D=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=8F=90=E5=87=BA=E6=9D=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contracts/core/HasSignature.sol | 28 ++++- contracts/interfaces/INFTFactory.sol | 2 +- contracts/logic/EvolveFactory.sol | 154 ++++++++------------------ contracts/logic/MinterFactory.sol | 71 +----------- contracts/logic/UserEvolveFactory.sol | 48 ++++++++ contracts/logic/UserMinterFactory.sol | 12 +- contracts/market/BEBoxMall.sol | 9 +- contracts/market/MarketPlace.sol | 20 +--- contracts/utils/Approval.sol | 30 +++++ contracts/utils/TimeChecker.sol | 36 ++++++ 10 files changed, 211 insertions(+), 199 deletions(-) create mode 100644 contracts/logic/UserEvolveFactory.sol create mode 100644 contracts/utils/Approval.sol create mode 100644 contracts/utils/TimeChecker.sol diff --git a/contracts/core/HasSignature.sol b/contracts/core/HasSignature.sol index fd49534..0398d71 100644 --- a/contracts/core/HasSignature.sol +++ b/contracts/core/HasSignature.sol @@ -1,8 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "../utils/Approval.sol"; -contract HasSignature { +contract HasSignature is Ownable, Approval{ bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; uint256 private immutable _CACHED_CHAIN_ID; address private immutable _CACHED_THIS; @@ -10,6 +12,7 @@ contract HasSignature { 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)); @@ -112,4 +115,25 @@ contract HasSignature { address recovered = ECDSA.recover(ethSignedMessageHash, signature); require(recovered == signer, "[BE] invalid signature"); } -} \ No newline at end of file + + 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; + } + } + +} diff --git a/contracts/interfaces/INFTFactory.sol b/contracts/interfaces/INFTFactory.sol index f4b19eb..4ae4002 100644 --- a/contracts/interfaces/INFTFactory.sol +++ b/contracts/interfaces/INFTFactory.sol @@ -23,7 +23,7 @@ interface INFTFactory { IBEERC1155 nft ) external; - function ignoreSignature( + function useSignature( bytes calldata signature ) external; } diff --git a/contracts/logic/EvolveFactory.sol b/contracts/logic/EvolveFactory.sol index 5fca83a..4776df3 100644 --- a/contracts/logic/EvolveFactory.sol +++ b/contracts/logic/EvolveFactory.sol @@ -6,28 +6,24 @@ import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "../interfaces/IBEERC721.sol"; import "../interfaces/IBEERC1155.sol"; import "../utils/UInt.sol"; +import "../utils/TimeChecker.sol"; import "../core/HasSignature.sol"; // this contract will transfer ownership to BETimelockController after deployed // all onlyowner method would add timelock -contract EvolveFactory is Ownable, Initializable, HasSignature { - +contract EvolveFactory is Ownable, TimeChecker, Initializable, HasSignature { using UInt for uint256; IBEERC721 public hero; IBEERC721 public equip; IBEERC1155 public chip; - - mapping(bytes => bool) public usedSignatures; + uint256 private _duration; address public executor; event TokenEvolved( - uint256 indexed evolveEventId, address indexed owner, - uint256 tokenEvolved, - uint256 tokenBurned, - uint256 chip + uint256[3] tokenIds ); constructor() @@ -48,70 +44,14 @@ contract EvolveFactory is Ownable, Initializable, HasSignature { executor = account; } - /** - * @dev evolve function to Blissful Elites Hero NFT - * tokenIds: [hero_to_evolve, hero_for_burn, chip] - */ - function evolveHero( - uint256 evolveEventId, - uint256[3] calldata tokenIds, - uint256 saltNonce, - bytes calldata signature - ) external { - require( - tokenIds[0] > 0 && tokenIds[1] > 0, - "EvolveFactory: hero to evolve and burn can not be 0" - ); - - require( - tokenIds[0] != tokenIds[1], - "EvolveFactory: hero to evolve and burn can not be same" - ); - - require( - hero.ownerOf(tokenIds[0]) == msg.sender, - "EvolveFactory: not owner of this hero now" - ); - - require( - !usedSignatures[signature], - "EvolveFactory: signature used. please send another transaction with new signature" - ); - bytes32 criteriaMessageHash = getMessageHash( - evolveEventId, - tokenIds[0], - tokenIds[1], - tokenIds[2], - saltNonce - ); - checkSigner(executor, criteriaMessageHash, signature); - hero.burn(msg.sender, tokenIds[1]); - if (tokenIds[2] > 0) { - uint256 amount = 1; - chip.burnBatch(msg.sender, - tokenIds[2].asSingletonArray(), - amount.asSingletonArray()); - } - usedSignatures[signature] = true; - emit TokenEvolved( - evolveEventId, - msg.sender, - tokenIds[0], - tokenIds[1], - tokenIds[2] - ); - } - - /** - * @dev evolve function to Blissful Elites Equip NFT - * tokenIds: [equip_to_evolve, equip_for_burn, chip] - */ - function evolveEquip( - uint256 evolveEventId, - uint256[3] calldata tokenIds, - uint256 saltNonce, - bytes calldata signature - ) external{ + function evolve721NFT( + address to, + uint256[3] calldata tokenIds, + uint256 startTime, + uint256 saltNonce, + bytes calldata signature, + IBEERC721 nft + ) internal signatureValid(signature) timeValid(startTime){ require( tokenIds[0] > 0 && tokenIds[1] > 0, "EvolveFactory: equip to evolve and burn can not be 0" @@ -123,56 +63,60 @@ contract EvolveFactory is Ownable, Initializable, HasSignature { ); require( - equip.ownerOf(tokenIds[0]) == msg.sender, - "EvolveFactory: current address is not owner of this equip now" + nft.ownerOf(tokenIds[0]) == to, + "EvolveFactory: current address is not owner of this nft now" ); require( - !usedSignatures[signature], - "EvolveFactory: signature used. please send another transaction with new signature" - ); + nft.ownerOf(tokenIds[1]) == to, + "EvolveFactory: current address is not owner of this nft now" + ); + if (tokenIds[2] > 0) { + require( + nft.ownerOf(tokenIds[2]) == to, + "EvolveFactory: current address is not owner of this nft now" + ); + } + + uint256[] memory signArray = new uint256[](3); + for (uint256 i = 0; i < tokenIds.length; ++ i) { + signArray[i] = tokenIds[i]; + } bytes32 criteriaMessageHash = getMessageHash( - evolveEventId, - tokenIds[0], - tokenIds[1], - tokenIds[2], - saltNonce + to, + startTime, + saltNonce, + signArray ); checkSigner(executor, criteriaMessageHash, signature); - equip.burn(msg.sender, tokenIds[1]); + nft.burn(to, tokenIds[1]); if (tokenIds[2] > 0) { uint256 amount = 1; - chip.burnBatch(msg.sender, + chip.burnBatch(to, tokenIds[2].asSingletonArray(), amount.asSingletonArray()); } - usedSignatures[signature] = true; + useSignature(signature); emit TokenEvolved( - evolveEventId, - msg.sender, - tokenIds[0], - tokenIds[1], - tokenIds[2] + to, + tokenIds ); } + + function getMessageHash( - uint256 _eventId, - uint256 _mainToken, - uint256 _burnToken, - uint256 _chipToken, - uint256 _saltNonce + address _to, + uint256 _startTime, + uint256 _saltNonce, + uint256[] memory _ids ) public pure returns (bytes32) { - return - keccak256( - abi.encodePacked( - _eventId, - _mainToken, - _burnToken, - _chipToken, - _saltNonce - ) - ); + bytes memory encoded = abi.encodePacked(_to, _startTime, _saltNonce); + uint256 len = _ids.length; + for (uint256 i = 0; i < len; ++i) { + encoded = bytes.concat(encoded, abi.encodePacked(_ids[i])); + } + return keccak256(encoded); } } diff --git a/contracts/logic/MinterFactory.sol b/contracts/logic/MinterFactory.sol index 93c7787..8d0e749 100644 --- a/contracts/logic/MinterFactory.sol +++ b/contracts/logic/MinterFactory.sol @@ -6,9 +6,10 @@ import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "../interfaces/IBEERC721.sol"; import "../interfaces/IBEERC1155.sol"; import "../utils/UInt.sol"; +import "../utils/TimeChecker.sol"; import "../core/HasSignature.sol"; -contract MinterFactory is Ownable, Initializable, HasSignature { +contract MinterFactory is Ownable, TimeChecker, Initializable, HasSignature { using UInt for uint256; address public executor; // NFT contract @@ -16,8 +17,6 @@ contract MinterFactory is Ownable, Initializable, HasSignature { IBEERC721 public equip; IBEERC1155 public chip; IBEERC1155 public shard; - uint256 private _duration; - mapping(address => bool) public approvalLists; event TokenMinted( address contractAddress, @@ -33,7 +32,6 @@ contract MinterFactory is Ownable, Initializable, HasSignature { uint256[] amounts ); - mapping(bytes => bool) public usedSignatures; constructor() HasSignature("MinterFactory", "1") {} @@ -42,23 +40,8 @@ contract MinterFactory is Ownable, Initializable, HasSignature { equip = IBEERC721(_erc721s[1]); chip = IBEERC1155(_erc721s[2]); shard = IBEERC1155(_erc721s[3]); - _duration = 1 days; } - /** - * @dev Allow operation to reverse signature. - */ - function addApprovalList(address user) external onlyOwner { - require(!approvalLists[user], "MinterFactory: Invalid user address"); - approvalLists[user] = true; - } - - /** - * @dev Remove operation from approval list. - */ - function removeApprovalList(address user) external onlyOwner { - approvalLists[user] = false; - } /** * @dev update executor @@ -68,21 +51,6 @@ contract MinterFactory is Ownable, Initializable, HasSignature { executor = account; } - /** - * @dev Returns the max duration for function called by user - */ - function getDuration() external view - returns (uint256 duration) { - return _duration; - } - - /** - * @dev Change duration value - */ - function updateDuation(uint256 valNew) external onlyOwner { - _duration = valNew; - } - /** * @dev mint function to distribute Hero NFT to user */ @@ -158,21 +126,13 @@ contract MinterFactory is Ownable, Initializable, HasSignature { uint256 saltNonce, bytes calldata signature, IBEERC721 nft - ) external { - require( - startTime + _duration >= block.timestamp, - "MinterFactory: signature expired, please send another transaction with new signature" - ); - require( - !usedSignatures[signature], - "MinterFactory: signature used. please send another transaction with new signature" - ); + ) external signatureValid(signature) timeValid(startTime){ uint256[] memory signArray = new uint256[](1); signArray[0] = id; bytes32 criteriaMessageHash = getMessageHash(to, startTime, saltNonce, signArray); checkSigner(executor, criteriaMessageHash, signature); mint721NFT(to, id, nft); - usedSignatures[signature] = true; + useSignature(signature); } function mint1155BatchByUser( @@ -183,21 +143,13 @@ contract MinterFactory is Ownable, Initializable, HasSignature { uint256 saltNonce, bytes calldata signature, IBEERC1155 nft - ) external { + ) 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" ); - require( - startTime + _duration >= block.timestamp, - "MinterFactory: signature expired, please send another transaction with new signature" - ); - require( - !usedSignatures[signature], - "MinterFactory: signature used. please send another transaction with new signature" - ); uint256[] memory signArray = new uint256[](len * 2); for (uint256 i = 0; i < len; ++i) { require( @@ -210,7 +162,7 @@ contract MinterFactory is Ownable, Initializable, HasSignature { bytes32 criteriaMessageHash = getMessageHash(to, startTime, saltNonce, signArray); checkSigner(executor, criteriaMessageHash, signature); mint1155NFTBatch(to, ids, amounts, nft); - usedSignatures[signature] = true; + useSignature(signature); } function mint721NFT( @@ -244,17 +196,6 @@ contract MinterFactory is Ownable, Initializable, HasSignature { emit TokenMintedBatch(address(nft), to, ids, amounts); } - function ignoreSignature( - bytes calldata signature - ) external { - require( - approvalLists[_msgSender()], - "Must be valid approval list" - ); - if (!usedSignatures[signature]) { - usedSignatures[signature] = true; - } - } function getMessageHash( address _to, diff --git a/contracts/logic/UserEvolveFactory.sol b/contracts/logic/UserEvolveFactory.sol new file mode 100644 index 0000000..9a76563 --- /dev/null +++ b/contracts/logic/UserEvolveFactory.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; +import "../interfaces/INFTFactory.sol"; +import "../interfaces/IBEERC721.sol"; +import "../interfaces/IBEERC1155.sol"; + +contract UserEvolveFactory is Ownable, Initializable { + IBEERC721 public hero; + IBEERC721 public equip; + IBEERC1155 public chip; + IBEERC1155 public shard; + + event TokenEvolveFail ( + address indexed to, + uint256 mainToken, + bytes signature, + string reason, + bytes byteReason + ); + + function init() external initializer onlyOwner { + } + + /** + * @dev evolve function to Blissful Elites Hero NFT + * tokenIds: [hero_to_evolve, hero_for_burn, chip] + */ + function evolveHero( + uint256[3] calldata tokenIds, + uint256 saltNonce, + bytes calldata signature + ) external { + + } + /** + * @dev evolve function to Blissful Elites Equip NFT + * tokenIds: [equip_to_evolve, equip_for_burn, chip] + */ + function evolveEquip( + uint256[3] calldata tokenIds, + uint256 saltNonce, + bytes calldata signature + ) external{ + } + +} diff --git a/contracts/logic/UserMinterFactory.sol b/contracts/logic/UserMinterFactory.sol index ab5a048..d717492 100644 --- a/contracts/logic/UserMinterFactory.sol +++ b/contracts/logic/UserMinterFactory.sol @@ -42,7 +42,7 @@ contract UserMinterFactory is Ownable, Initializable { return true; } catch Error(string memory reason) { bytes memory by; - factory.ignoreSignature(signature); + factory.useSignature(signature); emit TokenMintFail( to, signature, @@ -51,6 +51,7 @@ contract UserMinterFactory is Ownable, Initializable { ); return false; } catch (bytes memory lowLevelData) { + factory.useSignature(signature); string memory reason; emit TokenMintFail( to, @@ -77,7 +78,7 @@ contract UserMinterFactory is Ownable, Initializable { return true; } catch Error(string memory reason) { bytes memory by; - factory.ignoreSignature(signature); + factory.useSignature(signature); emit TokenMintFail( to, signature, @@ -86,6 +87,7 @@ contract UserMinterFactory is Ownable, Initializable { ); return false; } catch (bytes memory lowLevelData) { + factory.useSignature(signature); string memory reason; emit TokenMintFail( to, @@ -117,7 +119,7 @@ contract UserMinterFactory is Ownable, Initializable { return true; } catch Error(string memory reason) { bytes memory by; - factory.ignoreSignature(signature); + factory.useSignature(signature); emit TokenMintFail( to, signature, @@ -126,6 +128,7 @@ contract UserMinterFactory is Ownable, Initializable { ); return false; } catch (bytes memory lowLevelData) { + factory.useSignature(signature); string memory reason; emit TokenMintFail( to, @@ -152,7 +155,7 @@ contract UserMinterFactory is Ownable, Initializable { return true; } catch Error(string memory reason) { bytes memory by; - factory.ignoreSignature(signature); + factory.useSignature(signature); emit TokenMintFail( to, signature, @@ -161,6 +164,7 @@ contract UserMinterFactory is Ownable, Initializable { ); return false; } catch (bytes memory lowLevelData) { + factory.useSignature(signature); string memory reason; emit TokenMintFail( to, diff --git a/contracts/market/BEBoxMall.sol b/contracts/market/BEBoxMall.sol index 9532752..9ab01e8 100644 --- a/contracts/market/BEBoxMall.sol +++ b/contracts/market/BEBoxMall.sol @@ -36,7 +36,6 @@ contract BEBoxMall is Ownable, HasSignature, TimelockController{ ); address public paymentReceivedAddress; - mapping(bytes => bool) public usedSignatures; function setPaymentReceivedAddress(address _paymentReceivedAddress) public @@ -62,17 +61,13 @@ contract BEBoxMall is Ownable, HasSignature, TimelockController{ address paymentErc20, uint256 saltNonce, bytes calldata signature - ) external onlyOwner { + ) 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"); - require( - !usedSignatures[signature], - "BEBoxPayment: signature used. please send another transaction with new signature" - ); bytes32 criteriaMessageHash = getMessageHash( _type, paymentErc20, @@ -97,7 +92,7 @@ contract BEBoxMall is Ownable, HasSignature, TimelockController{ paymentReceivedAddress, price ); - usedSignatures[signature] = true; + useSignature(signature); // Emit payment event emit BEBoxPaid(boxId, userAddress, _type, price, paymentErc20); } diff --git a/contracts/market/MarketPlace.sol b/contracts/market/MarketPlace.sol index c652f97..6a48340 100644 --- a/contracts/market/MarketPlace.sol +++ b/contracts/market/MarketPlace.sol @@ -81,11 +81,7 @@ contract MarketPlace is Ownable, HasSignature { address[2] calldata addresses, uint256[3] calldata values, bytes calldata signature - ) external { - require( - !usedSignatures[signature], - "Marketplace: this signature has added to ignore list already" - ); + ) external signatureValid(signature){ bytes32 criteriaMessageHash = getMessageHash( addresses[0], values[0], @@ -95,8 +91,7 @@ contract MarketPlace is Ownable, HasSignature { ); checkSigner(_msgSender(), criteriaMessageHash, signature); - - usedSignatures[signature] = true; + useSignature(signature); } /** @@ -106,7 +101,7 @@ contract MarketPlace is Ownable, HasSignature { address[3] calldata addresses, uint256[3] calldata values, bytes calldata signature - ) external returns (bool) { + ) external signatureValid(signature) returns (bool) { // address[3] [seller_address,nft_address,payment_token_address] // uint256[3] [token_id,price,salt_nonce] // bytes seller_signature @@ -115,11 +110,6 @@ contract MarketPlace is Ownable, HasSignature { "Marketplace: invalid payment method" ); - require( - !usedSignatures[signature], - "Marketplace: signature used. please send another transaction with new signature" - ); - bytes32 criteriaMessageHash = getMessageHash( addresses[1], values[0], @@ -167,7 +157,7 @@ contract MarketPlace is Ownable, HasSignature { // transfer item to buyer nft.safeTransferFrom(addresses[0], _msgSender(), values[0]); - usedSignatures[signature] = true; + useSignature(signature); // emit sale event emitEvent(addresses, values); return true; @@ -210,4 +200,4 @@ contract MarketPlace is Ownable, HasSignature { ) ); } -} \ No newline at end of file +} diff --git a/contracts/utils/Approval.sol b/contracts/utils/Approval.sol new file mode 100644 index 0000000..8d25630 --- /dev/null +++ b/contracts/utils/Approval.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract Approval is Ownable { + mapping(address => bool) private approvalLists; + + /** + * @dev Allow operation to reverse signature. + */ + function addApprovalList(address user) external onlyOwner { + require(!approvalLists[user], "MinterFactory: Invalid user address"); + approvalLists[user] = true; + } + + /** + * @dev Remove operation from approval list. + */ + function removeApprovalList(address user) external onlyOwner { + approvalLists[user] = false; + } + + modifier approvaled() { + require( + _msgSender() == address(this) || approvalLists[_msgSender()], + "Must be valid approval list" + ); + _; + } +} diff --git a/contracts/utils/TimeChecker.sol b/contracts/utils/TimeChecker.sol new file mode 100644 index 0000000..e849406 --- /dev/null +++ b/contracts/utils/TimeChecker.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; +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, + "expired, please send another transaction with new signature" + ); + _; + } + + /** + * @dev Returns the max duration for function called by user + */ + function getDuration() external view + returns (uint256 duration) { + return _duration; + } + + /** + * @dev Change duration value + */ + function updateDuation(uint256 valNew) external onlyOwner { + _duration = valNew; + } + +} +