Security·9 min का पठन·Solingo द्वारा

Solidity 0.8 से पहले और बाद में Integer Overflow — क्या बदला

Integer overflow bugs की history, SafeMath era, Solidity 0.8 के built-in checks, और unchecked blocks का सही use।

# Solidity 0.8 से पहले और बाद में Integer Overflow — क्या बदला

Integer overflow DeFi की history में सबसे devastating bugs में से एक रहा है। 2018 में BeautyChain token hack से लेकर 2020 के Balancer vulnerability तक, इस bug ने millions डॉलर गंवाए हैं। Solidity 0.8.0 ने इसे fundamentally कैसे बदला, यह समझना हर smart contract developer के लिए critical है।

Problem की History

Classic Overflow Example (Pre-0.8)

// Solidity <0.8.0

contract VulnerableToken {

mapping(address => uint256) public balances;

function transfer(address to, uint256 amount) public {

// ❌ यह overflow हो सकता है!

balances[msg.sender] -= amount;

balances[to] += amount;

}

}

// Attack scenario:

// 1. Attacker balance: 100 tokens

// 2. Transfer 101 tokens

// 3. 100 - 101 = 2^256 - 1 (OVERFLOW!)

// 4. Attacker now has: 115792089237316195423570985008687907853269984665640564039457584007913129639935 tokens

Real-World Disaster: BeautyChain (2018)

BEC token में यह vulnerability था:

function batchTransfer(address[] memory receivers, uint256 value) public {

uint256 amount = receivers.length * value;

// ❌ Overflow check नहीं था!

require(balances[msg.sender] >= amount);

balances[msg.sender] -= amount;

for (uint256 i = 0; i < receivers.length; i++) {

balances[receivers[i]] += value;

}

}

// Attack:

// receivers.length = 2

// value = 2^255

// amount = 2 * 2^255 = 2^256 = 0 (OVERFLOW!)

// require(balance >= 0) ✅ passes

// Attacker mints unlimited tokens

$900 million market cap evaporate हो गया overnight।

The SafeMath Era

OpenZeppelin का SafeMath library temporary solution बना:

library SafeMath {

function add(uint256 a, uint256 b) internal pure returns (uint256) {

uint256 c = a + b;

require(c >= a, "SafeMath: addition overflow");

return c;

}

function sub(uint256 a, uint256 b) internal pure returns (uint256) {

require(b <= a, "SafeMath: subtraction overflow");

uint256 c = a - b;

return c;

}

function mul(uint256 a, uint256 b) internal pure returns (uint256) {

if (a == 0) return 0;

uint256 c = a * b;

require(c / a == b, "SafeMath: multiplication overflow");

return c;

}

function div(uint256 a, uint256 b) internal pure returns (uint256) {

require(b > 0, "SafeMath: division by zero");

return a / b;

}

}

// Usage:

using SafeMath for uint256;

function transfer(address to, uint256 amount) public {

balances[msg.sender] = balances[msg.sender].sub(amount);

balances[to] = balances[to].add(amount);

}

SafeMath की Problems

1. Gas cost:

हर operation में extra check = extra gas

2. Verbose syntax:

// Natural math:

uint256 result = (a + b) * c - d;

// SafeMath version:

uint256 result = a.add(b).mul(c).sub(d);

3. Easy to forget:

// ❌ Developer भूल गया SafeMath use करना

uint256 newBalance = balance + amount;

Solidity 0.8.0: Game Changer

January 2021 में Solidity 0.8.0 ने built-in overflow/underflow checks introduce किए।

Automatic Checks

// Solidity >=0.8.0

contract SafeToken {

mapping(address => uint256) public balances;

function transfer(address to, uint256 amount) public {

// ✅ Automatically reverts on underflow!

balances[msg.sender] -= amount;

balances[to] += amount;

}

}

// Attack attempt:

// balance = 100, amount = 101

// 100 - 101 => REVERTS with "Arithmetic operation underflowed or overflowed"

How It Works

Compiler automatically यह bytecode inject करता है:

// Source code:

uint256 c = a + b;

// Generated bytecode equivalent:

uint256 c = a + b;

if (c < a) revert("overflow");

Supported Operations

| Operation | Check |

|-----------|-------|

| a + b | overflow |

| a - b | underflow |

| a * b | overflow |

| ++a, a++ | overflow |

| --a, a-- | underflow |

| -a (unary) | overflow (int only) |

Not checked:

  • Bitwise operations (&, |, ^, <<, >>)
  • Division by zero (separate revert)

The unchecked Block

कभी-कभी आप deliberately overflow चाहते हैं, या gas save करना चाहते हैं जब overflow impossible हो:

Syntax

unchecked {

// operations here can overflow without reverting

uint256 c = a + b;

}

