主页EIPs
EIPsERC-6604
ERC-6604

Abstract Token

move tokens on- and off-chain as desired, enabling zero-cost minting while preserving on-chain composability
DraftStandards Track: ERC
创建时间: 2023-03-03
关联 EIP: EIP-20, EIP-165, EIP-721, EIP-1155
Chris Walker (@cr-walker) <chris@ckwalker.com>
社区讨论原文链接编辑
1 分钟了解

相关视频
欢迎补充好内容
去提交
正文

Abstract

Abstract tokens provide a standard interface to:

  • Mint tokens off-chain as messages
  • Reify tokens on-chain via smart contract
  • Dereify tokens back into messages

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:

  • airdrops
  • POAPs / receipts
  • identity / access credentials

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:

  • Generic: compatible with merkle trees, digital signatures, or other eligibility proofs
  • Legible: users can query an abstract token contract to understand their potential tokens (e.g. token id, quantity, or uri)
  • Contained: users do not need to understand the proof mechanism used by the particular token implementation contract

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:

  • MUST burn the exact tokens from the holder as defined in the token message
  • MUST NOT dereify token messages scoped to the same contract and chain.
  • MAY succeed or revert if the token message is already used.
  • MUST emit the Reify event on only the first reify call with a specific token message

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.

  • Applications handling abstract tokens can interact with the implementation contract for token metadata rather than parsing this field, so legibility is of secondary importance
  • A byte array can be decoded as a struct and checked for errors within the implementation contract
  • Future token standards will include unpredictable metadata

Proof format

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

  • The contents of this field may vary, e.g. an array of bytes32 merkle tree nodes or a 65 byte signature.
  • a byte array handles all potential use cases at the expense of increased message size.

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:

  • Does total supply need to audited on-chain and/or off-chain?
  • Does the mechanism require ongoing access to a secret (e.g. digital signature) or is it immutable (e.g. merkle proof)?
  • Is there any way for an attacker to prevent the reification of an otherwise valid token message?

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:

  • Dereify the token from chain A to with message M
  • Reify the token on chain B with message M

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:

  • An attacker requests bridging tokens they hold on chain A to chain B
  • A bridging mechanism creates an abstract token message M
  • The attacker reifies message M on chain B but does not dereify message M on chain A
  • The attacker continues to use tokens

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 and related rights waived via CC0.

扩展阅读
欢迎补充好内容
去提交
相关项目
欢迎补充好内容
去提交

不想错过最新的 EIP 动态?

订阅 EIPs Fun 周刊以跟进相关更新,建⽴你与 EIP 之间的连接 ,更好地建设以太坊。

详情
聚集 EIP 贡献者, 扩展以太坊
资源
GitHub
支持社区