When to Add Exemptions?

Exemptions are typically used for setup, and to better optimize protocols known to only interface with ERC20 logic. A few examples of common exemptions have been provided below:

  • When deploying and minting initial supply, the receiver of this supply is typically whitelisted to avoid large volume ERC721 transfers during liquidity provisioning.
  • When providing initial liquidity or setting up Uniswap V3 pools, a pool for the desired fee tier is typically whitelisted to both avoid large volume transfers on provisioning and ensure reduced gas fees for user swaps.
  • External DeFi protocols are typically granted exemptions as integrations and use-cases are revealed over a collections lifecycle to ensure a better user experience via reduced gas fees.

How to Add Exemptions?

It’s recommended to set exemptions during setup and deployment when possible, as a means of ensuring that pooling adjustments are not required.

First, as usage of exemption functionality is up to the implementer, consider the minimal ERC404 contract below.

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {ERC404} from "../ERC404.sol";

contract ExampleERC404 is Ownable, ERC404 {
  constructor(
    string memory name_,
    string memory symbol_,
    uint8 decimals_,
    uint256 maxTotalSupplyERC721_,
    address initialOwner_
  ) ERC404(name_, symbol_, decimals_) Ownable(initialOwner_) {
    _setERC721TransferExempt(initialOwner_, true);
    _mintERC20(initialOwner_, maxTotalSupplyERC721_ * units, false);
  }

  function tokenURI(uint256 id_) public pure override returns (string memory) {
    return string.concat("https://example.com/token/", Strings.toString(id_));
  }

  function setERC721TransferExempt(address account_, bool value_) external onlyOwner {
    _setERC721TransferExempt(account_, value_);
  }
}

A simple setup example has already been provided above, whereby the initial contract owner is immediately exempted and receives the full initial supply.

Beyond this, setting exemptions is typically very simple, and can be demonstrated through the sample forge script below.

contract Whitelist is Script {
    modifier broadcast(address deployer) {
        vm.startBroadcast(deployer);
        _;
        vm.stopBroadcast();
    }

    function run() external override {
        deploy(address(this));
    }

    function deploy(address deployer) public broadcast(deployer) {
        ExampleERC404 exampleERC404 = ExampleERC404(contractAddress);
        exampleERC404.setERC721TransferExempt(targetAddress, true);
    }
}

The Global Exemption List

A novel means of ensuring accurate, useful out-of-box exemptions for ecosystem partners and protocols is currently in the works via what will become a fixed, in-memory exemption check extension. While not completed yet, this list should provide a non-negligible improvement to gas consumption while offering a safer and easier launch experience for developers.