Use Case 1: Counter Loops

function sumArray(uint256[] memory arr) public pure returns (uint256 sum) {

for (uint256 i = 0; i < arr.length; ) {

sum += arr[i];

unchecked {

// i < arr.length, तो i++ overflow नहीं होगा

// Gas saved: ~30-40 per iteration

++i;

}

}

}

Use Case 2: Delta Calculations

function calculateDelta(uint256 a, uint256 b) public pure returns (uint256) {

unchecked {

// जब हम difference चाहते हैं, न कि safe subtraction

return a > b ? a - b : b - a;

}

}

Use Case 3: Hash Calculations

function hashData(uint256 a, uint256 b) public pure returns (bytes32) {

unchecked {

// Hash में overflow acceptable है

uint256 combined = a * 31 + b;

return keccak256(abi.encodePacked(combined));

}

}

❌ Dangerous Unchecked Usage

// ❌ NEVER do this:

function transfer(address to, uint256 amount) public {

unchecked {

balances[msg.sender] -= amount; // VULNERABLE!

balances[to] += amount;

}

}

Gas Cost Comparison

contract GasTest {

uint256 public counter;

// ~43,000 gas (first write)

// ~26,100 gas (subsequent)

function incrementChecked() public {

counter++;

}

// ~42,800 gas (first write)

// ~25,900 gas (subsequent)

function incrementUnchecked() public {

unchecked {

counter++;

}

}

// Savings: ~200 gas per increment

}

Large loops में यह significant हो सकता है।

Migration Strategy: 0.7 → 0.8

Step 1: Remove SafeMath

// Before (0.7):

import "@openzeppelin/contracts/math/SafeMath.sol";

contract Token {

using SafeMath for uint256;

function transfer(address to, uint256 amount) public {

balances[msg.sender] = balances[msg.sender].sub(amount);

balances[to] = balances[to].add(amount);

}

}

// After (0.8):

contract Token {

// SafeMath not needed!

function transfer(address to, uint256 amount) public {

balances[msg.sender] -= amount;

balances[to] += amount;

}

}

Step 2: Add unchecked Where Appropriate

// Optimize loops

for (uint256 i = 0; i < length; ) {

// logic

unchecked { ++i; }

}

Step 3: Test Thoroughly

// Foundry test example

function testOverflowReverts() public {

uint256 max = type(uint256).max;

vm.expectRevert();

max + 1; // should revert in 0.8+

}

function testUnderflowReverts() public {

uint256 zero = 0;

vm.expectRevert();

zero - 1; // should revert in 0.8+

}

Edge Cases और Best Practices

1. Signed Integer Overflow

function testSignedOverflow() public pure {

int256 max = type(int256).max; // 2^255 - 1

// ✅ Reverts

max + 1;

// ✅ Unary negation also checked

int256 min = type(int256).min; // -2^255

-min; // Reverts! (no positive equivalent)

}

2. Type Casting

function downcast(uint256 value) public pure returns (uint128) {

// ❌ यह overflow check नहीं करता!

return uint128(value);

// ✅ Explicit check

require(value <= type(uint128).max, "Overflow");

return uint128(value);

}

3. External Call Results

interface IOracle {

function getPrice() external view returns (uint256);

}

function calculateValue(IOracle oracle, uint256 amount) public view returns (uint256) {

uint256 price = oracle.getPrice();

// ✅ Overflow-safe multiplication

return amount * price;

// अगर price malicious oracle से आ रहा है, built-in checks protect करते हैं

}

Real-World Impact: Post-0.8 Security

Statistics:

  • Pre-0.8 audits: ~15-20% contracts में overflow issues
  • Post-0.8 audits: <2% (mostly unchecked misuse)

Famous fixes:

  • Uniswap V3 (0.7) → V4 (0.8): SafeMath removal saved ~5-10% gas
  • Compound V2 → V3: Cleaner codebase, fewer bugs

Conclusion

Solidity 0.8 का integer overflow protection एक massive security improvement है। यह एक whole class of bugs को practically eliminate करता है।

Key takeaways:

  • Always use Solidity ≥0.8.0 नए projects के लिए
  • SafeMath obsolete है (0.8+ में)
  • unchecked sparingly use करें — सिर्फ जहाँ safe और necessary हो
  • Downcasting still dangerous है — explicit checks add करें
  • Gas savings unchecked से minimal हैं, safety sacrifice न करें unless tight loops में
  • Integer overflow का era खत्म हो गया है — लेकिन vigilance still जरूरी है। Tools evolve करते हैं, लेकिन developer responsibility constant रहती है।

    Practice में लगाने के लिए तैयार हैं?

    Solingo पर interactive exercises के साथ इन concepts को apply करें।

    मुफ्त में शुरू करें