Skip to content
Published on

Blockchain & Web3 Engineer Guide: Smart Contracts, DeFi, ZK Proofs, and AI+Blockchain

Authors

1. Blockchain Fundamentals: Hashes, Merkle Trees, and Consensus

1.1 Hash Functions and Immutability

The foundation of blockchain is the cryptographic hash function. Each block includes the hash of the previous block, forming a chain.

import hashlib
import json

def compute_block_hash(index, previous_hash, timestamp, data, nonce):
    block_content = json.dumps({
        "index": index,
        "previous_hash": previous_hash,
        "timestamp": timestamp,
        "data": data,
        "nonce": nonce
    }, sort_keys=True)
    return hashlib.sha256(block_content.encode()).hexdigest()

# SHA-256 properties: deterministic, one-way, avalanche effect
hash1 = hashlib.sha256(b"hello").hexdigest()
hash2 = hashlib.sha256(b"hellO").hexdigest()
# A single character difference produces a completely different hash
print(hash1[:16], "vs", hash2[:16])

1.2 Merkle Tree

A Merkle tree verifies the integrity of large datasets with O(log n) proofs. Both Bitcoin and Ethereum use it for transaction verification.

def build_merkle_tree(leaves):
    if not leaves:
        return None
    layer = [hashlib.sha256(leaf.encode()).hexdigest() for leaf in leaves]
    while len(layer) > 1:
        if len(layer) % 2 != 0:
            layer.append(layer[-1])  # duplicate last if odd
        layer = [
            hashlib.sha256((layer[i] + layer[i+1]).encode()).hexdigest()
            for i in range(0, len(layer), 2)
        ]
    return layer[0]  # Merkle Root

txs = ["tx_alice_to_bob", "tx_carol_to_dave", "tx_eve_to_frank", "tx_grace_to_heidi"]
root = build_merkle_tree(txs)
print("Merkle Root:", root)

1.3 Consensus Algorithm Comparison

AlgorithmEnergyThroughputDecentralizationRepresentative Chain
PoWVery High~7 TPSHighBitcoin
PoSLow~Thousands TPSMediumEthereum
DPoSLow~Tens of Thousands TPSLowEOS, TRON
PBFTLow~Thousands TPSLowHyperledger

PoS Slashing: In Ethereum PoS, if a validator double-signs (double votes), a portion of their staked ETH is burned. This economic penalty is the cornerstone of security.


2. Ethereum Internals: EVM, Gas, and State Tree

2.1 EVM (Ethereum Virtual Machine)

The EVM is a 256-bit stack-based virtual machine — a deterministic environment that executes identically on every Ethereum node.

EVM Stack Operation Example (PUSH1, ADD, MUL):
PUSH1 0x03Stack: [3]
PUSH1 0x04Stack: [3, 4]
ADDStack: [7]
PUSH1 0x02Stack: [7, 2]
MULStack: [14]

Key EVM storage areas:

  • Stack: Up to 1024 elements, temporary operations
  • Memory: Byte array, temporary use during function execution
  • Storage: Key-value persistent store (most expensive, SSTORE = 20,000 gas)
  • Calldata: Input data for external function calls

2.2 EIP-1559 Gas Mechanism

Before EIP-1559, gas was auctioned. After the upgrade, the base fee is burned and only the tip (priority fee) goes to validators.

# EIP-1559 transaction cost calculation
base_fee = 15  # gwei (auto-adjusted based on network state)
priority_fee = 2  # gwei (tip)
max_fee = 20  # gwei (maximum allowed)

actual_fee = min(base_fee + priority_fee, max_fee)
gas_used = 21000  # simple ETH transfer
total_cost_gwei = actual_fee * gas_used
total_cost_eth = total_cost_gwei / 1e9
print(f"Transaction fee: {total_cost_eth:.6f} ETH")
# base_fee burned → ETH deflationary effect
burned = base_fee * gas_used / 1e9
print(f"Burned ETH: {burned:.6f} ETH")

2.3 The Merge: PoW to PoS

