Skip to content

Commit

Permalink
feat: use cusotm errors
Browse files Browse the repository at this point in the history
  • Loading branch information
adjisb committed Apr 26, 2024
1 parent f12b36a commit 372e4a0
Show file tree
Hide file tree
Showing 15 changed files with 350 additions and 183 deletions.
3 changes: 1 addition & 2 deletions packages/land/.solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
}
],
"compiler-version": ["error", "^0.8.0"],
"func-visibility": ["error", {"ignoreConstructors": true}],
"custom-errors": "off"
"func-visibility": ["error", {"ignoreConstructors": true}]
}
}
8 changes: 6 additions & 2 deletions packages/land/contracts/Land.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,19 @@ contract Land is LandBase, Initializable, WithMetadataRegistry, WithRoyalties, W
function initialize(address admin) external initializer {
// We must be able to initialize the admin if this is a fresh deploy, but we want to
// be backward compatible with the current deployment
require(_getAdmin() == address(0), "already initialized");
if (_getAdmin() != address(0)) {
revert InvalidInitialization();
}
_changeAdmin(admin);
}

/// @notice This function is used to register Land contract on the Operator Filterer Registry of Opensea.
/// @param subscriptionOrRegistrantToCopy registration address of the list to subscribe.
/// @param subscribe bool to signify subscription 'true' or to copy the list 'false'.
function register(address subscriptionOrRegistrantToCopy, bool subscribe) external onlyAdmin {
require(subscriptionOrRegistrantToCopy != address(0), "subscription can't be zero");
if (subscriptionOrRegistrantToCopy == address(0)) {
revert InvalidAddress();
}
_register(subscriptionOrRegistrantToCopy, subscribe);
}

Expand Down
27 changes: 22 additions & 5 deletions packages/land/contracts/LandMetadataRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity 0.8.23;

