Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
 Building Full Stack DeFi Applications

You're reading from   Building Full Stack DeFi Applications A practical guide to creating your own decentralized finance projects on blockchain

Arrow left icon
Product type Paperback
Published in Mar 2024
Publisher Packt
ISBN-13 9781837634118
Length 490 pages
Edition 1st Edition
Languages
Concepts
Arrow right icon
Author (1):
Arrow left icon
Samuel Zhou Samuel Zhou
Author Profile Icon Samuel Zhou
Samuel Zhou
Arrow right icon
View More author details
Toc

Table of Contents (21) Chapters Close

Preface 1. Part 1: Introduction to DeFi Application Development FREE CHAPTER
2. Chapter 1: Introduction to DeFi 3. Chapter 2: Getting Started with DeFi Application Development 4. Chapter 3: Interacting with Smart Contracts and DeFi Wallets in the Frontend 5. Part 2: Design and Implementation of a DeFi Application for Trading Cryptos
6. Chapter 4: Introduction to Decentralized Exchanges 7. Chapter 5: Building Crypto-Trading Smart Contracts 8. Chapter 6: Implementing a Liquidity Management Frontend with Web3 9. Chapter 7: Implementing a Token-Swapping Frontend with Web3 10. Chapter 8: Working with Native Tokens 11. Part 3: Building a DeFi Application for Staking and Yield Farming
12. Chapter 9: Building Smart Contracts for Staking and Farming 13. Chapter 10: Implementing a Frontend for Staking and Farming 14. Part 4: Building a Crypto Loan App for Lending and Borrowing
15. Chapter 11: An Introduction to Crypto Loans 16. Chapter 12: Implementing an Asset Pool Smart Contract for a Crypto Loan 17. Chapter 13: Implementing a Price Oracle for Crypto Loans 18. Chapter 14: Implementing the Crypto Loan Frontend with Web3 19. Index 20. Other Books You May Enjoy

Vulnerabilities of DeFi applications

DeFi is one of the innovative technologies that introduced new financial activities for people and potentially changed the existing financial infrastructure. In this section, we will focus on the vulnerabilities that may occur in DeFi applications, especially the applications we are going to build in this book since hackers can leverage the vulnerabilities of smart contracts to exploit the crypto assets from smart contracts and users' wallets. Figure 1.8 shows that the total value hacked for DeFi has been around $6 billion since mid-2016:

Figure 1.8 – DefiLlama – DeFi loss by month

Figure 1.8 – DefiLlama – DeFi loss by month

Fortunately, most of the vulnerabilities have solutions. We will discuss various causes of these vulnerabilities and best practices to prevent these issues in this section. Some knowledge of the Solidity programming language will help you understand the code snippets in this section, but it is not required for you to understand the principles.

Reentrancy

Reentrancy is one of the most destructive security attacks in smart contracts written with Solidity. A reentrancy attack occurs when a function makes an external call to another untrusted contract. Then, the untrusted contract makes a recursive call back to the original function in an attempt to drain funds.

For example, an attack smart contract could implement a fallback function that withdraws funds from a vulnerable smart contract. When the attack smart contract receives the fund, the fallback function will be called automatically, which makes recursive calls, at which point it will withdraw the fund again until the fund in the vulnerable smart contract is drained. Figure 1.9 demonstrates the sequence of actions to perform this attack:

Figure 1.9 – The workflow of a reentrancy attack

Figure 1.9 – The workflow of a reentrancy attack

To find the relevant code example and learn more about reentrancy attacks, please go to https://solidity-by-example.org/hacks/re-entrancy/.

