Skip to main content

Command Palette

Search for a command to run...

Goodbye Eth_sign, Hello Eip-712: The Safer Way To Sign Data

Published
18 min read

INTRODUCTION

What is eth_sign?

In the early days of Ethereum development, the eth_sign method was introduced to allow users to cryptographically sign arbitrary messages using their private key. This cryptographic operation is fundamental in blockchain applications for enabling authentication, identity verification, and off-chain message integrity (Wood, 2014).

When a dApp (decentralized application) calls eth_sign, the user is prompted to sign a hash of the provided data. The returned signature is valid from a cryptographic standpoint, but the signed content is often opaque, offering little or no readability to users.

Why It's a Problem for Users and Developers

Despite its simplicity and broad support in tools like MetaMask and Web3.js, eth_sign has critical flaws that compromise both user experience and security.

  1. Lack of Human-Readable Information

The most immediate problem with eth_sign is that users typically see only a hash or an unintelligible message, like 0x6f1a...e9bc, during signing. This makes it impossible for users to verify what they’re actually signing. Consequently, attackers can exploit this by prompting users to sign malicious data, such as a token approval or a contract interaction, under the guise of a harmless operation (OpenZeppelin, 2022).

  1. Ambiguity and Misinterpretation

Another critical issue is ambiguity. There is no standardized format for the data being signed, which means developers must establish off-chain agreements about data structure. Any mismatch in interpretation between systems can lead to failed verifications or worse — unintended actions on-chain (Buterin, 2018).

  1. Signature Reuse and Replay Attacks

Because the signature generated by eth_sign isn’t tied to any specific domain, chain ID, or smart contract, it becomes susceptible to replay attacks. A valid signature from one dApp could potentially be reused maliciously in another context, leading to unauthorized transactions (EIP-712, 2018).

  1. Developer Pain Points

From a developer’s perspective, using eth_sign increases the implementation burden. There’s no type safety or validation framework, so developers must:

  • Define custom data schemas.

  • Serialize and deserialize raw bytes manually.

  • Implement off-chain logic to verify the authenticity and meaning of signed data.

This fragmentation creates room for bugs, increases audit complexity, and makes integration across platforms more fragile.

WHAT IS EIP-712?

Overview of Ethereum Improvement Proposal 712

Ethereum Improvement Proposal 712 (EIP-712) is a standardized method for hashing and signing typed structured data on Ethereum. Proposed by Vitalik Buterin and others in 2017, and finalized in 2018, it introduces a secure, human-readable way for users to sign off-chain data while maintaining compatibility with Ethereum's cryptographic signature system (Buterin, 2018).

EIP-712 differs from traditional eth_sign methods by requiring that all data to be signed is explicitly structured and typed — similar to how data is defined in programming languages like C or Solidity. This structure enables precise verification of what is being signed, by whom, and for what context.

At its core, EIP-712 provides:

  • A typed data schema for consistent message formatting.

  • A domain separator to ensure signatures are application-specific.

  • A secure signing algorithm that hashes both the schema and the data.

These improvements reduce ambiguity, enhance security, and offer transparency during the signing process.

Key Goals and Benefits

  1. Human-Readable Messages: EIP-712 enables wallets and signing interfaces (e.g., MetaMask) to display message content in a structured, readable format. This empowers users to make informed decisions before signing — a huge leap forward in usability and safety.

  2. Contextual Security via Domain Separator: One of EIP-712’s cornerstone innovations is the domain separator. This ties the signed data to a specific context — such as a contract address, chain ID, app name, and version — preventing replay attacks across different dApps or networks.

  3. Gasless Meta-Transactions: Because EIP-712 separates the data hashing/signing process from on-chain execution, it enables gasless transactions. A user can sign a structured message off-chain, and a relayer can then submit it to the blockchain and pay the gas fees — useful for onboarding new users or enabling mobile dApp interactions (OpenZeppelin, 2022).

  4. Improved Developer Ergonomics: Developers benefit from standardized data structures and reduced guesswork. EIP-712 improves interoperability between frontends, smart contracts, and verification libraries — streamlining both implementation and security audits.

Use Cases:

  • Token approvals via Permit (EIP-2612)

  • Off-chain voting (e.g., Snapshot)

  • Gasless NFT listings (e.g., OpenSea)

  • Identity verification

  • Cross-chain signatures

PROBLEMS WITH TRADITIONAL SIGNATURES

