In short
A smart contract is a program stored on a blockchain whose code and state are replicated on every node, and which executes deterministically when a transaction calls it - "code is the agreement". Solidity is the dominant statically typed, contract-oriented language that compiles to Ethereum Virtual Machine (EVM) bytecode and runs on Ethereum and every EVM-compatible chain (Polygon PoS, BNB Chain, Avalanche, Arbitrum, Polygon zkEVM). University syllabi examine the EVM execution model, gas, Solidity data and control constructs, visibility, modifiers, events, token standards and the classic vulnerabilities; GATE CS has no dedicated blockchain section, but blockchain appears in university electives, UGC-NET computer science and professional certification. Indian employers hiring on this skill include Infosys, TCS, Wipro, Tech Mahindra, Accenture, Cognizant, Polygon Labs, CoinDCX, WazirX, Zebpay, Mudrex and bank innovation units; the RBI's e-rupee pilots and ONDC-adjacent pilots keep demand live. Note that in India crypto-assets are legal to hold and trade but are not legal tender, and token issuance has no dedicated statute yet.
Explained
What a smart contract is, and what it is not
- Definition: an autonomous program with its own address, persistent storage and balance, that runs on a blockchain's virtual machine. The term is from Nick Szabo (1994); Ethereum (2015) made general-purpose contracts practical.
- Accounts: an EVM chain has externally owned accounts (EOAs) - controlled by a private key, can start transactions - and contract accounts - controlled by code, can only react to being called.
- Properties: deterministic (same input and state → same output on every node), immutable once deployed (code cannot be edited; only upgrade patterns work around it), transparent (bytecode is public), self-executing but not self-triggering - something must send a transaction. Time-based action needs an external keeper/oracle.
- Not possible inside a contract: HTTP calls, random numbers, file I/O, floating-point money maths, system time beyond
block.timestamp. External data comes from oracles (Chainlink and similar). - Legal status in India: a smart contract is code, not automatically a contract in law. Enforceability is judged under the Indian Contract Act, 1872 (offer, acceptance, consideration, competence, lawful object); electronic records and electronic signatures are recognised by the Information Technology Act, 2000 - but s.1(4) with the First Schedule excludes certain instruments (for example negotiable instruments other than cheques, powers of attorney, trusts, wills, immovable-property conveyance) from electronic form, so those cannot be done purely on-chain.
The EVM execution model and gas
- EVM: a 256-bit stack machine, stack depth 1,024, with four data areas - stack, memory (volatile byte array, per call), storage (persistent key→value, 32-byte slots, per contract) and calldata (read-only input).
- Transaction lifecycle: sign → broadcast to mempool → a block producer orders it → EVM executes the called function → state root updated → receipt with logs, status and gas used. All state changes in one transaction are atomic: a revert undoes everything (but gas already consumed is not refunded).
- Gas: every opcode has a gas cost, so computation is priced and infinite loops are bounded. Units:
1 ether = 10^18 wei,1 gwei = 10^9 wei. - Costs to remember: a plain transfer is 21,000 gas;
SSTOREof a zero slot to non-zero is 20,000; modifying a non-zero slot is 5,000 (2,900 plus access cost under EIP-2929); clearing a slot refunds up to 4,800;SLOADcold 2,100 / warm 100;LOGandCALLextra. Memory grows quadratically beyond a point. - EIP-1559 fee market (proposed April 2019, live August 2021 in the London upgrade): fee =
gasUsed × (baseFee + priorityFee); baseFee is burnt, priority fee goes to the proposer. The user setsmaxFeePerGasandmaxPriorityFeePerGas. - Gas-saving rules: pack variables into 32-byte slots; use
calldatafor read-only arrays; cache storage reads in memory;uint256is cheaper thanuint8unless packed; events are far cheaper than storage;immutable/constantcost no storage.
Solidity language essentials
- File skeleton:
// SPDX-License-Identifier: MIT, thenpragma solidity ^0.8.20;, thencontract,interface,libraryorabstract contract. - Value types:
bool,int8..int256/uint8..uint256(step 8),addressandaddress payable,bytes1..bytes32,enum,stringandbytes(dynamic, reference types). No floating point - money is held in the smallest unit (wei, or paise for an INR-pegged token). - Reference types: fixed and dynamic arrays,
struct,mapping(KeyType => ValueType). A mapping has no length and is not iterable - keep a parallel array if you must enumerate. - Data location:
storage(persistent, expensive),memory(temporary, mutable),calldata(immutable function input). Assigning storage→memory copies; storage→storage by reference. - Visibility:
public(anyone, auto-getter for state variables),external(only via a call from outside),internal(this contract and children, default for state variables),private(this contract only - *private does not mean secret; the data is readable on-chain*). - State mutability:
view(reads state, no write),pure(neither),payable(can receive ether).view/purecalls off-chain cost no gas. - Special functions:
constructor(runs once at deployment, not stored in the deployed code),receive() external payable(plain ether transfer, empty calldata),fallback() external [payable](unknown function selector),modifier(reusable pre/post check with_;). - Events and logs:
event Transfer(address indexed from, address indexed to, uint256 value);emitted withemit. Up to 3indexedtopics for filtering. Logs are not readable by contracts - they exist for off-chain indexers (The Graph, ethers.js listeners). - Inheritance: single file multiple inheritance with C3 linearisation (
contract A is B, C- most base-like first),virtual/override,super,abstract,interface(no state, all external).libraryfunctions can beinternal(inlined) or deployed and linked;using SafeMath for uint256is obsolete since 0.8. - Error handling:
require(cond, "msg")for input and condition checks (refunds remaining gas),revert CustomError(args)- cheaper than strings, preferred since 0.8.4,assert(cond)for invariants that must never fail,try/catcharound external calls and contract creation. - Version milestones for exams: 0.8.0 made arithmetic checked by default (overflow reverts; use
unchecked{}to opt out); 0.6 splitreceive/fallback; 0.5 required explicitaddress payableand data locations.
Calling, sending value and the standard contract patterns
- Transfer options:
payable(addr).transfer(x)- 2,300 gas stipend, reverts on failure;addr.send(x)- same stipend, returns bool;addr.call{value:x}("")- forwards all gas, returns(bool, bytes). Since the Istanbul gas repricing, **callwith a reentrancy guard is the recommended pattern** rather thantransfer. - Low-level calls:
call(callee's context),delegatecall(callee's code, **caller's storage andmsg.sender** - the basis of proxies and of the Parity multisig loss),staticcall(no state change).msg.senderchanges on each call hop;tx.originis the original EOA and must never be used for authorisation. - Globals:
msg.sender,msg.value,msg.data,block.timestamp,block.number,blockhash(n),gasleft(),address(this),type(C).creationCode. - ABI and selectors: the Application Binary Interface is the JSON description of functions and events. A call's first 4 bytes are the selector = first 4 bytes of
keccak256("transfer(address,uint256)"), followed by 32-byte-padded arguments.abi.encode,abi.encodePacked(collision-prone with dynamic types),abi.decode. - Checks-Effects-Interactions: validate first, update state next, call outside last - the single most examined safe-coding order.
- Common patterns: access control (
Ownable, role-basedAccessControl), pull over push payments (withdraw pattern), circuit breaker /Pausable, state machine, commit-reveal for hidden inputs,ReentrancyGuard, factory (new, CREATE2 for deterministic addresses), proxy upgrade (transparent proxy, UUPS, EIP-1967 storage slots, diamond EIP-2535) with initializer instead of constructor and reserved storage gaps. - Determinism rules: never use
block.timestamp/blockhashas randomness (a proposer can influence them) - use a VRF; never loop over an unbounded array (gas limit DoS); never rely on contract balance for accounting.
Token standards and the ecosystem
| Standard | What it is | Key interface |
|---|---|---|
| ERC-20 | Fungible token | totalSupply, balanceOf, transfer, approve, allowance, transferFrom, events Transfer, Approval |
| ERC-721 | Non-fungible token (unique IDs) | ownerOf, safeTransferFrom, approve, setApprovalForAll, tokenURI |
| ERC-1155 | Multi-token (fungible + NFT, batch) | balanceOfBatch, safeBatchTransferFrom |
| ERC-4626 | Tokenised vault (yield) | deposit, mint, withdraw, redeem, convertToShares |
| ERC-2612 | Gasless approve by signature | permit(owner,spender,value,deadline,v,r,s) |
| ERC-4337 | Account abstraction (smart accounts, paymasters) | EntryPoint, UserOperation |
- ERC-20 decimals: usually 18; the on-chain integer is
amount × 10^decimals. USDT/USDC use 6 - a classic production bug. - Approve pattern:
approvethentransferFromenables DEX trades; the approve race condition is whyincreaseAllowance/permitexist. - Toolchain: Remix (browser IDE, used in most labs), Hardhat (JS/TS,
npx hardhat test, mainnet forking, console.log), Foundry (Rust-based, tests written in Solidity,forge test, fuzzing), Truffle (legacy, sunset 2023). OpenZeppelin Contracts is the audited library every project should inherit from rather than rewrite. - Testing and deployment: unit tests with Hardhat/Chai or Foundry;
solidity-coverage; static analysis with Slither, Mythril, Echidna fuzzing; deploy to Sepolia (application testnet) or Hoodi (validator/staking testnet) - Ropsten, Rinkeby, Goerli and Holesky are retired, verify source on Etherscan, then mainnet or an L2. Wallets: MetaMask; libraries: ethers.js v6, Web3.js, web3.py. - Other contract languages (for comparison only): Vyper (Python-like, EVM), Move (Aptos/Sui), Rust (Solana, NEAR), Go/Java chaincode (Hyperledger Fabric).
Advantages, limitations and the Indian regulatory frame
- Advantages: no intermediary and no counterparty discretion; automatic, auditable settlement; 24×7; tamper-evident history; composability (contracts call contracts - "money Lego").
- Limitations: immutability makes bugs permanent and exploits irreversible; gas makes computation and storage expensive (never store files on-chain - store an IPFS hash); public state means no confidentiality; throughput is limited (Ethereum L1 is roughly 15-30 transactions per second, hence L2 rollups); oracle dependence reintroduces trust; legal and jurisdictional uncertainty; key loss is final.
- India - what is settled: Virtual Digital Assets (VDAs) are taxed at a flat 30% with no deduction except cost of acquisition and no set-off of losses, plus a 1% TDS on transfers above the threshold - originally sections 115BBH and 194S of the Income-tax Act, 1961; the Income-tax Act, 2025 replaced the 1961 Act from 1 April 2026 and renumbered every section, so quote the provisions by name (the VDA special-rate provision and the TDS-on-VDA-transfer provision) unless you are certain of the new numbers. Gifts of VDAs are taxable in the recipient's hands.
- Other Indian rules: VDA service providers are reporting entities under the Prevention of Money-Laundering Act, 2002 and must register with FIU-IND (notification of March 2023), with KYC under the PMLA Maintenance of Records Rules. The Supreme Court struck down the RBI's banking ban in *Internet and Mobile Association of India v. RBI* (2020). The e-rupee (CBDC) retail and wholesale pilots run on RBI infrastructure and are legal tender; private crypto is not. The DPDP Act, 2023 applies to personal data - and because on-chain data cannot be erased, personal data should stay off-chain (note: under the DPDP Rules, 2025 the Data Fiduciary duties including erasure apply only from 13 May 2027; until then IT Act s.43A and the SPDI Rules apply). There is no dedicated Indian statute on token issuance or smart-contract enforceability as of November 2026 - a discussion paper has been promised more than once, so treat the position as unsettled.
Key points
- Smart contract - code with an address, storage and balance, executed by every node deterministically; self-executing, not self-triggering.
- EVM - 256-bit stack machine; data areas stack, memory, storage, calldata.
- Gas - price of computation;
1 ether = 10^18 wei; plain transfer 21,000 gas;gasUsed × (baseFee + priorityFee)with baseFee burnt (EIP-1559). - Atomicity - a revert rolls back all state changes in the transaction, but consumed gas is not returned.
- Visibility -
public,external,internal,private;privatehides from contracts, never from observers of the chain. - Mutability -
viewreads,pureneither reads nor writes,payableaccepts ether. - Data location -
storagepersists and costs most,memoryis per-call,calldatais read-only input. - Mapping - O(1) key→value, default zero value, no length, not iterable.
- Modifier - reusable guard; the function body runs where
_;sits. - Events - cheap off-chain log with up to 3 indexed topics; contracts cannot read logs.
- Error handling -
require/customrevertfor conditions,assertfor invariants; custom errors are the cheapest. - Solidity 0.8+ - arithmetic overflow reverts by default;
uncheckedrestores the old behaviour and SafeMath is no longer needed. - delegatecall - runs other code in *this* contract's storage; powers proxies, caused the Parity freeze.
- Checks-Effects-Interactions plus a reentrancy guard - the defence against the DAO-class bug (The DAO, 2016, ~3.6M ETH).
- tx.origin must never authorise; use
msg.sender. - Standards - ERC-20 fungible, ERC-721 NFT, ERC-1155 multi-token, ERC-4337 account abstraction; inherit OpenZeppelin, do not rewrite.
- Tooling - Remix, Hardhat, Foundry, Slither; test on Sepolia/Hoodi, verify on Etherscan.
- India - VDA income taxed at a flat 30% plus 1% TDS (provisions renumbered by the Income-tax Act, 2025 from 1 April 2026); exchanges are FIU-IND-registered PMLA reporting entities; crypto is not legal tender, the e-rupee is.
Formulas and examples
1 ether = 1018 wei; 1 gwei = 109 wei. Fee in ether = fee_wei / 1018.
Token raw amount = humanAmount × 10decimals (uint256, no fractions).
Storage slots used = ceil(Σ variable bytes packed in declaration order / 32).
Function selector = first 4 bytes of keccak256("name(type1,type2)"); calldata size = 4 + 32 × (number of static words).
Simple interest accrued on-chain = principal × rate_bps × elapsedSeconds / (10000 × 365 × 24 × 3600), integer division.
Example 1 - cost of a transfer in rupees. An ERC-20 transfer uses 51,000 gas. baseFee = 18 gwei, priorityFee = 2 gwei, 1 ETH = ₹2,40,000.
- Effective gas price = 18 + 2 = 20 gwei = 20 × 10⁹ wei.
- Fee = 51,000 × 20 × 10⁹ = 1.02 × 10¹⁵ wei = 1.02 × 10¹⁵ / 10¹⁸ = 0.00102 ETH.
- In rupees = 0.00102 × 2,40,000 = ₹244.80.
- Burnt portion = 18/20 = 90% → 0.000918 ETH ≈ ₹220.32; proposer gets ₹24.48.
- On Polygon PoS at 30 gwei with POL at ₹40: 51,000 × 30 × 10⁹ = 1.53 × 10¹⁵ wei = 0.00153 POL ≈ ₹0.06 - this is why Indian pilots deploy on L2s.
Example 2 - token decimals. A rupee-pegged token with 2 decimals must transfer ₹1,250.75.
- Raw = 1250.75 × 10² = 125075 units. With 18 decimals it would be 1250.75 × 10¹⁸ = 1.25075 × 10²¹.
- Interest of 7.5% for 30 days on ₹1,250.75 at 2 decimals: 125075 × 750 × 30 / (10000 × 365) = 2,814,187,500 / 3,650,000 = 771 (integer division) → ₹7.71, remainder truncated. Always compute
principal × rate × time*before* dividing, or truncation eats the result.
Example 3 - storage packing and gas saved.
contract Bad { uint256 a; uint8 b; uint256 c; uint8 d; } // 4 slots
contract Good { uint256 a; uint256 c; uint8 b; uint8 d; } // 3 slots
Bad: slot0 = a, slot1 = b (alone), slot2 = c, slot3 = d → 4 slots.Good: slot0 = a, slot1 = c, slot2 = b+d (1+1 = 2 bytes of 32) → 3 slots.- One fewer zero→non-zero
SSTOREat deployment saves 20,000 gas; at 20 gwei and ₹2,40,000/ETH that is 20,000 × 20 × 10⁹ = 4 × 10¹⁴ wei = 0.0004 ETH = ₹96 per deployment, and the same saving on every write path removed.
Example 4 - a complete, safe contract.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Escrow {
address public immutable buyer;
address public immutable seller;
uint256 public deadline;
bool public released;
mapping(address => uint256) public refunds; // pull-payment ledger
error NotBuyer();
error TooEarly(uint256 now_, uint256 deadline_);
event Deposited(address indexed from, uint256 amount);
event Released(address indexed to, uint256 amount);
modifier onlyBuyer() {
if (msg.sender != buyer) revert NotBuyer();
_; // function body runs here
}
constructor(address seller_, uint256 days_) payable {
buyer = msg.sender;
seller = seller_;
deadline = block.timestamp + days_ * 1 days;
emit Deposited(msg.sender, msg.value);
}
receive() external payable { emit Deposited(msg.sender, msg.value); }
function release() external onlyBuyer {
require(!released, "already released");
released = true; // EFFECT before INTERACTION
uint256 amount = address(this).balance;
(bool ok, ) = payable(seller).call{value: amount}("");
require(ok, "transfer failed");
emit Released(seller, amount);
}
function claimRefund() external {
if (block.timestamp < deadline) revert TooEarly(block.timestamp, deadline);
uint256 amount = refunds[msg.sender];
refunds[msg.sender] = 0; // zero first
(bool ok, ) = payable(msg.sender).call{value: amount}("");
require(ok, "refund failed");
}
}
- Points an examiner looks for:
immutable(no storage slot), custom errors, a modifier with_;,receive, state set before the external call, pull refunds, notx.origin.
Example 5 - the reentrancy bug and its fix.
// VULNERABLE: interaction before effect
function withdraw() external {
uint256 bal = balances[msg.sender];
(bool ok, ) = msg.sender.call{value: bal}(""); // attacker re-enters here
require(ok);
balances[msg.sender] = 0; // too late
}
// FIXED
function withdraw() external nonReentrant {
uint256 bal = balances[msg.sender];
balances[msg.sender] = 0; // effect first
(bool ok, ) = msg.sender.call{value: bal}("");
require(ok, "fail");
}
- With 10 ETH in the contract and a 1 ETH balance, the attacker's
receive()can loop the vulnerablewithdrawuntil gas or balance runs out and drain all 10 ETH - 10× the entitlement. This is the DAO class of bug.
| Concern | transfer | send | call{value:} |
|---|---|---|---|
| Gas forwarded | 2,300 | 2,300 | all remaining (or specified) |
| On failure | reverts | returns false | returns false |
| Reentrancy risk | low | low | real - needs guard |
| Recommended now | no (stipend may break) | no | yes, with CEI + guard |
call | delegatecall | staticcall | |
|---|---|---|---|
| Code run | callee's | callee's | callee's |
| Storage used | callee's | caller's | callee's (read-only) |
msg.sender seen | the caller contract | original sender | the caller contract |
| Typical use | normal interaction | proxy / upgradeable | safe view call |
Where it is used
- IT services and consulting: Infosys (Finacle blockchain), TCS Quartz, Wipro, Tech Mahindra, LTIMindtree and Accenture India build EVM contracts for trade finance, insurance claims and loyalty programmes - roles titled Blockchain Developer, Solidity Engineer, Web3 Backend Engineer, typically ₹6-25 lakh for 0-4 years' experience.
- Exchanges and Web3 product firms: CoinDCX, WazirX, Zebpay, Mudrex, Polygon Labs, Biconomy (ERC-4337 paymasters) and Instadapp - contract development, auditing and relayer infrastructure.
- Banking and capital markets: IndusInd, ICICI, Axis and HDFC pilots on domestic trade and letters of credit; bank-run e-rupee (CBDC) programmable pilots for tied-purpose subsidies; NSE/BSE and NSDL/CDSL tokenisation experiments; the IFSCA sandbox at GIFT City.
- Supply chain, agriculture and government: fertiliser and PDS traceability pilots, land-record and certificate issuance pilots by state governments, NITI Aayog's early drug-traceability proof of concept, and university degree certificates issued as verifiable credentials or NFTs.
- Gaming, ticketing and loyalty: ERC-721/1155 in-game items, event tickets and brand reward points on Polygon, where fees are a fraction of a rupee.
- Audit and security roles: Solidity knowledge is the entry requirement for smart-contract auditor posts at firms such as Trail of Bits, Halborn, QuillAudits and Zellic, usually tested with Ethernaut, Damn Vulnerable DeFi and Code4rena contests.
Interview questions
- Q: What makes a smart contract deterministic, and why must it be? A: Its output depends only on the transaction input and on-chain state, with no I/O or randomness, so every node re-executing it reaches the same state root - otherwise consensus would be impossible.
- Q: Difference between
storage,memoryandcalldata? A:storageis persistent per-contract state (costliest),memoryis a mutable scratch area that lives for one external call,calldatais the immutable, cheapest-to-read function input. - Q: What happens to gas when a transaction reverts? A: All state changes are rolled back, but the gas already consumed is paid to the network;
requireandrevertrefund only the unused portion. - Q: Does
privatekeep data secret? A: No. It only stops other contracts and derived contracts from reading it in Solidity; the storage slot is public on the chain and readable witheth_getStorageAt. - Q: Explain reentrancy and two ways to prevent it. A: An external call lets the callee re-enter the function before state is updated and withdraw repeatedly; prevent it with Checks-Effects-Interactions ordering and a
nonReentrantmutex, and by using pull payments. - Q:
callversusdelegatecall? A:callexecutes the callee's code in the callee's storage context;delegatecallexecutes the callee's code against the caller's storage and preservesmsg.sender- which is how proxies work and why a baddelegatecalltarget is catastrophic. - Q: Why is
tx.originunsafe for access control? A: It is the original EOA, so a malicious contract the user is tricked into calling will still pass atx.origin == ownercheck;msg.senderreflects the immediate caller and is safe. - Q: How do you get a random number in a contract? A: Not from
block.timestamporblockhash, which a proposer can influence - use a verifiable random function oracle (Chainlink VRF) or a commit-reveal scheme. - Q: If contracts are immutable, how are bugs fixed? A: With an upgrade pattern - a proxy holding state that
delegatecalls to a replaceable logic contract (transparent or UUPS, EIP-1967 slots), using aninitializerinstead of a constructor and reserved storage gaps; or by migrating users to a new contract. - Q: What changed in Solidity 0.8 that made SafeMath redundant? A: Arithmetic became checked by default - overflow and underflow revert - with
unchecked { }available where the developer has proved the bound and wants the gas back. - Q: Why can a contract not read an event it emitted? A: Logs are stored outside the state trie in the receipt for off-chain consumers; the EVM has no opcode to read them, so events are for indexers, never for contract logic.
- Q: Minimum functions needed for a token to be ERC-20 compliant? A:
totalSupply,balanceOf,transfer,transferFrom,approve,allowance, plus theTransferandApprovalevents;name,symbolanddecimalsare optional metadata. - Q: In India, how is profit from selling a token taxed? A: As Virtual Digital Asset income at a flat 30% with only the cost of acquisition deductible and no loss set-off, plus 1% TDS on the transfer by the buyer or exchange; the governing provisions were renumbered by the Income-tax Act, 2025 effective 1 April 2026.
Common mistakes
- Treating
block.timestamporblockhashas random, or as reliable clock precision - a proposer has leeway of seconds. - Writing the external call before the state update, then being surprised by reentrancy; or assuming
transfer's 2,300-gas stipend is still a safe guard after gas repricings. - Using floating point or
/before*in money maths - Solidity has no decimals and integer division truncates, so multiply first and track the smallest unit. - Assuming every ERC-20 has 18 decimals, returns
true, or does not take a fee on transfer - USDT-style and fee-on-transfer tokens break naive integrations. - Looping over an unbounded array or mapping-backed list, so the function eventually exceeds the block gas limit and is permanently stuck.
- Forgetting that a
constructorcannot be used in a proxy-upgradeable contract, and that storage layout must only be appended to, never reordered. - Deploying unverified, untested code straight to mainnet instead of running Hardhat/Foundry tests, Slither and a Sepolia deployment first; and keeping private keys or mnemonics in the repository rather than in a
.envfile that is git-ignored.
Related topics
Blockchain security and smart-contract audits · Enterprise blockchains: Hyperledger Fabric and Corda · Solana: architecture, Rust programs and applications · Blockchain in finance and supply chain · Crypto-assets in India: tax, TDS and FIU registration · Consensus algorithms and cryptographic hashing