By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.
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.
pragma solidity ^0.8.20;
^
solidity uint256 public totalSupply;
mapping(address => uint256) balances;
struct Order { address maker; uint256 amount; bool isBuy; }
solidity Order[] public orderBook;
function transfer(address to, uint256 amount) external returns (bool);
external
this
require(condition, "Error message");
condition
emit Transfer(msg.sender, to, amount);
modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; }
require
_
address payable recipient = payable(msg.sender);
payable
.transfer()
.call{value: …}
bytes4(keccak256("transfer(address,uint256)"))
call
unchecked { totalSupply += amount; }
library SafeMath { ... }
Token.sol
npx hardhat compile
contracts/Token.sol
ethers.js
chai
test/Token.test.js
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);
js const Token = await ethers.getContractFactory("Token"); const token = await Token.deploy("MyToken", "MTK", 18); await token.deployed(); console.log("Deployed to:", token.address);
npx hardhat run scripts/deploy.js --network goerli
new ethers.Contract(address, abi, signer)
token.transfer(...)
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.
tx.origin
msg.sender
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.
view
pure
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.
mapping
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.
decimals
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).
address.transfer
call{value: …}
nonReentrant
delegatecall
tokensReceived
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.
tx.origin == owner
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.
bool isKYCed
struct
mapping(address => UserInfo)
bool
uint256
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.
Transfer(msg.sender, to, amount)
Transfer
to
Checks‑Effects‑Interactions
uint128 a; uint128 b;
pragma solidity ^0.8.x;
SafeMath
address payable
.transfer
address
transfer
Join 4M+ learners. Unlock unlimited quizzes, wrong-answer tracking, flashcards + reminders, study guides, and 1-on-1 challenges.