Before the introduction of EIP-712, Ethereum dApps relied heavily on primitive signing methods like eth_sign. While functional, these approaches introduced serious issues related to usability, security, and trust — ultimately limiting the adoption and scalability of decentralized applications.

Poor User Experience (UX) and Readability

When users are prompted to sign a message using eth_sign, they’re typically shown a raw hexadecimal hash (e.g., 0x4e944ce...). This offers no visibility into what they're actually approving, especially for non-technical users.

This disconnect results in a high risk of users blindly signing critical operations like token transfers, contract approvals, or identity assertions.

"Most users can't interpret cryptographic hashes, which makes phishing significantly easier" (Buterin, 2018).

Wallets like MetaMask have struggled with this limitation, often displaying unintelligible data or requiring developers to create workaround modals — a fragile and error-prone approach.

Security Vulnerabilities

Lack of readable content directly contributes to multiple attack vectors, including:

  • Phishing attacks: Users tricked into signing malicious transactions because they can't see the message.

  • Replay attacks: Signatures can be reused on other networks or dApps due to lack of contextual separation.

  • Ambiguity: Without typed data, two different dApps might interpret the same message differently.

In one well-known incident, attackers used an innocuous-looking message to trick users into signing transactions that drained their wallets of ERC-20 tokens. Since the users couldn’t decipher the meaning of the hash, they unknowingly approved a malicious contract (Wong, 2019).

Lack of Domain Context

Traditional signatures lack domain separation — the metadata that defines the signing context (e.g., chain ID, dApp name, contract address). Without this, a valid signature from one dApp could be replayed on another, enabling cross-application attacks.

This absence of scoped security makes signatures dangerously portable and undermines trust between users and applications.

CORE CONCEPTS OF EIP-712

EIP-712 introduces a powerful mechanism for signing data on Ethereum: typed structured data signing. Unlike traditional approaches, this standard offers semantic clarity and cryptographic precision, enabling secure and user-friendly message approvals.

The core of EIP-712 is based on four primary concepts:

Structured Data

At the heart of EIP-712 is the idea of signing structured data, not arbitrary byte strings. This data is organized into readable and typed objects (like JSON), which make it easier for users and applications to interpret.

For example, instead of signing:

you sign:

Structured data makes signatures transparent, meaning users can clearly see and understand what they are signing (Buterin, 2018).

Type Definitions

EIP-712 uses type definitions — similar to structs in programming — to define the shape of the data being signed. These types are encoded in a deterministic way, which contributes to the hash that is ultimately signed.

Example:

This explicit definition helps ensure that everyone — wallet, dApp, and smart contract — interprets the data the same way.

Domain Separator

One of the most important safeguards in EIP-712 is the domain separator. This is a unique fingerprint that ties the signature to a specific context. It prevents a signed message in one application from being reused in another (known as a replay attack).

The domain separator includes:

  • name: Application name

  • version: Version of the signing protocol

  • chainId: Ethereum chain ID

  • verifyingContract: The contract address verifying the signature

Together, this creates a namespace that protects both the user and the dApp from cross-domain exploits (EIP-712, 2018).

Message Signing Flow

The EIP-712 signing process consists of the following steps:

  1. Define typed data: Include all struct types and the message content.

  2. Generate domain separator: Contextual metadata is hashed.

  3. Encode and hash: Typed data and domain separator are encoded and hashed according to the EIP-712 rules.

  4. Sign: The resulting digest is signed using eth_signTypedData_v4.

Diagrammatically:

This ensures the signature is both verifiable and human-readable, and can only be validated within the correct application context.

ANATOMY OF AN EIP-712 MESSAGE

To truly grasp how EIP-712 revolutionizes Ethereum signatures, it's essential to understand the internal anatomy of a typed data message. An EIP-712 message is composed of three main parts:

  1. Domain Separator

  2. Type Definitions

  3. Message Object

Each plays a critical role in ensuring the security, readability, and contextual accuracy of signatures. Together, they are hashed and then signed to produce a verifiable digital signature.

  1. Domain Separator

The domain separator establishes the "scope" of the data being signed. It's like a namespace that ensures a signature is only valid within a specific application, contract, and blockchain context. Without this, the same signed message could be maliciously replayed in another environment — a serious security risk.

Standard domain fields:

  • name: Name of your application or protocol.

  • version: Version of your app or schema.

  • chainId: Network identifier (e.g., 1 for Ethereum mainnet).

  • verifyingContract: Smart contract address expected to verify the signature.

This domain is encoded and hashed using EIP-712's encoding rules, and it ensures the signature is tied to a specific dApp and blockchain environment.

  1. Type Definitions

EIP-712 allows defining complex, nested types (structs), similar to Solidity's structs. These definitions make the data self-descriptive and verifiable across platforms (frontends, wallets, and smart contracts).

Example:

The above defines a Mail message with nested Person objects. These types are then ABI-encoded and hashed in a deterministic way, forming part of the signature digest.

  1. Message Object

This is the actual data that users approve and sign. It must conform exactly to the type structure defined above.

Example:

The frontend app displays this to the user in a readable format, and if approved, the data proceeds to hashing and signing.

  1. Hashing & Signature Generation

The final step involves hashing the combination of:

  • Domain Separator Hash

  • Typed Data Hash

The full signature hash is constructed using this formula:

  • The prefix \x19\x01 ensures compatibility with EIP-191.

  • domainSeparator is the keccak256 hash of the domain struct.

  • hashStruct(message) is the keccak256 of the message using its defined types.

Then, the resulting digest is signed with the user’s private key using the eth_signTypedData_v4 method (commonly in MetaMask and other wallets).

The final output is a signature (v, r, s) tuple, which can be verified on-chain using ecrecover or via helper libraries like OpenZeppelin’s ECDSA.sol.

CODE EXAMPLES

Signing with Ethers.js & Verifying in Solidity

One of the most powerful aspects of EIP-712 is how easily it integrates across the stack — from frontend wallets to backend smart contracts. Here, we walk through two essential parts of the workflow:

  1. Signing typed data off-chain using Ethers.js (frontend)

  2. Verifying the signature on-chain using Solidity

  1. Signing Typed Data with Ethers.js

Ethers.js supports EIP-712 through the signTypedData or eth_signTypedData_v4 RPC method via the wallet provider.

Here’s a complete example using Ethers.js v6+:

Note: Make sure the wallet (like MetaMask) supports eth_signTypedData_v4, which ensures proper domain and message structure support as per EIP-712.

  1. Verifying EIP-712 Signature in Solidity

Here’s how you can verify that a message was signed by the correct address using Solidity and the EIP712 utility from OpenZeppelin:

Explanation:

  • _hashTypedDataV4(...) combines the domain separator and the typed struct hash.

  • ECDSA.recover() returns the signer’s address if the signature is valid.

  • You can compare this recovered address to an expected signer on-chain.

REAL-WORLD USE CASES OF EIP-712

EIP-712 isn’t just a theoretical improvement — it's already transforming the way Ethereum applications handle off-chain signing. Below are key real-world implementations that show how typed structured data improves security, reduces friction, and enables new UX patterns.

Permit-Based Token Approvals (EIP-2612)

Traditionally, ERC-20 token holders had to make two on-chain transactions:

  1. approve() to allow a contract to spend their tokens

  2. Followed by the actual transferFrom() call by the spender

With EIP-2612, token approvals can be signed off-chain using EIP-712 and submitted by anyone on-chain — removing the need for gas-spending by the token holder.

Benefits:

  • No need for approve() transactions

  • Enables one-click onboarding

  • Reduced gas costs and friction

Example: Uniswap and DAI token both support permit() to allow meta-approvals.

Gasless Meta-Transactions

EIP-712 enables meta-transactions, where users sign a message off-chain, and a relayer (or dApp backend) pays the gas fee to submit the transaction.

How it works:

  • User signs a structured message (intent to execute a function)

  • A relayer sends it on-chain, often getting reimbursed in tokens or fees

Benefits:

  • Removes the barrier of owning ETH

  • Great for onboarding new users or mobile-first wallets

Tools: Biconomy, OpenZeppelin Defender Relay, and Gelato Network utilize this pattern.

DAO Voting

Decentralized Autonomous Organizations (DAOs) often use off-chain vote signing to capture member intent before final execution.

Projects like Snapshot use EIP-712 to allow users to vote without gas fees, while maintaining signature authenticity and integrity.

Example Message:

Snapshot verifies the EIP-712 signature off-chain and records the vote.

Benefits:

  • Free voting for DAO members

  • Better UX than on-chain governance

  • Still cryptographically secure