On September 15, 2022, Ethereum reduced energy consumption by approximately 99.95% through The Merge.

  • Beacon Chain: The PoS chain that ran in parallel since December 2020
  • Execution Layer: Handles existing EVM state and transaction processing
  • Consensus Layer: Manages validators, attestations, and finality
  • Minimum Staking: 32 ETH per validator

3. Smart Contracts: Solidity Patterns and Security

3.1 ERC-20 Token with ReentrancyGuard

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract SecureToken is ERC20, ReentrancyGuard, Ownable {
    uint256 public constant MAX_SUPPLY = 1_000_000 * 10**18;
    mapping(address => uint256) public lockTime;

    event TokensLocked(address indexed user, uint256 amount, uint256 unlockTime);
    event TokensUnlocked(address indexed user, uint256 amount);

    constructor() ERC20("SecureToken", "STKN") Ownable(msg.sender) {
        _mint(msg.sender, 100_000 * 10**18);
    }

    // Applies the Checks-Effects-Interactions pattern
    function lockAndRelease(uint256 amount, uint256 duration)
        external
        nonReentrant  // reentrancy defense
    {
        // 1. Checks: validate conditions
        require(amount > 0, "Amount must be positive");
        require(balanceOf(msg.sender) >= amount, "Insufficient balance");
        require(duration >= 1 days && duration <= 365 days, "Invalid duration");

        // 2. Effects: state changes (before external calls)
        lockTime[msg.sender] = block.timestamp + duration;
        _transfer(msg.sender, address(this), amount);

        // 3. Interactions: external calls (after state changes)
        emit TokensLocked(msg.sender, amount, lockTime[msg.sender]);
    }

    function unlock() external nonReentrant {
        // Checks
        require(block.timestamp >= lockTime[msg.sender], "Still locked");
        uint256 contractBalance = balanceOf(address(this));
        require(contractBalance > 0, "Nothing to unlock");

        // Effects
        lockTime[msg.sender] = 0;

        // Interactions
        _transfer(address(this), msg.sender, contractBalance);
        emit TokensUnlocked(msg.sender, contractBalance);
    }

    function mint(address to, uint256 amount) external onlyOwner {
        require(totalSupply() + amount <= MAX_SUPPLY, "Exceeds max supply");
        _mint(to, amount);
    }
}

3.2 How Reentrancy Attacks Work

This pattern was exploited in The DAO hack (2016, 3.6 million ETH stolen).

// Vulnerable contract (never use this)
contract VulnerableBank {
    mapping(address => uint256) public balances;

    function withdraw() external {
        uint256 amount = balances[msg.sender];
        // External call before state change → reentrancy possible!
        (bool success,) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
        balances[msg.sender] = 0;  // too late
    }
}

// Attacker contract
contract Attacker {
    VulnerableBank public target;

    receive() external payable {
        if (address(target).balance >= 1 ether) {
            target.withdraw();  // recursive call to drain repeatedly
        }
    }
}

3.3 Hardhat Tests

const { expect } = require('chai')
const { ethers } = require('hardhat')

describe('SecureToken', function () {
  let token, owner, alice, bob

  beforeEach(async function () {
    ;[owner, alice, bob] = await ethers.getSigners()
    const SecureToken = await ethers.getContractFactory('SecureToken')
    token = await SecureToken.deploy()
    await token.waitForDeployment()
  })

  it('should defend against reentrancy attacks', async function () {
    // Transfer tokens to alice
    await token.transfer(alice.address, ethers.parseEther('1000'))
    expect(await token.balanceOf(alice.address)).to.equal(ethers.parseEther('1000'))
  })

  it('should not allow unlock before lock period ends', async function () {
    await token.transfer(alice.address, ethers.parseEther('100'))
    await token.connect(alice).lockAndRelease(
      ethers.parseEther('100'),
      86400 // 1 day
    )
    await expect(token.connect(alice).unlock()).to.be.revertedWith('Still locked')
  })

  it('Foundry-style fuzz test simulation', async function () {
    const amounts = [1, 100, 1000].map((n) => ethers.parseEther(n.toString()))
    for (const amount of amounts) {
      await token.mint(alice.address, amount)
    }
    const total = amounts.reduce((a, b) => a + b, 0n)
    expect(await token.balanceOf(alice.address)).to.equal(total)
  })
})

