{"id":1265,"date":"2026-01-14T17:20:41","date_gmt":"2026-01-14T15:20:41","guid":{"rendered":"https:\/\/ackee.xyz\/blog\/?p=1265"},"modified":"2026-01-14T17:20:41","modified_gmt":"2026-01-14T15:20:41","slug":"wake-debugging-guide","status":"publish","type":"post","link":"https:\/\/ackee.xyz\/blog\/wake-debugging-guide\/","title":{"rendered":"Wake Debugging Guide"},"content":{"rendered":"<p>When a fuzz run fails or contract behavior surprises you, effective debugging turns hours of frustration into minutes of focused investigation. Wake gives you Python&#8217;s full debugging ecosystem combined with Solidity-specific visibility tools. This guide shows you the techniques Ackee&#8217;s audit team uses to isolate issues fast.<\/p>\n<h2>Visibility Into Contract State<\/h2>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">The first step in debugging any failing test is understanding what your contracts are actually doing. Wake provides several approaches to expose internal state.<\/p>\n<h3>Console Logging in Solidity<\/h3>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">Wake includes a console logging library that works directly in your Solidity code:<\/p>\n<pre><code class=\"language-solidity\">import &quot;wake\/console.sol&quot;;<\/code><\/pre>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">You can log values inline during contract execution with <code class=\"codehl\">console.log()<\/code>, <code class=\"codehl\">console.logBytes32()<\/code>, and <code class=\"codehl\">console.logBytes()<\/code>. The library includes variants for dynamic-length data. This gives you printf-style debugging without deploying to a live network or setting up complex tooling.<\/p>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">When you need readable call traces, use <code class=\"codehl\">account.label<\/code> to replace raw addresses with meaningful names. Instead of seeing <code class=\"codehl\">0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb<\/code> in traces, you&#8217;ll see &#8220;TokenContract&#8221; or &#8220;UserWallet.&#8221;<\/p>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">For pure functions, you&#8217;ll need a workaround since they can&#8217;t emit logs during normal execution. The quick fix is temporarily marking the function and any dependent pure functions as <code class=\"codehl\">view<\/code>. If that&#8217;s not feasible, emit a custom event instead.<\/p>\n<h2>Manually Guided Fuzzing (MGF) Techniques<\/h2>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">Wake&#8217;s Python-based fuzzing gives you control that coverage-guided approaches can&#8217;t match. The key is maintaining accurate Python state that mirrors your contract state.<\/p>\n<h3>Invariant Functions<\/h3>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">Write more invariant functions than flow functions. Invariants verify your Python state stays synchronized with on-chain reality, which becomes essential when debugging complex failures. When a test fails, you can trust your Python state to understand what went wrong.<\/p>\n<h3>Find the Flow That Caused a Failure<\/h3>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">To identify which flow caused a failure, add logging at the start of each flow:<\/p>\n<pre><code class=\"language-python\">def pre_flow(self, flow: Callable):\n    logger.info(f&quot;[FLOW] {flow.__name__}&quot;)<\/code><\/pre>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">This creates a breadcrumb trail showing exactly which sequence of operations led to the failure.<\/p>\n<h2>Interactive Debugging with ipdb<\/h2>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">Wake integrates seamlessly with Python&#8217;s debugger. When you need to inspect state mid-execution or step through transaction sequences, drop into ipdb.<\/p>\n<h3><a href=\"https:\/\/x.com\/WakeFramework\/status\/1962863088144683511\">Transaction Debugging<\/a><\/h3>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">To examine recent transactions, use <code class=\"codehl\">chain.txs[-1].call_trace<\/code> for the most recent transaction or <code class=\"codehl\">chain.txs[-2].call_trace<\/code> for the one before. This gives you complete visibility into what happened on-chain.<\/p>\n<pre><code class=\"language-bash\">&gt; chain.txs[-1].call_trace\n&gt; chain.txs[-2].call_trace<\/code><\/pre>\n<h3>Systematic Debugging Setup<\/h3>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">For systematic debugging, set up handlers that automatically print traces when problems occur:<\/p>\n<pre><code class=\"language-python\">def revert_handler(e: RevertError):\n    if e.tx is not None:\n        print(e.tx.call_trace)\n\ndef tx_callback_fn(tx: TransactionAbc) -&gt; None:\n    print(tx.call_trace)\n    # so no print(tx.call_trace) everywhere, only for debug because too slow\n\n@chain.connect()\n@on_revert(revert_handler)\ndef test_fuzz_stethpool():\n    chain.tx_callback = tx_callback_fn\n    FuzzVWStEth().run(1, 100000)<\/code><\/pre>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">Note that printing traces for every transaction slows execution significantly, so enable <code class=\"codehl\">tx_callback<\/code> only when actively debugging.<\/p>\n<h3>Reproducing with Random Seeds<\/h3>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">When you&#8217;ve found a failure, reproduce it with the exact random seed:<\/p>\n<pre><code class=\"language-bash\">wake test tests\/test_counter_fuzz.py -S62061e838798ad0f -d -v<\/code><\/pre>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">The <code class=\"codehl\">-d<\/code> flag drops you into the debugger, <code class=\"codehl\">-v<\/code> increases verbosity, and the seed ensures you hit the same sequence every time.<\/p>\n<h2>Shrinking Failed Sequences<\/h2>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">Once you&#8217;ve found a failing sequence, shrinking isolates the minimal flow combination that triggers the bug. This confirms whether the issue appears earlier in the sequence or requires the full setup.<\/p>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">Run shrinking with <code class=\"codehl\">wake test tests\/test_something.py -SH<\/code> to shrink to a human-readable sequence or <code class=\"codehl\">-SR<\/code> for a compact representation. The smaller sequence often reveals the core issue more clearly than the full failure.<\/p>\n<pre><code class=\"language-bash\">wake test tests\/test_something.py -SH\nwake test tests\/test_something.py -SR<\/code><\/pre>\n<h2>More Tips<\/h2>\n<h3><a href=\"..\/..\/wake\/tuesday-x-articles\/expecting-the-unexpected-revert-handling.md\">Handling Expected Reverts<\/a><\/h3>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">When testing edge cases, you&#8217;ll often want to verify that operations revert under specific conditions. Wake&#8217;s <code class=\"codehl\">may_revert<\/code> context manager lets you assert both successful execution and expected failures:<\/p>\n<pre><code class=\"language-python\">with may_revert() as e:\n    tx = contract.operation()\n\nif condition_that_causes_revert:\n    assert e.value == Contract.ExpectedError()\n    return &quot;expected_revert_reason&quot;\n\nassert e.value is None  # Must succeed otherwise<\/code><\/pre>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">This pattern makes tests self-documenting &#8211; it&#8217;s immediately clear which paths should revert and which should succeed.<\/p>\n<h3><a href=\"https:\/\/x.com\/WakeFramework\/status\/1978811387511022053\">Performance Analysis<\/a><\/h3>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">When tests run slowly, profile them to find bottlenecks:<\/p>\n<pre><code class=\"language-bash\">wake --profile test tests\/test_fuzz.py<\/code><\/pre>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">This generates a profile file that you can visualize with:<\/p>\n<pre><code class=\"language-bash\">gprof2dot -f pstats .wake\/wake.prof | dot -Tsvg -o wake.prof.svg<\/code><\/pre>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">The resulting SVG shows exactly where execution time goes, making optimization targets obvious.<\/p>\n<h3><a href=\"https:\/\/x.com\/WakeFramework\/status\/1975548855467843609\">Fuzzing Coverage<\/a><\/h3>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">For understanding test coverage, run:<\/p>\n<pre><code class=\"language-bash\">wake test tests\/test_fuzz.py --coverage<\/code><\/pre>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">This generates <code class=\"codehl\">coverage.cov<\/code> in your project root. Open VS Code&#8217;s command palette and select &#8220;Wake: Show Coverage&#8221; to visualize which contract code your tests exercised.<\/p>\n<h3><a href=\"https:\/\/x.com\/WakeFramework\/status\/1967936361651900531\">Token Testing Patterns<\/a><\/h3>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">Testing DeFi protocols requires minting tokens and managing approvals. Wake makes this straightforward:<\/p>\n<pre><code class=\"language-python\"># Mint directly to your test user\nmint_erc20(token, user, 100 * 10**18)\ntoken.approve(contract, 100 * 10**18, from_=user)\ncontract.pullToken(from_=user)<\/code><\/pre>\n<h3><a href=\"https:\/\/x.com\/WakeFramework\/status\/1973735342579577320\">Cross-Chain Testing<\/a><\/h3>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">For cross-chain scenarios, Wake lets you run multiple independent chains simultaneously:<\/p>\n<pre><code class=\"language-python\">from wake.testing import Chain\n\n# Create two separate blockchain instances\nethereum_chain = Chain()\npolygon_chain = Chain()\n\n@ethereum_chain.connect()\n@polygon_chain.connect()\ndef test_cross_chain_transfer():\n    # Both chains are now connected and ready\n    pass<\/code><\/pre>\n<h2>Making Debugging Faster<\/h2>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">Start with clear invariants that verify your assumptions about contract state. Keep call traces easily accessible through handlers and callbacks. When tests fail, shrink them to minimal reproducers before diving deep. These patterns make the difference between spending hours hunting bugs and isolating them in minutes.<\/p>\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">The techniques here leverage Python&#8217;s mature debugging ecosystem while giving you direct access to EVM internals. Wake combines the expressiveness of Python with the precision needed for smart contract security. That&#8217;s the foundation for both fast development and thorough testing.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When a fuzz run fails or contract behavior surprises you, effective debugging turns hours of frustration into minutes of focused investigation. Wake gives you Python&#8217;s full debugging ecosystem combined with Solidity-specific visibility tools. This guide shows you the techniques Ackee&#8217;s audit team uses to isolate issues fast. Visibility Into Contract State The first step in debugging any failing test is understanding what&hellip;<\/p>\n","protected":false},"author":24,"featured_media":852,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[61,10,80,63,1,103],"tags":[],"class_list":["post-1265","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-education","category-ethereum","category-solidity","category-tutorial","category-uncategorized","category-wake"],"aioseo_notices":[],"featured_image_src":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Read-Only-Reentrancy-Attack-600x400.png","featured_image_src_square":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/06\/Read-Only-Reentrancy-Attack-600x600.png","author_info":{"display_name":"Naoki Yoshida","author_link":"https:\/\/ackee.xyz\/blog\/author\/naoki-yoshida\/"},"_links":{"self":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts\/1265","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/users\/24"}],"replies":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/comments?post=1265"}],"version-history":[{"count":0,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts\/1265\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media\/852"}],"wp:attachment":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media?parent=1265"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/categories?post=1265"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/tags?post=1265"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}