Why signatures are important in testing
The entered signatures drive permission flows, meta-transactions, and off-chain approvals. Being able to create this within your test allows you to verify your confirmation logic without having to connect an external wallet. Wake exposes APIs focused on: Account It covers three common cases: Raw message signature (sign), EIP-712 Structured Signature (sign_structured) and low-level hash signatures (sign_hash). This guide distills official account and address documentation into real-world examples that can be directly applied to testing.
Prepare your account using your private key
Wake is only signed if your account has a private key. You can import or create it in one 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
Your account is now ready for transactions and all three signature modes.
Raw Message Signature (EIP-191)
use Account.sign For human-readable bytes, e.g. personal_sign The style flow of the test:
from wake.testing import Account
account = Account.from_mnemonic(" ".join(("test") * 11 + ("junk")))
signature = account.sign(b"Hello, world!")
Wake starts with the EIP-191 prefix (version 0x45) automatically ensures that signatures match standard wallet behavior. Use this mode for UX prompts or legacy flows that intentionally avoid entered data.
Structured Message Signature (EIP-712)
Data entered keeps approvals clear. Wake mirrors the EIP-712 pipeline using data classes and explicit domains.
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. Combine this with the following Solidity features in your protocol testing: hashTypedData Ensures that the contract computes the same digest before verifying the signature.
Precomputed hash signature
use Account.sign_hash This is only possible if the external API already provides a digest. This method skips the prefix 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 need to match a known hash, such as the hash of a contract. hashTypedData calculation. If you don’t control or understand the pre-image, don’t sign.
Hardening Tips for Signature Testing
- Domain Alignment: Guaranteed
name,version,chainIdandverifyingContractWake and contract code match exactly. - Separate raw and input flows: Enabled
signFor private messages prefixed withsign_structuredFor entered datasign_hashIntentional extreme case. - Label test actor:
chain.accounts(i).label = "ALICE"Improves trace readability without affecting behavior. - Capturing traces on failure: Wrapping tests with
@on_revertprint ande.tx.call_traceWhen debugging signature-related reverts. - End-to-end assertion: Recalculate and verify digests inside Solidity.
ecrecoverreportaccount.addressDetect encoding errors early.
Simple Permission Test Recipe
- Define the Allow structure as a Wake data class that mirrors the Solidity structure.
- Build your domain using the contract’s real name, version, chain ID, and verification address.
- Create a signature using:
sign_structuredCall the grant or confirm function in your test. - Solidity recalculates the digest and checks:
ecrecoverCalculates the signer. - We add fuzzing to the amount and due date to ensure that the hashing remains stable across inputs.
conclusion
Wake’s Signature Assistant adds a wallet-like interface inside your tests, allowing you to run permission flows and entered approvals without having to connect external tools. use sign_structured Maintain for EIP-712 route sign Handling legacy prompts sign_hash Used as an intentional edge case hook. The API is kept in one line, so you can focus on asserting contract behavior rather than wrestling with the signature plumbing.
