Skip to content

Commit

Permalink
Merge pull request #14 from defientco/updated-tokentransfer
Browse files Browse the repository at this point in the history
added ownerOf hook+
  • Loading branch information
Bulalu authored Aug 5, 2023
2 parents f72dcc9 + 6a8f30e commit 0d47340
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 2 deletions.
15 changes: 15 additions & 0 deletions src/ERC721ACH.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {ERC721AC} from "ERC721C/erc721c/ERC721AC.sol";
import {IERC721A} from "erc721a/contracts/IERC721A.sol";
import {IBeforeTokenTransfersHook} from "./interfaces/IBeforeTokenTransfersHook.sol";
import {IAfterTokenTransfersHook} from "./interfaces/IAfterTokenTransfersHook.sol";
import {IOwnerOfHook} from "./interfaces/IOwnerOfHook.sol";
import {IERC721ACH} from "./interfaces/IERC721ACH.sol";

/**
Expand Down Expand Up @@ -93,6 +94,20 @@ contract ERC721ACH is ERC721AC, IERC721ACH {
}
}

function ownerOf(uint256 tokenId) public view virtual override returns (address) {

IOwnerOfHook ownerOfHook = IOwnerOfHook(hooks[HookType.OwnerOf]);

if (
address(ownerOfHook) != address(0) &&
ownerOfHook.useOwnerOfHook(tokenId)
) {
return ownerOfHook.ownerOfOverrideHook(tokenId);
}

return super.ownerOf(tokenId);
}


/**
* @notice Returns the contract address for a specified hook type.
Expand Down
5 changes: 3 additions & 2 deletions src/interfaces/IERC721ACH.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ interface IERC721ACH {
/// @notice Hook for custom logic before a token transfer occurs.
BeforeTokenTransfers,
/// @notice Hook for custom logic after a token transfer occurs.
AfterTokenTransfers

AfterTokenTransfers,
/// @notice Hook for custom logic for ownerOf() function.
OwnerOf
}


Expand Down
35 changes: 35 additions & 0 deletions src/interfaces/IOwnerOfHook.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

/// @title IOwnerOfHook
/// @dev Interface that defines hooks for retrieving the owner of a token.
interface IOwnerOfHook {

/**
@notice Emitted when the owner of hook is used.
@param tokenId The ID of the token whose owner is being retrieved.
@param owner The address of the owner of the token.
*/
event OwnerOfHookUsed(
uint256 tokenId,
address owner
);

/**
@notice Checks if the owner retrieval function should use the custom hook.
@param tokenId The ID of the token whose owner is being retrieved.
@return A boolean indicating whether or not to use the custom hook for the owner retrieval function.
*/
function useOwnerOfHook(
uint256 tokenId
) external view returns (bool);

/**
@notice Provides a custom implementation for the owner retrieval process.
@param tokenId The ID of the token whose owner is being retrieved.
@return The address of the owner of the token.
*/
function ownerOfOverrideHook(
uint256 tokenId
) external view returns (address);
}
54 changes: 54 additions & 0 deletions test/hooks/OwnerOfHook.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import {Vm} from "forge-std/Vm.sol";
import {DSTest} from "ds-test/test.sol";
import {ERC721ACHMock} from "../utils/ERC721ACHMock.sol";
import {IERC721A} from "lib/ERC721A/contracts/IERC721A.sol";
import {OwnerOfHookMock} from "../utils/hooks/OwnerOfHookMock.sol";
import {IERC721ACH} from "../../src/interfaces/IERC721ACH.sol";

contract OwnerOfHookTest is DSTest {
Vm public constant vm = Vm(HEVM_ADDRESS);
address public constant DEFAULT_OWNER_ADDRESS = address(0xC0FFEE);
address public constant DEFAULT_BUYER_ADDRESS = address(0xBABE);
ERC721ACHMock erc721Mock;
OwnerOfHookMock hookMock;

IERC721ACH.HookType constant OwnerOf = IERC721ACH.HookType.OwnerOf;

function setUp() public {
erc721Mock = new ERC721ACHMock(DEFAULT_OWNER_ADDRESS);
hookMock = new OwnerOfHookMock();
}

function test_ownerOfHook() public {
assertEq(address(0), address(erc721Mock.getHook(OwnerOf)));
}

function test_setOwnerOfHook() public {
assertEq(address(0), address(erc721Mock.getHook(OwnerOf)));

// calling an admin function without being the contract owner should revert
vm.expectRevert();
erc721Mock.setHook(OwnerOf, address(hookMock));

vm.prank(DEFAULT_OWNER_ADDRESS);
erc721Mock.setHook(OwnerOf, address(hookMock));
assertEq(address(hookMock), address(erc721Mock.getHook(OwnerOf)));
}

function test_ownerOfHook(uint256 tokenId) public {
vm.assume(tokenId > 0);
vm.assume(tokenId < 10);

test_setOwnerOfHook();
erc721Mock.mint(DEFAULT_BUYER_ADDRESS, tokenId);

assertEq(DEFAULT_BUYER_ADDRESS, erc721Mock.ownerOf(tokenId));

hookMock.setHooksEnabled(true);
vm.expectRevert(OwnerOfHookMock.OwnerOfHook_Executed.selector);
erc721Mock.ownerOf(tokenId);
}
}
37 changes: 37 additions & 0 deletions test/utils/hooks/OwnerOfHookMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import {IOwnerOfHook} from "../../../src/interfaces/IOwnerOfHook.sol";

contract OwnerOfHookMock is IOwnerOfHook {
/// @notice hook was executed
error OwnerOfHook_Executed();

bool public hooksEnabled;
address public fixedOwner;

/// @notice toggle ownerOf hook.
function setHooksEnabled(bool _enabled) public {
hooksEnabled = _enabled;
}

/// @notice set fixed owner returned by the hook.
function setFixedOwner(address _fixedOwner) public {
fixedOwner = _fixedOwner;
}

/// @notice Check if the ownerOf function should use hook.
/// @dev Returns whether or not to use the hook for ownerOf function
function useOwnerOfHook(
uint256
) external view override returns (bool) {
return hooksEnabled;
}

/// @notice custom implementation for ownerOf Hook.
function ownerOfOverrideHook(
uint256
) external view override returns (address) {
revert OwnerOfHook_Executed();
}
}

0 comments on commit 0d47340

Please sign in to comment.