• YouTube Channel
  • System Status
  • VS Code Extension
  • uint to int Conversion

    Overview

    What is the uint to int Conversion vulnerability?

    Solidity supports both signed and unsigned integers at various bit widths, for example int32 or uint128. When casting from an unsigned to a signed type, even at the same bit width, there is a possibility that large values will silently overflow due to bit truncation, as signed types require a sign bit. For this reason, such casts should either be checked or avoided if program logic permits.

    Further reading: Solidity Documentation: Integers

    Technical example of vulnerable code

      // SPDX-License-Identifier: Unlicense
      pragma solidity ^0.8.0;
    
      contract TokenManager {
          // Mapping of user addresses to their token balances
          mapping(address => uint256) public balances;
    
          address owner;
    
          constructor() {
              owner = msg.sender;
          }
    
          // Event to log balance adjustments
          event BalanceAdjusted(address user, int256 adjustment);
    
          // Function to add tokens to a user's balance
          function addTokens(address user, uint256 amount) public {
              balances[user] += amount;
          }
    
          // Function to adjust a user's balance. Adjustments can be positive or negative.
          function adjustBalance(address user, int256 adjustment) public {
              require(msg.sender == owner, "Only the owner may make balance adjustments.");
    
              balances[user] = uint256(int256(balances[user]) + adjustment);
    
              emit BalanceAdjusted(user, adjustment);
          }
    
          // Additional functionality for token management omitted for brevity
      }
    
    

    In the above example, contract TokenManager represents a portion of a DeFi system that can manage user tokens, e.g. ERC-20 tokens. It provides a function, adjustBalance(), that only the contract owner can use to manually adjust token balances (for example, if something were recorded erroneously elsewhere in the system). In order to perform the adjustment with a signed integer, the user balance is cast from an unsigned integer, which could possibly overflow silently.

    Technical example of how to fix the vulnerability

      // SPDX-License-Identifier: Unlicense
      pragma solidity ^0.8.0;
    
      contract TokenManagerUpdated {
          // Mapping of user addresses to their token balances
          mapping(address => uint256) public balances;
    
          address owner;
    
          constructor() {
              owner = msg.sender;
          }
    
          // Event to log balance adjustments
          event BalanceAdjusted(address user, uint256 adjustment, bool negative);
    
          // Function to add tokens to a user's balance
          function addTokens(address user, uint256 amount) public {
              balances[user] += amount;
          }
    
          // Function to adjust a user's balance. Adjustments can be positive or negative.
          function adjustBalance(address user, uint256 adjustment, bool negative) public {
              require(msg.sender == owner, "Only the owner may make balance adjustments.");
    
              if (negative) {
                  balances[user] -= adjustment;
              } else {
                  balances[user] += adjustment;
              }
    
              emit BalanceAdjusted(user, adjustment, negative);
          }
    
          // Additional functionality for token management omitted for brevity
      }
    
    

    In the revised example above, contract TokenManagerUpdated has reworked the logic of the adjustBalance() function to take an extra parameter indicating if the adjustment should be negative; thus, it can use only unsigned integers and avoid the cast in the previous example.