3.4 Foundry forge Tests

// test/SecureToken.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "forge-std/Test.sol";
import "../src/SecureToken.sol";

contract SecureTokenTest is Test {
    SecureToken public token;
    address public alice = makeAddr("alice");

    function setUp() public {
        token = new SecureToken();
        token.transfer(alice, 1000 ether);
    }

    function testLockRequiresDuration() public {
        vm.startPrank(alice);
        vm.expectRevert("Invalid duration");
        token.lockAndRelease(100 ether, 12 hours);  // less than 1 day
        vm.stopPrank();
    }

    // Fuzz test: repeated verification with arbitrary amounts
    function testFuzz_LockAmount(uint256 amount) public {
        amount = bound(amount, 1 ether, 1000 ether);
        vm.startPrank(alice);
        token.lockAndRelease(amount, 1 days);
        assertEq(token.balanceOf(address(token)), amount);
        vm.stopPrank();
    }
}

4. DeFi Protocols: AMM, Lending, and MEV

4.1 Uniswap v3 Concentrated Liquidity

The v2 x*y=k formula distributes liquidity evenly across the entire price range. v3 allows LPs to concentrate liquidity within a specific price range, improving capital efficiency by up to 4000x.

import math

# Uniswap v3 price calculation (tick-based)
# tick = log(sqrt(price)) / log(sqrt(1.0001))

def tick_to_price(tick):
    return 1.0001 ** tick

def price_to_tick(price):
    return math.floor(math.log(price) / math.log(1.0001))

def calculate_liquidity_v3(amount0, amount1, price_current, price_lower, price_upper):
    """Calculate L (liquidity) for a concentrated liquidity position"""
    sqrt_p = math.sqrt(price_current)
    sqrt_pa = math.sqrt(price_lower)
    sqrt_pb = math.sqrt(price_upper)

    if price_current <= price_lower:
        # entirely token0
        L = amount0 * (sqrt_pa * sqrt_pb) / (sqrt_pb - sqrt_pa)
    elif price_current >= price_upper:
        # entirely token1
        L = amount1 / (sqrt_pb - sqrt_pa)
    else:
        # mixed
        L0 = amount0 * (sqrt_p * sqrt_pb) / (sqrt_pb - sqrt_p)
        L1 = amount1 / (sqrt_p - sqrt_pa)
        L = min(L0, L1)
    return L

# ETH/USDC pool example
# Current price: 3000 USDC/ETH, range: 2500 ~ 3500
L = calculate_liquidity_v3(
    amount0=1.0,       # 1 ETH
    amount1=3000.0,    # 3000 USDC
    price_current=3000,
    price_lower=2500,
    price_upper=3500
)
print(f"Liquidity L: {L:.4f}")

4.2 MEV (Maximal Extractable Value)

MEV is the maximum value a block producer can extract by manipulating transaction ordering.

Key MEV Types:

  • Sandwich Attack: Buying before and selling after a large swap to profit from slippage
  • Arbitrage: Capturing price differences between decentralized exchanges
  • Liquidation: Earning rewards by liquidating under-collateralized positions

Flashbots: Infrastructure for democratizing MEV

  • mev-boost: Connects Ethereum validators with block builders
  • Improves transparency of MEV distribution through open auctions
  • Mitigates the dark forest (mempool attacks)

5. ZK Proofs: zk-SNARK, PLONK, ZK Rollup

5.1 How zk-SNARKs Work

zk-SNARK (Zero-Knowledge Succinct Non-interactive ARguments of Knowledge) allows a prover to convince a verifier of the truth of a statement without revealing secret information (the witness).

Core properties:

  • Completeness: A true statement can always be proved
  • Soundness: A false statement cannot be proved
  • Zero-knowledge: No secrets are leaked during the proof
// Simple ZK proof example using snarkjs
// circuit: a * b = c (proving multiplication without revealing a and b)

const snarkjs = require('snarkjs')
const fs = require('fs')

