Denial of Service

Branch Info

Author: Temirzhan Yussupov Source code: https://github.com/scaffold-eth/scaffold-eth-examples/tree/denial-of-service-example Intended audience: Intermediate Topics: Scaffold-eth basics, Smart Contracts

🏃‍♀️ Quick Start

Make contract unusable by exploiting push external calls 😈
Table of Contents

About The Project

This little side quest will allow you to explore the concept of "Denial of Service".
One exploit we introduce here is denial of service by making the function to send Ether fail.

Getting Started


You should be familiar with calls and reverts.


Let's start our environment for tinkering and exploring how "DoS attack" works.
  1. 1.
    Clone the repo first
git clone -b https://github.com/scaffold-eth/scaffold-eth-examples.git denial-of-service-example
cd denial-of-service-example
  1. 1.
    Install dependencies
yarn install
  1. 1.
    Start your React frontend
yarn start
  1. 1.
    Spin up your local blockchain using Hardhat
yarn chain
  1. 1.
    Deploy your smart contracts to a local blockchain
yarn deploy
Pro Tip: Use tmux to easily start all commands in a single terminal window!
This is how it looks like in my terminal:
If everything worked fine, you have to have something like this opened in your browser:

Smart contracts

Let's navigate to packages/hardhat/contracts folder and check out what contracts we have there.


This smart contract will become unusable once we exploit it.
The logic is pretty straightforward:
function bid() payable external {
require(msg.value >= highestBid);
if (highestBidder != address(0)) {
(bool success, ) = highestBidder.call.value(highestBid)("");
highestBidder = msg.sender;
highestBid = msg.value;
Smart contract immitates auction by keeping track of the highest bid made. If you want to become a highestBidder you have to send ETH greater than the previous highestBid.
Try to find a way to exploit this contract (make it unusable) before reading further.


Our contract for exploitation.
Note that this block of code is commented in the contract.
function () external payable {
Try to guess why :)

Attack vector

The attack we are going to do is called DoS with (Unexpected) revert. So how does it work?
Basically, If attacker bids using a smart contract which has a fallback function that reverts any payment, the attacker can win any auction.
When it tries to refund the old leader, it reverts if the refund fails. This means that a malicious bidder can become the leader while making sure that any refunds to their address will always fail. In this way, they can prevent anyone else from calling the bid() function, and stay the leader forever.
This is why part with fallback() was commented in our Attack.sol.


Let's use our awesome frontend provided by scaffold-eth to make sure our assumption works fine.
Run two different sessions. One will be for a simple user and one will be for an evil hacker.
This is how it looks like for me:
My first tab is for a simple user and second tab is for a hacker.
Let's make an initial bid and become a highestBidder as a simple user.
Now let's run our attack method as an attacker and disable our VulnerableAuction forever!
Seems we became a new highestBidder.
Now simple user can not become a new highestBidder even though he puts more ETH that we did.


In order to mitigate this attack, we have to favor pull over push for external calls.
This is demonstrated nicely in GoodAuction.sol. Note how we added a new method withdrawRefund. Now we do not depend on any push external calls like sending money back to someone.
function withdrawRefund() external {
uint refund = refunds[msg.sender];
refunds[msg.sender] = 0;
(bool success, ) = msg.sender.call.value(refund)("");
Using this we are no longer vulnerable!

Additonal resources


Join the telegram support chat 💬 to ask questions and find others building with 🏗 scaffold-eth!