Skip to content

Instantly share code, notes, and snippets.

@d10r
Last active February 14, 2025 18:22
Show Gist options
  • Save d10r/a250cd9e8025db7c93618a5a62ef137e to your computer and use it in GitHub Desktop.
Save d10r/a250cd9e8025db7c93618a5a62ef137e to your computer and use it in GitHub Desktop.
ERC20x draft proposal

About

This is a modular proposal for the ERC20x EVM token Standard which extends ERC20.

Modules:

  • Primitives:
    • transfer to 1 (covered by ERC20. We add a variant which is allowed to invoke hooks)
    • flow to 1
    • transfer to n
    • flow to n
  • ACL: allow delegation to an operator. Arbitrary restrictions can be implemented by making the operator a contract.
  • Hooks: Any account can designate any contract implementing a hook interface as handler. For 1 to n actions, only the pool manager can set up receive hooks.
interface IERC20xPrimitives is IERC20 {
    /// Note: This overloads ERC20.transfer() and will invoke hooks.
    function transfer(address to, uint256 amount, bytes data) returns (bool);
    function flow(address to, uint256 flowRate, bytes data) returns (bool);
    function transferToMany(IPool pool, uint256 amount, bytes data) returns (bool);
    function flowToMany(IPool pool, uint256 flowRate, bytes data) returns (bool);
}

interface IERC20xACL {
    function authorizeOperator(address operator);
    function revokeOperator(address operator);
    function isOperatorFor(address operator, address holder) view returns (bool);

    function operatorTransfer(address to, uint256 amount, bytes data, bytes operatorData) returns (bool);
    function operatorFlow(address to, uint256 flowRate, bytes data, bytes operatorData) returns (bool);
    function operatorTransferToMany(IPool pool, uint256 amount, bytes data, bytes operatorData) returns (bool);
    function operatorFlowToMany(IPool pool, uint256 flowRate, bytes data, bytes operatorData) returns (bool);
}

interface IPool is IERC20 {
    /// has privileged access. Should be a contract.
    function getManager() exernal view returns(address);
}

// HOOKS
// Inspired by ERC-777: any account (incl. EOAs) can set up hooks,
// the hook logic is not tied to the sender/receiver.
// Thus EOAs can also set up hooks, and a hook implementation can be shared by many.

interface IERC20xHooks {
    function getHookHandlerRegistry() view returns(address);
    
    // Note: returns 0 if not supported
    function getReceiveHookGasLimit() view returns(uint256);
    function getACLSendHookGasLimit() view returns(uint256);
    function getACLReceiveHookGasLimit() view returns(uint256);
}

interface IHookHandlerRegistry {
    /// msg.sender sets up a hook handler (contract) for themselves. handlerAddress zero resets.
    function setHookHandler(bytes32 hookTypeHash, address handlerAddress);
}

interface IERC20xReceiveHook {
    function onTransferReceive(address from, address to, uint256 amount, bytes data);
    function onFlowReceive(address from, address to, uint256 flowRate, uint256 prevFlowRate, bytes data);
    /// can only be set up by pool managers
    function onTransferToManyReceive(address from, IPool to, uint256 amount, bytes data);
    /// can only be set up by pool managers
    function onFlowToManyReceive(address from, address IPool, uint256 flowRate, uint256 prevFlowRate, bytes data);
}

// HOOKS for ACL actions

/// Send hooks can be useful together with ACL to enforce arbitrary and dynamic restrictions on operator actions.
interface IERC20xACLSendHook {
    function onTransferSend(address operator, address from, address to, uint256 amount, bytes data, bytes operatorData);
    function onFlowSend(address operator, address from, address to, uint256 flowRate, uint256 prevFlowRate, bytes data, bytes operatorData);
    function onTransferToManySend(address operator, address from, IPool to, uint256 amount, bytes data, bytes operatorData);
    function onFlowToManySend(address operator, address from, address IPool, uint256 flowRate, uint256 prevFlowRate, bytes data, bytes operatorData);
}

interface IERC20xACLReceiveHook {
    function onTransferReceive(address operator, address from, address to, uint256 amount, bytes data, bytes operatorData);
    function onFlowReceive(address operator, address from, address to, uint256 flowRate, uint256 prevFlowRate, bytes data, bytes operatorData);
    /// can only be set up by pool managers
    function onTransferToManyReceive(address operator, address from, IPool to, uint256 amount, bytes data, bytes operatorData);
    /// can only be set up by pool managers
    function onFlowToManyReceive(address operator, address from, address IPool, uint256 flowRate, uint256 prevFlowRate, bytes data, bytes operatorData);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment