Numerous protocols employ distinct EIP-712 schemas, leading to unavoidable inconsistencies across the ecosystem. To address this issue, we propose a standardized approach for dApps to implement an on-chain view function called visualizeEIP712Message
. This function takes an abi encoded EIP-712 payload message as input and returns a universally agreed-upon structured data format that emphasizes the potential impact on users' assets. Wallets can then display this structured data in a user-friendly manner, ensuring a consistent experience for end-users when interacting with various dApps and protocols.
The rapid expansion of the web3.0 ecosystem has unlocked numerous opportunities and innovations. However, this growth has also heightened users' vulnerability to security threats, such as phishing scams. Ensuring that users have a comprehensive understanding of the transactions they sign is crucial for mitigating these risks.
In an attempt to address this issue, we developed an in-house, open-source off-chain SDK for wallets to visualize various protocols. However, we encountered several challenges along the way:
To overcome these challenges, we propose a standardized, on-chain solution that can accommodate the diverse and ever-changing web3.0 ecosystem. This approach would enhance scalability, reliability, and maintainability by shifting the responsibility of visualizing EIP-712 payloads from the wallets to the protocols themselves. Consequently, wallets can use a consistent and effective approach to EIP-712 message visualization.
The adoption of a universal solution will not only streamline the efforts and reduce the maintenance burden for wallet providers, but it will also allow for faster and more extensive coverage across the ecosystem. This will ultimately result in users gaining a clearer understanding of the transactions they're signing, leading to increased security and an improved overall user experience within the crypto space.
Currently, most of the wallets display something similar to image below
With visualization we can achieve something similar to image below where more insightful details are revealed to user thanks to the structured data returned from the EIP
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.
Contracts implementing this proposal MUST include the visualizeEIP712Message
function in the verifyingContract
implementation so that wallets upon receiving a request to sign an EIP-712 message(eth_signTypedData
) MAY call the function visualizeEIP712Message
at the smart contract and chain specified in the EIP-712 message domain separator verifyingContract
and chainId
fields, respectively.
Wallets SHOULD ignore this proposal if the domain separator does not include the verifyingContract
and chainId
fields.
/**
* @notice This function processes an EIP-712 payload message and returns a structured data format emphasizing the potential impact on users' assets.
* @dev The function returns assetsOut (assets the user is offering), assetsIn (assets the user would receive), and liveness (validity duration of the EIP-712 message).
* @param encodedMessage The ABI-encoded EIP-712 message (abi.encode(types, params)).
* @param domainHash The hash of the EIP-712 domain separator as defined in the EIP-712 proposal; see https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator.
* @return Result struct containing the user's assets impact and message liveness.
*/
function visualizeEIP712Message(
bytes memory encodedMessage,
bytes32 domainHash
) external view returns (Result memory);
encodedMessage
is bytes that represents the encoded EIP-712 message with abi.encode
and it can be decoded using abi.decode
domainHash
is the bytes32 hash of the EIP-712 domain separator as defined in the EIP-712 proposal
The function MUST return Result
, a struct that contains information's about user’s assets impact and the liveness of such a message if it gets signed.
struct Liveness {
uint256 from;
uint256 to;
}
struct UserAssetMovement {
address assetTokenAddress;
uint256 id;
uint256[] amounts;
}
struct Result {
UserAssetMovement[] assetsIn;
UserAssetMovement[] assetsOut;
Liveness liveness;
}
Liveness
Liveness
is a struct that defines the timestamps which the message is valid where:
from
is the starting timestamp.to
is the expiry timestampfrom
MUST be less than to
UserAssetMovement
UserAssetMovement
defines the user’s asset where:
assetTokenAddress
is the token (ERC-20, ERC-721, ERC-1155) smart contract address where the zero address MUST represents the Native coin (Native ETH in the case of Ethereum network).id
is the NFT ID, this item MUST ignored if the asset is not an NFTid
doesn’t exist in an NFT collection, this SHOULD be considered as any token within that collectionamounts
is an Array of uint256
where items MUST define the amount per time curve, with time defined within liveness boundariesamounts
Array (amounts[0]) MUST be the amount of the asset at liveness.from
timestampamounts
Array (amounts[amounts.length-1]) MUST be the amount of the asset at liveness.to
timestampamounts
will be an Array with a single item which is MUST be the minimum amount of the asset.assetsIn
assetsIn
are the minimum assets which the user MUST get if the message is signed and fulfilled
assetsOut
assetsOut
are the maximum assets which the user MUST offer if the message is signed and fulfilled
One might argue that certain processes can be done off-chain, which is true, but our experience building an off-chain TypeScript SDK to solve this matter revealed some issues:
DomainHash
The domainHash
is much needed by protocols to revert against unsupported versions of its EIP-712 implementation. It identifies the needed implementation in case the protocol implements various EIP-712 implementations (name
) or to revert if the domainHash
belongs to a different protocol.
In the future, if there is a registry that reroutes this EIP implementation for already deployed protocols that can't upgrade the existing deployed smart contract, domainHash
can be used to identify protocols.
We suggest using an array of amounts (uint256[]) instead of a single uint256 to cover auctions, which are common in NFT protocols.
No backward compatibility issues found.
openSea Seaport NFT marketplace implementation example is available here
visualizeEIP712Message
function should be reliable and accurately represent the potential impact of the EIP-712 message on users' assets. Wallet providers and users must trust the protocol's implementation of this function to provide accurate and up-to-date information.
visualizeEIP712Message
function results should be treated based on the reputation of its verifyingContract
, if the verifyingContract
is trusted it means the visualizeEIP712Message
function results are trusted as the this proposal implementation lives at the same address of verifyingContract
.
Copyright and related rights waived via CC0.