Skip to content

Commit

Permalink
Add transfer hook extension back into erc721a
Browse files Browse the repository at this point in the history
  • Loading branch information
iainnash committed Mar 12, 2024
1 parent 8b9b7bb commit 5271db1
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 1 deletion.
30 changes: 29 additions & 1 deletion src/ERC721Drop.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ import {IERC721Drop} from "./interfaces/IERC721Drop.sol";
import {IOwnable} from "./interfaces/IOwnable.sol";
import {IERC4906} from "./interfaces/IERC4906.sol";
import {IFactoryUpgradeGate} from "./interfaces/IFactoryUpgradeGate.sol";
import {ITransferHookExtension} from "./interfaces/ITransferHookExtension.sol";
import {OwnableSkeleton} from "./utils/OwnableSkeleton.sol";
import {FundsReceiver} from "./utils/FundsReceiver.sol";
import {Version} from "./utils/Version.sol";
import {PublicMulticall} from "./utils/PublicMulticall.sol";
import {ERC721DropStorageV1} from "./storage/ERC721DropStorageV1.sol";
import {ERC721DropStorageV2} from "./storage/ERC721DropStorageV2.sol";
import {ERC721TransferHookStorageV1, TransferHookStorage} from "./storage/ERC721TransferHookStorageV1.sol";


/**
Expand All @@ -64,7 +66,8 @@ contract ERC721Drop is
ERC721DropStorageV1,
ERC721DropStorageV2,
ERC721Rewards,
ERC721RewardsStorageV1
ERC721RewardsStorageV1,
ERC721TransferHookStorageV1
{
/// @dev This is the max mint batch size for the optimized ERC721A mint contract
uint256 internal immutable MAX_MINT_BATCH_SIZE = 8;
Expand Down Expand Up @@ -856,6 +859,31 @@ contract ERC721Drop is
_setOwner(newOwner);
}

/// @notice Admin function to set the NFT transfer hook, useful for metadata and non-transferrable NFTs.
/// @dev Set to 0 to disable, address to enable transfer hook.
/// @param newTransferHook new transfer hook to receive before token transfer events
function setTransferHook(address newTransferHook) public onlyAdmin {
if (newTransferHook == address(0) || ITransferHookExtension(newTransferHook).supportsInterface(type(ITransferHookExtension).interfaceId)) {
_setTransferHook(newTransferHook);
} else {
revert InvalidTransferHook();
}
}

/// @notice Handles the internal before token transfer hook
/// @param from address transfer is coming from
/// @param to address transfer is going to
/// @param tokenId token id for transfer
/// @param amount number of transfers
function _beforeTokenTransfers(address from, address to, uint256 tokenId, uint256 amount) internal override virtual {
TransferHookStorage storage transferHookStorage = _getTransferHookStorage();
if (transferHookStorage.transferHookExtension != address(0)) {
ITransferHookExtension(transferHookStorage.transferHookExtension).beforeTokenTransfers(from, to, tokenId, amount);
}

super._beforeTokenTransfers(from, to, tokenId, amount);
}

/// @notice Set a new metadata renderer
/// @param newRenderer new renderer address to use
/// @param setupRenderer data to setup new renderer with
Expand Down
7 changes: 7 additions & 0 deletions src/interfaces/ITransferHookExtension.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface ITransferHookExtension {
function beforeTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) external;
function supportsInterface(bytes4 interfaceId) external returns (bool);
}
29 changes: 29 additions & 0 deletions src/storage/ERC721TransferHookStorageV1.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {IERC721Drop} from "../interfaces/IERC721Drop.sol";

/// @custom:storage-location erc7201:zora.erc721drop.transferhook
struct TransferHookStorage {
address transferHookExtension;
}

contract ERC721TransferHookStorageV1 {
error InvalidTransferHook();
event SetNewTransferHook(address _newTransferHook);

// keccak256(abi.encode(uint256(keccak256("zora.erc721drop.transferhook")) - 1)) & ~bytes32(uint256(0xff));
bytes32 private constant TRANSFER_HOOK_STORAGE_LOCATION =
0x7dd1076582dd9e0dc6a5073ed536c067f2e92ed46866d3076f6f2d9a5e36b400;

function _getTransferHookStorage() internal pure returns (TransferHookStorage storage $) {
assembly {
$.slot := TRANSFER_HOOK_STORAGE_LOCATION
}
}

function _setTransferHook(address _newTransferHook) internal {
_getTransferHookStorage().transferHookExtension = _newTransferHook;
emit SetNewTransferHook(_newTransferHook);
}
}

0 comments on commit 5271db1

Please sign in to comment.