• YouTube Channel
  • System Status
  • VS Code Extension
  • Unsafe Downcast

    Overview

    What is the Unsafe Downcast vulnerability?

    In Solidity, as in many other typed programming languages, a developer may (attempt to) cast between types in their code. Solidity offers many variants of certain builtin types at different bit widths (for example, uint8 vs. uint256), and it is possible to cast between these. However, a narrowing type cast (from a higher to a lower bit width) may inadvertently truncate bits and cause the value after the cast to not be equivalent to that before the cast. This can lead to inadvertent logical errors in smart contract execution.

    Further reading: Solidity Documentation: Explicit Type Conversions

    Technical example of vulnerable code

      // SPDX-License-Identifier: Unlicense
      pragma solidity ^0.8.0;
    
      contract TokenSale {
          uint public basePrice = 1 ether; // Base price of 1 token in wei
          uint constant public PRICE_MULTIPLIER = 25;
    
          address private owner;
    
          constructor() {
              owner = msg.sender;
          }
    
          // Event to emit the price at which tokens are sold
          event TokenSold(address buyer, uint256 amount, uint256 price);
    
          // Function to allow the owner to update the base price
          function updatePrice(uint _newPrice) private {
              require(msg.sender == owner, "Only the owner may update the base price.");
              basePrice = _newPrice;
          }
    
          // Function to sell tokens to a buyer
          function sellTokens(address buyer, uint256 tokenAmount) public {
              // Incorrectly assuming that the price will fit in a uint16
              uint16 priceToCharge = uint16(basePrice * PRICE_MULTIPLIER);
    
              // Logic to proceed with the token sale at the incorrectly calculated price
              emit TokenSold(buyer, tokenAmount, priceToCharge);
    
              // Further logic for transferring tokens and accepting payment would go here
          }
      }
    
    

    In the example above, contract TokenSale (partially) implements a function sellTokens() designed to sell tokens to a user at a calculated price based on the current value of basePrice (set by the contract owner) and the fixed PRICE_MULTIPLIER value. However, this value (possibly for efficiency reasons) is stored in a uint16 variable, which the developer perhaps inadvertently failed to realize would not contain the value of an ether unit specified in the original basePrice variable (which is 1e18 as an integer). This will cause the price to be calculated incorrectly, subverting the intended logic of the contract.

    Technical example of how to fix the vulnerability

      // SPDX-License-Identifier: Unlicense
      pragma solidity ^0.8.0;
    
      contract TokenSaleUpdated {
          uint public basePrice = 1 ether; // Base price of 1 token in wei
          uint constant public PRICE_MULTIPLIER = 25;
    
          address private owner;
    
          constructor() {
              owner = msg.sender;
          }
    
          // Event to emit the price at which tokens are sold
          event TokenSold(address buyer, uint256 amount, uint256 price);
    
          // Function to allow the owner to update the base price
          function updatePrice(uint _newPrice) private {
              require(msg.sender == owner, "Only the owner may update the base price.");
              basePrice = _newPrice;
          }
    
          // Function to sell tokens to a buyer
          function sellTokens(address buyer, uint256 tokenAmount) public {
              uint256 priceToCharge = basePrice * PRICE_MULTIPLIER;
    
              emit TokenSold(buyer, tokenAmount, priceToCharge);
    
              // Further logic for transferring tokens and accepting payment would go here
          }
      }
    
    

    In the revised example above, contract TokenSaleUpdated uses a uint256 variable to capture the price to charge, rather than downcasting the product of wider unsigned integers.