Off-Chain Order Signing (e.g., DEXs like 0x)

Decentralized exchanges like 0x Protocol, dYdX, and CowSwap rely heavily on off-chain order creation and signing.

Users sign a structured order (e.g., sell X token for Y token) using EIP-712, which is then matched and submitted on-chain only when a trade is finalized.

Example Order:

Benefits:

  • Lower gas costs

  • Enables order books

  • Efficient high-frequency trading on-chain

WALLET AND LIBRARY SUPPORT

A major reason for the growing adoption of EIP-712 is its strong ecosystem compatibility across wallets and development libraries. As security and UX have become top priorities in web3 applications, most modern wallets and toolkits have implemented support for structured data signing.

Compatibility with Wallets

Several leading wallets have implemented native support for EIP-712, allowing users to sign structured data safely and understandably.

Wallet

EIP-712 Support

Notes

MetaMask

Full support via eth_signTypedData_v4

Shows readable message

WalletConnect

Supported in v1 and v2

Works with most mobile wallets

Coinbase Wallet

Supported

Also shows domain info

Trust Wallet

Supported

EIP-712 support added in updates

Frame, Rabby, etc.

Supported

Focused on secure, readable signing

MetaMask is the most widely used wallet supporting the eth_signTypedData_v4 method — the most secure and standard-conformant version of EIP-712. It renders the structured message in a human-readable UI, reducing phishing risks.

JavaScript/TypeScript Libraries

For developers, various JavaScript/TypeScript libraries make it easy to build and verify EIP-712 signatures across frontend and smart contracts.

Common Libraries

Library

Description

ethers.js

Popular lightweight Ethereum lib; supports signTypedData

viem

Modern typed library built for speed & DX

web3.js

Older but widely-used Ethereum lib; support varies

eth-sig-util

MetaMask’s internal tool for encoding/decoding EIP-712 data

@metamask/eth-sig-util

Maintained NPM package for hashing and recovering signatures

Why This Matters

  • Security: Wallets show human-readable messages, reducing phishing risk.

  • Developer Experience: Frontend and smart contracts use shared, typed schemas.

  • Interoperability: Consistent signing flow across wallets and dApps.

SECURITY BENEFITS OF EIP-712

One of the strongest motivations behind Ethereum Improvement Proposal 712 is to mitigate common security vulnerabilities associated with traditional signing methods. By introducing structured, typed data and domain-specific signing, EIP-712 provides a robust framework for secure off-chain message signing.

Prevention of Phishing Attacks

Traditional signing methods like eth_sign ask users to sign raw hexadecimal strings. These are unintelligible to most users and leave them vulnerable to phishing:

  • Malicious dApps could trick users into signing arbitrary transactions or permission grants.

  • Users often have no way of understanding what they're agreeing to.

EIP-712 prevents this by enforcing the display of human-readable, structured messages. Instead of a hex blob, users see clearly labeled fields — e.g., “Permit spender 0xABC to transfer 100 tokens from your account.”

✅ This drastically reduces the risk of social engineering and phishing attacks.

App-Specific Signatures (Domain Separation)

A key feature of EIP-712 is the domain separator, which includes:

  • App name (e.g., MyDApp)

  • Version (e.g., 1.0)

  • Chain ID (e.g., 1 for Ethereum Mainnet)

  • Verifying contract address

This domain data gets included in the signature hash, making it unique to a specific application.

🔐 Why this matters:

  • Even if an attacker reuses the same structured data on another site, the domain mismatch makes the signature invalid.

  • Prevents cross-site replay attacks and spoofing.

Improved User Awareness

EIP-712 makes signing more transparent:

  • Users see what they’re signing: the sender, recipient, amount, deadline, etc.

  • Many wallets (e.g., MetaMask, Rabby) render this information in a clean, readable format.

This gives users:

  • Greater confidence in what they approve.

  • The ability to review before committing.

  • A reduction in accidental approvals or token losses.

CHALLENGES AND GOTCHAS OF EIP-712

While EIP-712 brings powerful improvements to data signing in Ethereum, it also comes with non-trivial implementation hurdles. Developers need to be mindful of various challenges that can arise during integration across the frontend, backend, and smart contract layers.

Complex Implementation Details

Unlike eth_sign, which simply signs a message string or hash, EIP-712 requires:

  • Structuring typed data using JSON

  • Computing nested type hashes

  • Encoding domain and message data following ABI encoding rules

  • Concatenating hashes and signing the final digest

