{"id":1207,"date":"2025-11-06T14:34:36","date_gmt":"2025-11-06T12:34:36","guid":{"rendered":"https:\/\/ackee.xyz\/blog\/?p=1207"},"modified":"2025-11-06T19:57:20","modified_gmt":"2025-11-06T17:57:20","slug":"mastering-wake-printers-for-solidity-security-analysis","status":"publish","type":"post","link":"https:\/\/ackee.xyz\/blog\/mastering-wake-printers-for-solidity-security-analysis\/","title":{"rendered":"Mastering Wake Printers for Solidity Security Analysis"},"content":{"rendered":"<h2 dir=\"auto\" data-line=\"3\">Introduction<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"3\">Manually reviewing Solidity code is slow and prone to errors. A single overlooked function can hide a costly vulnerability. Wake\u2019s printer system automates the search for these risky patterns, turning hours of manual review into quick, reliable scans.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"3\">Wake printers combine Python\u2019s simplicity with Wake\u2019s Intermediate Representation (IR) to turn complex static analysis into simple scripts that highlight things like unrestricted withdrawals or missing access controls. This guide will walk you through creating custom printers that highlight security-relevant patterns in your smart contracts.<\/p>\n<h2 dir=\"auto\" data-line=\"7\">Prerequisites and Setup<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"9\">For this tutorial, we&#8217;ll use the workshop repository as our example project:<\/p>\n<pre><code class=\"language-bash\">git clone https:\/\/github.com\/Ackee-Blockchain\/2025-workshop-fuzzing\ncd 2025-workshop-fuzzing\nnpm install<\/code><\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"17\">Before continuing, check that Wake compiles your project successfully by running:<\/p>\n<pre><code class=\"language-bash\">wake up<\/code><\/pre>\n<h2 dir=\"auto\" data-line=\"23\">Understanding Wake Printers<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"25\">Wake comes with several built-in printers that showcase different kinds of analysis. You can list them with:<\/p>\n<pre><code class=\"language-bash\">wake print<\/code><\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"31\">Run a specific printer by name:<\/p>\n<pre><code class=\"language-bash\">wake print storage-layout<\/code><\/pre>\n<figure id=\"attachment_1208\" aria-describedby=\"caption-attachment-1208\" style=\"width: 1480px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-1208\" src=\"https:\/\/abchprod.wpengine.com\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-18.32.53.png\" alt=\"\" width=\"1480\" height=\"1016\" srcset=\"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-18.32.53.png 1480w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-18.32.53-300x206.png 300w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-18.32.53-1024x703.png 1024w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-18.32.53-768x527.png 768w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-18.32.53-370x254.png 370w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-18.32.53-760x522.png 760w\" sizes=\"auto, (max-width: 1480px) 100vw, 1480px\" \/><figcaption id=\"caption-attachment-1208\" class=\"wp-caption-text\">storage layout output<\/figcaption><\/figure>\n<p class=\"code-line\" dir=\"auto\" data-line=\"37\">Built-in printers demonstrate the system&#8217;s capabilities, but the real power comes from writing custom printers tailored to your security analysis needs. Once you understand how built-in printers work, you\u2019ll see how easy it is to extend Wake with your own analysis tools. By the end of this guide, you\u2019ll know how to create printers that detect vulnerability patterns relevant to your auditing approach.<\/p>\n<h2 dir=\"auto\" data-line=\"39\">Tutorial 1: Creating Your First Printer &#8211; Listing Contracts<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"41\">Let&#8217;s start with a simple printer that lists all contracts in your project. This example introduces the core concepts you&#8217;ll use in more complex analysis.<\/p>\n<h3 dir=\"auto\" data-line=\"43\">Creating the Printer Structure<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"45\">Run this command to scaffold your first printer::<\/p>\n<pre><code class=\"language-bash\">wake up printer list-contracts<\/code><\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-1209\" src=\"https:\/\/abchprod.wpengine.com\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-17.40.37.png\" alt=\"\" width=\"1504\" height=\"374\" srcset=\"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-17.40.37.png 1504w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-17.40.37-300x75.png 300w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-17.40.37-1024x255.png 1024w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-17.40.37-768x191.png 768w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-17.40.37-370x92.png 370w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-17.40.37-760x189.png 760w\" sizes=\"auto, (max-width: 1504px) 100vw, 1504px\" \/><\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"51\">Wake generates a new printer directory and a starter file with this structure:<\/p>\n<ul>\n<li class=\"code-line\" dir=\"auto\" data-line=\"52\"><code class=\"codehl\">printers\/<\/code> directory for all custom printers<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"53\"><code class=\"codehl\">list-contracts.py<\/code> with the basic printer structure<\/li>\n<\/ul>\n<h3 dir=\"auto\" data-line=\"55\">Understanding the Template<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"57\">The generated template provides this starting structure:<\/p>\n<pre><code class=\"language-python\">from __future__ import annotations\n\nimport networkx as nx\nimport rich_click as click\nimport wake.ir as ir\nimport wake.ir.types as types\nfrom rich import print\nfrom wake.cli import SolidityName\nfrom wake.printers import Printer, printer\n\n\nclass ListContractsPrinter(Printer):\n    def print(self) -&gt; None:\n        pass\n\n    @printer.command(name=&quot;list-contracts&quot;)\n    def cli(self) -&gt; None:\n        pass<\/code><\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"79\">Here\u2019s what each part of the template does:<\/p>\n<ul>\n<li class=\"code-line\" dir=\"auto\" data-line=\"80\"><strong><code class=\"codehl\">print()<\/code><\/strong>: Main execution method where analysis results are displayed<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"81\"><strong><code class=\"codehl\">cli()<\/code><\/strong>: Command-line interface handler for custom arguments<\/li>\n<\/ul>\n<h3 dir=\"auto\" data-line=\"83\">Implementing the Visitor Pattern<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"85\">Wake uses the Visitor pattern to traverse the contract&#8217;s Abstract Syntax Tree (AST). The visitor pattern allows Wake to automatically navigate through your code\u2019s structure, enabling you to react to specific elements\u2014such as contract or function definitions.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"85\">To list contracts, we&#8217;ll override the <code class=\"codehl\">visit_contract_definition<\/code> method, which gets called for each contract in the codebase.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"87\">Add this method to your <code class=\"codehl\">ListContractsPrinter<\/code> class:<\/p>\n<pre><code class=\"language-python\">def visit_contract_definition(self, node: ir.ContractDefinition) -&gt; None:\n    print(node.name)<\/code><\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"94\">Test your printer:<\/p>\n<pre><code class=\"language-bash\">wake print list-contracts<\/code><\/pre>\n<p dir=\"auto\" data-line=\"100\">This command runs your printer and prints all contract names found in your project.<\/p>\n<h3 dir=\"auto\" data-line=\"100\">Refining the Output<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"102\">The basic implementation shows all contracts, including interfaces and inherited contracts. Let&#8217;s improve it to show only deployable contracts:<\/p>\n<pre><code class=\"language-python\">from __future__ import annotations\n\nimport networkx as nx\nimport rich_click as click\nimport wake.ir as ir\nimport wake.ir.types as types\nfrom rich import print\nfrom wake.cli import SolidityName\nfrom wake.printers import Printer, printer\n\n\nclass ListContractsPrinter(Printer):\n\n    def visit_contract_definition(self, node: ir.ContractDefinition) -&gt; None:\n        print(node.name)\n\n    def print(self) -&gt; None:\n        pass\n\n    @printer.command(name=&quot;list-contracts&quot;)\n    def cli(self) -&gt; None:\n        pass<\/code><\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"129\">Right now, the printer lists every contract, including interfaces and base classes. Let\u2019s refine it to show only deployable ones.<\/p>\n<p dir=\"auto\" data-line=\"129\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-1210\" src=\"https:\/\/abchprod.wpengine.com\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-18.32.38.png\" alt=\"\" width=\"1122\" height=\"986\" srcset=\"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-18.32.38.png 1122w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-18.32.38-300x264.png 300w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-18.32.38-1024x900.png 1024w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-18.32.38-768x675.png 768w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-18.32.38-370x325.png 370w, https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-04-at-18.32.38-760x668.png 760w\" sizes=\"auto, (max-width: 1122px) 100vw, 1122px\" \/><\/p>\n<h3 dir=\"auto\" data-line=\"162\">Filtering for Deployable Contracts<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"164\">Add conditions to filter out interfaces, libraries, and base contracts. This helps identify which contracts are actually deployable:<\/p>\n<pre><code class=\"language-python\">def visit_contract_definition(self, node: ir.ContractDefinition) -&gt; None:\n    if len(node.child_contracts) != 0:\n        return\n\n    if node.kind != ir.enums.ContractKind.CONTRACT:\n        return\n\n    print(node.name)<\/code><\/pre>\n<p>The <span class=\"s1\">ContractDefinition<\/span> class includes attributes you can use to filter your results.\u00a0For complete reference, see: <a href=\"https:\/\/ackee.xyz\/wake\/docs\/latest\/api-reference\/ir\/declarations\/contract-definition\/\">https:\/\/ackee.xyz\/wake\/docs\/latest\/api-reference\/ir\/declarations\/contract-definition\/<\/a><\/p>\n<h3 dir=\"auto\" data-line=\"179\">Complete Implementation<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"181\">Here&#8217;s the final version with proper separation of concerns\u2014collecting data during traversal and displaying it in the <code class=\"codehl\">print()<\/code> method:<\/p>\n<pre><code class=\"language-python\">from __future__ import annotations\n\nimport networkx as nx\nimport rich_click as click\nimport wake.ir as ir\nimport wake.ir.types as types\nfrom rich import print\nfrom wake.cli import SolidityName\nfrom wake.printers import Printer, printer\n\n\nclass ListContractsPrinter(Printer):\n    contracts: list[ir.ContractDefinition]\n\n    def __init__(self):\n        self.contracts = []\n\n    def visit_contract_definition(self, node: ir.ContractDefinition) -&gt; None:\n        if len(node.child_contracts) != 0:\n            return\n\n        if node.kind != ir.enums.ContractKind.CONTRACT:\n            return\n\n        self.contracts.append(node)\n\n    def print(self) -&gt; None:\n        for contract in self.contracts:\n            print(contract.name)\n\n    @printer.command(name=&quot;list-contracts&quot;)\n    def cli(self) -&gt; None:\n        pass<\/code><\/pre>\n<p dir=\"auto\" data-line=\"219\">You\u2019ve just built your first printer. It collects and prints deployable contracts\u2014your first step toward automated contract mapping.<\/p>\n<h2 dir=\"auto\" data-line=\"219\">Tutorial 2: Analyzing Contract Functions<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"221\">Understanding which functions are externally callable is crucial for security: public \u2018withdraw\u2019 or \u2018transfer\u2019 functions often define a contract\u2019s attack surface. Let&#8217;s create a printer that maps out the attack surface by listing all public and external functions.<\/p>\n<h3 dir=\"auto\" data-line=\"223\">Setting Up the Functions Printer<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"225\">Create a new printer:<\/p>\n<pre><code class=\"language-bash\">wake up printer list-functions<\/code><\/pre>\n<h3 dir=\"auto\" data-line=\"231\">Implementation Strategy<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"233\">Now we\u2019ll expand our printer to map each contract\u2019s external attack surface. Our goal: List only the final, callable public\/external functions for each deployable contract, excluding interfaces and overridden functions.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"235\">While we could use <code class=\"codehl\">visit_function_definition<\/code> to iterate all functions, grouping them by contract provides better context. We&#8217;ll use <code class=\"codehl\">visit_contract_definition<\/code> and access the <code class=\"codehl\">functions<\/code> attribute.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"237\">Start by collecting all contracts:<\/p>\n<pre><code class=\"language-python\">class ListFunctionsPrinter(Printer):\n\n    contracts: list[ir.ContractDefinition] = []\n\n    def visit_contract_definition(self, node: ir.ContractDefinition) -&gt; None:\n        self.contracts.append(node)<\/code><\/pre>\n<h3 dir=\"auto\" data-line=\"247\">Processing the Inheritance Hierarchy<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"249\">In the <code class=\"codehl\">print()<\/code> method, we traverse the inheritance hierarchy from base to derived contracts, showing callable functions at each level:<\/p>\n<pre><code class=\"language-python\">   def print(self) -&gt; None:\n        for node in self.contracts:\n\n            # Skip if not a contract (interface, library)\n            if node.kind != ir.enums.ContractKind.CONTRACT:\n                continue\n\n            #Process leaf contracts only\n            if len(node.child_contracts) !=0:\n                continue\n\n            # Print the inheritance hierarchy (from base to derived)\n            for base_contract in reversed(node.linearized_base_contracts):\n                print(f&quot;Contract: {base_contract.name}&quot;)\n\n                functions = self.get_callable_final_functions(base_contract)\n\n                if len(functions) &gt; 0:\n                    print(&quot;Functions:&quot;)\n                    for function in functions:\n                        print(f&quot;  {function.name}&quot;)\n            print(&quot;--------------------&quot;)<\/code><\/pre>\n<h3 dir=\"auto\" data-line=\"275\">Filtering for Attack Surface Functions<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"277\">The <code class=\"codehl\">get_callable_final_functions<\/code> helper method identifies which functions can actually be called by external actors. It checks that a function is a final implementation (not overridden by child contracts) and has public or external visibility. These are the functions that matter for security analysis since they represent the contract&#8217;s actual attack surface.<\/p>\n<pre><code class=\"language-python\">    def get_callable_final_functions(self, contract: ir.ContractDefinition) -&gt; list[ir.FunctionDefinition]:\n        return [\n            func for func in contract.functions\n            if len(func.child_functions) == 0  # Is final implementation\n            and func.visibility in [ir.enums.Visibility.PUBLIC, ir.enums.Visibility.EXTERNAL]\n        ]<\/code><\/pre>\n<h3 dir=\"auto\" data-line=\"290\">Running the Functions Printer<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"292\">Execute the printer to see the inheritance hierarchy and callable functions:<\/p>\n<pre><code class=\"language-bash\">wake print list-functions<\/code><\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"296\">Output:<\/p>\n<pre><code class=\"language-bash\">Contract: Context\nContract: Ownable\nFunctions:\n  owner\n  renounceOwnership\n  transferOwnership\nContract: SingleTokenVault\nFunctions:\n  constructor\n  deposit\n  withdraw\n  emergencyWithdraw\n  balanceOf\n  setDepositLimits\n--------------------\nContract: EIP712Example\nFunctions:\n  constructor\n  DOMAIN_SEPARATOR\n  castVoteBySignature\n  getVoteCounts\n--------------------\nContract: Context\nContract: IERC20\nContract: IERC20Metadata\nContract: IERC20Errors\nContract: ERC20\nFunctions:\n  name\n  symbol\n  decimals\n  totalSupply\n  balanceOf\n  transfer\n  allowance\n  approve\n  transferFrom\nContract: IERC20Permit\nContract: IERC5267\nContract: EIP712\nFunctions:\n  eip712Domain\nContract: Nonces\nContract: ERC20Permit\nFunctions:\n  permit\n  nonces\n  DOMAIN_SEPARATOR\nContract: PermitToken\nFunctions:\n  constructor\n--------------------\nContract: Token\nFunctions:\n  constructor\n  mintTokens\n  transfer\n  transferWithBytes\n  getBalance\n--------------------\nContract: Context\nContract: IERC20\nContract: IERC20Metadata\nContract: IERC20Errors\nContract: ERC20\nFunctions:\n  name\n  symbol\n  decimals\n  totalSupply\n  balanceOf\n  transfer\n  allowance\n  approve\n  transferFrom\nContract: MockERC20\nFunctions:\n  constructor\n--------------------<\/code><\/pre>\n<p dir=\"auto\" data-line=\"380\">The output gives you a quick visual map of each contract\u2019s inheritance and callable entry points.<\/p>\n<h2 dir=\"auto\" data-line=\"380\">Tutorial 3: Adding Command-Line Options<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"382\">Real-world analysis often requires focusing on specific contracts. Let&#8217;s enhance our printer to accept command-line arguments, allowing targeted analysis of individual contracts.<\/p>\n<h3 dir=\"auto\" data-line=\"384\">Understanding CLI Integration<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"386\">Wake printers can accept command-line options through the <code class=\"codehl\">@click.option<\/code> decorator. This enables dynamic analysis based on user input. We&#8217;ll add a <code class=\"codehl\">--contract-name<\/code> option to filter results for a specific contract.<\/p>\n<h3 dir=\"auto\" data-line=\"388\">Implementing the Option<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"390\">First, add a class member to store the contract name, then use <code class=\"codehl\">@click.option<\/code> to capture the command-line argument:<\/p>\n<pre><code class=\"language-python\">@printer.command(name=&quot;list-functions&quot;)\n@click.option(&quot;--contract-name&quot;, type=str, required=False)\ndef cli(self, contract_name: str | None) -&gt; None:\n    self.contract_name = contract_name<\/code><\/pre>\n<h3 dir=\"auto\" data-line=\"399\">Conditional Filtering Logic<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"401\">The <code class=\"codehl\">print()<\/code> method now checks if a specific contract was requested. If no contract name is provided, the printer lists all deployable contracts. If a name is specified, it drills into that contract\u2019s hierarchy only, even if it&#8217;s not a leaf contract.<\/p>\n<h3 dir=\"auto\" data-line=\"403\">Complete Implementation with CLI Options<\/h3>\n<p>Here\u2019s the final printer with optional contract filtering built in.<\/p>\n<pre><code class=\"language-python\">from __future__ import annotations\n\nimport networkx as nx\nimport rich_click as click\nimport wake.ir as ir\nimport wake.ir.types as types\nfrom rich import print\nfrom wake.cli import SolidityName\nfrom wake.printers import Printer, printer\n\n\nclass ListFunctionsPrinter(Printer):\n\n    contracts: list[ir.ContractDefinition] = []\n    contract_name: str | None = None\n\n\n    def get_callable_final_functions(self, contract: ir.ContractDefinition) -&gt; list[ir.FunctionDefinition]:\n        return [\n            func for func in contract.functions\n            if len(func.child_functions) == 0  # Is final implementatione\n            and func.visibility in [ir.enums.Visibility.PUBLIC, ir.enums.Visibility.EXTERNAL]\n        ]\n\n    def visit_contract_definition(self, node: ir.ContractDefinition) -&gt; None:\n        self.contracts.append(node)\n\n\n    def print(self) -&gt; None:\n        for node in self.contracts:\n            # If contract name is specified, only process that contract\n            if self.contract_name is not None and node.name != self.contract_name:\n                continue\n\n            # Skip if not a contract (e.g., interface, library)\n            if node.kind != ir.enums.ContractKind.CONTRACT:\n                continue\n\n            # If no contract name specified, only process leaf contracts\n            if self.contract_name is None and len(node.child_contracts) != 0:\n                continue\n\n            # Print the inheritance hierarchy (from base to derived)\n            for base_contract in reversed(node.linearized_base_contracts):\n                print(f&quot;Contract: {base_contract.name}&quot;)\n\n                functions = self.get_callable_final_functions(base_contract)\n\n                if len(functions) &gt; 0:\n                    print(&quot;Functions:&quot;)\n                    for function in functions:\n                        print(f&quot;  {function.name}&quot;)\n            print(&quot;--------------------&quot;)\n\n    @printer.command(name=&quot;list-functions&quot;)\n    @click.option(&quot;--contract-name&quot;, type=str, required=False)\n    def cli(self, contract_name: str | None) -&gt; None:\n        self.contract_name = contract_name\n        pass<\/code><\/pre>\n<p dir=\"auto\" data-line=\"467\">Now your printer can analyze specific contracts on demand\u2014a feature that makes targeted auditing fast and repeatable.<\/p>\n<h3 dir=\"auto\" data-line=\"467\">Using the Enhanced Printer<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"469\">Now you can analyze specific contracts:<\/p>\n<pre><code class=\"language-bash\"># Analyze all deployable contracts\nwake print list-functions\n\n# Focus on a specific contract\nwake print list-functions --contract-name Token<\/code><\/pre>\n<h2 dir=\"auto\" data-line=\"479\">Practical Applications for Security Auditing<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"481\">With these foundational skills, you can create printers that visualize and analyze your codebase structure. Custom printers let you visualize key code relationships quickly. They don\u2019t detect vulnerabilities directly, but they reveal patterns that guide your manual review.<\/p>\n<h3 dir=\"auto\" data-line=\"483\">Useful Analysis Patterns to Visualize<\/h3>\n<p>Once you\u2019re comfortable building printers, try these advanced patterns to visualize complex security relationships.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"485\"><strong>Access Control Mapping<\/strong>: Create printers that list all state-changing functions alongside their access modifiers. This overview helps you quickly identify which functions might need additional protection.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"487\"><strong>Call Flow Visualization<\/strong>: Map out which contracts call which functions. Understanding these relationships reveals potential attack paths and helps prioritize your audit focus.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"489\"><strong>State Variable Usage<\/strong>: Track how storage variables are accessed across functions. This analysis helps identify complex state dependencies that warrant closer inspection.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"491\"><strong>Inheritance Hierarchies<\/strong>: Visualize the complete inheritance tree of your contracts. Complex inheritance can hide function implementations and create unexpected behaviors.<\/p>\n<h3 dir=\"auto\" data-line=\"493\">Building Your Analysis Toolkit<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"495\">Start small. Build printers that solve your immediate needs. Each one adds to your personal toolkit. Over time, you\u2019ll develop reusable scripts that make every audit faster.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"497\">The Wake printer system&#8217;s flexibility means you can adapt your analysis tools to different audit scenarios. Whether you&#8217;re mapping upgrade patterns, visualizing DeFi protocol interactions, or understanding storage layouts, custom printers transform hours of manual code reading into seconds of automated analysis and visualization.<\/p>\n<h2 dir=\"auto\" data-line=\"499\">Next Steps<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"501\">Printers give you the map; detectors find the vulnerabilities. Together, they turn Solidity auditing from a manual grind into a structured, insightful process. Every printer you write makes complex code clearer\u2014and strengthens the safety of the smart contracts you review.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"503\">For vulnerability detection, Wake provides a separate detector system that goes beyond visualization to identify actual security issues. Printers give you the map; detectors find the problems.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"505\">Consider contributing your printers back to the community. Analysis tools are most powerful when shared, and your custom printer might help another auditor understand complex codebases more efficiently.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"507\">For more advanced topics and complete API reference, visit: <a href=\"https:\/\/ackee.xyz\/wake\/docs\/latest\/static-analysis\/using-printers\/\">Wake static analysis documents<\/a><\/p>\n<p dir=\"auto\" data-line=\"507\">Also read <a href=\"https:\/\/ackee.xyz\/blog\/a-beginners-guide-to-manually-guided-fuzzing\/\">The Beginners Guide for Manually Guided Fuzzing<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction Manually reviewing Solidity code is slow and prone to errors. A single overlooked function can hide a costly vulnerability. Wake\u2019s printer system automates the search for these risky patterns, turning hours of manual review into quick, reliable scans. Wake printers combine Python\u2019s simplicity with Wake\u2019s Intermediate Representation (IR) to turn complex static analysis into simple scripts that highlight things like unrestricted&hellip;<\/p>\n","protected":false},"author":24,"featured_media":1061,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[61,10,84,80,63,103],"tags":[96,160,64,28,102,104],"class_list":["post-1207","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-education","category-ethereum","category-hacks","category-solidity","category-tutorial","category-wake","tag-educational","tag-printer","tag-security","tag-smart-contract","tag-tutorial","tag-wake"],"aioseo_notices":[],"featured_image_src":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/06\/kiloex-600x400.png","featured_image_src_square":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/06\/kiloex-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\/1207","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=1207"}],"version-history":[{"count":0,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts\/1207\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media\/1061"}],"wp:attachment":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media?parent=1207"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/categories?post=1207"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/tags?post=1207"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}