🚀 Gate Square “Gate Fun Token Challenge” is Live!
Create tokens, engage, and earn — including trading fee rebates, graduation bonuses, and a $1,000 prize pool!
Join Now 👉 https://www.gate.com/campaigns/3145
💡 How to Participate:
1️⃣ Create Tokens: One-click token launch in [Square - Post]. Promote, grow your community, and earn rewards.
2️⃣ Engage: Post, like, comment, and share in token community to earn!
📦 Rewards Overview:
Creator Graduation Bonus: 50 GT
Trading Fee Rebate: The more trades, the more you earn
Token Creator Pool: Up to $50 USDT per user + $5 USDT for the first 50 launche
Automate Solidity auditing using Wake Printer scripts
Prerequisites and Setup
In this tutorial, we will use the workshop repository as an example project:
wake up
Bash
Copy
wake print
Bash
Copy
Run a specific printer by name:
Tutorial 1: Create Your First Printer – List Contracts
Let’s start with a simple printer that lists all contracts in the project. This example introduces core concepts you’ll use in more complex analyses.
Create the Printer Structure
Run the following commands to set up your first printer:
from future import annotations import networkx as nx import rich_click as click import wake.ir as ir import wake.ir.types as types from rich import print from wake.cli import SolidityName from wake.printers import Printer, printer
class ListContractsPrinter(Printer): (def print)self( -> None: pass @printer.command)name=“list-contracts” (def cli)self( -> None: pass
Python
Copy
The following explains the purpose of each part of the template:
Implementing the Visitor Pattern
Wake uses the visitor pattern to traverse the abstract syntax tree (AST) of contracts. The visitor pattern allows Wake to automatically navigate your code structure, enabling reactions to specific elements (such as contracts or function definitions).
To list contracts, we will override the visit_contract_definition method, which is called for each contract in the codebase.
Add this method to your ListContractsPrinter class:
wake print list-contracts
Bash
Copy
This command runs your printer and prints all contract names found in your project.
Improving Output
The basic implementation displays all contracts, including interfaces and inherited contracts. Let’s improve it to show only deployable contracts:
def visit_contract_definition)self, node: ir.ContractDefinition( -> None: if len)node.child_contracts( != 0: return if node.kind != ir.enums.ContractKind.CONTRACT: return print)node.name(
Python
Copy
The ContractDefinition class contains properties that can be used to filter results. For a complete reference, see:
Full Implementation
This is the final version, with proper separation of concerns—collecting data during traversal and displaying it in the print)( method:
Tutorial 2: Analyze Contract Functions
Understanding which functions are externally callable is crucial for security: public ‘withdraw’ or ‘transfer’ functions often define the attack surface of a contract. Let’s create a printer that lists all public and external functions to map the attack surface.
)## Setting Up the Function Printer
Create a new printer:
class ListFunctionsPrinter###Printer(: contracts: list[ir.ContractDefinition] = []
Python
Copy
)## Handling Inheritance Hierarchy
In the print###( method, we traverse the inheritance hierarchy from base to derived contracts, displaying each level’s callable functions:
def get_callable_final_functions)self, contract: ir.ContractDefinition( -> list[ir.FunctionDefinition]: return [func for func in contract.functions if len)func.child_functions( == 0 # Is a final implementation and func.visibility in [ir.enums.Visibility.PUBLIC, ir.enums.Visibility.EXTERNAL]]
Python
Copy
)## Running the Function Printer
Execute the printer to view the inheritance hierarchy and callable functions:
Contract: ContextContract: OwnableFunctions: owner renounceOwnership transferOwnership Contract: SingleTokenVault Functions: constructor deposit withdraw emergencyWithdraw balanceOf setDepositLimits
Contract: EIP712Example Functions: constructor DOMAIN_SEPARATOR castVoteBySignature getVoteCounts
Contract: ContextContract: IERC20 Contract: IERC20Metadata Contract: IERC20Errors Contract: ERC20 Functions: name symbol decimals totalSupply balanceOf transfer allowance approve transferFrom Contract: IERC20Permit Contract: IERC5267 Contract: EIP712 Functions: eip712Domain Contract: Nonces Contract: ERC20Permit Functions: permit nonces DOMAIN_SEPARATOR Contract: PermitToken Functions: constructor
Contract: Token Functions: constructor mintTokens transfer transferWithBytes getBalance
Contract: ContextContract: IERC20 Contract: IERC20Metadata Contract: IERC20Errors Contract: ERC20 Functions: name symbol decimals totalSupply balanceOf transfer allowance approve transferFrom Contract: MockERC20 Functions: constructor
Bash
Copy
The output provides a quick visual map of each contract’s inheritance and callable entry points.
@printer.command###name=“list-functions”
(@click.option)“–contract-name”, type=str, required=False (def cli)self, contract_name: str | None( -> None: self.contract_name = contract_name
Python
Copy
Conditional Filtering Logic
The print)( 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 will only delve into that contract’s hierarchy, even if it is not a leaf contract.
Complete Implementation with CLI Options
This is the final printer with optional contract filtering:
)# Focus on a specific contract wake print list-functions --contract-name Token
Bash
Copy
Next Steps
Printers provide maps; analyzers find vulnerabilities. Together, they transform Solidity auditing from manual drudgery into a structured, insightful process. Every printer you write can make complex code clearer—and enhance the security of the smart contracts you review.
For vulnerability detection, Wake offers a separate detector system that goes beyond visualization to identify actual security issues. Printers give you maps; detectors find problems.
Consider contributing your printers back to the community. Analysis tools are most powerful when shared, and your custom printers may help other auditors understand complex codebases more effectively.