Fatskills
Practice. Master. Repeat.
Study Guide: Blockchain and Web3 Development: Ethereum and Smart Contracts Solidity Programming Syntax Data Types Mappings Structs
Source: https://www.fatskills.com/cryptocurrency-bitcoin-blockchain-and-more/chapter/blockchain-and-web3-development-blockchain-and-web3-development-ethereum-and-smart-contracts-solidity-programming-syntax-data-types-mappings-structs

Blockchain and Web3 Development: Ethereum and Smart Contracts Solidity Programming Syntax Data Types Mappings Structs

By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.

⏱️ ~5 min read

What This Is

Solidity is the high‑level, contract‑oriented language that compiles to EVM bytecode. It lets you write smart contracts—self‑executing programs that live on a blockchain. Because the code is immutable and runs exactly as written, you can create trust‑less financial primitives such as a Uniswap swap (an automated market maker), an NFT minting portal, or a DAO voting contract where token holders decide on proposals without a central authority.


Key Terms & Code Snippets

  • pragma solidity ^0.8.20; – Compiler version directive. The caret (^) tells the compiler to accept any 0.8.x version, giving you the latest safety fixes (e.g., built‑in overflow checks).
  • State Variable – Declared at contract level, stored on‑chain.
    solidity uint256 public totalSupply;
  • mapping(address => uint256) balances; – A hash table that maps an address to a uint256. Lookups are O(1) and cheap; writes cost 20 000 gas for a new slot.
  • struct Order { address maker; uint256 amount; bool isBuy; } – Custom data type that groups related fields.
    solidity Order[] public orderBook;
  • function transfer(address to, uint256 amount) external returns (bool); – Typical ERC‑20 signature; external means the function can be called from other contracts or transactions but not internally via this.
  • require(condition, "Error message"); – Runtime guard that reverts the transaction if condition is false, refunding remaining gas.
  • emit Transfer(msg.sender, to, amount); – Emits an event that external indexers (TheGraph, etherscan) can listen to.
  • modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; } – Reusable guard that inserts the require before the function body (_).
  • address payable recipient = payable(msg.sender); – Casts an address to payable so you can use .transfer() or .call{value: …}.
  • bytes4(keccak256("transfer(address,uint256)")) – The function selector used in low‑level call.
  • unchecked { totalSupply += amount; } – Bypasses overflow checks for gas‑tight loops (only safe when you know overflow cannot happen).
  • library SafeMath { ... } – Pre‑0.8 pattern for safe arithmetic; now largely unnecessary but still seen in legacy code.


Step‑by‑Step / Process Flow

  1. Write the contract – Open Remix, create Token.sol, paste a minimal ERC‑20 skeleton using the snippets above.
  2. Compile – In Remix click Compile, or run npx hardhat compile after adding the file to a Hardhat project (contracts/Token.sol).
  3. Test locally – Write a JavaScript test with ethers.js and chai (test/Token.test.js). Example:
    js
    const [owner, alice] = await ethers.getSigners();
    await token.mint(owner.address, 1_000);
    await token.transfer(alice.address, 100);
    expect(await token.balanceOf(alice.address)).to.equal(100);
  4. Deploy to a testnet – Use Hardhat’s deploy script:
    js
    const Token = await ethers.getContractFactory("Token");
    const token = await Token.deploy("MyToken", "MTK", 18);
    await token.deployed();
    console.log("Deployed to:", token.address);


    Run npx hardhat run scripts/deploy.js --network goerli (Infura/Alchemy endpoint required).
  5. Interact from a dApp – In a React front‑end, connect MetaMask, instantiate the contract with new ethers.Contract(address, abi, signer), then call token.transfer(...).
  6. Verify & Verify – Publish the source on Etherscan (or Blockscout) so users can read the Solidity code and the contract is marked “verified”.

Common Mistakes

  • Mistake: Using tx.origin for access control.
    Correction: Always check msg.sender; tx.origin can be spoofed through a malicious contract, leading to unauthorized actions.

  • Mistake: Forgetting to mark functions that read state as view or pure.
    Correction: Adding view/pure reduces gas (no state read/write) and signals intent to auditors and tooling.

  • Mistake: Storing large structs in a mapping and iterating over them on‑chain.
    Correction: Keep on‑chain data minimal; use events to log full structs and let off‑chain services reconstruct histories.

  • Mistake: Hard‑coding the ERC‑20 decimals value in UI calculations (e.g., assuming 18).
    Correction: Read decimals() from the contract at runtime; some tokens use 6 or 8 decimals.

  • Mistake: Using address.transfer for sending ETH after a state change.
    Correction: Prefer the Checks‑Effects‑Interactions pattern and use low‑level call{value: …} with proper re‑entrancy guards (nonReentrant from OpenZeppelin).


Blockchain Developer Interview / Practical Insights

  • call vs delegatecall – Interviewers love to ask why delegatecall is dangerous: it runs the callee’s code in the caller’s storage context, enabling proxy patterns but also opening up storage‑collision attacks.
  • ERC‑20 vs ERC‑777 – Be ready to explain that ERC‑777 adds hooks (tokensReceived) for richer interactions, but its added complexity can increase attack surface; most production tokens still stick to ERC‑20.
  • Optimistic Rollup vs ZK Rollup – Know the trade‑off: Optimistic Rollups assume transactions are valid and challenge fraud later (faster L1 finality, but 1‑week challenge window), while ZK Rollups provide instant validity proofs at higher proof‑generation cost.
  • Gas‑Optimization mindset – Auditors look for “SLOAD/SSTORE” patterns, packing variables into a single 256‑bit slot, and avoiding loops that grow with user input.


Quick Check Questions

  1. Scenario: A contract uses tx.origin == owner to restrict a privileged function.
    Answer: Dangerous – a phishing contract can call the vulnerable contract on behalf of the owner, making tx.origin equal to the owner’s address and bypassing the check.

  2. Scenario: You need to store a user’s balance and a flag (bool isKYCed) for each address.
    Answer: Pack them into a single struct and store in a mapping(address => UserInfo); Solidity will place the bool in the same 256‑bit slot as the uint256 balance, saving gas.

  3. Scenario: After a token transfer, you emit Transfer(msg.sender, to, amount).
    Answer: Correct – the ERC‑20 spec requires the Transfer event to be emitted with the sender (msg.sender) and recipient (to) addresses, enabling wallets and explorers to track token movement.


Last‑Minute Cram Sheet

  1. ⚠️ Re‑entrancy: Always update state before external calls (Checks‑Effects‑Interactions).
  2. ⚠️ tx.origin is never safe for auth; use msg.sender.
  3. Packing: uint128 a; uint128 b; share one storage slot → 2× gas saving.
  4. pragma solidity ^0.8.x; gives you built‑in overflow checks; no need for SafeMath in new code.
  5. ERC‑20 = 20, ERC‑721 = 721, ERC‑1155 = multi‑token standard (fungible + non‑fungible).
  6. view functions cost 0 gas when called off‑chain, but still read state on‑chain.
  7. pure functions cannot read any state, only use inputs.
  8. address payable is required for .transfer/.call{value: …}; plain address cannot receive ether.
  9. Gas stipend: transfer forwards 2300 gas – enough for a log but not for complex logic.
  10. delegatecall keeps the caller’s storage context; use only in well‑audited proxy patterns.


ADVERTISEMENT