async function generateAndVerifyProof() {
  // 1. Compute witness (private input)
  const input = {
    a: 3, // secret
    b: 11, // secret
    c: 33, // public: a * b = c
  }

  // 2. Generate proof (requires proving key)
  const { proof, publicSignals } = await snarkjs.groth16.fullProve(
    input,
    'multiply.wasm',
    'multiply_final.zkey'
  )

  console.log('Public signals:', publicSignals) // [33]
  console.log('Proof size:', JSON.stringify(proof).length, 'bytes')

  // 3. Verify (only verification key needed)
  const vKey = JSON.parse(fs.readFileSync('verification_key.json'))
  const isValid = await snarkjs.groth16.verify(vKey, publicSignals, proof)
  console.log('Proof valid:', isValid) // true
  // Without the proof, a=3 and b=11 cannot be deduced
}

generateAndVerifyProof()

5.2 ZK Rollup Architecture

A ZK Rollup processes thousands of transactions on L2 and posts only a validity proof to L1.

L2 transaction batch processing:
[tx1, tx2, ..., tx1000]
        ↓ prover
ZK Proof (hundreds of bytes)
L1 verifier
Ethereum Mainnet (99%+ gas savings)

zkSync Era vs StarkNet:

  • zkSync Era: EVM-compatible (Boojum proving system, PLONK-based)
  • StarkNet: Cairo language, STARK proofs (quantum computing resistant)
  • Polygon zkEVM: Type 2 EVM equivalence

6. AI + Blockchain

6.1 Decentralized AI Computing

Gensyn: A protocol that makes AI model training verifiable on distributed GPU clusters.

  • Splits training tasks into smaller sub-tasks
  • Generates verifiable proofs for each sub-task
  • Enables consumer GPUs to participate in the network

Bittensor (TAO): A decentralized marketplace for AI models

  • Specialized AI tasks per subnet
  • Validators evaluate miner outputs
  • Contribution-based TAO token rewards

6.2 On-Chain AI Verification

# Conceptual example of FHE (Fully Homomorphic Encryption)
# Performing computation on encrypted data

# Real implementations: Microsoft SEAL, OpenFHE
# Conceptual example:
def fhe_inference_concept(encrypted_input, model_weights):
    """
    With FHE, AI inference can be performed on encrypted input data
    without decryption:
    - Medical data: diagnosis without exposing patient information
    - Financial data: credit scoring with privacy preserved
    """
    # Homomorphic operations (addition and multiplication possible in encrypted state)
    encrypted_result = homomorphic_matrix_multiply(
        encrypted_input,
        model_weights  # model is public, only input is encrypted
    )
    return encrypted_result

7. Development Tools

7.1 Smart Contract Interaction with web3.py

from web3 import Web3
import json

# Alchemy/Infura RPC connection
w3 = Web3(Web3.HTTPProvider("https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"))
print("Connected:", w3.is_connected())
print("Latest block:", w3.eth.block_number)

# ERC-20 balance query
ERC20_ABI = [
    {"inputs": [{"name": "account", "type": "address"}],
     "name": "balanceOf",
     "outputs": [{"name": "", "type": "uint256"}],
     "type": "function"},
    {"inputs": [],
     "name": "symbol",
     "outputs": [{"name": "", "type": "string"}],
     "type": "function"}
]

USDC_ADDRESS = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
usdc = w3.eth.contract(
    address=Web3.to_checksum_address(USDC_ADDRESS),
    abi=ERC20_ABI
)

address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"  # vitalik.eth
balance = usdc.functions.balanceOf(address).call()
symbol = usdc.functions.symbol().call()
print(f"{symbol} balance: {balance / 10**6:.2f}")

# Send transaction (with signing)
def send_erc20(private_key, to_address, amount_wei):
    account = w3.eth.account.from_key(private_key)
    nonce = w3.eth.get_transaction_count(account.address)
    tx = usdc.functions.transfer(to_address, amount_wei).build_transaction({
        "from": account.address,
        "nonce": nonce,
        "gas": 100000,
        "maxFeePerGas": w3.to_wei(20, "gwei"),
        "maxPriorityFeePerGas": w3.to_wei(2, "gwei"),
    })
    signed = w3.eth.account.sign_transaction(tx, private_key)
    tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
    return tx_hash.hex()

7.2 ethers.js v6

import { ethers } from 'ethers'

