The project implements a new detector into the Woke tool (https://github.com/Ackee-Blockchain/woke). The detector can automatically detect and report a case when a contract can receive Ether but cannot send it to anyone. It also detects cases when a contract implements a function to send Ether but does not have any payable function to receive it.
The detector can be run using the
woke detect
command in a Solidity project root directory. It is possible to run only this detector with awoke.toml
config file (located in the project root dir):[detectors] only = ["locked-ether"]
There is a VS Code extension named
Tools for Solidity
(https://marketplace.visualstudio.com/items?itemName=AckeeBlockchain.tools-for-solidity) that uses Woke in the background and automatically runs detectors on every change to the code. Because of this, the detector implemented in this project can also be used by VS Code users and warn Solidity developers using theTools for Solidity
extension of locked Ether issues.The detected "locked-ether" issues can save Ether that might be otherwise locked in a contract and lost forever (because of a missing function to withdraw it). Issues, when a contract can send Ether but cannot receive it, may also point to an implementation logic error or a bad practice.
When implementing the detector, we discovered certain scenarios when the detector could report false-positive detections (the issue actually is not present) or may fail to find a "locked-ether" issue.
A contract has a payable function, but it always reverts
When a contract has payable functions, but all of them always revert the transaction, it cannot effectively receive Ether, and the issue should not be reported. We used control flow graphs generated by the Woke tool and checked every execution path of every payable function. When we found a revert statement in every execution path of a payable function, we ignored that function.
A contract inherits a function that can send Ether from a base contract but overrides that function
When a contract inherits a function sending Ether but overrides it, it cannot effectively send Ether using that function (unless the new implementation also allows sending Ether). Because of this, we first collected functions that receive/send Ether and then assigned them to contracts. When assigning to contracts, we looked at a contract that originally implemented the function and then discovered all contracts inheriting from this contract. When discovering contracts, we ensured that the function was not overridden by the given contract.
Abstract contracts, libraries, and interfaces can only receive / only send Ether
We excluded such contracts, libraries, and interfaces from the detection. We also ignored payable functions that do not have implementation.
We also identified some issues that could not be resolved in the limited hackathon time.
Discussion