Unable to extend ERC721Consecutive

Hey guys! I'm trying to extend ERC721Consecutive to have blacklist functionality, this approach was working well with just ERC721 but with the Consecutive extension, I have an unexpected error on deploy time.

    Error: Transaction reverted: function returned an unexpected amount of data
    at ERC721CollectionBlacklist._beforeTokenTransfer (contracts/Mechanics/Collection/ERC721CollectionBlacklist.sol:27)
    at ERC721CollectionBlacklist._mintConsecutive (@openzeppelin/contracts/token/ERC721/extensions/ERC721Consecutive.sol:94)
    at ERC721CollectionBlacklist.constructor (contracts/Mechanics/Collection/ERC721CollectionSimple.sol:14)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

It seems don't like the call of this.isBlacklisted(from), in _beforeTokenTransfer, but I don't understand how to work around it. Please advice!

:1234: Code to reproduce

pragma solidity ^0.8.13;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Consecutive.sol";

contract ERC721CollectionSimple is ERC721, ERC721Consecutive {
  constructor(string memory name, string memory symbol, uint96 batchSize, address owner) ERC721(name, symbol) {
    _mintConsecutive(owner, batchSize);

  function _mint(address to, uint256 tokenId) internal virtual override(ERC721, ERC721Consecutive) {
    super._mint(to, tokenId);

  function _ownerOf(uint256 tokenId) internal view virtual override(ERC721, ERC721Consecutive) returns (address) {
    return super._ownerOf(tokenId);

  function _afterTokenTransfer(
    address from,
    address to,
    uint256 firstTokenId,
    uint256 batchSize
  ) internal virtual override(ERC721, ERC721Consecutive) {
    super._afterTokenTransfer(from, to, firstTokenId, batchSize);

import "./ERC721CollectionSimple.sol";
import "./BlackList.sol";

contract ERC721CollectionBlacklist is ERC721CollectionSimple, BlackList {
    string memory name,
    string memory symbol,
    uint96 batchSize,
    address owner
  ) ERC721CollectionSimple(name, symbol, batchSize, owner) {}

  function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, AccessControl) returns (bool) {
    return super.supportsInterface(interfaceId);

  function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal override {
    require(from == address(0) || !this.isBlacklisted(from), "Blacklist: sender is blacklisted");
    require(to == address(0) || !this.isBlacklisted(to), "Blacklist: receiver is blacklisted");
    super._beforeTokenTransfer(from, to, firstTokenId, batchSize);

pragma solidity ^0.8.13;

import "@openzeppelin/contracts/access/AccessControl.sol";

interface IBlackList {
  error BlackListError(address account);

  event Blacklisted(address indexed account);
  event UnBlacklisted(address indexed account);

  function blacklist(address account) external;

  function unBlacklist(address account) external;

  function isBlacklisted(address account) external view returns (bool);

abstract contract BlackList is IBlackList, AccessControl {
  mapping(address => bool) blackList;

  function blacklist(address account) external onlyRole(DEFAULT_ADMIN_ROLE) {
    blackList[account] = true;
    emit Blacklisted(account);

  function unBlacklist(address account) external onlyRole(DEFAULT_ADMIN_ROLE) {
    blackList[account] = false;
    emit UnBlacklisted(account);

  function isBlacklisted(address account) external view returns (bool) {
    return blackList[account];

  function _blacklist(address account) internal view {
    if (this.isBlacklisted(account)) {
      revert BlackListError(account);

  modifier onlyNotBlackListed() {

:computer: Environment

"ethers": "5.7.2",
"hardhat": "2.14.0",
"hardhat-contract-sizer": "2.8.0",
"hardhat-deploy": "0.11.26",
"hardhat-gas-reporter": "1.0.9",
"solidity-coverage": "0.8.2",
"web3": "1.9.0",
"chai": "4.3.7"

The error-message:

Implies that this is not the problem:

For starters, I would change function isBlacklisted from external to internal or to public, and then call it without this (which probably triggers an external contract function call).
It's probably not the cause of the problem, but it might help clean up a bit.

You can add some printouts inside _beforeTokenTransfer, in order to point out exactly where the function reverts. My guess is that it fails when it calls super._beforeTokenTransfer, and not before.

thanks @barakman !
converting it from external to internal fixed the issue. it looks like I have to have 2 functions then.

I also played around with it and can say that the problem was with this.isBlacklisted and not with super._beforeTokenTransfer . Someone also told me I can't mix this and super inside the constructor call

Oh, this is from the context of the constructor... well, that explains everything.

The contract hasn't been deployed yet, hence this cannot be used here, as it triggers an external contract function call on a contract which isn't deployed yet.

When you replace that external contract function call with an internal function call, the code of that internal function is simply embedded into the contract's deployment byte-code (along with the rest of the code executed from the constructor).

Anyways, glad to hear that it has actually resolved the problem.