-
Notifications
You must be signed in to change notification settings - Fork 572
OpenEditionERC721 flat platform fee #614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
f3f566b
to
ee7774b
Compare
if (getPlatformFeeType() == IPlatformFee.PlatformFeeType.Flat) { | ||
(platformFeeRecipient, platformFees) = getFlatPlatformFeeInfo(); | ||
} else { | ||
(address recipient, uint16 platformFeeBps) = getPlatformFeeInfo(); |
Check notice
Code scanning / Slither
Local variable shadowing
if (getPlatformFeeType() == IPlatformFee.PlatformFeeType.Flat) { | ||
(platformFeeRecipient, platformFees) = getFlatPlatformFeeInfo(); | ||
} else { | ||
(address recipient, uint16 platformFeeBps) = getPlatformFeeInfo(); |
Check notice
Code scanning / Slither
Local variable shadowing
|
||
uint256 totalPrice = _quantityToClaim * _pricePerToken; | ||
uint256 platformFees; | ||
address platformFeeRecipient; |
Check notice
Code scanning / Slither
Local variable shadowing
contract OpenEditionERC721FlatFee is | ||
Initializable, | ||
ContractMetadata, | ||
PlatformFee, | ||
Royalty, | ||
PrimarySale, | ||
Ownable, | ||
SharedMetadata, | ||
PermissionsEnumerable, | ||
Drop, | ||
ERC2771ContextUpgradeable, | ||
Multicall, | ||
ERC721AQueryableUpgradeable | ||
{ | ||
using StringsUpgradeable for uint256; | ||
|
||
/*/////////////////////////////////////////////////////////////// | ||
State variables | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @dev Only transfers to or from TRANSFER_ROLE holders are valid, when transfers are restricted. | ||
bytes32 private transferRole; | ||
/// @dev Only MINTER_ROLE holders can update the shared metadata of tokens. | ||
bytes32 private minterRole; | ||
|
||
/// @dev Max bps in the thirdweb system. | ||
uint256 private constant MAX_BPS = 10_000; | ||
|
||
/*/////////////////////////////////////////////////////////////// | ||
Constructor + initializer logic | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
constructor() initializer {} | ||
|
||
/// @dev Initializes the contract, like a constructor. | ||
function initialize( | ||
address _defaultAdmin, | ||
string memory _name, | ||
string memory _symbol, | ||
string memory _contractURI, | ||
address[] memory _trustedForwarders, | ||
address _saleRecipient, | ||
address _royaltyRecipient, | ||
uint128 _royaltyBps, | ||
uint128 _platformFeeBps, | ||
address _platformFeeRecipient | ||
) external initializerERC721A initializer { | ||
bytes32 _transferRole = keccak256("TRANSFER_ROLE"); | ||
bytes32 _minterRole = keccak256("MINTER_ROLE"); | ||
|
||
// Initialize inherited contracts, most base-like -> most derived. | ||
__ERC2771Context_init(_trustedForwarders); | ||
__ERC721A_init(_name, _symbol); | ||
|
||
_setupContractURI(_contractURI); | ||
_setupOwner(_defaultAdmin); | ||
|
||
_setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin); | ||
_setupRole(_minterRole, _defaultAdmin); | ||
_setupRole(_transferRole, _defaultAdmin); | ||
_setupRole(_transferRole, address(0)); | ||
|
||
_setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); | ||
_setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); | ||
_setupPrimarySaleRecipient(_saleRecipient); | ||
|
||
transferRole = _transferRole; | ||
minterRole = _minterRole; | ||
} | ||
|
||
/*/////////////////////////////////////////////////////////////// | ||
ERC 165 / 721 / 2981 logic | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @dev Returns the URI for a given tokenId. | ||
function tokenURI( | ||
uint256 _tokenId | ||
) public view virtual override(ERC721AUpgradeable, IERC721AUpgradeable) returns (string memory) { | ||
if (!_exists(_tokenId)) { | ||
revert("!ID"); | ||
} | ||
|
||
return _getURIFromSharedMetadata(_tokenId); | ||
} | ||
|
||
/// @dev See ERC 165 | ||
function supportsInterface( | ||
bytes4 interfaceId | ||
) public view virtual override(ERC721AUpgradeable, IERC165, IERC721AUpgradeable) returns (bool) { | ||
return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId; | ||
} | ||
|
||
/// @dev The start token ID for the contract. | ||
function _startTokenId() internal pure override returns (uint256) { | ||
return 1; | ||
} | ||
|
||
function startTokenId() public pure returns (uint256) { | ||
return _startTokenId(); | ||
} | ||
|
||
/*/////////////////////////////////////////////////////////////// | ||
Internal functions | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @dev Collects and distributes the primary sale value of NFTs being claimed. | ||
function _collectPriceOnClaim( | ||
address _primarySaleRecipient, | ||
uint256 _quantityToClaim, | ||
address _currency, | ||
uint256 _pricePerToken | ||
) internal override { | ||
if (_pricePerToken == 0) { | ||
require(msg.value == 0, "!Value"); | ||
return; | ||
} | ||
|
||
uint256 totalPrice = _quantityToClaim * _pricePerToken; | ||
uint256 platformFees; | ||
address platformFeeRecipient; | ||
|
||
if (getPlatformFeeType() == IPlatformFee.PlatformFeeType.Flat) { | ||
(platformFeeRecipient, platformFees) = getFlatPlatformFeeInfo(); | ||
} else { | ||
(address recipient, uint16 platformFeeBps) = getPlatformFeeInfo(); | ||
platformFeeRecipient = recipient; | ||
platformFees = ((totalPrice * platformFeeBps) / MAX_BPS); | ||
} | ||
require(totalPrice >= platformFees, "price less than platform fee"); | ||
|
||
bool validMsgValue; | ||
if (_currency == CurrencyTransferLib.NATIVE_TOKEN) { | ||
validMsgValue = msg.value == totalPrice; | ||
} else { | ||
validMsgValue = msg.value == 0; | ||
} | ||
require(validMsgValue, "!V"); | ||
|
||
address saleRecipient = _primarySaleRecipient == address(0) ? primarySaleRecipient() : _primarySaleRecipient; | ||
|
||
CurrencyTransferLib.transferCurrency(_currency, _msgSender(), platformFeeRecipient, platformFees); | ||
CurrencyTransferLib.transferCurrency(_currency, _msgSender(), saleRecipient, totalPrice - platformFees); | ||
} | ||
|
||
/// @dev Transfers the NFTs being claimed. | ||
function _transferTokensOnClaim( | ||
address _to, | ||
uint256 _quantityBeingClaimed | ||
) internal override returns (uint256 startTokenId_) { | ||
startTokenId_ = _nextTokenId(); | ||
_safeMint(_to, _quantityBeingClaimed); | ||
} | ||
|
||
/// @dev Checks whether primary sale recipient can be set in the given execution context. | ||
function _canSetPrimarySaleRecipient() internal view override returns (bool) { | ||
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
} | ||
|
||
/// @dev Checks whether owner can be set in the given execution context. | ||
function _canSetOwner() internal view override returns (bool) { | ||
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
} | ||
|
||
/// @dev Checks whether royalty info can be set in the given execution context. | ||
function _canSetRoyaltyInfo() internal view override returns (bool) { | ||
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
} | ||
|
||
/// @dev Checks whether contract metadata can be set in the given execution context. | ||
function _canSetContractURI() internal view override returns (bool) { | ||
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
} | ||
|
||
/// @dev Checks whether platform fee info can be set in the given execution context. | ||
function _canSetClaimConditions() internal view override returns (bool) { | ||
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
} | ||
|
||
/// @dev Returns whether the shared metadata of tokens can be set in the given execution context. | ||
function _canSetSharedMetadata() internal view virtual override returns (bool) { | ||
return hasRole(minterRole, _msgSender()); | ||
} | ||
|
||
/// @dev Checks whether platform fee info can be set in the given execution context. | ||
function _canSetPlatformFeeInfo() internal view override returns (bool) { | ||
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
} | ||
|
||
/*/////////////////////////////////////////////////////////////// | ||
Miscellaneous | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/** | ||
* Returns the total amount of tokens minted in the contract. | ||
*/ | ||
function totalMinted() external view returns (uint256) { | ||
unchecked { | ||
return _nextTokenId() - _startTokenId(); | ||
} | ||
} | ||
|
||
/// @dev The tokenId of the next NFT that will be minted / lazy minted. | ||
function nextTokenIdToMint() external view returns (uint256) { | ||
return _nextTokenId(); | ||
} | ||
|
||
/// @dev The next token ID of the NFT that can be claimed. | ||
function nextTokenIdToClaim() external view returns (uint256) { | ||
return _nextTokenId(); | ||
} | ||
|
||
/// @dev Burns `tokenId`. See {ERC721-_burn}. | ||
function burn(uint256 tokenId) external virtual { | ||
// note: ERC721AUpgradeable's `_burn(uint256,bool)` internally checks for token approvals. | ||
_burn(tokenId, true); | ||
} | ||
|
||
/// @dev See {ERC721-_beforeTokenTransfer}. | ||
function _beforeTokenTransfers( | ||
address from, | ||
address to, | ||
uint256 startTokenId_, | ||
uint256 quantity | ||
) internal virtual override { | ||
super._beforeTokenTransfers(from, to, startTokenId_, quantity); | ||
|
||
// if transfer is restricted on the contract, we still want to allow burning and minting | ||
if (!hasRole(transferRole, address(0)) && from != address(0) && to != address(0)) { | ||
if (!hasRole(transferRole, from) && !hasRole(transferRole, to)) { | ||
revert("!T"); | ||
} | ||
} | ||
} | ||
|
||
function _dropMsgSender() internal view virtual override returns (address) { | ||
return _msgSender(); | ||
} | ||
|
||
function _msgSenderERC721A() internal view virtual override returns (address) { | ||
return _msgSender(); | ||
} | ||
|
||
function _msgSender() | ||
internal | ||
view | ||
virtual | ||
override(ERC2771ContextUpgradeable, Multicall) | ||
returns (address sender) | ||
{ | ||
return ERC2771ContextUpgradeable._msgSender(); | ||
} | ||
} |
Check warning
Code scanning / Slither
Missing inheritance
contract OpenEditionERC721FlatFee is | ||
Initializable, | ||
ContractMetadata, | ||
PlatformFee, | ||
Royalty, | ||
PrimarySale, | ||
Ownable, | ||
SharedMetadata, | ||
PermissionsEnumerable, | ||
Drop, | ||
ERC2771ContextUpgradeable, | ||
Multicall, | ||
ERC721AQueryableUpgradeable | ||
{ | ||
using StringsUpgradeable for uint256; | ||
|
||
/*/////////////////////////////////////////////////////////////// | ||
State variables | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @dev Only transfers to or from TRANSFER_ROLE holders are valid, when transfers are restricted. | ||
bytes32 private transferRole; | ||
/// @dev Only MINTER_ROLE holders can update the shared metadata of tokens. | ||
bytes32 private minterRole; | ||
|
||
/// @dev Max bps in the thirdweb system. | ||
uint256 private constant MAX_BPS = 10_000; | ||
|
||
/*/////////////////////////////////////////////////////////////// | ||
Constructor + initializer logic | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
constructor() initializer {} | ||
|
||
/// @dev Initializes the contract, like a constructor. | ||
function initialize( | ||
address _defaultAdmin, | ||
string memory _name, | ||
string memory _symbol, | ||
string memory _contractURI, | ||
address[] memory _trustedForwarders, | ||
address _saleRecipient, | ||
address _royaltyRecipient, | ||
uint128 _royaltyBps, | ||
uint128 _platformFeeBps, | ||
address _platformFeeRecipient | ||
) external initializerERC721A initializer { | ||
bytes32 _transferRole = keccak256("TRANSFER_ROLE"); | ||
bytes32 _minterRole = keccak256("MINTER_ROLE"); | ||
|
||
// Initialize inherited contracts, most base-like -> most derived. | ||
__ERC2771Context_init(_trustedForwarders); | ||
__ERC721A_init(_name, _symbol); | ||
|
||
_setupContractURI(_contractURI); | ||
_setupOwner(_defaultAdmin); | ||
|
||
_setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin); | ||
_setupRole(_minterRole, _defaultAdmin); | ||
_setupRole(_transferRole, _defaultAdmin); | ||
_setupRole(_transferRole, address(0)); | ||
|
||
_setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); | ||
_setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); | ||
_setupPrimarySaleRecipient(_saleRecipient); | ||
|
||
transferRole = _transferRole; | ||
minterRole = _minterRole; | ||
} | ||
|
||
/*/////////////////////////////////////////////////////////////// | ||
ERC 165 / 721 / 2981 logic | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @dev Returns the URI for a given tokenId. | ||
function tokenURI( | ||
uint256 _tokenId | ||
) public view virtual override(ERC721AUpgradeable, IERC721AUpgradeable) returns (string memory) { | ||
if (!_exists(_tokenId)) { | ||
revert("!ID"); | ||
} | ||
|
||
return _getURIFromSharedMetadata(_tokenId); | ||
} | ||
|
||
/// @dev See ERC 165 | ||
function supportsInterface( | ||
bytes4 interfaceId | ||
) public view virtual override(ERC721AUpgradeable, IERC165, IERC721AUpgradeable) returns (bool) { | ||
return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId; | ||
} | ||
|
||
/// @dev The start token ID for the contract. | ||
function _startTokenId() internal pure override returns (uint256) { | ||
return 1; | ||
} | ||
|
||
function startTokenId() public pure returns (uint256) { | ||
return _startTokenId(); | ||
} | ||
|
||
/*/////////////////////////////////////////////////////////////// | ||
Internal functions | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @dev Collects and distributes the primary sale value of NFTs being claimed. | ||
function _collectPriceOnClaim( | ||
address _primarySaleRecipient, | ||
uint256 _quantityToClaim, | ||
address _currency, | ||
uint256 _pricePerToken | ||
) internal override { | ||
if (_pricePerToken == 0) { | ||
require(msg.value == 0, "!Value"); | ||
return; | ||
} | ||
|
||
uint256 totalPrice = _quantityToClaim * _pricePerToken; | ||
uint256 platformFees; | ||
address platformFeeRecipient; | ||
|
||
if (getPlatformFeeType() == IPlatformFee.PlatformFeeType.Flat) { | ||
(platformFeeRecipient, platformFees) = getFlatPlatformFeeInfo(); | ||
} else { | ||
(address recipient, uint16 platformFeeBps) = getPlatformFeeInfo(); | ||
platformFeeRecipient = recipient; | ||
platformFees = ((totalPrice * platformFeeBps) / MAX_BPS); | ||
} | ||
require(totalPrice >= platformFees, "price less than platform fee"); | ||
|
||
bool validMsgValue; | ||
if (_currency == CurrencyTransferLib.NATIVE_TOKEN) { | ||
validMsgValue = msg.value == totalPrice; | ||
} else { | ||
validMsgValue = msg.value == 0; | ||
} | ||
require(validMsgValue, "!V"); | ||
|
||
address saleRecipient = _primarySaleRecipient == address(0) ? primarySaleRecipient() : _primarySaleRecipient; | ||
|
||
CurrencyTransferLib.transferCurrency(_currency, _msgSender(), platformFeeRecipient, platformFees); | ||
CurrencyTransferLib.transferCurrency(_currency, _msgSender(), saleRecipient, totalPrice - platformFees); | ||
} | ||
|
||
/// @dev Transfers the NFTs being claimed. | ||
function _transferTokensOnClaim( | ||
address _to, | ||
uint256 _quantityBeingClaimed | ||
) internal override returns (uint256 startTokenId_) { | ||
startTokenId_ = _nextTokenId(); | ||
_safeMint(_to, _quantityBeingClaimed); | ||
} | ||
|
||
/// @dev Checks whether primary sale recipient can be set in the given execution context. | ||
function _canSetPrimarySaleRecipient() internal view override returns (bool) { | ||
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
} | ||
|
||
/// @dev Checks whether owner can be set in the given execution context. | ||
function _canSetOwner() internal view override returns (bool) { | ||
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
} | ||
|
||
/// @dev Checks whether royalty info can be set in the given execution context. | ||
function _canSetRoyaltyInfo() internal view override returns (bool) { | ||
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
} | ||
|
||
/// @dev Checks whether contract metadata can be set in the given execution context. | ||
function _canSetContractURI() internal view override returns (bool) { | ||
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
} | ||
|
||
/// @dev Checks whether platform fee info can be set in the given execution context. | ||
function _canSetClaimConditions() internal view override returns (bool) { | ||
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
} | ||
|
||
/// @dev Returns whether the shared metadata of tokens can be set in the given execution context. | ||
function _canSetSharedMetadata() internal view virtual override returns (bool) { | ||
return hasRole(minterRole, _msgSender()); | ||
} | ||
|
||
/// @dev Checks whether platform fee info can be set in the given execution context. | ||
function _canSetPlatformFeeInfo() internal view override returns (bool) { | ||
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
} | ||
|
||
/*/////////////////////////////////////////////////////////////// | ||
Miscellaneous | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/** | ||
* Returns the total amount of tokens minted in the contract. | ||
*/ | ||
function totalMinted() external view returns (uint256) { | ||
unchecked { | ||
return _nextTokenId() - _startTokenId(); | ||
} | ||
} | ||
|
||
/// @dev The tokenId of the next NFT that will be minted / lazy minted. | ||
function nextTokenIdToMint() external view returns (uint256) { | ||
return _nextTokenId(); | ||
} | ||
|
||
/// @dev The next token ID of the NFT that can be claimed. | ||
function nextTokenIdToClaim() external view returns (uint256) { | ||
return _nextTokenId(); | ||
} | ||
|
||
/// @dev Burns `tokenId`. See {ERC721-_burn}. | ||
function burn(uint256 tokenId) external virtual { | ||
// note: ERC721AUpgradeable's `_burn(uint256,bool)` internally checks for token approvals. | ||
_burn(tokenId, true); | ||
} | ||
|
||
/// @dev See {ERC721-_beforeTokenTransfer}. | ||
function _beforeTokenTransfers( | ||
address from, | ||
address to, | ||
uint256 startTokenId_, | ||
uint256 quantity | ||
) internal virtual override { | ||
super._beforeTokenTransfers(from, to, startTokenId_, quantity); | ||
|
||
// if transfer is restricted on the contract, we still want to allow burning and minting | ||
if (!hasRole(transferRole, address(0)) && from != address(0) && to != address(0)) { | ||
if (!hasRole(transferRole, from) && !hasRole(transferRole, to)) { | ||
revert("!T"); | ||
} | ||
} | ||
} | ||
|
||
function _dropMsgSender() internal view virtual override returns (address) { | ||
return _msgSender(); | ||
} | ||
|
||
function _msgSenderERC721A() internal view virtual override returns (address) { | ||
return _msgSender(); | ||
} | ||
|
||
function _msgSender() | ||
internal | ||
view | ||
virtual | ||
override(ERC2771ContextUpgradeable, Multicall) | ||
returns (address sender) | ||
{ | ||
return ERC2771ContextUpgradeable._msgSender(); | ||
} | ||
} |
Check warning
Code scanning / Slither
Missing inheritance
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## main #614 +/- ##
==========================================
+ Coverage 64.26% 64.55% +0.29%
==========================================
Files 215 216 +1
Lines 6640 6701 +61
==========================================
+ Hits 4267 4326 +59
- Misses 2373 2375 +2 ☔ View full report in Codecov by Sentry. |
No description provided.