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.
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:
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.
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;
}
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
}
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.
Returns the status of a particular message
function status(AbstractTokenMessage calldata message) external view returns (AbstractTokenMessageStatus status);
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:
Reify
event on only the first reify
call with a specific token messageReturn 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.
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.
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.
All abstract token contracts must support ERC-165 and include the Abstract Token interface ID in their supported interfaces.
The Reify event MUST be emitted when a token message is reified into tokens
event Reify(AbstractTokenMessage);
The Dereify event MUST be emitted when tokens are dereified into a message
event Dereify(AbstractTokenMessage);
Abstract tokens compatible with existing token standards MUST overload existing token transfer functions to allow transfers from abstract token messages.
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);
}
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;
}
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;
}
The abstract token message meta
field is simply a byte array to preserve the widest possible accesibility.
Similar considerations went into defining the proof
field as a plain byte array:
bytes32
merkle tree nodes or a 65 byte signature.No backward compatibility issues found.
See here.
Several concerns are highlighted.
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.
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:
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 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 and related rights waived via CC0.