This EIP standardizes the passing of unstructured call data to functions to enable future extensibility.
The purpose of having extra data in a method is to allow further extensions to existing method interfaces.
It is it useful to make methods extendable. Any methods complying with this EIP, such as overloaded transfer
and vote
could use string reasons as the extra data. Existing EIPs that have exported methods compliant with this EIP can be extended for behaviors such as using the extra data to prove endorsement, as a salt, as a nonce, or as a commitment for a reveal/commit scheme. Finally, data can be passed forward to callbacks.
There are two ways to achieve extensibility for existing functions. Each comes with their set of challenges:
Add a new method
What will the method name be?
Does this support off-chain signatures?
Use one or more existing parameters, or add one or more new ones
Should existing parameters be repurposed, or should more be added?
Standardizing how methods can be extended helps to answer these questions.
Finally, this EIP aims to achieve maximum backward and future compatibility. Many EIPs already partially support this EIP, such as EIP-721 and EIP-1155. This EIP supports many use cases, from commit-reveal schemes (EIP-5732), to adding digital signatures alongside with a method call. Other implementers and EIPs should be able to depend on the compatibility granted by this EIP so that all compliant method interfaces are eligible for future new behaviors.
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174.
When used in this EIP, the term bytes
MUST be interpreted as the dynamically-sized byte array in Solidity data types.
Unlike many other ERCs which is compliant at the contract
level, this ERC's specification specify compliance at method
level.
Any method with a bytes as this method's last parameter is an eligible method. It looks like this function methodName(type1 value1, type2 value2, ... bytes data)
.
A compliant method MUST be an eligible method and MUST also designate that last bytes
field in its method parameter for behaviors extensions.
If an eligible method has an overloaded sibling method that
has the exact same method name and exact same preceding parameters
except for not having the last bytes
parameter, the behavior
of the compliant method MUST be identical to
its overloaded sibling method when last bytes
is an empty array.
methodName1
in a Foo
contractcontract Foo {
// @dev This method allows extension behavior via `_data` field;
function methodName1(uint256 _param1, address _param2, bytes calldata _data);
function firstNonRelatedMethod(uint256 someValue);
function secondNonRelatedMethod(uint256 someValue);
}
methodName2
in a Bar
contract which is an overloaded method for another methodName2
.contract Foo {
// @dev This is a sibling method to `methodName2(uint256 _param1, address _param2, bytes calldata _data);`
function methodName2(uint256 _param1, address _param2);
// @dev This method allows extension behavior via `_data` field;
// When passed in an empty array for `_data` field, this method
// MUST behave IDENTICAL to
// its overloaded sibling `methodName2(uint256 _param1, address _param2);`
function methodName2(uint256 _param1, address _param2, bytes calldata _data);
function firstNonRelatedMethod(uint256 someValue);
function secondNonRelatedMethod(uint256 someValue);
}
methodName1
because it do not allow extending behaviorcontract Foo {
// @dev This method DO NOT allow extension behavior via `_data` field;
function methodName1(uint256 _param1, address _param2, bytes calldata _data);
function firstNonRelatedMethod(uint256 someValue);
function secondNonRelatedMethod(uint256 someValue);
}
methodName2(uint256 _param1, address _param2, bytes calldata _data);
because it behaves differently
to its overloaded sibling method
methodName2(uint256 _param1, address _param2);
when _data
is empty array.contract Foo {
// @dev This is a sibling method to `methodName2(uint256 _param1, address _param2, bytes calldata _data);`
function methodName2(uint256 _param1, address _param2);
// @dev This method allows extension behavior via `_data` field;
// When passed in an empty array for `_data` field, this method
// behave DIFFERENTLY to
// its overloaded sibling `methodName2(uint256 _param1, address _param2);`
function methodName2(uint256 _param1, address _param2, bytes calldata _data);
function firstNonRelatedMethod(uint256 someValue);
function secondNonRelatedMethod(uint256 someValue);
}
bytes
type allows for maximum flexibility by enabling payloads of arbitrary types.Many existing EIPs already have compliant methods as part of their specification. All contracts compliant with those EIPs are either fully or partially compliant with this EIP.
Here is an incomplete list:
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
is already compliantfunction safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;
function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external;
function burn(uint256 amount, bytes calldata data) external;
function send(address to, uint256 amount, bytes calldata data) external;
However, not all functions that have a bytes
as the last parameter are compliant. The following functions are not compliant without an overload since their last parameter is involved in functionality:
function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;
function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata, bytes calldata _data) external;
which adds a new _data
after all parameters of original method.bytes memory _calldata
MUST be relaxed to allow for extending behaviors.function isValidSignature(bytes32 _hash, bytes memory _signature) public view returns (bytes4 magicValue);
function isValidSignature(bytes32 _hash, bytes memory _signature, bytes calldata _data) public view returns (bytes4 magicValue);
which adds a new _data
after all parameters of original method.bytes memory _signature
MUST be relaxed to allow for extending behaviors.Copyright and related rights waived via CC0.