ERC-6604 - Abstract Token

Created 2023-03-03
Status Draft
Category ERC
Type Standards Track
Authors
  • Chris Walker (@cr-walker) <chris at ckwalker.com>

Requires

Abstract

Abstract tokens provide a standard interface to:

Abstract tokens can comply with existing standards like ERC-20, ERC-721, and ERC-1155. The standard allows wallets and other applications to better handle potential tokens before any consensus-dependent events occur on-chain.

Motivation

Abstract tokens enable zero-cost token minting, facilitating high-volume applications by allowing token holders to reify tokens (place the tokens on-chain) as desired. Example use cases:

Merkle trees are often used for large token distributions to spread mint/claim costs to participants, but they require participants to provide a markle proof when claiming tokens. This standard aims to improve the claims proces for similar distributions:

Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.

Data Types

Token Messages

A token message defines one or more tokens along with the context needed to reify the token(s) using a smart contract.

chainId & implementation: set the domain of the token message to a specific chain and contract: this is where the token can be reified owner: the address that owns the tokens defined in the messages when reified meta: implementation-specific context necessary to reify the defined token(s), such as id, amount, or uri. proof: implementation-specific authorization to reify the defined token(s). nonce: counter that may be incremented when multiple otherwise-identical abstract token messages are needed

struct AbstractTokenMessage {
  uint256 chainId;
  address implementation;
  address owner;
  bytes meta;
  uint256 nonce;
  bytes proof;
}

Message Status

A message status may be defined for every (abstract token contract, abstract token message) pair. invalid: the contract cannot interact with the message valid: the contract can interact with the message used: the contract has already interacted with the message

enum AbstractTokenMessageStatus {
  invalid,
  valid,
  used
}

Methods

reify

Moves token(s) from a message to a contract function reify(AbstractTokenMessage calldata message) external;

The token contract MUST reify a valid token message.

Reification MUST be idempotent: a particular token message may be used to reify tokens at most once. Calling reify with an already used token message MAY succeed or revert.

status

Returns the status of a particular message function status(AbstractTokenMessage calldata message) external view returns (AbstractTokenMessageStatus status);

dereify

Moves token(s) from a contract to a message intended for another contract and/or chain. function dereify(AbstractTokenMessage calldata message) external;

OPTIONAL - allows tokens to be moved between contracts and/or chains by dereifying them from one context and reifying them in another. Dereification MUST be idempotent: a particular token message must be used to dereify tokens at most once.

If implemented, dereification:

id

Return the id of token(s) defined in a token message. function id(AbstractTokenMessage calldata message) external view returns (uint256);

OPTIONAL - abstract token contracts without a well-defined token ID (e.g. ERC-20) MAY return 0 or not implement this method.

amount

Return the amount of token(s) defined in a token message. function amount(AbstractTokenMessage calldata message) external view returns (uint256);

OPTIONAL - abstract token contracts without a well-defined token amount (e.g. ERC-721) MAY return 0 or not implement this method.

uri

Return the amount of token(s) defined in a token message. function uri(AbstractTokenMessage calldata message) external view returns (string memory);

OPTIONAL - abstract token contracts without a well-defined uri (e.g. ERC-20) MAY return "" or not implement this method.

supportsInterface

All abstract token contracts must support ERC-165 and include the Abstract Token interface ID in their supported interfaces.

Events

Reify

The Reify event MUST be emitted when a token message is reified into tokens event Reify(AbstractTokenMessage);

Dereify

The Dereify event MUST be emitted when tokens are dereified into a message event Dereify(AbstractTokenMessage);

Application to existing token standards

Abstract tokens compatible with existing token standards MUST overload existing token transfer functions to allow transfers from abstract token messages.

Abstract ERC-20

interface IAbstractERC20 is IAbstractToken, IERC20, IERC165 {
  // reify the message and then transfer tokens
  function transfer(
    address to,
    uint256 amount,
    AbstractTokenMessage calldata message
  ) external returns (bool);

  // reify the message and then transferFrom tokens
  function transferFrom(
    address from,
    address to,
    uint256 amount,
    AbstractTokenMessage calldata message
  ) external returns (bool);
}

Abstract ERC-721

interface IAbstractERC721 is IAbstractToken, IERC721 {
  function safeTransferFrom(
    address from,
    address to,
    uint256 tokenId,
    bytes calldata _data,
    AbstractTokenMessage calldata message
  ) external;

  function transferFrom(
    address from,
    address to,
    uint256 tokenId,
    AbstractTokenMessage calldata message
  ) external;
}

Abstract ERC-1155

interface IAbstractERC1155 is IAbstractToken, IERC1155 {
  function safeTransferFrom(
    address from,
    address to,
    uint256 id,
    uint256 amount,
    bytes calldata data,
    AbstractTokenMessage calldata message
  ) external;

  function safeBatchTransferFrom(
    address from,
    address to,
    uint256[] calldata ids,
    uint256[] calldata amounts,
    bytes calldata data,
    AbstractTokenMessage[] calldata messages
  ) external;
}

Rationale

Meta format

The abstract token message meta field is simply a byte array to preserve the widest possible accesibility.

Proof format

Similar considerations went into defining the proof field as a plain byte array:

Backwards Compatibility

No backward compatibility issues found.

Reference Implementation

See here.

Security Considerations

Several concerns are highlighted.

Message Loss

Because token messages are not held on-chain, loss of the message may result in loss of the token. Applications that issue abstract tokens to their users can store the messages themselves, but ideally users would be able to store and interact with abstract token messages within their crypto wallets.

Authorizing Reification

Token messages may only be reified if they include a validity proof. While the proof mechanism itself is out of scope for this standard, those designing proof mechanisms should consider:

Non-owner (De)Reification

Can non-owners (de)reify a token message on behalf of the owner?

Pro: supporting apps should be able to handle this because once a valid message exists, the owner could (de)reify the message at any time Con: if the token contract reverts upon (de)reification of a used message, an attacker could grief the owner by front-running the transaction

Abstract Token Bridge Double Spend

Abstract tokens could be used for a token-specific bridge:

Because the abstract token standard does not specify any cross-chain message passing, the abstract token contracts on chains A and B cannot know whether a (de)reification of message M has occurred on the other chain.

A naive bridge would be subject to double spend attacks:

Some oracle mechanism is necessary to prevent the reification of message M on chain B until the corresponding tokens on chain A are dereified.

Copyright

Copyright and related rights waived via CC0.