# Signing Data in Wake: Raw, Structured, and Hash Flows

> Learn how to sign raw, structured, and hashed messages in Wake tests. Validate EIP-712, EIP-191, and permit flows with clear examples and reliable verification.

- Canonical: https://ack3.ai/research/signing-data-in-wake-raw-structured-and-hash-flows/
- Published: December 10, 2025
- Updated: January 5, 2026
- Authors: Naoki Yoshida
- Category: Technical guide
- Topics: education, ethereum, solidity, wake

## Why Signing Matters in Tests

Typed signatures drive permit flows, meta-transactions, and off-chain approvals. Being able to generate them inside tests lets you confirm your verification logic without wiring external wallets. Wake exposes a focused API on `Account` that covers the three common cases: raw message signing (`sign`), EIP-712 structured signing (`sign_structured`), and low-level hash signing (`sign_hash`). This guide distills the official [Accounts and addresses](/wake/docs/latest/testing-framework/accounts-and-addresses/) documentation into practical examples you can drop directly into your tests.

## Preparing an Account With a Private Key

Wake signs only when an account holds a private key. You can import or generate one in a single line:

```python
from wake.testing import Account

account = Account.from_mnemonic(" ".join(["test"] * 11 + ["junk"]))
# Other options:
# Account.from_key("0x" + "a" * 64)
# Account.from_alias("alice")  # uses the alias from config
# Account.new()                # fresh random key
```

The account is now ready for transactions and all three signing modes.

## Signing Raw Messages (EIP-191)

Use `Account.sign` for human-readable bytes, such as when you need a `personal_sign` style flow in a test:

```python
from wake.testing import Account

account = Account.from_mnemonic(" ".join(["test"] * 11 + ["junk"]))
signature = account.sign(b"Hello, world!")
```

Wake applies the EIP-191 prefix (version `0x45`) automatically so the signature matches standard wallet behavior. Use this mode for UX prompts or legacy flows that intentionally avoid typed data.

## Signing Structured Messages (EIP-712)

Typed data keeps approvals unambiguous. Wake mirrors the EIP-712 pipeline using dataclasses and an explicit domain:

```python
from dataclasses import dataclass
from wake.testing import *


@dataclass
class Transfer:
    sender: Address
    recipient: Address
    amount: uint256


account = Account.from_mnemonic(" ".join(["test"] * 11 + ["junk"]))
signature = account.sign_structured(
    Transfer(
        sender=account.address,
        recipient=Address(1),
        amount=10,
    ),
    domain=Eip712Domain(
        name="Test",
        chainId=chain.chain_id,
        verifyingContract=Address(0),  # set to your contract if needed
    ),
)
```

Wake builds the type string, hashes each field, and signs the EIP-712 digest. In protocol tests, pair this with a Solidity function such as `hashTypedData` to confirm the contract computes the same digest before verifying the signature.

## Signing a Precomputed Hash

Use `Account.sign_hash` only when an external API already gives you the digest. This method skips prefixes and signs the bytes as-is:

```python
from wake.testing import *

account = Account.from_mnemonic(" ".join(["test"] * 11 + ["junk"]))
message_hash = keccak256(b"Hello, world!")
signature = account.sign_hash(message_hash)
```

Use this when you must match a known hash, such as a contract’s `hashTypedData` output. If you do not control or understand the preimage, do not sign it.

## Hardening Tips for Signature Tests

  * Align domains: ensure `name`, `version`, `chainId`, and `verifyingContract` match exactly between Wake and contract code.
  * Separate raw and typed flows: use `sign` for prefixed personal messages, `sign_structured` for typed data, and `sign_hash` for deliberate edge cases.
  * Label test actors: `chain.accounts[i].label = "ALICE"` improves trace readability without affecting behavior.
  * Capture traces on failure: wrap tests with `@on_revert` and print `e.tx.call_trace` when debugging signature-related reverts.
  * Assert end-to-end: inside Solidity, recompute the digest and check that `ecrecover` returns `account.address` to catch encoding errors early.



### A Simple Permit-Test Recipe

  1. Define the permit struct as a Wake dataclass mirroring the Solidity struct.
  2. Build the domain using the contract’s actual name, version, chain ID, and verifying address.
  3. Generate the signature with `sign_structured` and call the permit or verification function in your test.
  4. Recompute the digest in Solidity and ensure `ecrecover` yields your signer.
  5. Add fuzzing for amounts and deadlines to confirm that hashing stays stable across inputs.



## Conclusion

Wake’s signing helpers put a wallet-like interface inside your tests, so you can exercise permit flows and typed approvals without wiring external tools. Use `sign_structured` for EIP-712 paths, keep `sign` for legacy prompts, and treat `sign_hash` as an intentional edge-case hook. The APIs stay one-liners, letting you focus on asserting contract behavior instead of wrestling with signature plumbing.
