Inspiration

At Ackee Blockchain, auditors and developers used 3rd party tools, such as Slither, Echidna, or Brownie. Even though we liked these tools, we thought they could be written better and benefit more from a tighter integration of a static analyzer, a fuzzer, and a testing framework in one package. The final step, which was the scope of this hackathon, was to add support for on-chain deployments, so developers won't have to use any other tool, such as abandoned Brownie. This final step extended Woke from a purely testing framework to a testing and development framework.

What it does

It extends Woke framework by adding a deployment module that allows interactions with live chains (testnets and mainnnets). The deployment module can be used to implement integration tests (targeting testnets) and deployment scripts. Deployment scripts are written in Python and follow the same syntax as Woke unit tests. Deployment scripts can be executed from a CLI.

How we built it

We extended and refactored the core of the Woke testing framework. Before the hackathon, it only supported type 0 (legacy) transactions and expected zero gas prices. In the hackathon, we added support for type 1 and 2 transactions, implemented support for access lists (eth_createAccessList), gas estimations (eth_estimateGas), and automatic setting of gas price parameters (gasPrice, maxFeePerGas, maxPriorityFeePerGas).

In the hackathon, we also added woke accounts command-line commands to manage accounts and their private keys. Together with this, we implemented functions to sign:

  • arbitrary (raw) data (EIP-191 type 0x45),
  • structured data (EIP-712),
  • any keccak-256 hash.

Challenges we ran into

Setting the default parameters of transactions can be challenging. Especially the gas (gas limit), gasPrice, maxFeePerGas, and maxPriorityFeePerGas fields.

We ended up with the following settings in the woke.deployment module:

  • gas limit as the value returned from eth_estimateGas or eth_createAccessList multiplied by 1.1 (10% margin),
  • gas price (legacy transactions) as the value returned from eth_gasPrice,
  • max priority fee per gas as the value returned from eth_maxPriorityFeePerGas,
  • max fee per gas as (max priority fee) + (base fee of the pending block) * 1.125 (a base fee can be increased at max by 12.5% between two blocks).

Another huge issue was that Woke returned a function return value from transaction calls. To get the return value of a function executed in a transaction, it is necessary to perform tracing or debugging of the transaction. However, many node providers do not offer trace_transaction or debug_traceTransaction so it is not possible to return a function return value by default. Because of this, we changed the default behavior (even in the testing framework) and transaction requests now return a receipt-like object. Through the receipt-like object, it is possible to access the return value (if possible).

Accomplishments that we're proud of

We now have a swiss-knife for Solidity developers who prefer Python. While implementing the deployment module, we sent dozens of Woke-crafted transactions that were successfully mined (Woke does not use any external library to build transactions).

What we learned

Transactions were never meant to return return data of the function being called. Any important data should be emitted using events (logs) unless the function is read-only and can be executed using eth_call.

What's next for Woke - development and testing framework for Solidity

We plan on adding the new deployment support for our VS Code extension, Tools for Solidity.

Built With

Share this project:

Updates