220 lines
6.8 KiB
Solidity
220 lines
6.8 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.10;
|
|
|
|
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
|
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
import "@openzeppelin/contracts/utils/Address.sol";
|
|
import "@openzeppelin/contracts/governance/TimelockController.sol";
|
|
import "../core/HasSignature.sol";
|
|
import "../interfaces/IBEERC721.sol";
|
|
import "../interfaces/IBEERC1155.sol";
|
|
import "../utils/TimeChecker.sol";
|
|
|
|
contract BENftMall is Ownable, HasSignature, TimeChecker {
|
|
using SafeERC20 for IERC20;
|
|
|
|
constructor() HasSignature("NftMall", "1") {}
|
|
|
|
// Supported payment token WETH & list of authorized ERC20
|
|
mapping(address => bool) public paymentTokens;
|
|
mapping(bytes => bool) public usedSignatures;
|
|
|
|
mapping(address => bool) public nftTokenSupported;
|
|
|
|
// Address to receive transaction fee
|
|
address public feeToAddress;
|
|
|
|
// Events
|
|
event BuyTransactionBatch(
|
|
address indexed buyer,
|
|
uint256 indexed nonce,
|
|
address[3] addresses,
|
|
uint256 price,
|
|
uint256[] ids,
|
|
uint256[] amounts
|
|
);
|
|
event BuyTransaction(
|
|
address indexed buyer,
|
|
uint256 indexed nonce,
|
|
uint256 tokenId,
|
|
address[3] addresses,
|
|
uint256 price
|
|
);
|
|
|
|
function setFeeToAddress(address _feeToAddress) external onlyOwner {
|
|
require(
|
|
_feeToAddress != address(0),
|
|
"fee received address can not be zero"
|
|
);
|
|
feeToAddress = _feeToAddress;
|
|
}
|
|
|
|
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 addNFTTokenSupport(address nftToken) external onlyOwner {
|
|
nftTokenSupported[nftToken] = true;
|
|
}
|
|
|
|
function removeNFTTokenSupport(address nftToken) external onlyOwner {
|
|
nftTokenSupported[nftToken] = false;
|
|
}
|
|
|
|
function ignoreSignature(
|
|
address[4] calldata addresses,
|
|
uint256[] calldata signArray,
|
|
bytes calldata signature
|
|
) external signatureValid(signature) {
|
|
// address[4] [seller_address,nft_address,payment_token_address, buyer_address]
|
|
// uint256[4] [token_id,price,salt_nonce,startTime]
|
|
bytes32 criteriaMessageHash = getMessageHash(
|
|
addresses[1],
|
|
addresses[2],
|
|
addresses[3],
|
|
signArray
|
|
);
|
|
|
|
checkSigner(_msgSender(), criteriaMessageHash, signature);
|
|
_useSignature(signature);
|
|
}
|
|
|
|
/**
|
|
* @dev Function matched transaction with user signatures
|
|
*/
|
|
function buy721NFT(
|
|
address[3] calldata addresses,
|
|
uint256[4] calldata values,
|
|
bytes calldata signature
|
|
) external signatureValid(signature) timeValid(values[3]) {
|
|
// address[3] [seller_address,nft_address,payment_token_address]
|
|
// uint256[4] [token_id,price,salt_nonce,startTime]
|
|
// bytes seller_signature
|
|
require(nftTokenSupported[addresses[1]], "BENftMall: Unsupported NFT");
|
|
require(paymentTokens[addresses[2]], "BENftMall: invalid payment method");
|
|
address to = _msgSender();
|
|
|
|
uint256[] memory signArray = new uint256[](values.length);
|
|
for (uint256 i = 0; i < values.length; ++i) {
|
|
signArray[i] = values[i];
|
|
}
|
|
bytes32 criteriaMessageHash = getMessageHash(
|
|
addresses[1],
|
|
addresses[2],
|
|
to,
|
|
signArray
|
|
);
|
|
|
|
checkSigner(addresses[0], criteriaMessageHash, signature);
|
|
// Check payment approval and buyer balance
|
|
IERC20 paymentContract = IERC20(addresses[2]);
|
|
require(
|
|
paymentContract.balanceOf(to) >= values[1],
|
|
"BENftMall: buyer doesn't have enough token to buy this item"
|
|
);
|
|
require(
|
|
paymentContract.allowance(to, address(this)) >= values[1],
|
|
"BENftMall: buyer doesn't approve marketplace to spend payment amount"
|
|
);
|
|
paymentContract.safeTransferFrom(to, feeToAddress, values[1]);
|
|
|
|
// mint item to user
|
|
IBEERC721 nft = IBEERC721(addresses[1]);
|
|
nft.mint(to, values[0]);
|
|
_useSignature(signature);
|
|
// emit sale event
|
|
emit BuyTransaction(to, values[2], values[0], addresses, values[1]);
|
|
}
|
|
|
|
function buy1155NFT(
|
|
address[3] calldata addresses,
|
|
uint256[3] calldata values,
|
|
uint256[] memory ids,
|
|
uint256[] memory amounts,
|
|
bytes calldata signature
|
|
) external signatureValid(signature) timeValid(values[2]) {
|
|
// address[3] [seller_address,nft_address,payment_token_address]
|
|
// uint256[3] [price,salt_nonce,startTime]
|
|
require(nftTokenSupported[addresses[1]], "BENftMall: Unsupported NFT");
|
|
require(paymentTokens[addresses[2]], "BENftMall: invalid payment method");
|
|
require(ids.length > 0, "BENftMall: ids cannot be empty");
|
|
require(
|
|
ids.length == amounts.length,
|
|
"BENftMall: ids and amounts length mismatch"
|
|
);
|
|
IBEERC1155 nft = IBEERC1155(addresses[1]);
|
|
uint256[] memory signArray = new uint256[](ids.length * 2 + values.length);
|
|
for (uint256 i = 0; i < ids.length; ++i) {
|
|
require(
|
|
nft.canMint(ids[i]),
|
|
"BENftMall: can not mint for current nft rule setting"
|
|
);
|
|
signArray[i * 2] = ids[i];
|
|
signArray[i * 2 + 1] = amounts[i];
|
|
}
|
|
for (uint256 i = 0; i < values.length; ++i) {
|
|
signArray[ids.length * 2 + i] = values[i];
|
|
}
|
|
|
|
bytes32 criteriaMessageHash = getMessageHash(
|
|
addresses[1],
|
|
addresses[2],
|
|
_msgSender(),
|
|
signArray
|
|
);
|
|
checkSigner(addresses[0], criteriaMessageHash, signature);
|
|
// Check payment approval and buyer balance
|
|
IERC20 paymentContract = IERC20(addresses[2]);
|
|
require(
|
|
paymentContract.balanceOf(_msgSender()) >= values[0],
|
|
"BENftMall: buyer doesn't have enough token to buy this item"
|
|
);
|
|
require(
|
|
paymentContract.allowance(_msgSender(), address(this)) >= values[0],
|
|
"BENftMall: buyer doesn't approve marketplace to spend payment amount"
|
|
);
|
|
paymentContract.safeTransferFrom(_msgSender(), feeToAddress, values[0]);
|
|
nft.mintBatch(_msgSender(), ids, amounts, "");
|
|
_useSignature(signature);
|
|
emit BuyTransactionBatch(_msgSender(), values[1], addresses, values[0], ids, amounts);
|
|
}
|
|
|
|
function getMessageHash(
|
|
address _nftAddress,
|
|
address _tokenAddress,
|
|
address _buyerAddress,
|
|
uint256[] memory _datas
|
|
) public pure returns (bytes32) {
|
|
bytes memory encoded = abi.encodePacked(
|
|
_nftAddress,
|
|
_tokenAddress,
|
|
_buyerAddress
|
|
);
|
|
uint256 len = _datas.length;
|
|
for (uint256 i = 0; i < len; ++i) {
|
|
encoded = bytes.concat(encoded, abi.encodePacked(_datas[i]));
|
|
}
|
|
return keccak256(encoded);
|
|
}
|
|
}
|