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