import {AccessControlEnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol";
import {IErrors} from "./interfaces/IErrors.sol";
import {ILandMetadataRegistry} from "./interfaces/ILandMetadataRegistry.sol";
import {LandMetadataBase} from "./registry/LandMetadataBase.sol";

Expand All @@ -10,7 +11,15 @@ import {LandMetadataBase} from "./registry/LandMetadataBase.sol";
* @author The Sandbox
* @notice Store information about the lands (premiumness and neighborhood)
*/
contract LandMetadataRegistry is ILandMetadataRegistry, AccessControlEnumerableUpgradeable, LandMetadataBase {
contract LandMetadataRegistry is IErrors, ILandMetadataRegistry, AccessControlEnumerableUpgradeable, LandMetadataBase {
/// @notice the base token id used for a batch operation is wrong
/// @param tokenId the id of the token
error InvalidBaseTokenId(uint256 tokenId);

/// @notice the neighborhoodId is invalid
/// @param neighborhoodId the invalid neighborhoodId
error InvalidNeighborhoodId(uint256 neighborhoodId);

struct BatchSetData {
// baseTokenId the token id floor 32
uint256 baseTokenId;
Expand Down Expand Up @@ -46,7 +55,9 @@ contract LandMetadataRegistry is ILandMetadataRegistry, AccessControlEnumerableU
event BatchMetadataSet(address indexed operator, BatchSetData[] data);

modifier onlyAdmin() {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "only admin");
if (!hasRole(DEFAULT_ADMIN_ROLE, _msgSender())) {
revert OnlyAdmin();
}
_;
}

Expand Down Expand Up @@ -112,7 +123,9 @@ contract LandMetadataRegistry is ILandMetadataRegistry, AccessControlEnumerableU
uint256 len = data.length;
for (uint256 i; i < len; i++) {
BatchSetData calldata d = data[i];
require(_getBits(d.baseTokenId) == 0, "invalid base tokenId");
if (_getBits(d.baseTokenId) != 0) {
revert InvalidBaseTokenId(d.baseTokenId);
}
_setMetadata(d.baseTokenId, d.metadata);
}
emit BatchMetadataSet(_msgSender(), data);
Expand Down Expand Up @@ -173,8 +186,12 @@ contract LandMetadataRegistry is ILandMetadataRegistry, AccessControlEnumerableU
/// @param neighborhoodId the number that identifies the neighborhood
function _isValidNeighborhoodId(uint256 neighborhoodId) internal pure {
// Cannot set it to unknown (zero).
require(neighborhoodId > 0, "neighborhoodId must be >0");
if (neighborhoodId == 0) {
revert InvalidNeighborhoodId(neighborhoodId);
}
// NEIGHBORHOOD_MASK (127) is left out to use as escape char if needed.
require(neighborhoodId < NEIGHBORHOOD_MASK, "neighborhoodId must be <127");
if (neighborhoodId >= NEIGHBORHOOD_MASK) {
revert InvalidNeighborhoodId(neighborhoodId);
}
}
}
11 changes: 8 additions & 3 deletions packages/land/contracts/PolygonLand.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

pragma solidity 0.8.23;

import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import {IERC2981} from "@openzeppelin/contracts/interfaces/IERC2981.sol";
import {IOperatorFilterRegistry} from "./interfaces/IOperatorFilterRegistry.sol";
import {WithMetadataRegistry} from "./common/WithMetadataRegistry.sol";
Expand All @@ -14,15 +15,17 @@ import {PolygonLandBase} from "./polygon/PolygonLandBase.sol";
/// @notice LAND contract
/// @dev LAND contract implements ERC721, quad and marketplace filtering functionalities
/// @dev LandBase must be the first contract in the inheritance list so we keep the storage slot backward compatible
contract PolygonLand is PolygonLandBase, WithMetadataRegistry, WithRoyalties, WithOwner {
contract PolygonLand is PolygonLandBase, Initializable, WithMetadataRegistry, WithRoyalties, WithOwner {
event OperatorRegistrySet(IOperatorFilterRegistry indexed registry);

/// @notice Initializes the contract with the trustedForwarder, admin & royalty-manager
/// @param admin Admin of the contract
function initialize(address admin) external initializer {
// We must be able to initialize the admin if this is a fresh deploy, but we want to
// be backward compatible with the current deployment
require(_getAdmin() == address(0), "already initialized");
if (_getAdmin() != address(0)) {
revert InvalidInitialization();
}
_changeAdmin(admin);
}

Expand Down Expand Up @@ -55,7 +58,9 @@ contract PolygonLand is PolygonLandBase, WithMetadataRegistry, WithRoyalties, Wi
/// @param subscriptionOrRegistrantToCopy registration address of the list to subscribe.
/// @param subscribe bool to signify subscription 'true' or to copy the list 'false'.
function register(address subscriptionOrRegistrantToCopy, bool subscribe) external onlyAdmin {
require(subscriptionOrRegistrantToCopy != address(0), "subscription can't be zero");
if (subscriptionOrRegistrantToCopy == address(0)) {
revert InvalidAddress();
}
_register(subscriptionOrRegistrantToCopy, subscribe);
}

Expand Down
86 changes: 49 additions & 37 deletions packages/land/contracts/common/ERC721BaseToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {IERC721MandatoryTokenReceiver} from "../interfaces/IERC721MandatoryTokenReceiver.sol";
import {IERC721Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {IERC721MandatoryTokenReceiver} from "../interfaces/IERC721MandatoryTokenReceiver.sol";
import {IErrors} from "../interfaces/IErrors.sol";
import {WithSuperOperators} from "./WithSuperOperators.sol";

/// @title ERC721BaseTokenCommon
/// @author The Sandbox
/// @notice Basic functionalities of a NFT
/// @dev ERC721 implementation that supports meta-transactions and super operators
abstract contract ERC721BaseToken is Context, IERC721, IERC721Errors, WithSuperOperators {
abstract contract ERC721BaseToken is IERC721, IERC721Errors, IErrors, Context, WithSuperOperators {
using Address for address;

bytes4 internal constant _ERC721_RECEIVED = 0x150b7a02;
Expand Down Expand Up @@ -173,7 +174,7 @@ abstract contract ERC721BaseToken is Context, IERC721, IERC721Errors, WithSuperO
address msgSender = _msgSender();
_doTransfer(msgSender, from, to, tokenId);
if (to.code.length > 0 && _checkInterfaceWith10000Gas(to, ERC721_MANDATORY_RECEIVER)) {
require(_checkOnERC721Received(msgSender, from, to, tokenId, ""), "ERC721_TRANSFER_REJECTED");
_checkOnERC721Received(msgSender, from, to, tokenId, "");
}
}

Expand All @@ -186,7 +187,7 @@ abstract contract ERC721BaseToken is Context, IERC721, IERC721Errors, WithSuperO
address msgSender = _msgSender();
_doTransfer(msgSender, from, to, id);
if (to.code.length > 0) {
require(_checkOnERC721Received(msgSender, from, to, id, data), "ERC721_TRANSFER_REJECTED");
_checkOnERC721Received(msgSender, from, to, id, data);
}
}

Expand All @@ -195,14 +196,14 @@ abstract contract ERC721BaseToken is Context, IERC721, IERC721Errors, WithSuperO
/// @param to The address receiving the token.
/// @param tokenId The token being transferred.
function _doTransfer(address msgSender, address from, address to, uint256 tokenId) internal {
require(to != address(0), "NOT_TO_ZEROADDRESS");
if (to == address(0)) {
revert InvalidAddress();
}
bool operatorEnabled = _checkFromIsOwner(from, tokenId);
require(
msgSender == from ||
_isApprovedForAllOrSuperOperator(from, msgSender) ||
(operatorEnabled && _getOperator(tokenId) == msgSender),
"UNAUTHORIZED_TRANSFER"
);
bool authorized = msgSender == from || _isApprovedForAllOrSuperOperator(from, msgSender);
if (!authorized && !(operatorEnabled && _getOperator(tokenId) == msgSender)) {
revert ERC721InsufficientApproval(msgSender, tokenId);
}
_transferNumNFTPerAddress(from, to, 1);
_updateOwnerData(tokenId, to, false);
emit Transfer(from, to, tokenId);
Expand All @@ -220,28 +221,33 @@ abstract contract ERC721BaseToken is Context, IERC721, IERC721Errors, WithSuperO
bytes memory data,
bool safe
) internal {
require(from != address(0), "NOT_FROM_ZEROADDRESS");
require(to != address(0), "NOT_TO_ZEROADDRESS");
if (from == address(0) || to == address(0)) {
revert InvalidAddress();
}

address msgSender = _msgSender();
bool authorized = msgSender == from || _isApprovedForAllOrSuperOperator(from, msgSender);
uint256 numTokens = ids.length;
for (uint256 i = 0; i < numTokens; i++) {
uint256 id = ids[i];
(address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(id);
require(owner == from, "BATCHTRANSFERFROM_NOT_OWNER");
require(authorized || (operatorEnabled && _getOperator(id) == msgSender), "NOT_AUTHORIZED");
_updateOwnerData(id, to, false);
emit Transfer(from, to, id);
uint256 tokenId = ids[i];
(address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(tokenId);
if (from != owner) {
revert ERC721InvalidOwner(from);
}
if (!authorized && !(operatorEnabled && _getOperator(tokenId) == msgSender)) {
revert ERC721InsufficientApproval(msgSender, tokenId);
}
_updateOwnerData(tokenId, to, false);
emit Transfer(from, to, tokenId);
}
_transferNumNFTPerAddress(from, to, numTokens);

if (to.code.length > 0) {
if (_checkInterfaceWith10000Gas(to, ERC721_MANDATORY_RECEIVER)) {
require(_checkOnERC721BatchReceived(msgSender, from, to, ids, data), "ERC721_BATCH_RECEIVED_REJECTED");
_checkOnERC721BatchReceived(msgSender, from, to, ids, data);
} else if (safe) {
for (uint256 i = 0; i < numTokens; i++) {
require(_checkOnERC721Received(msgSender, from, to, ids[i], data), "ERC721_RECEIVED_REJECTED");
_checkOnERC721Received(msgSender, from, to, ids[i], data);
}
}
}
Expand All @@ -255,8 +261,12 @@ abstract contract ERC721BaseToken is Context, IERC721, IERC721Errors, WithSuperO
revert ERC721InvalidSender(from);
}
address msgSender = _msgSender();
require(msgSender == from || _isSuperOperator(msgSender), "UNAUTHORIZED_APPROVE_FOR_ALL");
require(!_isSuperOperator(operator), "INVALID_APPROVAL_CHANGE");
if (msgSender != from && !_isSuperOperator(msgSender)) {
revert ERC721InvalidApprover(msgSender);
}
if (_isSuperOperator(operator)) {
revert ERC721InvalidOperator(operator);
}
_setOperatorForAll(from, operator, approved);
emit ApprovalForAll(from, operator, approved);
}
Expand All @@ -268,7 +278,10 @@ abstract contract ERC721BaseToken is Context, IERC721, IERC721Errors, WithSuperO
_checkFromIsOwner(from, tokenId);

address msgSender = _msgSender();
require(from == msgSender || _isApprovedForAllOrSuperOperator(from, msgSender), "UNAUTHORIZED_APPROVAL");
bool authorized = msgSender == from || _isApprovedForAllOrSuperOperator(from, msgSender);
if (!authorized) {
revert ERC721InvalidApprover(msgSender);
}
if (operator == address(0)) {
_updateOwnerData(tokenId, from, false);
} else {
Expand All @@ -282,14 +295,11 @@ abstract contract ERC721BaseToken is Context, IERC721, IERC721Errors, WithSuperO
/// @param tokenId token id to burn
function _burn(address from, uint256 tokenId) internal {
bool operatorEnabled = _checkFromIsOwner(from, tokenId);

address msgSender = _msgSender();
require(
from == msgSender ||
(operatorEnabled && _getOperator(tokenId) == msgSender) ||
_isApprovedForAllOrSuperOperator(from, msgSender),
"UNAUTHORIZED_BURN"
);
bool authorized = msgSender == from || _isApprovedForAllOrSuperOperator(from, msgSender);
if (!authorized && !(operatorEnabled && _getOperator(tokenId) == msgSender)) {
revert ERC721InsufficientApproval(msgSender, tokenId);
}
_setOwnerData(tokenId, (_getOwnerData(tokenId) & (NOT_ADDRESS & NOT_OPERATOR_FLAG)) | BURNED_FLAG);
_subNumNFTPerAddress(from, 1);
emit Transfer(from, address(0), tokenId);
Expand Down Expand Up @@ -353,16 +363,17 @@ abstract contract ERC721BaseToken is Context, IERC721, IERC721Errors, WithSuperO
/// @param to The address we want to transfer to.
/// @param tokenId The id of the token we would like to transfer.
/// @param _data Any additional data to send with the transfer.
/// @return Whether the expected value of 0x150b7a02 is returned.
function _checkOnERC721Received(
address operator,
address from,
address to,
uint256 tokenId,
bytes memory _data
) internal returns (bool) {
) internal {
bytes4 retval = IERC721Receiver(to).onERC721Received(operator, from, tokenId, _data);
return (retval == _ERC721_RECEIVED);
if (retval != _ERC721_RECEIVED) {
revert ERC721InvalidReceiver(to);
}
}

/// @notice Check if receiving contract accepts erc721 batch transfers.
Expand All @@ -371,16 +382,17 @@ abstract contract ERC721BaseToken is Context, IERC721, IERC721Errors, WithSuperO
/// @param to The address we want to transfer to.
/// @param ids The ids of the tokens we would like to transfer.
/// @param _data Any additional data to send with the transfer.
/// @return Whether the expected value of 0x4b808c46 is returned.
function _checkOnERC721BatchReceived(
address operator,
address from,
address to,
uint256[] memory ids,
bytes memory _data
) internal returns (bool) {
) internal {
bytes4 retval = IERC721MandatoryTokenReceiver(to).onERC721BatchReceived(operator, from, ids, _data);
return (retval == _ERC721_BATCH_RECEIVED);
if (retval != _ERC721_BATCH_RECEIVED) {
revert ERC721InvalidBatchReceiver(to);
}
}

/// @notice Check if there was enough gas.
Expand Down

0 comments on commit 372e4a0

Please sign in to comment.