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 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:


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:

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:

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:

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.