ERC-7281: Sovereign Bridged Tokens

Generally speaking, we encourage standards to omit functions that can only be called by the owner/deployer of a contract (like setLimits and presumably setLockbox) because the owner of a contract can be expected to understand the specifics of a particular implementation. There’s no need for interoperability.

@SamWilsn thanks that you took a time to look at this.

@arjunbhuptani The more I work with xERC20 the more I agree with Sam. IMHO the standard should enforce only the critical interoperability parts.

I think it should be broken into core (xERC20 basic interoperability standard) and related extensions (lockbox ERC20, lockbox native) if desired.

Example, where I am in doubt the xERC20 implementation have to include following (in case there is no related lockbox on the chain):

All EIP-XX tokens MUST implement the following interface.
...
event LockboxSet(address _lockbox);

function setLockbox(address _lockbox) external;
...

Additionally we had to separate the xERC20 minting by bridges vs by lockbox:

    /**
     * @notice Mints xERC20 tokens for a user by the `lockbox`
     * @dev Can only be called by a lockbox
     * @param _user The address of the user who needs tokens minted
     * @param _amount The amount of tokens being minted
     */
    function mintByLockbox(address _user, uint256 _amount) external virtual onlyLockbox {}

Would we comply with the standard when lockbox is not minting via mint() function?

Example of the extension - we needed to implement this accounting for the xERC20 on the home chain.

  /**
     * @notice Overall circulating supply of this token on all chains
     * This is an optimistic figure for the behavior of the bridges and/or other chains
     *
     * @dev Considering a correct behavior of bridges and other chains, this figure should
     * cover whole circulating supply. This is because other chains can bridge out to tertiary
     * chains only a portion of the supply bridged to them from this contract.
     */
    function totalXChainSupply() public view virtual returns (uint256) {
        return totalSupply() + totalOtherChainsSupply();
    }

which is used for further checks like this:

    function _mintWithCaller(address _caller, address _user, uint256 _amount) internal virtual override {
        uint256 _totalLockedERC20 = IXERC20HomeTokenLockbox(lockbox).totalLockedForXERC20();

        // A bridge tries to mint more leading to increased local supply over what is locked
        if ((_amount + totalSupply()) > _totalLockedERC20) revert IXERC20_NotEnoughInLockbox();

        super._mintWithCaller(_caller, _user, _amount);

        // A bridge minted xERC20, so it SHOULD decrease the supply on the other chain
        _totalOtherChainsSupply = _totalOtherChainsSupply - _amount;
...

Overall, here is the proposal to adjust the MUST have interface:

interface IXERC20 {
    /**
     * @notice Reverts when a bridge with too low of a limit tries to call mint/burn
     */
    error IXERC20_NotHighEnoughLimits();

    /**
     * @notice Returns the max limit of a minter
     *
     * @param _bridge The bridge we are viewing the limits of
     * @return _limit The limit the bridge has
     */
    function mintingMaxLimitOf(address _bridge) external view returns (uint256 _limit);

    /**
     * @notice Returns the max limit of a bridge
     *
     * @param _bridge the bridge we are viewing the limits of
     * @return _limit The limit the bridge has
     */
    function burningMaxLimitOf(address _bridge) external view returns (uint256 _limit);

    /**
     * @notice Returns the current limit of a minter
     *
     * @param _bridge The bridge we are viewing the limits of
     * @return _limit The limit the minter has
     */
    function mintingCurrentLimitOf(address _bridge) external view returns (uint256 _limit);

    /**
     * @notice Returns the current limit of a bridge
     *
     * @param _bridge the bridge we are viewing the limits of
     * @return _limit The limit the bridge has
     */
    function burningCurrentLimitOf(address _bridge) external view returns (uint256 _limit);

    /**
     * @notice Mints tokens for a user
     * @dev Can only be called by a bridge
     * @param _user The address of the user who needs tokens minted
     * @param _amount The amount of tokens being minted
     */
    function mint(address _user, uint256 _amount) external;

    /**
     * @notice Burns tokens for a user
     * @dev Can only be called by a bridge
     * @param _user The address of the user who needs tokens burned
     * @param _amount The amount of tokens being burned
     */
    function burn(address _user, uint256 _amount) external;
}

Hi authors of ERC-7281, this is Victor, an EIP editor and current operator of AllERCDevs.

I like to invite you to our next AllERCDevs meeting (online) to present for 10min of your ERCs if you are interested!

AllERCDevs is a bi-weekly meeting for ERC authors, builders and editors to meet and help the drafting and adoption of an ERC. The next one is 2024-04-30 UTC 2300, let us know if this time works for you, I can put this ERC in the agenda, or you can add a response directly at S2E4 AllERCDevs Agenda 2024-04-30 Tuesday UTC2300 (APEC friendly time) · Issue #22 · ercref/AllERCDevs · GitHub