To prevent a reentrancy attack, we will use ReentrancyGuard from the OpenZeppelin (https://www.openzeppelin.com/) library when building DeFi applications later in this book.

Self-destruct operation

In the early days of Ethereum, one of the earliest DAO projects lost $3.6 million worth of ETH due to a hack. What’s even worse is that the attack continued for days due to the immutability of the smart contract on the blockchain, so the developer could not add a function to take back the ETH from smart contracts or destroy the smart contract to prevent hacking. In 2016, Ethereum introduced the selfdestruct function to serve as an exit door for smart contracts in case of an attack. Here is an example of how to use the selfdestruct function:

contract SelfdestructExample {
  function killContract(address payable receiver) external {
    selfdestruct(receiver);
  }
}

This code snippet defines a smart contract called SelfdestructExample. A person can call the killContract function to destroy the smart contract, at which point all the ETH held by the smart contract will be transferred to receiver when selfdestruct is called.

The behavior of transferring ETH to a specific address could cause a side effect. Hackers can then use this side effect to forcefully send ETH from a self-destruct smart contract to another smart contract to make it vulnerable.

The example at https://solidity-by-example.org/hacks/self-destruct/ shows the act of forcefully transferring ETH to a smart contract to break the rules of the game. There is a game that only allows players to transfer 1 ETH at a time. The person can win when the balance of the smart contract is equal to or greater than 7 ETH, and the winner can take all the ETHs. Although the game smart contract only allows a player to transfer 1 ETH every time, the attacker broke the rule by forcibly transferring more ETHs to the game smart contract in one transaction with the selfdestruct function.

The solution is using a storage variable in the smart contract to store the balance instead of using address(current_contract).balance. This will be the source of truth for the smart contract to rely on, and the selfdestruct function cannot manipulate the variable.

Gas overflow

All the transactions that need to write data on the blockchain need to pay for gas. For EVM-based blockchains, the gas is precalculated before the transaction and the gas is consumed while executing the bytecode of the smart contract. However, the gas estimation can be temporarily or consistently inaccurate due to the indeterminacy of the Solidity programming language and network traffic. As developers, we need to pay attention to the code that could cause this gas variation and try to optimize the code.

For example, a gaming smart contract may implement a function to reward winners, like so:

function rewardPlayers() external {
  if (isWinner(msg.sender)) {
    safeTransfer(token, msg.sender, winAmount);
    emit Win(msg.sender, winAmount);
  }
}

If isWinner(msg.sender) determines the winner with some randomness at the time of calling it, it would cause differences between the gas estimation and gas actual usage. This means that the gas estimation assumes that the safeTransfer function is not called, so it assigns a small amount of gas to run the transaction. However, at the time of execution, the caller of the function is selected as the winner, and the safeTransfer call in the if statement exceeds the gas limit, which causes a denial-of-service (DoS) attack.

Iterations can also cause gas overflow if the size of the iteration grows over time. Figure 1.10 shows the relationship between gas usage and the number of iterations:

Figure 1.10 – The relationship between gas usage and the number of iterations (benchmarked with Solidity v0.8.3)

Figure 1.10 – The relationship between gas usage and the number of iterations (benchmarked with Solidity v0.8.3)

Based on the data shown in Figure 1.10, gas usage grows exponentially along with the number of iterations. We need to be careful about arrays of a dynamic size and try to reduce this size when possible.

There are many ways to prevent gas overflow. The key thing is optimizing the Solidity code by following good practices. You can refer to https://dev.to/jamiescript/gas-saving-techniques-in-solidity-324c for some techniques for optimizing your Solidity code to save gas usage.

Random number manipulation

Randomness drives people to play against uncertainties. Nowadays, DeFi projects are increasingly introducing lotteries or other forms of randomness to give bonus rewards to their users and attract more users to use their DeFi applications. However, there is no ideal way to generate random numbers within EVM-compatible blockchains. This may cause attackers to manipulate the random number generation and get the number to steal the assets from the reward pool.

If you want to implement a random number generator with the facilities in EVM, you can use code similar to the following:

/*
 * Returns a random number.
 * If the caller of the function gets a random number
 * that can be divided by 10000, then the caller will win.
 */
function getRandomNumber() private view returns (uint256) {
  return uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender)));
}

As you can see, the getRandomNumber function returns a random integer. Inside the function, it concatenates block.timestamp and msg.sender to generate a long byte array, then hashes the bytes with the keccak256 algorithm to generate a pseudo-random number. Here, msg.sender is the caller’s address, and block.timestamp is the timestamp field of a block in its header. Because the timestamp is set by the miner, a hacker can set the block.timestamp function of the next block by being a miner to generate a random number that makes them win.

To get a true random number, we can use an oracle service such as Chainlink VRF. It relies on many nodes being on the network to generate a random number that is secure and almost impossible to manipulate by hackers. However, this random number retrieval requires a request and a callback to fulfill the request. The duration between the request and its fulfillment may take dozens of seconds to more than one minute, and each request may take an amount of LINK tokens plus the gas fee. As a result, it is better for a smart contract that relies on random numbers such as lottery games to wait for a certain period to reveal rewards instead of doing that on the spot (for example, reveal a group of winners daily or weekly).

To learn how to get random numbers with Chainlink oracle, go to https://docs.chain.link/vrf/v2/subscription/examples/get-a-random-number/.

There are many more types of vulnerabilities in DeFi. We will discuss this in more detail when we build DeFi applications later in this book. Now, let’s summarize what we have learned so far.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime