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, andverifyingContractmatch exactly between Wake and contract code. - Separate raw and typed flows: use
signfor prefixed personal messages,sign_structuredfor typed data, andsign_hashfor 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_revertand printe.tx.call_tracewhen debugging signature-related reverts. - Assert end-to-end: inside Solidity, recompute the digest and check that
ecrecoverreturnsaccount.addressto catch encoding errors early.
A Simple Permit-Test Recipe
- Define the permit struct as a Wake dataclass mirroring the Solidity struct.
- Build the domain using the contract’s actual name, version, chain ID, and verifying address.
- Generate the signature with
sign_structuredand call the permit or verification function in your test. - Recompute the digest in Solidity and ensure
ecrecoveryields your signer. - Add fuzzing for amounts and deadlines to confirm that hashing stays stable across inputs.
Conclusion
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.