This complexity increases the potential for bugs — particularly in how types are defined and encoded.

Common pitfalls include:

  • Incorrect field ordering

  • Mismatched types between frontend and smart contracts

  • Errors in ABI-encoding nested structs

Pro Tip: Use established libraries like ethers.js or eth-sig-util that abstract away most of this logic.

Wallet Compatibility Quirks

Not all wallets fully support EIP-712, especially across mobile vs. desktop or older versions. While modern wallets like MetaMask, WalletConnect, and Rabby offer strong support, there are still edge cases:

  • Some wallets do not support complex nested types

  • Certain versions only support eth_signTypedData (deprecated) or eth_signTypedData_v4

  • UI inconsistencies may lead to confusing UX for users

Recommendation: Check wallet compatibility in your user base, and fallback to eth_sign (with user warnings) if necessary. Always test across devices and environments.

Keeping Frontend and Smart Contracts in Sync

One of the biggest sources of errors in EIP-712 implementation is a mismatch between frontend and on-chain struct definitions.

Example issues:

  • The frontend uses uint256 but the contract expects uint8

  • The frontend omits a field that the smart contract requires for signature validation

  • Differences in struct ordering or nesting

These subtle differences will cause signature verification to fail in Solidity (ecrecover returns invalid signer).

Best Practices:

  • Define your types in a shared schema (e.g., TypeScript interface or JSON schema)

  • Use code generators or tools like typechain to reduce desyncs

  • Include end-to-end tests for signing and verification

BEST PRACTICES FOR IMPLEMENTING EIP-712

Implementing EIP-712 correctly ensures that your dApp is not only secure and user-friendly, but also interoperable across wallets and tools. Below are key best practices to follow when integrating EIP-712 typed structured data signing into your Ethereum-based applications.

Structure Your Types Clearly

Why it matters: EIP-712 relies on consistent and predictable type definitions. The order, naming, and structure of types directly affect how the data is hashed and verified on-chain.

Recommendations:

  • Define a clear and concise set of types, starting from the domain struct.

  • Use meaningful names (Person, Order, Permit, etc.) to improve readability.

  • Keep the struct fields ordered exactly as expected by the Solidity contract.

  • Prefer flat structures when possible — deep nesting adds complexity and can cause compatibility issues.

Example:

Maintain a Consistent Domain Separator

Why it matters: The domain separator is what makes EIP-712 signatures unique to a specific dApp, network, and contract. Inconsistency can result in invalid or replayable signatures.

Recommendations:

  • Always include the following fields in your domain:

    • name: Your dApp's name

    • version: Schema or app version

    • chainId: Use dynamic chainId to prevent cross-network replay attacks

    • verifyingContract: The smart contract address that will verify the signature

  • Match these fields exactly in both the frontend (JS/TS) and backend (Solidity).

Sample Domain:

Test Thoroughly Across Networks and Wallets

Why it matters: Wallet implementations of eth_signTypedData_v4 can vary, and certain quirks may only appear under specific conditions or devices.

Recommendations:

  • Test signature generation and verification on mainnet, testnets (Goerli, Sepolia), and sidechains if applicable.

  • Run signing tests with:

    • MetaMask (desktop and mobile)

    • WalletConnect

    • Brave wallet

    • Coinbase Wallet

    • Hardware wallets (Ledger, Trezor)

  • Include both unit tests (e.g., hashing logic) and integration tests (e.g., full signing + contract verification).

  • Use mock users with various environments to simulate real-world usage and wallet behaviors.

CONCLUSION

Why EIP-712 Should Be the Default

EIP-712 represents a significant improvement over legacy signature methods like eth_sign. Its core advantage lies in its ability to make off-chain signed messages human-readable, structured, and secure. Where traditional raw byte signing is opaque and vulnerable to phishing, EIP-712 ensures that:

  • Users see exactly what they're signing, improving transparency.

  • Signatures are tied to a specific domain, preventing replay attacks across apps and networks.

  • Developers can build safer and more intuitive UX for signing data off-chain, including permits, orders, and meta-transactions.

In the evolving landscape of decentralized applications, especially those prioritizing usability and security, EIP-712 should be considered a default standard for any feature that requires off-chain signing.

Next Steps for Developers

For teams and builders ready to transition or start using EIP-712, here are practical next actions:

1. Audit Your Signature Flow

  • If you're still using eth_sign or personal_sign, evaluate where structured signing could improve security and usability.

2. Adopt Ethers.js or Viem for Frontend Integration

  • Use Ethers.js' signTypedData method with MetaMask or WalletConnect.

  • Consider libraries like viem or @metamask/eth-sig-util for easier schema management.

3. Implement EIP-712 Verification in Smart Contracts

  • Use Solidity's EIP712 and ECDSA libraries (OpenZeppelin provides robust utilities).

4. Test Across Wallets and Devices

  • Don’t assume uniform behavior. Simulate signing on MetaMask mobile, Ledger, WalletConnect, and Brave.

5. Start with Common Use Cases

  • Migrate approve() flows to use permit() (see EIP-2612).

  • Implement gasless meta-transactions.

  • Use EIP-712 for DAO voting, trade orders, or content attestations.

REFERENCES

Buterin, V. (2018). EIP-712: Ethereum typed structured data hashing and signing. Ethereum Improvement Proposals. https://eips.ethereum.org/EIPS/eip-712

Buterin, V., & Wolovim, L. (2018). EIP-712: Ethereum typed structured data hashing and signing. Ethereum Foundation. https://eips.ethereum.org/EIPS/eip-712

EIP-2612: Permit Extension to ERC-20. (n.d.). Ethereum Improvement Proposals. https://eips.ethereum.org/EIPS/eip-2612

Ethers.js Docs. (2024). Signer.signTypedData. https://docs.ethers.org/v5/api/signer/#Signer-signTypedData

Ethers.js. (2024). Signing Typed Data. https://docs.ethers.org/v6/api/signer/#Signer-signTypedData

Ethereum Foundation. (2018). EIP-712: Ethereum typed structured data hashing and signing. https://eips.ethereum.org/EIPS/eip-712

Ethereum Foundation. (n.d.). EIP-191: Signed Data Standard. https://eips.ethereum.org/EIPS/eip-191

MetaMask. (2023). eth-sig-util GitHub Repository. https://github.com/MetaMask/eth-sig-util

MetaMask. (2023). eth_signTypedData: Signing structured messages. https://docs.metamask.io/guide/signing-data.html

MetaMask. (2023). eth_signTypedData_v4 Documentation. https://docs.metamask.io/guide/signing-data.html#signing-data-with-eip-712

MetaMask. (2023). Signing Typed Data. https://docs.metamask.io/guide/signing-data.html#signing-data-with-eip-712

MetaMask. (2023). Understanding Signature Requests. https://support.metamask.io

MetaMask Docs. (2024). Phishing Protection via Message Signing. https://docs.metamask.io/

MetaMask Docs. (2024). Signing structured data. https://docs.metamask.io/guide/signing-data.html

MetaMask Docs. (2024). Signing typed data. https://docs.metamask.io/guide/signing-data.html

OpenZeppelin. (2022). EIP-712 Signing. https://docs.openzeppelin.com/contracts/4.x/api/utils#EIP712

OpenZeppelin. (2022). Security Considerations in Ethereum dApps. https://docs.openzeppelin.com

OpenZeppelin. (2023). EIP712 - OpenZeppelin Contracts. https://docs.openzeppelin.com/contracts/4.x/api/utils#EIP712

OpenZeppelin. (2024). Contracts - EIP712. https://docs.openzeppelin.com/contracts/4.x/api/utils#EIP712

OpenZeppelin. (2024). Understanding EIP-712 and Meta-transactions. https://docs.openzeppelin.com/contracts/4.x/api/utils#EIP712

Snapshot. (2024). Gasless Governance. https://docs.snapshot.org/

Ternoa Devs. (2023). EIP-712: Gotchas and Workarounds. https://dev.ternoa.network/docs/tutorials/eip-712

WalletConnect. (2023). Secure Signing Methods. https://docs.walletconnect.com/security/signing

Wong, J. (2019). Ethereum phishing attack uses eth_sign to steal tokens. Medium. https://medium.com/mycrypto/stop-using-eth-sign-7c6c4d8c14c1

Wood, G. (2014). Ethereum: A secure decentralized generalized transaction ledger. Ethereum Project Yellow Paper. https://ethereum.github.io/yellowpaper/paper.pdf

0x Protocol. (2024). Off-Chain Orders. https://0x.org/docs/guides