// Provider setup
const provider = new ethers.JsonRpcProvider('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY')

// ENS name resolution
const address = await provider.resolveName('vitalik.eth')
console.log('vitalik.eth →', address)

// Event subscription (real-time Transfer detection)
const ERC20_FILTER_ABI = ['event Transfer(address indexed from, address indexed to, uint256 value)']
const usdc = new ethers.Contract(
  '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
  ERC20_FILTER_ABI,
  provider
)

usdc.on('Transfer', (from, to, value, event) => {
  const amount = ethers.formatUnits(value, 6)
  if (parseFloat(amount) > 100000) {
    console.log(`Large USDC transfer: ${amount} USDC`)
    console.log(`from: ${from} → to: ${to}`)
    console.log(`tx: ${event.log.transactionHash}`)
  }
})

Quiz

Q1. What is the economic mechanism that allows Ethereum PoS to maintain security while being more energy-efficient than PoW?

Answer: The economic penalty mechanism through Slashing

Explanation: PoW consumes physical energy (electricity) to create attack costs, whereas PoS requires validators to lock up 32 ETH as collateral. For malicious behavior such as double signing or surround voting, a portion of the staked ETH (at minimum 1/32) is automatically burned. If more than one-third of the entire network attempts a coordinated attack, up to 100% slashing occurs, causing catastrophic losses for attackers. This achieves the same deterrent effect as PoW — through economic loss rather than energy consumption.

Q2. What is the pattern by which reentrancy attacks occur in smart contracts, and how can they be defended against?

Answer: Checks-Effects-Interactions pattern + ReentrancyGuard

Explanation: A reentrancy attack is when an external contract repeatedly calls the original function via a callback (receive/fallback). The vulnerable pattern sends ETH before zeroing the balance. There are three defenses: (1) Checks-Effects-Interactions — complete all state changes before any external call; (2) OpenZeppelin's ReentrancyGuard — the nonReentrant modifier blocks reentry into the same function; (3) Pull Payment pattern — instead of the contract pushing funds, users withdraw directly.

Q3. Why is Uniswap v3's concentrated liquidity more capital-efficient than v2?

Answer: LPs concentrate liquidity within a specific price range, maximizing the ratio of active capital

Explanation: v2's x*y=k formula distributes liquidity evenly from 0 to infinity across all price ranges. Since actual trades occur in a narrow price band, most capital sits idle. In v3, LPs deploy capital only within an anticipated price range (e.g., 2500–3500 USDC/ETH). With the same capital, this provides up to 4000x deeper liquidity within that range, and fee revenue is also concentrated there. The downside is that no fees are earned when the price moves outside the range.

Q4. How does a prover in zk-SNARK convince a verifier without revealing the knowledge?

Answer: Polynomial Commitments and pairing-based cryptography

Explanation: zk-SNARKs transform a computation into polynomial equations (R1CS → QAP). Instead of revealing the secret witness directly, the prover submits polynomial evaluations encoded as points on an elliptic curve. The verifier uses a bilinear pairing operation to confirm that the polynomial relationships hold. The hardness of the discrete logarithm problem makes it impossible to reverse-engineer the actual witness. Proof sizes are a few hundred bytes and verification takes milliseconds.

Q5. What are the effects of MEV on the blockchain ecosystem, and what role does Flashbots play?

Answer: MEV has dual effects; Flashbots mitigates the negatives through a transparent auction

Explanation: The positive side of MEV includes price efficiency through arbitrage and maintaining DeFi stability through timely liquidations. The negative side includes harm to ordinary users from sandwich attacks, mempool congestion and gas wars, and incentives for chain reorganization (reorgs). Flashbots, through mev-boost, separates block builders from validators to create a transparent open-auction market. Users can prevent front-running with bundled transactions, and MEV revenue is fairly distributed to validators.


Conclusion

Blockchain engineering is a complex field at the intersection of cryptography, distributed systems, and economics. The Ethereum ecosystem is rapidly evolving through EVM optimization, the maturation of ZK technology, and convergence with AI. Smart contract security is as important as writing the code itself — Hardhat/Foundry tests and formal audits are essential steps. The combination of ZK Rollups and AI+blockchain will define the next phase of Web3.