diff --git a/contracts/BucketAuctionOperatorFilterer.sol b/contracts/BucketAuctionOperatorFilterer.sol index 9aa5838..fa3df3a 100644 --- a/contracts/BucketAuctionOperatorFilterer.sol +++ b/contracts/BucketAuctionOperatorFilterer.sol @@ -3,9 +3,13 @@ pragma solidity ^0.8.4; import "./BucketAuction.sol"; -import "./OperatorFilter/DefaultOperatorFilterer.sol"; +import {UpdatableOperatorFilterer} from "operator-filter-registry/src/UpdatableOperatorFilterer.sol"; +import {CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, ME_SUBSCRIPTION} from "./utils/Constants.sol"; -contract BucketAuctionOperatorFilterer is BucketAuction, DefaultOperatorFilterer { +contract BucketAuctionOperatorFilterer is + BucketAuction, + UpdatableOperatorFilterer +{ constructor( string memory collectionName, string memory collectionSymbol, @@ -17,6 +21,11 @@ contract BucketAuctionOperatorFilterer is BucketAuction, DefaultOperatorFilterer uint64 startTimeUnixSeconds, uint64 endTimeUnixSeconds ) + UpdatableOperatorFilterer( + CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, + ME_SUBSCRIPTION, + true + ) BucketAuction( collectionName, collectionSymbol, @@ -30,6 +39,15 @@ contract BucketAuctionOperatorFilterer is BucketAuction, DefaultOperatorFilterer ) {} + function owner() + public + view + override(Ownable, UpdatableOperatorFilterer) + returns (address) + { + return Ownable.owner(); + } + function transferFrom( address from, address to, diff --git a/contracts/ERC721M.sol b/contracts/ERC721M.sol index 077f2e1..9468e62 100644 --- a/contracts/ERC721M.sol +++ b/contracts/ERC721M.sol @@ -166,7 +166,8 @@ contract ERC721M is IERC721M, ERC721AQueryable, Ownable, ReentrancyGuard { if (i >= 1) { if ( newStages[i].startTimeUnixSeconds < - newStages[i - 1].endTimeUnixSeconds + _timestampExpirySeconds + newStages[i - 1].endTimeUnixSeconds + + _timestampExpirySeconds ) { revert InsufficientStageTimeGap(); } @@ -361,7 +362,7 @@ contract ERC721M is IERC721M, ERC721AQueryable, Ownable, ReentrancyGuard { bytes32[] calldata proof, uint64 timestamp, bytes calldata signature - ) virtual external payable nonReentrant { + ) external payable virtual nonReentrant { _mintInternal(qty, msg.sender, proof, timestamp, signature); } @@ -413,7 +414,8 @@ contract ERC721M is IERC721M, ERC721AQueryable, Ownable, ReentrancyGuard { stage = _mintStages[activeStage]; // Check value if minting with ETH - if (_mintCurrency == address(0) && msg.value < stage.price * qty) revert NotEnoughValue(); + if (_mintCurrency == address(0) && msg.value < stage.price * qty) + revert NotEnoughValue(); // Check stage supply if applicable if (stage.maxStageSupply > 0) { @@ -446,7 +448,11 @@ contract ERC721M is IERC721M, ERC721AQueryable, Ownable, ReentrancyGuard { } if (_mintCurrency != address(0)) { - IERC20(_mintCurrency).safeTransferFrom(msg.sender, address(this), stage.price * qty); + IERC20(_mintCurrency).safeTransferFrom( + msg.sender, + address(this), + stage.price * qty + ); } _stageMintedCountsPerWallet[activeStage][to] += qty; @@ -624,4 +630,4 @@ contract ERC721M is IERC721M, ERC721AQueryable, Ownable, ReentrancyGuard { } return chainID; } -} \ No newline at end of file +} diff --git a/contracts/ERC721MAutoApprover.sol b/contracts/ERC721MAutoApprover.sol index 29e7376..4008127 100644 --- a/contracts/ERC721MAutoApprover.sol +++ b/contracts/ERC721MAutoApprover.sol @@ -6,7 +6,7 @@ import "./ERC721M.sol"; contract ERC721MAutoApprover is ERC721M { address private _autoApproveAddress; - + event SetAutoApproveAddress(address autoApproveAddress); constructor( @@ -56,9 +56,10 @@ contract ERC721MAutoApprover is ERC721M { return _autoApproveAddress; } - function setAutoApproveAddress( - address autoApproveAddress - ) external onlyOwner { + function setAutoApproveAddress(address autoApproveAddress) + external + onlyOwner + { _autoApproveAddress = autoApproveAddress; emit SetAutoApproveAddress(autoApproveAddress); } diff --git a/contracts/ERC721MIncreasableOperatorFilterer.sol b/contracts/ERC721MIncreasableOperatorFilterer.sol index c4e96c6..6f29f89 100644 --- a/contracts/ERC721MIncreasableOperatorFilterer.sol +++ b/contracts/ERC721MIncreasableOperatorFilterer.sol @@ -4,9 +4,13 @@ pragma solidity ^0.8.4; import "./ERC721M.sol"; import "./ERC721MIncreasableSupply.sol"; -import "./OperatorFilter/DefaultOperatorFilterer.sol"; +import {UpdatableOperatorFilterer} from "operator-filter-registry/src/UpdatableOperatorFilterer.sol"; +import {CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, ME_SUBSCRIPTION} from "./utils/Constants.sol"; -contract ERC721MIncreasableOperatorFilterer is ERC721MIncreasableSupply, DefaultOperatorFilterer { +contract ERC721MIncreasableOperatorFilterer is + ERC721MIncreasableSupply, + UpdatableOperatorFilterer +{ constructor( string memory collectionName, string memory collectionSymbol, @@ -17,6 +21,11 @@ contract ERC721MIncreasableOperatorFilterer is ERC721MIncreasableSupply, Default uint64 timestampExpirySeconds, address mintCurrency ) + UpdatableOperatorFilterer( + CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, + ME_SUBSCRIPTION, + true + ) ERC721MIncreasableSupply( collectionName, collectionSymbol, @@ -29,6 +38,15 @@ contract ERC721MIncreasableOperatorFilterer is ERC721MIncreasableSupply, Default ) {} + function owner() + public + view + override(Ownable, UpdatableOperatorFilterer) + returns (address) + { + return Ownable.owner(); + } + function transferFrom( address from, address to, diff --git a/contracts/ERC721MLite.sol b/contracts/ERC721MLite.sol index 1b54fb5..c186cb2 100644 --- a/contracts/ERC721MLite.sol +++ b/contracts/ERC721MLite.sol @@ -9,7 +9,8 @@ import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProo import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; import {ERC721A, ERC721AQueryable, ERC721A__IERC721Receiver} from "erc721a/contracts/extensions/ERC721AQueryable.sol"; import {IERC721A, IERC721M} from "./IERC721M.sol"; -import {DefaultOperatorFilterer} from "./OperatorFilter/DefaultOperatorFilterer.sol"; +import {UpdatableOperatorFilterer} from "operator-filter-registry/src/UpdatableOperatorFilterer.sol"; +import {CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, ME_SUBSCRIPTION} from "./utils/Constants.sol"; /** * @title ERC721MLite @@ -21,7 +22,7 @@ import {DefaultOperatorFilterer} from "./OperatorFilter/DefaultOperatorFilterer. contract ERC721MLite is IERC721M, ERC721AQueryable, - DefaultOperatorFilterer, + UpdatableOperatorFilterer, Ownable, ReentrancyGuard { @@ -66,7 +67,14 @@ contract ERC721MLite is uint256 globalWalletLimit, address cosigner, uint64 timestampExpirySeconds - ) ERC721A(collectionName, collectionSymbol) { + ) + UpdatableOperatorFilterer( + CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, + ME_SUBSCRIPTION, + true + ) + ERC721A(collectionName, collectionSymbol) + { if (globalWalletLimit > maxMintableSupply) revert GlobalWalletLimitOverflow(); _mintable = false; @@ -190,9 +198,11 @@ contract ERC721MLite is /** * @dev Sets maximum mintable supply. */ - function setMaxMintableSupply( - uint256 maxMintableSupply - ) external virtual onlyOwner { + function setMaxMintableSupply(uint256 maxMintableSupply) + external + virtual + onlyOwner + { if (maxMintableSupply > _maxMintableSupply) { revert CannotIncreaseMaxMintableSupply(); } @@ -211,18 +221,29 @@ contract ERC721MLite is /** * @dev Returns number of minted token for a given address. */ - function totalMintedByAddress( - address a - ) external view virtual override returns (uint256) { + function totalMintedByAddress(address a) + external + view + virtual + override + returns (uint256) + { return _numberMinted(a); } /** * @dev Returns info for one stage specified by index (starting from 0). */ - function getStageInfo( - uint256 index - ) external view override returns (MintStageInfo memory, uint32, uint256) { + function getStageInfo(uint256 index) + external + view + override + returns ( + MintStageInfo memory, + uint32, + uint256 + ) + { if (index >= _mintStages.length) { revert("InvalidStage"); } @@ -302,10 +323,11 @@ contract ERC721MLite is * NOTE: This function bypasses validations thus only available for owner. * This is typically used for owner to pre-mint or mint the remaining of the supply. */ - function ownerMint( - uint32 qty, - address to - ) external onlyOwner hasSupply(qty) { + function ownerMint(uint32 qty, address to) + external + onlyOwner + hasSupply(qty) + { _safeMint(to, qty); } @@ -330,9 +352,12 @@ contract ERC721MLite is /** * @dev Returns token URI for a given token id. */ - function tokenURI( - uint256 tokenId - ) public view override(ERC721A, IERC721A) returns (string memory) { + function tokenURI(uint256 tokenId) + public + view + override(ERC721A, IERC721A) + returns (string memory) + { if (!_exists(tokenId)) revert URIQueryForNonexistentToken(); string memory baseURI = _currentBaseURI; @@ -392,9 +417,11 @@ contract ERC721MLite is /** * @dev Returns the current active stage based on timestamp. */ - function getActiveStageFromTimestamp( - uint64 timestamp - ) public view returns (uint256) { + function getActiveStageFromTimestamp(uint64 timestamp) + public + view + returns (uint256) + { for (uint256 i = 0; i < _mintStages.length; i++) { if ( timestamp >= _mintStages[i].startTimeUnixSeconds && @@ -417,10 +444,10 @@ contract ERC721MLite is /** * @dev Validates the start timestamp is before end timestamp. Used when updating stages. */ - function _assertValidStartAndEndTimestamp( - uint64 start, - uint64 end - ) internal pure { + function _assertValidStartAndEndTimestamp(uint64 start, uint64 end) + internal + pure + { if (start >= end) revert InvalidStartAndEndTimestamp(); } @@ -435,6 +462,16 @@ contract ERC721MLite is return chainID; } + function owner() + public + view + virtual + override(Ownable, UpdatableOperatorFilterer) + returns (address) + { + return Ownable.owner(); + } + function transferFrom( address from, address to, diff --git a/contracts/ERC721MOnft.sol b/contracts/ERC721MOnft.sol index e9a759a..36b169f 100644 --- a/contracts/ERC721MOnft.sol +++ b/contracts/ERC721MOnft.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.4; import {IONFT721Core, ONFT721CoreLite} from "./onft/ONFT721CoreLite.sol"; -import {ERC721MLite, ERC721A, ERC721A__IERC721Receiver, IERC721A} from "./ERC721MLite.sol"; +import {ERC721MLite, ERC721A, ERC721A__IERC721Receiver, IERC721A, Ownable} from "./ERC721MLite.sol"; import {IONFT721} from "@layerzerolabs/solidity-examples/contracts/token/onft/IONFT721.sol"; /** @@ -11,11 +11,7 @@ import {IONFT721} from "@layerzerolabs/solidity-examples/contracts/token/onft/IO * * @dev ERC721MOnft is an ERC721M contract with LayerZero integration. */ -contract ERC721MOnft is - ERC721MLite, - ONFT721CoreLite, - ERC721A__IERC721Receiver -{ +contract ERC721MOnft is ERC721MLite, ONFT721CoreLite, ERC721A__IERC721Receiver { error CallerNotOwnerOrApproved(); error FromAddressNotOwner(); error NotExistOrNotOwnedByContract(); @@ -43,9 +39,16 @@ contract ERC721MOnft is ONFT721CoreLite(minGasToTransferAndStore, lzEndpoint) {} - function supportsInterface( - bytes4 interfaceId - ) + function owner() + public + view + override(Ownable, ERC721MLite) + returns (address) + { + return Ownable.owner(); + } + + function supportsInterface(bytes4 interfaceId) public view virtual @@ -61,7 +64,7 @@ contract ERC721MOnft is address _from, uint16, bytes memory, - uint _tokenId + uint256 _tokenId ) internal virtual override { if (!_isApprovedOrOwner(_msgSender(), _tokenId)) revert CallerNotOwnerOrApproved(); @@ -73,7 +76,7 @@ contract ERC721MOnft is function _creditTo( uint16, address _toAddress, - uint _tokenId + uint256 _tokenId ) internal virtual override { if (!_exists(_tokenId) || ERC721A.ownerOf(_tokenId) != address(this)) revert NotExistOrNotOwnedByContract(); @@ -83,16 +86,18 @@ contract ERC721MOnft is function onERC721Received( address, address, - uint, + uint256, bytes memory ) public virtual override returns (bytes4) { return ERC721A__IERC721Receiver.onERC721Received.selector; } - function _isApprovedOrOwner( - address spender, - uint256 tokenId - ) internal view virtual returns (bool) { + function _isApprovedOrOwner(address spender, uint256 tokenId) + internal + view + virtual + returns (bool) + { address owner = ownerOf(tokenId); return (spender == owner || isApprovedForAll(owner, spender) || diff --git a/contracts/ERC721MOperatorFilterer.sol b/contracts/ERC721MOperatorFilterer.sol index 56e8030..a72d0b9 100644 --- a/contracts/ERC721MOperatorFilterer.sol +++ b/contracts/ERC721MOperatorFilterer.sol @@ -2,10 +2,11 @@ pragma solidity ^0.8.4; -import "./ERC721M.sol"; -import "./OperatorFilter/DefaultOperatorFilterer.sol"; +import {ERC721A, ERC721M, IERC721A, Ownable} from "./ERC721M.sol"; +import {UpdatableOperatorFilterer} from "operator-filter-registry/src/UpdatableOperatorFilterer.sol"; +import {CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, ME_SUBSCRIPTION} from "./utils/Constants.sol"; -contract ERC721MOperatorFilterer is ERC721M, DefaultOperatorFilterer { +contract ERC721MOperatorFilterer is ERC721M, UpdatableOperatorFilterer { constructor( string memory collectionName, string memory collectionSymbol, @@ -16,6 +17,11 @@ contract ERC721MOperatorFilterer is ERC721M, DefaultOperatorFilterer { uint64 timestampExpirySeconds, address mintCurrency ) + UpdatableOperatorFilterer( + CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, + ME_SUBSCRIPTION, + true + ) ERC721M( collectionName, collectionSymbol, @@ -28,6 +34,15 @@ contract ERC721MOperatorFilterer is ERC721M, DefaultOperatorFilterer { ) {} + function owner() + public + view + override(Ownable, UpdatableOperatorFilterer) + returns (address) + { + return Ownable.owner(); + } + function transferFrom( address from, address to, diff --git a/contracts/ERC721MOperatorFiltererAutoApprover.sol b/contracts/ERC721MOperatorFiltererAutoApprover.sol index 67a63c9..abef74a 100644 --- a/contracts/ERC721MOperatorFiltererAutoApprover.sol +++ b/contracts/ERC721MOperatorFiltererAutoApprover.sol @@ -56,9 +56,10 @@ contract ERC721MOperatorFiltererAutoApprover is ERC721MOperatorFilterer { return _autoApproveAddress; } - function setAutoApproveAddress( - address autoApproveAddress - ) external onlyOwner { + function setAutoApproveAddress(address autoApproveAddress) + external + onlyOwner + { _autoApproveAddress = autoApproveAddress; emit SetAutoApproveAddress(autoApproveAddress); } diff --git a/contracts/ERC721MPausable.sol b/contracts/ERC721MPausable.sol index 2d22b8a..6321c30 100644 --- a/contracts/ERC721MPausable.sol +++ b/contracts/ERC721MPausable.sol @@ -16,16 +16,16 @@ contract ERC721MPausable is ERC721M, Pausable { uint64 timestampExpirySeconds, address mintCurrency ) - ERC721M( - collectionName, - collectionSymbol, - tokenURISuffix, - maxMintableSupply, - globalWalletLimit, - cosigner, - timestampExpirySeconds, - mintCurrency - ) + ERC721M( + collectionName, + collectionSymbol, + tokenURISuffix, + maxMintableSupply, + globalWalletLimit, + cosigner, + timestampExpirySeconds, + mintCurrency + ) {} function pause() external onlyOwner { @@ -60,4 +60,4 @@ contract ERC721MPausable is ERC721M, Pausable { ) public payable virtual override(ERC721A, IERC721A) whenNotPaused { super.safeTransferFrom(from, to, tokenId, data); } -} \ No newline at end of file +} diff --git a/contracts/ERC721MPausableOperatorFilterer.sol b/contracts/ERC721MPausableOperatorFilterer.sol index 75691fa..f865ecf 100644 --- a/contracts/ERC721MPausableOperatorFilterer.sol +++ b/contracts/ERC721MPausableOperatorFilterer.sol @@ -3,9 +3,13 @@ pragma solidity ^0.8.4; import "./ERC721MPausable.sol"; -import "./OperatorFilter/DefaultOperatorFilterer.sol"; +import {UpdatableOperatorFilterer} from "operator-filter-registry/src/UpdatableOperatorFilterer.sol"; +import {CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, ME_SUBSCRIPTION} from "./utils/Constants.sol"; -contract ERC721MPausableOperatorFilterer is ERC721MPausable, DefaultOperatorFilterer { +contract ERC721MPausableOperatorFilterer is + ERC721MPausable, + UpdatableOperatorFilterer +{ constructor( string memory collectionName, string memory collectionSymbol, @@ -16,7 +20,12 @@ contract ERC721MPausableOperatorFilterer is ERC721MPausable, DefaultOperatorFilt uint64 timestampExpirySeconds, address mintCurrency ) - ERC721MPausable( + UpdatableOperatorFilterer( + CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, + ME_SUBSCRIPTION, + true + ) + ERC721MPausable( collectionName, collectionSymbol, tokenURISuffix, @@ -28,6 +37,15 @@ contract ERC721MPausableOperatorFilterer is ERC721MPausable, DefaultOperatorFilt ) {} + function owner() + public + view + override(Ownable, UpdatableOperatorFilterer) + returns (address) + { + return Ownable.owner(); + } + function transferFrom( address from, address to, @@ -52,4 +70,4 @@ contract ERC721MPausableOperatorFilterer is ERC721MPausable, DefaultOperatorFilt ) public payable override(ERC721MPausable) onlyAllowedOperator(from) { super.safeTransferFrom(from, to, tokenId, data); } -} \ No newline at end of file +} diff --git a/contracts/IERC721M.sol b/contracts/IERC721M.sol index bd99dfd..96942c1 100644 --- a/contracts/IERC721M.sol +++ b/contracts/IERC721M.sol @@ -75,4 +75,4 @@ interface IERC721M is IERC721AQueryable { uint32, uint256 ); -} \ No newline at end of file +} diff --git a/contracts/OperatorFilter/DefaultOperatorFilterer.sol b/contracts/OperatorFilter/DefaultOperatorFilterer.sol deleted file mode 100644 index 4885588..0000000 --- a/contracts/OperatorFilter/DefaultOperatorFilterer.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -import "./OperatorFilterer.sol"; - -abstract contract DefaultOperatorFilterer is OperatorFilterer { - address constant DEFAULT_SUBSCRIPTION = - address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6); - - constructor() OperatorFilterer(DEFAULT_SUBSCRIPTION, true) {} -} diff --git a/contracts/OperatorFilter/IOperatorFilterRegistry.sol b/contracts/OperatorFilter/IOperatorFilterRegistry.sol deleted file mode 100644 index 9870821..0000000 --- a/contracts/OperatorFilter/IOperatorFilterRegistry.sol +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -interface IOperatorFilterRegistry { - function isOperatorAllowed(address registrant, address operator) - external - view - returns (bool); - - function register(address registrant) external; - - function registerAndSubscribe(address registrant, address subscription) - external; - - function registerAndCopyEntries( - address registrant, - address registrantToCopy - ) external; - - function updateOperator( - address registrant, - address operator, - bool filtered - ) external; - - function updateOperators( - address registrant, - address[] calldata operators, - bool filtered - ) external; - - function updateCodeHash( - address registrant, - bytes32 codehash, - bool filtered - ) external; - - function updateCodeHashes( - address registrant, - bytes32[] calldata codeHashes, - bool filtered - ) external; - - function subscribe(address registrant, address registrantToSubscribe) - external; - - function unsubscribe(address registrant, bool copyExistingEntries) external; - - function subscriptionOf(address addr) external returns (address registrant); - - function subscribers(address registrant) - external - returns (address[] memory); - - function subscriberAt(address registrant, uint256 index) - external - returns (address); - - function copyEntriesOf(address registrant, address registrantToCopy) - external; - - function isOperatorFiltered(address registrant, address operator) - external - returns (bool); - - function isCodeHashOfFiltered(address registrant, address operatorWithCode) - external - returns (bool); - - function isCodeHashFiltered(address registrant, bytes32 codeHash) - external - returns (bool); - - function filteredOperators(address addr) - external - returns (address[] memory); - - function filteredCodeHashes(address addr) - external - returns (bytes32[] memory); - - function filteredOperatorAt(address registrant, uint256 index) - external - returns (address); - - function filteredCodeHashAt(address registrant, uint256 index) - external - returns (bytes32); - - function isRegistered(address addr) external returns (bool); - - function codeHashOf(address addr) external returns (bytes32); -} diff --git a/contracts/OperatorFilter/OperatorFilterer.sol b/contracts/OperatorFilter/OperatorFilterer.sol deleted file mode 100644 index e140330..0000000 --- a/contracts/OperatorFilter/OperatorFilterer.sol +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -import "./IOperatorFilterRegistry.sol"; - -abstract contract OperatorFilterer { - error OperatorNotAllowed(address operator); - - IOperatorFilterRegistry constant operatorFilterRegistry = - IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E); - - constructor(address subscriptionOrRegistrantToCopy, bool subscribe) { - // If an inheriting token contract is deployed to a network without the registry deployed, the modifier - // will not revert, but the contract will need to be registered with the registry once it is deployed in - // order for the modifier to filter addresses. - if (address(operatorFilterRegistry).code.length > 0) { - if (subscribe) { - operatorFilterRegistry.registerAndSubscribe( - address(this), - subscriptionOrRegistrantToCopy - ); - } else { - if (subscriptionOrRegistrantToCopy != address(0)) { - operatorFilterRegistry.registerAndCopyEntries( - address(this), - subscriptionOrRegistrantToCopy - ); - } else { - operatorFilterRegistry.register(address(this)); - } - } - } - } - - modifier onlyAllowedOperator(address from) virtual { - // Check registry code length to facilitate testing in environments without a deployed registry. - if (address(operatorFilterRegistry).code.length > 0) { - // Allow spending tokens from addresses with balance - // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred - // from an EOA. - if (from == msg.sender) { - _; - return; - } - if ( - !(operatorFilterRegistry.isOperatorAllowed( - address(this), - msg.sender - ) && - operatorFilterRegistry.isOperatorAllowed( - address(this), - from - )) - ) { - revert OperatorNotAllowed(msg.sender); - } - } - _; - } -} diff --git a/contracts/OperatorFilter/OwnedRegistrant.sol b/contracts/OperatorFilter/OwnedRegistrant.sol new file mode 100644 index 0000000..61d3c77 --- /dev/null +++ b/contracts/OperatorFilter/OwnedRegistrant.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import {OwnedRegistrant} from "operator-filter-registry/src/OwnedRegistrant.sol"; diff --git a/contracts/mocks/MockERC20.sol b/contracts/mocks/MockERC20.sol index 894f63a..7b494c5 100644 --- a/contracts/mocks/MockERC20.sol +++ b/contracts/mocks/MockERC20.sol @@ -5,8 +5,7 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract MockERC20 is ERC20 { - constructor(uint256 initialSupply) ERC20("Mock", "MOCK") { - } + constructor(uint256 initialSupply) ERC20("Mock", "MOCK") {} function mint(address to, uint256 amount) public { _mint(to, amount); diff --git a/contracts/mocks/MockLayerZeroEndpoint.sol b/contracts/mocks/MockLayerZeroEndpoint.sol index 7961ca2..74beeb8 100644 --- a/contracts/mocks/MockLayerZeroEndpoint.sol +++ b/contracts/mocks/MockLayerZeroEndpoint.sol @@ -2,26 +2,54 @@ pragma solidity ^0.8.0; -import { ILayerZeroEndpoint } from "@layerzerolabs/solidity-examples/contracts/interfaces/ILayerZeroEndpoint.sol"; +import {ILayerZeroEndpoint} from "@layerzerolabs/solidity-examples/contracts/interfaces/ILayerZeroEndpoint.sol"; contract MockLayerZeroEndpoint is ILayerZeroEndpoint { - function send(uint16 _dstChainId, bytes calldata _destination, bytes calldata _payload, address payable _refundAddress, address _zroPaymentAddress, bytes calldata _adapterParams) external payable { + function send( + uint16 _dstChainId, + bytes calldata _destination, + bytes calldata _payload, + address payable _refundAddress, + address _zroPaymentAddress, + bytes calldata _adapterParams + ) external payable { // do nothing } - function receivePayload(uint16 _srcChainId, bytes calldata _srcAddress, address _dstAddress, uint64 _nonce, uint _gasLimit, bytes calldata _payload) external { + function receivePayload( + uint16 _srcChainId, + bytes calldata _srcAddress, + address _dstAddress, + uint64 _nonce, + uint256 _gasLimit, + bytes calldata _payload + ) external { // do nothing } - function getInboundNonce(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (uint64) { + function getInboundNonce(uint16 _srcChainId, bytes calldata _srcAddress) + external + view + returns (uint64) + { return 0; } - function getOutboundNonce(uint16 _dstChainId, address _srcAddress) external view returns (uint64) { + function getOutboundNonce(uint16 _dstChainId, address _srcAddress) + external + view + returns (uint64) + { return 0; } - function estimateFees(uint16 _dstChainId, address _userApplication, bytes calldata _payload, bool _payInZRO, bytes calldata _adapterParam) external view returns (uint nativeFee, uint zroFee) { + function estimateFees( + uint16 _dstChainId, + address _userApplication, + bytes calldata _payload, + bool _payInZRO, + bytes calldata _adapterParam + ) external view returns (uint256 nativeFee, uint256 zroFee) { return (0, 0); } @@ -29,19 +57,35 @@ contract MockLayerZeroEndpoint is ILayerZeroEndpoint { return 0; } - function retryPayload(uint16 _srcChainId, bytes calldata _srcAddress, bytes calldata _payload) external { + function retryPayload( + uint16 _srcChainId, + bytes calldata _srcAddress, + bytes calldata _payload + ) external { // do nothing } - function hasStoredPayload(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (bool) { + function hasStoredPayload(uint16 _srcChainId, bytes calldata _srcAddress) + external + view + returns (bool) + { return false; } - function getSendLibraryAddress(address _userApplication) external view returns (address) { + function getSendLibraryAddress(address _userApplication) + external + view + returns (address) + { return address(0); } - function getReceiveLibraryAddress(address _userApplication) external view returns (address) { + function getReceiveLibraryAddress(address _userApplication) + external + view + returns (address) + { return address(0); } @@ -53,19 +97,37 @@ contract MockLayerZeroEndpoint is ILayerZeroEndpoint { return false; } - function getConfig(uint16 _version, uint16 _chainId, address _userApplication, uint _configType) external view returns (bytes memory) { + function getConfig( + uint16 _version, + uint16 _chainId, + address _userApplication, + uint256 _configType + ) external view returns (bytes memory) { return ""; } - function getSendVersion(address _userApplication) external view returns (uint16) { + function getSendVersion(address _userApplication) + external + view + returns (uint16) + { return 0; } - function getReceiveVersion(address _userApplication) external view returns (uint16) { + function getReceiveVersion(address _userApplication) + external + view + returns (uint16) + { return 0; } - function setConfig(uint16 _version, uint16 _chainId, uint _configType, bytes calldata _config) external { + function setConfig( + uint16 _version, + uint16 _chainId, + uint256 _configType, + bytes calldata _config + ) external { // do nothing } @@ -77,9 +139,9 @@ contract MockLayerZeroEndpoint is ILayerZeroEndpoint { // do nothing } - function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external { + function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) + external + { // do nothing } - - -} \ No newline at end of file +} diff --git a/contracts/onft/LzAppLite.sol b/contracts/onft/LzAppLite.sol index a750776..b511772 100644 --- a/contracts/onft/LzAppLite.sol +++ b/contracts/onft/LzAppLite.sol @@ -3,82 +3,138 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/access/Ownable.sol"; -import { ILayerZeroReceiver } from "@layerzerolabs/solidity-examples/contracts/interfaces/ILayerZeroReceiver.sol"; -import { ILayerZeroUserApplicationConfig } from "@layerzerolabs/solidity-examples/contracts/interfaces/ILayerZeroUserApplicationConfig.sol"; -import { ILayerZeroEndpoint } from "@layerzerolabs/solidity-examples/contracts/interfaces/ILayerZeroEndpoint.sol"; -import { BytesLib } from "@layerzerolabs/solidity-examples/contracts/util/BytesLib.sol"; +import {ILayerZeroReceiver} from "@layerzerolabs/solidity-examples/contracts/interfaces/ILayerZeroReceiver.sol"; +import {ILayerZeroUserApplicationConfig} from "@layerzerolabs/solidity-examples/contracts/interfaces/ILayerZeroUserApplicationConfig.sol"; +import {ILayerZeroEndpoint} from "@layerzerolabs/solidity-examples/contracts/interfaces/ILayerZeroEndpoint.sol"; +import {BytesLib} from "@layerzerolabs/solidity-examples/contracts/util/BytesLib.sol"; /* * A lite version of generic LzReceiver implementation (LzApp) * - shorten error messages - * - remove unncessary functions including getConfig, setTrustedRemoteAddress, getTrustedRemoteAddress and isTrustedRemote + * - remove unncessary functions including getConfig, setTrustedRemoteAddress, getTrustedRemoteAddress and isTrustedRemote */ -abstract contract LzAppLite is Ownable, ILayerZeroReceiver, ILayerZeroUserApplicationConfig { +abstract contract LzAppLite is + Ownable, + ILayerZeroReceiver, + ILayerZeroUserApplicationConfig +{ using BytesLib for bytes; // ua can not send payload larger than this by default, but it can be changed by the ua owner - uint constant public DEFAULT_PAYLOAD_SIZE_LIMIT = 10000; + uint256 public constant DEFAULT_PAYLOAD_SIZE_LIMIT = 10000; ILayerZeroEndpoint public immutable lzEndpoint; mapping(uint16 => bytes) public trustedRemoteLookup; - mapping(uint16 => mapping(uint16 => uint)) public minDstGasLookup; - mapping(uint16 => uint) public payloadSizeLimitLookup; + mapping(uint16 => mapping(uint16 => uint256)) public minDstGasLookup; + mapping(uint16 => uint256) public payloadSizeLimitLookup; address public precrime; event SetPrecrime(address precrime); event SetTrustedRemote(uint16 _remoteChainId, bytes _path); event SetTrustedRemoteAddress(uint16 _remoteChainId, bytes _remoteAddress); - event SetMinDstGas(uint16 _dstChainId, uint16 _type, uint _minDstGas); + event SetMinDstGas(uint16 _dstChainId, uint16 _type, uint256 _minDstGas); constructor(address _endpoint) { lzEndpoint = ILayerZeroEndpoint(_endpoint); } - function lzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) public virtual override { + function lzReceive( + uint16 _srcChainId, + bytes calldata _srcAddress, + uint64 _nonce, + bytes calldata _payload + ) public virtual override { // lzReceive must be called by the endpoint for security require(_msgSender() == address(lzEndpoint), "invalid caller"); bytes memory trustedRemote = trustedRemoteLookup[_srcChainId]; // if will still block the message pathway from (srcChainId, srcAddress). should not receive message from untrusted remote. - require(_srcAddress.length == trustedRemote.length && trustedRemote.length > 0 && keccak256(_srcAddress) == keccak256(trustedRemote), "invalid source contract"); + require( + _srcAddress.length == trustedRemote.length && + trustedRemote.length > 0 && + keccak256(_srcAddress) == keccak256(trustedRemote), + "invalid source contract" + ); _blockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); } // abstract function - the default behaviour of LayerZero is blocking. See: NonblockingLzApp if you dont need to enforce ordered messaging - function _blockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual; - - function _lzSend(uint16 _dstChainId, bytes memory _payload, address payable _refundAddress, address _zroPaymentAddress, bytes memory _adapterParams, uint _nativeFee) internal virtual { + function _blockingLzReceive( + uint16 _srcChainId, + bytes memory _srcAddress, + uint64 _nonce, + bytes memory _payload + ) internal virtual; + + function _lzSend( + uint16 _dstChainId, + bytes memory _payload, + address payable _refundAddress, + address _zroPaymentAddress, + bytes memory _adapterParams, + uint256 _nativeFee + ) internal virtual { bytes memory trustedRemote = trustedRemoteLookup[_dstChainId]; - require(trustedRemote.length != 0, "destination chain not a trusted source"); + require( + trustedRemote.length != 0, + "destination chain not a trusted source" + ); _checkPayloadSize(_dstChainId, _payload.length); - lzEndpoint.send{value: _nativeFee}(_dstChainId, trustedRemote, _payload, _refundAddress, _zroPaymentAddress, _adapterParams); + lzEndpoint.send{value: _nativeFee}( + _dstChainId, + trustedRemote, + _payload, + _refundAddress, + _zroPaymentAddress, + _adapterParams + ); } - function _checkGasLimit(uint16 _dstChainId, uint16 _type, bytes memory _adapterParams, uint _extraGas) internal view virtual { - uint providedGasLimit = _getGasLimit(_adapterParams); - uint minGasLimit = minDstGasLookup[_dstChainId][_type] + _extraGas; + function _checkGasLimit( + uint16 _dstChainId, + uint16 _type, + bytes memory _adapterParams, + uint256 _extraGas + ) internal view virtual { + uint256 providedGasLimit = _getGasLimit(_adapterParams); + uint256 minGasLimit = minDstGasLookup[_dstChainId][_type] + _extraGas; require(minGasLimit > 0, "minGasLimit not set"); require(providedGasLimit >= minGasLimit, "gas limit too low"); } - function _getGasLimit(bytes memory _adapterParams) internal pure virtual returns (uint gasLimit) { + function _getGasLimit(bytes memory _adapterParams) + internal + pure + virtual + returns (uint256 gasLimit) + { require(_adapterParams.length >= 34, "invalid adapterParams"); assembly { gasLimit := mload(add(_adapterParams, 34)) } } - function _checkPayloadSize(uint16 _dstChainId, uint _payloadSize) internal view virtual { - uint payloadSizeLimit = payloadSizeLimitLookup[_dstChainId]; - if (payloadSizeLimit == 0) { // use default if not set + function _checkPayloadSize(uint16 _dstChainId, uint256 _payloadSize) + internal + view + virtual + { + uint256 payloadSizeLimit = payloadSizeLimitLookup[_dstChainId]; + if (payloadSizeLimit == 0) { + // use default if not set payloadSizeLimit = DEFAULT_PAYLOAD_SIZE_LIMIT; } require(_payloadSize <= payloadSizeLimit, "payload size too large"); } // generic config for LayerZero user Application - function setConfig(uint16 _version, uint16 _chainId, uint _configType, bytes calldata _config) external override onlyOwner { + function setConfig( + uint16 _version, + uint16 _chainId, + uint256 _configType, + bytes calldata _config + ) external override onlyOwner { lzEndpoint.setConfig(_version, _chainId, _configType, _config); } @@ -90,13 +146,20 @@ abstract contract LzAppLite is Ownable, ILayerZeroReceiver, ILayerZeroUserApplic lzEndpoint.setReceiveVersion(_version); } - function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external override onlyOwner { + function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) + external + override + onlyOwner + { lzEndpoint.forceResumeReceive(_srcChainId, _srcAddress); } // _path = abi.encodePacked(remoteAddress, localAddress) // this function set the trusted path for the cross-chain communication - function setTrustedRemote(uint16 _remoteChainId, bytes calldata _path) external onlyOwner { + function setTrustedRemote(uint16 _remoteChainId, bytes calldata _path) + external + onlyOwner + { trustedRemoteLookup[_remoteChainId] = _path; emit SetTrustedRemote(_remoteChainId, _path); } @@ -106,14 +169,21 @@ abstract contract LzAppLite is Ownable, ILayerZeroReceiver, ILayerZeroUserApplic emit SetPrecrime(_precrime); } - function setMinDstGas(uint16 _dstChainId, uint16 _packetType, uint _minGas) external onlyOwner { + function setMinDstGas( + uint16 _dstChainId, + uint16 _packetType, + uint256 _minGas + ) external onlyOwner { require(_minGas > 0, "invalid minGas"); minDstGasLookup[_dstChainId][_packetType] = _minGas; emit SetMinDstGas(_dstChainId, _packetType, _minGas); } // if the size is 0, it means default size limit - function setPayloadSizeLimit(uint16 _dstChainId, uint _size) external onlyOwner { + function setPayloadSizeLimit(uint16 _dstChainId, uint256 _size) + external + onlyOwner + { payloadSizeLimitLookup[_dstChainId] = _size; } -} \ No newline at end of file +} diff --git a/contracts/onft/NonblockingLzAppLite.sol b/contracts/onft/NonblockingLzAppLite.sol index d4c55c5..de5ee78 100644 --- a/contracts/onft/NonblockingLzAppLite.sol +++ b/contracts/onft/NonblockingLzAppLite.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import "./LzAppLite.sol"; -import { ExcessivelySafeCall } from "@layerzerolabs/solidity-examples/contracts/util/ExcessivelySafeCall.sol"; +import {ExcessivelySafeCall} from "@layerzerolabs/solidity-examples/contracts/util/ExcessivelySafeCall.sol"; /* * the default LayerZero messaging behaviour is blocking, i.e. any failed message will block the channel @@ -15,35 +15,89 @@ abstract contract NonblockingLzAppLite is LzAppLite { constructor(address _endpoint) LzAppLite(_endpoint) {} - mapping(uint16 => mapping(bytes => mapping(uint64 => bytes32))) public failedMessages; + mapping(uint16 => mapping(bytes => mapping(uint64 => bytes32))) + public failedMessages; - event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, bytes _reason); - event RetryMessageSuccess(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes32 _payloadHash); + event MessageFailed( + uint16 _srcChainId, + bytes _srcAddress, + uint64 _nonce, + bytes _payload, + bytes _reason + ); + event RetryMessageSuccess( + uint16 _srcChainId, + bytes _srcAddress, + uint64 _nonce, + bytes32 _payloadHash + ); // overriding the virtual function in LzReceiver - function _blockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual override { - (bool success, bytes memory reason) = address(this).excessivelySafeCall(gasleft(), 150, abi.encodeWithSelector(this.nonblockingLzReceive.selector, _srcChainId, _srcAddress, _nonce, _payload)); + function _blockingLzReceive( + uint16 _srcChainId, + bytes memory _srcAddress, + uint64 _nonce, + bytes memory _payload + ) internal virtual override { + (bool success, bytes memory reason) = address(this).excessivelySafeCall( + gasleft(), + 150, + abi.encodeWithSelector( + this.nonblockingLzReceive.selector, + _srcChainId, + _srcAddress, + _nonce, + _payload + ) + ); // try-catch all errors/exceptions if (!success) { - _storeFailedMessage(_srcChainId, _srcAddress, _nonce, _payload, reason); + _storeFailedMessage( + _srcChainId, + _srcAddress, + _nonce, + _payload, + reason + ); } } - function _storeFailedMessage(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload, bytes memory _reason) internal virtual { + function _storeFailedMessage( + uint16 _srcChainId, + bytes memory _srcAddress, + uint64 _nonce, + bytes memory _payload, + bytes memory _reason + ) internal virtual { failedMessages[_srcChainId][_srcAddress][_nonce] = keccak256(_payload); emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, _reason); } - function nonblockingLzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) public virtual { + function nonblockingLzReceive( + uint16 _srcChainId, + bytes calldata _srcAddress, + uint64 _nonce, + bytes calldata _payload + ) public virtual { // only internal transaction require(_msgSender() == address(this), "caller not LzApp"); _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); } //@notice override this function - function _nonblockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual; + function _nonblockingLzReceive( + uint16 _srcChainId, + bytes memory _srcAddress, + uint64 _nonce, + bytes memory _payload + ) internal virtual; - function retryMessage(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) public payable virtual { + function retryMessage( + uint16 _srcChainId, + bytes calldata _srcAddress, + uint64 _nonce, + bytes calldata _payload + ) public payable virtual { // assert there is message to retry bytes32 payloadHash = failedMessages[_srcChainId][_srcAddress][_nonce]; require(payloadHash != bytes32(0), "no stored message"); @@ -54,4 +108,4 @@ abstract contract NonblockingLzAppLite is LzAppLite { _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); emit RetryMessageSuccess(_srcChainId, _srcAddress, _nonce, payloadHash); } -} \ No newline at end of file +} diff --git a/contracts/onft/ONFT721CoreLite.sol b/contracts/onft/ONFT721CoreLite.sol index eff07dd..cb00fa7 100644 --- a/contracts/onft/ONFT721CoreLite.sol +++ b/contracts/onft/ONFT721CoreLite.sol @@ -2,12 +2,17 @@ pragma solidity ^0.8.0; -import { IONFT721Core } from "@layerzerolabs/solidity-examples/contracts/token/onft/IONFT721Core.sol"; +import {IONFT721Core} from "@layerzerolabs/solidity-examples/contracts/token/onft/IONFT721Core.sol"; import "./NonblockingLzAppLite.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -abstract contract ONFT721CoreLite is NonblockingLzAppLite, ERC165, ReentrancyGuard, IONFT721Core { +abstract contract ONFT721CoreLite is + NonblockingLzAppLite, + ERC165, + ReentrancyGuard, + IONFT721Core +{ uint16 public constant FUNCTION_TYPE_SEND = 1; struct StoredCredit { @@ -22,45 +27,140 @@ abstract contract ONFT721CoreLite is NonblockingLzAppLite, ERC165, ReentrancyGua mapping(uint16 => uint256) public dstChainIdToTransferGas; // per transfer amount of gas required to mint/transfer on the dst mapping(bytes32 => StoredCredit) public storedCredits; - constructor(uint256 _minGasToTransferAndStore, address _lzEndpoint) NonblockingLzAppLite(_lzEndpoint) { - require(_minGasToTransferAndStore > 0, "minGasToTransferAndStore must be > 0"); + constructor(uint256 _minGasToTransferAndStore, address _lzEndpoint) + NonblockingLzAppLite(_lzEndpoint) + { + require( + _minGasToTransferAndStore > 0, + "minGasToTransferAndStore must be > 0" + ); minGasToTransferAndStore = _minGasToTransferAndStore; } - function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { - return interfaceId == type(IONFT721Core).interfaceId || super.supportsInterface(interfaceId); + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(ERC165, IERC165) + returns (bool) + { + return + interfaceId == type(IONFT721Core).interfaceId || + super.supportsInterface(interfaceId); } - function estimateSendFee(uint16 _dstChainId, bytes memory _toAddress, uint _tokenId, bool _useZro, bytes memory _adapterParams) public view virtual override returns (uint nativeFee, uint zroFee) { - return estimateSendBatchFee(_dstChainId, _toAddress, _toSingletonArray(_tokenId), _useZro, _adapterParams); + function estimateSendFee( + uint16 _dstChainId, + bytes memory _toAddress, + uint256 _tokenId, + bool _useZro, + bytes memory _adapterParams + ) public view virtual override returns (uint256 nativeFee, uint256 zroFee) { + return + estimateSendBatchFee( + _dstChainId, + _toAddress, + _toSingletonArray(_tokenId), + _useZro, + _adapterParams + ); } - function estimateSendBatchFee(uint16 _dstChainId, bytes memory _toAddress, uint[] memory _tokenIds, bool _useZro, bytes memory _adapterParams) public view virtual override returns (uint nativeFee, uint zroFee) { + function estimateSendBatchFee( + uint16 _dstChainId, + bytes memory _toAddress, + uint256[] memory _tokenIds, + bool _useZro, + bytes memory _adapterParams + ) public view virtual override returns (uint256 nativeFee, uint256 zroFee) { bytes memory payload = abi.encode(_toAddress, _tokenIds); - return lzEndpoint.estimateFees(_dstChainId, address(this), payload, _useZro, _adapterParams); + return + lzEndpoint.estimateFees( + _dstChainId, + address(this), + payload, + _useZro, + _adapterParams + ); } - function sendFrom(address _from, uint16 _dstChainId, bytes memory _toAddress, uint _tokenId, address payable _refundAddress, address _zroPaymentAddress, bytes memory _adapterParams) public payable virtual override { - _send(_from, _dstChainId, _toAddress, _toSingletonArray(_tokenId), _refundAddress, _zroPaymentAddress, _adapterParams); + function sendFrom( + address _from, + uint16 _dstChainId, + bytes memory _toAddress, + uint256 _tokenId, + address payable _refundAddress, + address _zroPaymentAddress, + bytes memory _adapterParams + ) public payable virtual override { + _send( + _from, + _dstChainId, + _toAddress, + _toSingletonArray(_tokenId), + _refundAddress, + _zroPaymentAddress, + _adapterParams + ); } - function sendBatchFrom(address _from, uint16 _dstChainId, bytes memory _toAddress, uint[] memory _tokenIds, address payable _refundAddress, address _zroPaymentAddress, bytes memory _adapterParams) public payable virtual override { - _send(_from, _dstChainId, _toAddress, _tokenIds, _refundAddress, _zroPaymentAddress, _adapterParams); + function sendBatchFrom( + address _from, + uint16 _dstChainId, + bytes memory _toAddress, + uint256[] memory _tokenIds, + address payable _refundAddress, + address _zroPaymentAddress, + bytes memory _adapterParams + ) public payable virtual override { + _send( + _from, + _dstChainId, + _toAddress, + _tokenIds, + _refundAddress, + _zroPaymentAddress, + _adapterParams + ); } - function _send(address _from, uint16 _dstChainId, bytes memory _toAddress, uint[] memory _tokenIds, address payable _refundAddress, address _zroPaymentAddress, bytes memory _adapterParams) internal virtual { + function _send( + address _from, + uint16 _dstChainId, + bytes memory _toAddress, + uint256[] memory _tokenIds, + address payable _refundAddress, + address _zroPaymentAddress, + bytes memory _adapterParams + ) internal virtual { // allow 1 by default require(_tokenIds.length > 0, "tokenIds[] is empty"); - require(_tokenIds.length == 1 || _tokenIds.length <= dstChainIdToBatchLimit[_dstChainId], "batch size > limit"); + require( + _tokenIds.length == 1 || + _tokenIds.length <= dstChainIdToBatchLimit[_dstChainId], + "batch size > limit" + ); - for (uint i = 0; i < _tokenIds.length; i++) { + for (uint256 i = 0; i < _tokenIds.length; i++) { _debitFrom(_from, _dstChainId, _toAddress, _tokenIds[i]); } bytes memory payload = abi.encode(_toAddress, _tokenIds); - _checkGasLimit(_dstChainId, FUNCTION_TYPE_SEND, _adapterParams, dstChainIdToTransferGas[_dstChainId] * _tokenIds.length); - _lzSend(_dstChainId, payload, _refundAddress, _zroPaymentAddress, _adapterParams, msg.value); + _checkGasLimit( + _dstChainId, + FUNCTION_TYPE_SEND, + _adapterParams, + dstChainIdToTransferGas[_dstChainId] * _tokenIds.length + ); + _lzSend( + _dstChainId, + payload, + _refundAddress, + _zroPaymentAddress, + _adapterParams, + msg.value + ); emit SendToChain(_dstChainId, _from, _toAddress, _tokenIds); } @@ -71,18 +171,26 @@ abstract contract ONFT721CoreLite is NonblockingLzAppLite, ERC165, ReentrancyGua bytes memory _payload ) internal virtual override { // decode and load the toAddress - (bytes memory toAddressBytes, uint[] memory tokenIds) = abi.decode(_payload, (bytes, uint[])); + (bytes memory toAddressBytes, uint256[] memory tokenIds) = abi.decode( + _payload, + (bytes, uint256[]) + ); address toAddress; assembly { toAddress := mload(add(toAddressBytes, 20)) } - uint nextIndex = _creditTill(_srcChainId, toAddress, 0, tokenIds); + uint256 nextIndex = _creditTill(_srcChainId, toAddress, 0, tokenIds); if (nextIndex < tokenIds.length) { // not enough gas to complete transfers, store to be cleared in another tx bytes32 hashedPayload = keccak256(_payload); - storedCredits[hashedPayload] = StoredCredit(_srcChainId, toAddress, nextIndex, true); + storedCredits[hashedPayload] = StoredCredit( + _srcChainId, + toAddress, + nextIndex, + true + ); emit CreditStored(hashedPayload, _payload); } @@ -92,12 +200,26 @@ abstract contract ONFT721CoreLite is NonblockingLzAppLite, ERC165, ReentrancyGua // Public function for anyone to clear and deliver the remaining batch sent tokenIds function clearCredits(bytes memory _payload) external virtual nonReentrant { bytes32 hashedPayload = keccak256(_payload); - require(storedCredits[hashedPayload].creditsRemain, "no credits stored"); - - (, uint[] memory tokenIds) = abi.decode(_payload, (bytes, uint[])); - - uint nextIndex = _creditTill(storedCredits[hashedPayload].srcChainId, storedCredits[hashedPayload].toAddress, storedCredits[hashedPayload].index, tokenIds); - require(nextIndex > storedCredits[hashedPayload].index, "not enough gas"); + require( + storedCredits[hashedPayload].creditsRemain, + "no credits stored" + ); + + (, uint256[] memory tokenIds) = abi.decode( + _payload, + (bytes, uint256[]) + ); + + uint256 nextIndex = _creditTill( + storedCredits[hashedPayload].srcChainId, + storedCredits[hashedPayload].toAddress, + storedCredits[hashedPayload].index, + tokenIds + ); + require( + nextIndex > storedCredits[hashedPayload].index, + "not enough gas" + ); if (nextIndex == tokenIds.length) { // cleared the credits, delete the element @@ -105,14 +227,24 @@ abstract contract ONFT721CoreLite is NonblockingLzAppLite, ERC165, ReentrancyGua emit CreditCleared(hashedPayload); } else { // store the next index to mint - storedCredits[hashedPayload] = StoredCredit(storedCredits[hashedPayload].srcChainId, storedCredits[hashedPayload].toAddress, nextIndex, true); + storedCredits[hashedPayload] = StoredCredit( + storedCredits[hashedPayload].srcChainId, + storedCredits[hashedPayload].toAddress, + nextIndex, + true + ); } } // When a srcChain has the ability to transfer more chainIds in a single tx than the dst can do. // Needs the ability to iterate and stop if the minGasToTransferAndStore is not met - function _creditTill(uint16 _srcChainId, address _toAddress, uint _startIndex, uint[] memory _tokenIds) internal returns (uint256){ - uint i = _startIndex; + function _creditTill( + uint16 _srcChainId, + address _toAddress, + uint256 _startIndex, + uint256[] memory _tokenIds + ) internal returns (uint256) { + uint256 i = _startIndex; while (i < _tokenIds.length) { // if not enough gas to process, store this index for next loop if (gasleft() < minGasToTransferAndStore) break; @@ -126,33 +258,55 @@ abstract contract ONFT721CoreLite is NonblockingLzAppLite, ERC165, ReentrancyGua return i; } - function setMinGasToTransferAndStore(uint256 _minGasToTransferAndStore) external onlyOwner { + function setMinGasToTransferAndStore(uint256 _minGasToTransferAndStore) + external + onlyOwner + { require(_minGasToTransferAndStore > 0, "must be > 0"); minGasToTransferAndStore = _minGasToTransferAndStore; emit SetMinGasToTransferAndStore(_minGasToTransferAndStore); } // ensures enough gas in adapter params to handle batch transfer gas amounts on the dst - function setDstChainIdToTransferGas(uint16 _dstChainId, uint256 _dstChainIdToTransferGas) external onlyOwner { + function setDstChainIdToTransferGas( + uint16 _dstChainId, + uint256 _dstChainIdToTransferGas + ) external onlyOwner { require(_dstChainIdToTransferGas > 0, "must be > 0"); dstChainIdToTransferGas[_dstChainId] = _dstChainIdToTransferGas; emit SetDstChainIdToTransferGas(_dstChainId, _dstChainIdToTransferGas); } // limit on src the amount of tokens to batch send - function setDstChainIdToBatchLimit(uint16 _dstChainId, uint256 _dstChainIdToBatchLimit) external onlyOwner { + function setDstChainIdToBatchLimit( + uint16 _dstChainId, + uint256 _dstChainIdToBatchLimit + ) external onlyOwner { require(_dstChainIdToBatchLimit > 0, "must be > 0"); dstChainIdToBatchLimit[_dstChainId] = _dstChainIdToBatchLimit; emit SetDstChainIdToBatchLimit(_dstChainId, _dstChainIdToBatchLimit); } - function _debitFrom(address _from, uint16 _dstChainId, bytes memory _toAddress, uint _tokenId) internal virtual; + function _debitFrom( + address _from, + uint16 _dstChainId, + bytes memory _toAddress, + uint256 _tokenId + ) internal virtual; - function _creditTo(uint16 _srcChainId, address _toAddress, uint _tokenId) internal virtual; - - function _toSingletonArray(uint element) internal pure returns (uint[] memory) { - uint[] memory array = new uint[](1); + function _creditTo( + uint16 _srcChainId, + address _toAddress, + uint256 _tokenId + ) internal virtual; + + function _toSingletonArray(uint256 element) + internal + pure + returns (uint256[] memory) + { + uint256[] memory array = new uint256[](1); array[0] = element; return array; } -} \ No newline at end of file +} diff --git a/contracts/onft/ONFT721Lite.sol b/contracts/onft/ONFT721Lite.sol index 2cb3390..04678b6 100644 --- a/contracts/onft/ONFT721Lite.sol +++ b/contracts/onft/ONFT721Lite.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; -import { IONFT721 } from "@layerzerolabs/solidity-examples/contracts/token/onft/IONFT721.sol"; +import {IONFT721} from "@layerzerolabs/solidity-examples/contracts/token/onft/IONFT721.sol"; import "./ONFT721CoreLite.sol"; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; @@ -14,20 +14,48 @@ contract ONFT721Lite is ONFT721CoreLite, ERC721, IONFT721 { // The suffix for the token URL, e.g. ".json". string private _tokenURISuffix; - constructor(string memory _name, string memory _symbol, uint256 _minGasToTransfer, address _lzEndpoint) ERC721(_name, _symbol) ONFT721CoreLite(_minGasToTransfer, _lzEndpoint) {} + constructor( + string memory _name, + string memory _symbol, + uint256 _minGasToTransfer, + address _lzEndpoint + ) ERC721(_name, _symbol) ONFT721CoreLite(_minGasToTransfer, _lzEndpoint) {} - function supportsInterface(bytes4 interfaceId) public view virtual override(ONFT721CoreLite, ERC721, IERC165) returns (bool) { - return interfaceId == type(IONFT721).interfaceId || super.supportsInterface(interfaceId); + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(ONFT721CoreLite, ERC721, IERC165) + returns (bool) + { + return + interfaceId == type(IONFT721).interfaceId || + super.supportsInterface(interfaceId); } - function _debitFrom(address _from, uint16, bytes memory, uint _tokenId) internal virtual override { - require(_isApprovedOrOwner(_msgSender(), _tokenId), "caller not owner nor approved"); + function _debitFrom( + address _from, + uint16, + bytes memory, + uint256 _tokenId + ) internal virtual override { + require( + _isApprovedOrOwner(_msgSender(), _tokenId), + "caller not owner nor approved" + ); require(ERC721.ownerOf(_tokenId) == _from, "incorrect owner"); _transfer(_from, address(this), _tokenId); } - function _creditTo(uint16, address _toAddress, uint _tokenId) internal virtual override { - require(!_exists(_tokenId) || (_exists(_tokenId) && ERC721.ownerOf(_tokenId) == address(this))); + function _creditTo( + uint16, + address _toAddress, + uint256 _tokenId + ) internal virtual override { + require( + !_exists(_tokenId) || + (_exists(_tokenId) && ERC721.ownerOf(_tokenId) == address(this)) + ); if (!_exists(_tokenId)) { _safeMint(_toAddress, _tokenId); } else { @@ -52,7 +80,12 @@ contract ONFT721Lite is ONFT721CoreLite, ERC721, IONFT721 { /** * @dev Returns token URI for a given token id. */ - function tokenURI(uint256 tokenId) public view override returns (string memory) { + function tokenURI(uint256 tokenId) + public + view + override + returns (string memory) + { require(_exists(tokenId), "token not exist"); string memory baseURI = _currentBaseURI; @@ -71,7 +104,12 @@ contract ONFT721Lite is ONFT721CoreLite, ERC721, IONFT721 { /** * @dev Converts a uint256 to its ASCII string decimal representation. */ - function _toString(uint256 value) internal pure virtual returns (string memory str) { + function _toString(uint256 value) + internal + pure + virtual + returns (string memory str) + { assembly { // The maximum value of a uint256 contains 78 digits (1 byte per digit), but // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned. @@ -109,4 +147,4 @@ contract ONFT721Lite is ONFT721CoreLite, ERC721, IONFT721 { mstore(str, length) } } -} \ No newline at end of file +} diff --git a/contracts/utils/Constants.sol b/contracts/utils/Constants.sol new file mode 100644 index 0000000..207d13e --- /dev/null +++ b/contracts/utils/Constants.sol @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +address constant CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS = 0x000000000000AAeB6D7670E522A718067333cd4E; +address constant ME_SUBSCRIPTION = 0x0403c10721Ff2936EfF684Bbb57CD792Fd4b1B6c; diff --git a/hardhat.config.ts b/hardhat.config.ts index 2fd3ac8..6f37534 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -36,6 +36,7 @@ import { deployOnft } from './scripts/deployOnft'; import { setOnftMinDstGas } from './scripts/setOnftMinDstGas'; import { setTrustedRemote } from './scripts/setTrustedRemote'; import { sendOnft } from './scripts/sendOnft'; +import { deployOwnedRegistrant } from './scripts/deployOwnedRegistrant'; const config: HardhatUserConfig = { solidity: { @@ -86,6 +87,16 @@ const config: HardhatUserConfig = { accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], }, + fuji: { + url: process.env.FUJI_URL || 'https://api.avax-test.network/ext/bc/C/rpc', + accounts: + process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], + }, + sepolia: { + url: process.env.SEPOLIA_URL || 'https://rpc.sepolia.org', + accounts: + process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], + } }, gasReporter: { enabled: process.env.REPORT_GAS !== undefined, @@ -112,8 +123,8 @@ task('deploy', 'Deploy ERC721M') .addParam('symbol', 'symbol') .addParam('maxsupply', 'max supply') .addParam('tokenurisuffix', 'token uri suffix', '.json') - .addParam('globalwalletlimit', 'global wallet limit') - .addParam('timestampexpiryseconds', 'timestamp expiry in seconds') + .addParam('globalwalletlimit', 'global wallet limit', '0') + .addParam('timestampexpiryseconds', 'timestamp expiry in seconds', '300') .addOptionalParam( 'cosigner', 'cosigner address (0x00...000 if not using cosign)', @@ -318,4 +329,8 @@ task('sendOnft', 'Send tokens to target network') ) .setAction(sendOnft); +task('deployOwnedRegistrant', 'Deploy OwnedRegistrant') + .addParam('newowner', 'new owner address', '0x0000000000000000000000000000000000000000') + .setAction(deployOwnedRegistrant); + export default config; diff --git a/package-lock.json b/package-lock.json index e5d571a..09f6120 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "@inquirer/prompts": "^2.2.0", "@layerzerolabs/solidity-examples": "^0.0.13", "@openzeppelin/contracts": "^4.7.3", - "erc721a": "^4.2.3" + "erc721a": "^4.2.3", + "operator-filter-registry": "^1.4.2" }, "devDependencies": { "@nomicfoundation/hardhat-network-helpers": "^1.0.6", @@ -3181,9 +3182,9 @@ "integrity": "sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA==" }, "node_modules/@openzeppelin/contracts": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.7.3.tgz", - "integrity": "sha512-dGRS0agJzu8ybo44pCIf3xBaPQN/65AIXNgK8+4gzKd5kbvlqyxryUYVLJv7fK98Seyd2hDZzVEHSWAh0Bt1Yw==" + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.9.5.tgz", + "integrity": "sha512-ZK+W5mVhRppff9BE6YdR8CC52C8zAvsVAiWhEtQ5+oNxFE6h1WdeWo+FJSF8KKvtxxVYZ7MTP/5KoVpAU3aSWg==" }, "node_modules/@openzeppelin/contracts-upgradeable": { "version": "4.9.2", @@ -20533,6 +20534,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/operator-filter-registry": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/operator-filter-registry/-/operator-filter-registry-1.4.2.tgz", + "integrity": "sha512-BwbzpYUtYePAotSV+TMT0r4TU/eGjlhYKohbhu9sf1g/T3KLRMIfUau6bJp8QyLSCBODr6ega34lj4RakbredQ==", + "dependencies": { + "@openzeppelin/contracts": "^4.7.3", + "@openzeppelin/contracts-upgradeable": "^4.8.2" + } + }, "node_modules/optionator": { "version": "0.9.1", "dev": true, @@ -27131,9 +27141,9 @@ "integrity": "sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA==" }, "@openzeppelin/contracts": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.7.3.tgz", - "integrity": "sha512-dGRS0agJzu8ybo44pCIf3xBaPQN/65AIXNgK8+4gzKd5kbvlqyxryUYVLJv7fK98Seyd2hDZzVEHSWAh0Bt1Yw==" + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.9.5.tgz", + "integrity": "sha512-ZK+W5mVhRppff9BE6YdR8CC52C8zAvsVAiWhEtQ5+oNxFE6h1WdeWo+FJSF8KKvtxxVYZ7MTP/5KoVpAU3aSWg==" }, "@openzeppelin/contracts-upgradeable": { "version": "4.9.2", @@ -40350,6 +40360,15 @@ "is-wsl": "^2.1.1" } }, + "operator-filter-registry": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/operator-filter-registry/-/operator-filter-registry-1.4.2.tgz", + "integrity": "sha512-BwbzpYUtYePAotSV+TMT0r4TU/eGjlhYKohbhu9sf1g/T3KLRMIfUau6bJp8QyLSCBODr6ega34lj4RakbredQ==", + "requires": { + "@openzeppelin/contracts": "^4.7.3", + "@openzeppelin/contracts-upgradeable": "^4.8.2" + } + }, "optionator": { "version": "0.9.1", "dev": true, diff --git a/package.json b/package.json index dead0a5..975f2f9 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "@inquirer/prompts": "^2.2.0", "@layerzerolabs/solidity-examples": "^0.0.13", "@openzeppelin/contracts": "^4.7.3", - "erc721a": "^4.2.3" + "erc721a": "^4.2.3", + "operator-filter-registry": "^1.4.2" }, "peerDependencies": { "ethers": "^5.0.0" diff --git a/scripts/deployOwnedRegistrant.ts b/scripts/deployOwnedRegistrant.ts new file mode 100644 index 0000000..7d10c2f --- /dev/null +++ b/scripts/deployOwnedRegistrant.ts @@ -0,0 +1,41 @@ +import { confirm } from '@inquirer/prompts'; +import { HardhatRuntimeEnvironment } from 'hardhat/types'; + +export interface IDeployOwnedRegistrantParams { + newowner: string; +} + +export const deployOwnedRegistrant = async ( + args: IDeployOwnedRegistrantParams, + hre: HardhatRuntimeEnvironment, +) => { + // Compile again in case we have a coverage build (binary too large to deploy) + await hre.run('compile'); + const contractName = 'OwnedRegistrant'; + + const OwnedRegistrant = await hre.ethers.getContractFactory(contractName); + + const params = [ + args.newowner + ] as const; + + console.log( + `Constructor params: `, + JSON.stringify( + params.map((param) => { + if (hre.ethers.BigNumber.isBigNumber(param)) { + return param.toString(); + } + return param; + }), + ), + ); + + if (!(await confirm({ message: 'Continue to deploy?' }))) return; + + const contract = await OwnedRegistrant.deploy(...params); + + await contract.deployed(); + + console.log(`${contractName} deployed to:`, contract.address); +}; diff --git a/scripts/setBaseURI.ts b/scripts/setBaseURI.ts index 79aef29..0d2840f 100644 --- a/scripts/setBaseURI.ts +++ b/scripts/setBaseURI.ts @@ -12,7 +12,7 @@ export const setBaseURI = async ( hre: HardhatRuntimeEnvironment, ) => { const { ethers } = hre; - let overrides: any = {gasLimit: 500_000}; + const overrides: any = {gasLimit: 500_000}; if (args.gaspricegwei) { overrides.gasPrice = args.gaspricegwei * 1e9; diff --git a/scripts/setPrice.ts b/scripts/setPrice.ts index 723901a..015919a 100644 --- a/scripts/setPrice.ts +++ b/scripts/setPrice.ts @@ -10,7 +10,7 @@ export const setPrice = async ( args: ISetPriceParams, hre: HardhatRuntimeEnvironment, ) => { - let overrides: any = { gasLimit: 500_000 }; + const overrides: any = { gasLimit: 500_000 }; const { ethers } = hre; const ERC721M = await ethers.getContractFactory( ContractDetails.BucketAuction.name, diff --git a/scripts/setStages.ts b/scripts/setStages.ts index f97f4aa..3e51d0d 100644 --- a/scripts/setStages.ts +++ b/scripts/setStages.ts @@ -28,7 +28,7 @@ export const setStages = async ( fs.readFileSync(args.stages, 'utf-8'), ) as StageConfig[]; - let overrides: any = { gasLimit: 500_000 }; + const overrides: any = { gasLimit: 500_000 }; const ERC721M = await ethers.getContractFactory(ContractDetails.ERC721M.name); const contract = ERC721M.attach(args.contract);