Fatskills
Practice. Master. Repeat.
Study Guide: Blockchain and Web3 Development: DeFi and Tokenomics DeFi Primitives Lending AMMs Staking Yield Farming
Source: https://www.fatskills.com/cryptocurrency-bitcoin-blockchain-and-more/chapter/blockchain-and-web3-development-blockchain-and-web3-development-defi-and-tokenomics-defi-primitives-lending-amms-staking-yield-farming

Blockchain and Web3 Development: DeFi and Tokenomics DeFi Primitives Lending AMMs Staking Yield Farming

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

⏱️ ~6 min read

What This Is

DeFi primitives are the building blocks that let anyone create financial services on‑chain without a bank. They include lending markets, automated market makers (AMMs), staking contracts, and yield‑farming strategies. By composably linking these pieces, developers can launch a full‑featured DeFi app in weeks instead of years. Real‑world example: A user swaps USDC for DAI on Uniswap (an AMM), then deposits the DAI into Aave (a lending pool) to earn interest, and finally stakes the Aave‑aUSDC receipt token in a Curve pool to capture extra yield.


Key Terms & Code Snippets

  • Lending Pool (Aave‑style): A contract that holds deposited assets and issues interest‑bearing “aTokens”. Borrowers provide collateral and receive a loan at a variable rate.
  • Interest Rate Model: A Solidity library that computes borrow rates from utilization (U = borrowed / total). Example:

solidity function getBorrowRate(uint256 cash, uint256 borrows) public pure returns (uint256) {
uint256 utilization = borrows * 1e18 / (cash + borrows);
return utilization * 0.05e18; // 5 % APR per 100 % utilization }


  • AMM (x * y = k): The constant‑product formula that powers Uniswap V2. Swaps preserve the product k, guaranteeing liquidity for any token pair.
  • Swap Function (Solidity): Minimal Uniswap‑V2 style swap:

solidity function swap(uint amountIn, address tokenIn, address tokenOut) external {
uint amountOut = getAmountOut(amountIn, tokenIn, tokenOut);
IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
IERC20(tokenOut).transfer(msg.sender, amountOut); }


  • Staking Contract: Holds a token and mints a receipt token (e.g., sToken) that represents the share of the pool. Rewards are distributed proportionally each block.
  • Reward Distribution (Solidity):

solidity uint256 public rewardPerBlock; mapping(address => uint256) public rewardDebt; function updateRewards() internal {
uint256 pending = (block.number - lastRewardBlock) * rewardPerBlock;
accRewardPerShare += pending * 1e12 / totalStaked; }


  • Yield Farming Strategy (JS/Hardhat): A script that deposits into a pool, harvests rewards, and reinvests.

js async function compound() {
await vault.deposit(amount);
await vault.harvest(); // claim rewards
await vault.reinvest(); // add rewards back to the pool }


  • ERC‑4626 Vault: A standard interface for tokenized vaults (deposit/withdraw) that makes composability with other DeFi primitives trivial.
  • Reentrancy Guard: A modifier that prevents a contract from being called again before the first call finishes.

solidity uint256 private _status; modifier nonReentrant() {
require(_status == 0, "REENTRANCY");
_status = 1;
_;
_status = 0; }


  • Flash Loan: A loan that must be repaid within the same transaction; used for arbitrage or liquidation bots.

solidity function executeFlashLoan(address token, uint256 amount) external {
lendingPool.flashLoan(address(this), token, amount, abi.encode(msg.sender)); }


  • Liquidity Provider (LP) Token: The ERC‑20 receipt you receive when you add assets to an AMM pair; it represents your share of the pool and can be staked elsewhere.


Step‑by‑Step / Process Flow

  1. Write the contracts – Open Remix or VS Code, create three Solidity files: LendingPool.sol, AMM.sol, and StakingVault.sol. Use the snippets above as a scaffold.
  2. Compile & test – Run npx hardhat compile (Solidity 0.8.20) and write unit tests with chai/ethers. Verify that swap, borrow, and stake functions update balances correctly.
  3. Deploy to a testnet

bash
npx hardhat run scripts/deploy.js --network goerli

The script should deploy the three contracts, set the AMM pair address in the LendingPool, and grant the StakingVault permission to pull LP tokens.
4. Add front‑end interaction – In a React app, install ethers@6, connect MetaMask, and call:

js
const pool = new ethers.Contract(poolAddr, poolAbi, signer);
await pool.deposit(usdcAmount);
const tx = await amm.swap(usdcAmount, USDC, DAI);
await stakingVault.stake(lpTokenAmount);

5. Run a “compound” bot – Schedule the compound() script (step 4) with a cron job or a serverless function to harvest and reinvest rewards automatically.


Common Mistakes

  • Mistake: Using tx.origin for access control.
    Correction: Use msg.sender and role‑based modifiers (onlyOwner, AccessControl). tx.origin can be spoofed through a malicious contract, opening a phishing vector.

  • Mistake: Forgetting to update the AMM’s invariant after a manual token transfer.
    Correction: Always call the AMM’s sync() or updateReserves() after any external token movement; otherwise price calculations become stale and arbitrageurs will drain the pool.

  • Mistake: Not accounting for slippage when swapping large amounts.
    Correction: Compute minAmountOut with getAmountOut and pass it to the swap call; revert if the market moves beyond your tolerance.

  • Mistake: Deploying a staking contract without a nonReentrant guard.
    Correction: Add the nonReentrant modifier to any function that transfers user tokens (deposit, withdraw, claim). This blocks classic reentrancy attacks that could double‑spend rewards.

  • Mistake: Assuming ERC‑20 transfer returns true and ignoring the return value.
    Correction: Use OpenZeppelin’s SafeERC20 wrapper (safeTransfer, safeTransferFrom) which reverts on failure and handles non‑standard tokens.


Blockchain Developer Interview / Practical Insights

  1. “call vs delegatecall” – Interviewers love to ask why delegatecall is used in proxy patterns (it runs the code in the context of the calling contract, preserving storage). A wrong use can let an attacker overwrite admin variables.
  2. ERC‑20 vs ERC‑777 – Explain that ERC‑777 adds hooks (tokensReceived) and can prevent accidental token loss, but it also introduces extra complexity and potential reentrancy; most DeFi primitives still stick to ERC‑20 for simplicity.
  3. Optimistic vs ZK Rollups – Be ready to compare latency (optimistic: ~1 day fraud proof window; ZK: instant finality) and gas cost (ZK rollups are cheaper for high‑throughput swaps). Show awareness of which rollup a given dApp targets.
  4. Flash‑loan safety – Auditors will check that any function callable via a flash loan validates that the loaned amount is returned (require(balanceAfter >= balanceBefore)). Missing this check leads to “unlimited loan” exploits.

Quick Check Questions

  1. Scenario: A contract uses tx.origin to restrict withdrawals to the original user.
    Answer: Dangerous – a malicious contract can trick a user into calling it, and tx.origin will still be the user’s address, allowing the attacker to withdraw funds.

  2. Scenario: You add 10 k USDC to a Uniswap V2 pool but forget to call sync().
    Answer: The pool’s reserves stay unchanged, so the price oracle becomes stale; arbitrage bots will exploit the mismatch and you’ll lose value.

  3. Scenario: A staking contract distributes rewards using block.timestamp instead of block.number.
    Answer: Timestamps can be manipulated by miners within a ~15‑second window, leading to slightly inaccurate reward calculations; using block numbers is deterministic.


Last‑Minute Cram Sheet (10 one‑liners)

  1. ⚠️ Reentrancy → always apply Checks‑Effects‑Interactions or OpenZeppelin’s nonReentrant.
  2. Gas tip: Use uint256 instead of uint128 when you need arithmetic overflow protection; the EVM works on 256‑bit words, so smaller types cost extra SLOAD/SSTORE.
  3. ERC‑20 = 0x3635 (standard fungible token); ERC‑4626 = tokenized vault, ERC‑777 = advanced token with hooks.
  4. AMM invariant: k = reserveX * reserveY must stay constant after each swap (minus fees).
  5. Flash loan safety: require(token.balanceOf(address(this)) >= amountBefore, "not repaid").
  6. Liquidity mining: Reward per block = totalReward / (endBlock - startBlock).
  7. Delegatecall runs callee code in caller’s storage → essential for upgradeable proxies.
  8. Optimistic Rollup → fraud proof window (usually 7 days) vs ZK Rollup → instant finality.
  9. SafeERC20safeTransferFrom(token, from, to, amount) prevents silent failures.
  10. Compiler version: Solidity 0.8.20 is the current LTS; it includes built‑in overflow checks, removing the need for SafeMath.


ADVERTISEMENT