Wake Framework: Modern Tooling for Smart Contract Development - Michal Převrátil
EthPrague 2025 · May 2025 · 23:23
Michal Převrátil introduces Wake, a Python-based framework for developing, testing, and analyzing Solidity smart contracts, covering how it fits into a modern Ethereum development workflow. He walks through the static analysis and testing features that distinguish Wake from earlier smart contract tooling.
Speakers
Topics
From EthPrague 2025, hosted by ETHPrague.
Transcript
[Music] Uh thank you very much. No problem. No problem at all. All right. So thank you for coming to this presentation. Uh my name is Mikall Microvatil. I'm the head of tooling at Ekki blockchain and we are smart contract auditors and so we have a pleasure to work with many exciting projects like for example a lido or safe. We've also received several grants from Ethereum Foundation, Coinbase, Optimism, and actually the the last ones from Coinbase and Optimism allowed us to extend the capabilities of Vake and add some new interesting features. As I said, we are smart
contract auditors, but we also love open source and building open source and V is one example of that. Another one is Trident which is a leading tool for Solana fuzzing and except for open source we are also on boarding new developers to the ecosystem for example through curses on the Czech technical university here in Prague. All right but let's start with V. So what's really is a framework a framework for testing. So it allows to write uh simple unit tests just testing a single use case or for example to do integration testing through through f uh
through forking. So basically it's possible to fork any chain existing into local testing environment and just use all the contracts from from the external chain from the fork chain in your local testing environment and test all the integration with your contracts you are developing. And finally, it's also a framework for fuzzing. I will get to fuzzing later on, so I will skip it for now. But it's a special testing technique. It's also a static analysis framework, and so it allows to detect some common issues or vulnerabilities in your code through detectors.
There are also printers that are somehow the same as detectors except that they don't detect some issues but they just print some interesting information about the code in your codebase. And finally, it's also a language server, which is kind of harder to explain, but what it means basically, there's an extension. It's called Solidity Wake, which is powered by a wake and it basically gives the the users all the nice features like code highlighting, go to definition, it shows the references of variables above each variable and so on. There's a lot of features. I will also speak about it later on. So I'm going to skip it for
now. All right, let's move on hopefully. Yeah, so let's compare vague to other solutions out there. There's quite a lot of tools in this image and I will try to very briefly describe like the differences and the tools what they are about and so on. So first of all, we've got Slitter here. And well it's uh mainly a static analysis tool. So it only has uh detectors for finding some common issues in the codebase. It's still maintained.
It's written in Python and well yeah this basically the only use case to find some interesting detections. Then there's also a framework in Python again which is maintained and well it's mainly for testing. We've also got brownie here which unfortunately is no longer maintained but it was used or it can be still used for testing. It also has support for fuzzing but only through hypothesis plug-in like an external plug-in and it's not built in into the framework directly. There's also hardhead which is not in Python but in Typescript. It's still maintained used for testing and it also has its own language server and also an extension.
Then of course there's foundry written in rust mainly for testing fuzzing and as far as I believe there's also uh support for detectors being added but uh as of today there are no detectors to be run and finally there's wake it's written in Python it's got all of the features combined together still very powerful but well this table is actually outdated because I am pretty excited to announce today that we finally open sourced a new testing part, a new testing core for Wake which is written in Rust and I will speak about it later on but basically wake is no longer just Python tool but a hybrid tool with
Python and Rust combined together to bring new features to the framework while keeping the old ones keeping the same syntax the same use cases and so on but with higher speed. So yeah, let's move on. Uh wake helps us to find many interesting issues. There's a sample of findings we have found thanks to wake in many interesting projects and some of them were f found with single unit test. Some of them were found with fuzzing and some also with static analysis with detectors and I believe actually a few of them also were found with both static and even dynamic analysis. So one could choose the the the preferred
method. All right. So let's now focus on deployment and testing. I've got a very simple example here of a deployment script. So this is how a very simple deployment script may look like in wake. First first of all we deploy uh we import from v.ep deployment import all the necessary stuff we may need to use. Then there's something called pi types and it's basically typing stops for contracts in your codebase but exported to python. So each time you are interacting with your contracts in python you have the typing stops. So you have the autocomp completions and also typing support and let's say there's a
function that in solidity accepts a string. You pass an integer by mistake in Python. It's all highlighted because there's typing support and it's also easy to to fix it and use uh autocomp completions to to write the the code easier and it actually also helps AI agents to fill in the code because it they know Python and they can they can look into the PI types and so it's also easier or uh more more efficient to write uh scripts like these. Uh let's continue with the code snippets. So there's uh some connection to a node. We are assuming we are running a local node. And also with V 5.0 which is coming in the next weeks, there will be
added support for treasure. So it will be possible to deploy contracts using a treasure account. And once we have the account, we can just deploy the contract and set some parameters and it will send a transaction. And once the transaction is mined, we can print the address of the token. and for example also print balance of the account we've used for for the deployment. Now let's compare the deployment script to the testing script. And as you can see it's almost the same and it's intend it was intended to make it the same for deployment and for testing. So it's one single user interface or like API but only because of different import. we may have uh
either a deployment script or a testing script and otherwise I think it's quite the same except in the testing script there's a mint ERC20 function which is like a cheat code that allows us to mint ERC20 tokens for anyone for any ERC20 token and it's only possible because of the of the testing environment. All right. So, uh, sorry, I skipped one slide. Yeah. So, this is what helps with debugging in a way quite a lot. This is a call trace of the transaction. And as you can see, there are all the sub calls with the parameters inside inside the transaction. And this helps quite a lot
to debug some complicated stuff when when debug when testing or maybe even when doing a deployment of a contract. So this is this is something that's offered by Wake. Then there's the animation. So we are running a test in wake. We are running a fast test with some debugging uh option. And once we encounter an issue, an assertion error, we can enter the debugger. We can print the code around the the issue or the line where the issue occurred and we can print also a call trace for any transaction that happened in the past and we can print the local state and even some let's say global state saved in Python or we can
interact with the contracts on chain. We can even send new transactions deploy new contracts from within the debugger. So it's very powerful and it helps a lot when again debugging some complicated stuff. All right. So let's talk about the performance. Now you may think or it's quite well known that Python is not very fast in general. Yeah, let's admit that. So we've taken a UNISO v3 uh repository which already contains a lot of uh unit tests and we've migrated 271 unit tests which were originally written in
hardhead so in Typescript and we have reimplemented the test those tests in ape brownie and wake we've also used like different testing backends or how you want to call it basically deployment uh development chain paints uh which are anvil, hardhead and ganache. Ganache is unfortunately discontinued but we still kept it for the reference and we've run all the 271 tests and these are the results how long it took to run all the tests and as you can see it's not only Python but it's how you use Python. So V was quite fast especially with anvil. It also also shows that
hardhead was quite optimized for hard head node obviously but well Python still can be quite fast but that isn't enough in my opinion because well there's hard head missing uh sorry not hardhead but founder missing for example so we've performed another benchmark but before I get to the benchmark I want to introduce fuzzing finally but first maybe one question how How many of you have ever heard about fuzzing? Please raise your hand. Good. That's very good. Okay.
Thank you. So just a brief introduction to fuzzing. It's basically about creating random sequences of functions being called or let's say random sequences of transactions also using random inputs, random data inside the transactions for for the function calls. And so uh since we are dealing with random data, it's very important to have the the execution speed as high as possible. So with the higher execution speed, we can test as many test cases as possible because it's random data. So the the more test cases we we are testing, the higher the probability we will find an interesting issue. And I've got here an example of a very simple unit test where
we are basically transferring 1,000 tokens from Lisb. And there's also assertion that the that Bob actually received those tokens. This is a simple unit test. And if I had to rewrite it into fastest then it would look like this. And what's the difference here? We have a sender which is a random account also the recipient for for the tokens which is again random account. And there's also the amount which is random integer in between let's say zero and 10,000.
And otherwise it's quite the same except we don't set specific numbers and parameters. Instead we use just random data. And what's the difference here? It's quite significant in this case because because of of the random data we may actually have a collision in between the sender and the recipient. And so we can test how will the contract behave if we send some tokens from account to the same account. how it will behave. Will it will it revert or will will it continue? Well, it depends. It depends on the implementation. But this was naturally tested because of fuzzing. And in the unit test, we would need to like
write a very specific unit test just for this case. Also what's being tested in the fastest in the fastest flow is what if we try to transfer zero tokens because there's also zero in that amount that may be generated but it's not it's not being tested in the unit test because it's a unit test and there's 1,000 but in in fuzzing there's a range uh in between zero and 10,000 and so we may also generate zero and so test how it will behave when transferring zero amount of tokens.
And finally I can show you the second benchmark we've performed which mainly focuses about on fuzzing hopefully. Yeah. So let's compare some well-known fuzzers and we we didn't implement our own fuzzing benchmark but instead we've taken dialoo benchmark which is originally implemented I believe by consensus and well basically it's uh five mazes which you can imagine as a grid of 7* 7 and for each maze which means for each grid we've run a test or a fast test for 20
minutes on one uh uh CPU on one processor core and we've compared the results and I hope I can show you how a maze looks like also what's important and I will wait for the image. Yeah. So this is the maze as I said it's 7* 7 and always we are at one at one cell in in the maze and we can move in four directions. We can move up, we can move down, we can move left and we can move left or right. And each time we are moving, we also need to set eight UN 64 parameters. And depending on these parameters, we may either find a bug or not. So the finding a bug depends not
only on the location inside the grid, but also on the parameters that we are passing when moving to to a specific cell. So yeah, there are also walls sometimes and we cannot walk over the walls obviously. So this is how a function that moves us to a different cell looks like. As I said, there's eight UN 64 parameters. And what's this benchmark is all about? We need to find or we want to find as many bugs as possible within those 20 minutes on one uh computer core. All right, this is an example how a bug in the in the in the benchmark
looks like. So the first if is about the location about XY coordinates inside the grid and the rest of the if conditions is for the parameters we are always passing when moving in a certain direction. Some of those uh conditions are quite easy to accomplish to satisfy. Let's see. Let's say for example P4 is less or equal than P6. But some are quite well uneasy to to satisfy. For example, P3 must be less or equal than P3 and multiplied by P 0.
This doesn't give us a lot of space. Basically, we need to supply zero or one in this case. Uh I can also say uh that this specific bug was uh detected by wake. We were able to to detect this one within the 20 minutes. And let's finally see the results. So as I said there's five different mazes and we have the most popular uh fuzzing frameworks for solid smart contracts here. So there's Akidna, there's also Foundry, there's original V just in Python but with the anvil as as as the back end for EVM execution and as I said uh we have open sourced a new addition to to V. It's a
testing core testing framework written in Rust and we call it V RS and so you can also see the differences otherwise what's also important to say the numbers here are how many issues we have found within those 20 minutes and yeah so as you can see it it's well it depends on how you use Python and if you use let's say something more powerful for execution in the background And well what made the difference here is just the execution speed. Otherwise as you compare V plus Anvil and V RS they use the completely same algorithm for generating random numbers and the only
difference was the execution speed. I've also added here uh how how fast we were able to submit or send transactions. So with V plus Anvil, we've achieved around 145 transactions per second. While with Wake RS, which means wake plus the Rust execution engine, we've achieved almost 9,000 transactions per second. That's almost 60x speed up. That's quite a lot. And uh as you can even see on the results, it it helped us to basically make uh wake RS or V plus the Rust part the the most efficient framework for fuzzing in terms of block blackbox fuzzing and also of this of this
benchmark. All right. And finally, let's talk also about the extension and and the language server. As I said, we have our own VS Code extension that's called Solidity Wake. And yeah, this is the extension how it looks like on the marketplace. If you want, you can scan the QR code and it's just the extension for for VS code. There's many features like well code highlighting, go to definition. There's number of references for each variable and control flow graphs, inheritance graph and so on. I will also show show you some of the interesting features later on. So I will leave you let you to scan the QR code and yeah I think we can continue.
So this is an example as I said it's V is also static canal framework and so it can detect some issues in the codebase. There's an example of reffency and obviously there's a command line interface to to run only detectors and see some results but we have also integration into VS code with with the extension and so we can show the detections right inside the code as typing the code new detections may appear. So this is an example of reentrancing. So there's possibly a re-entrant uh external call and this is how the detection looks like inside VS code.
As I said, there's also some interesting graphs that can be exported. So there's an example of inheritance graph. And basically when there's the contract interchain token, then some inheritance and there's always a label above the contract name. And when clicking on the label, we can basically see this exact graph inside VS code. It's it's rendered. It's shown inside VS code. And also all the all the nodes all the base contracts in the graph are clickable. So it's possible to click on them and open to to the the contract itself like the code of the contract itself. And finally this is the newest
addition to the extension. We call it deploy and interact UI. And well it's kind of remix like feel but inside VS code. So we can compile all the contracts within the codebase. Then we can see list of those contracts in a list. We can deploy any of the contract and once we have the contract deployed we can send any transactions or just calls to interact with the contract and once we sign the transaction we can also see the call traces for the transaction. you can see the events uh and so on every basically any parameter of the transaction and yeah so this is how the UI looks like we tried to make it as easy to use as possible and there's also
support for forking so within this UI it's possible to fork any chain any existing chain for example the Ethereum mainet and when forking it's possible to add existing contracts to this UI. So we can just simply copy the address of any contract, insert it into the UI like you see on the screen and then because if the contract has the source code public, so we can fetch the API either from EtherCAN or from sourceify, we can see all the functions that are available for the given contract and we can interact with the contract as if we deployed it ourself except it's it's been forked and
we We don't even know need the source code in our local project. And yeah, I think that's all from my side. This is uh a QR code leading to the landing page of V. So if you are interested in V in fuzzing in static analysis or even about the extension and this is the main landing page and there's also a link to GitHub to the documentation and so on. There's also a list of issues that were found thanks to V. So uh be sure to scan the QR code. Uh eat wake is the name of the Python package and we have uh also a telegram group where you can connect ask any questions and we are trying to help newcomers to to wake to the wake
framework. Okay. Thanks a lot. So yeah I think that's all from my side. Thank you.