返回 SC 笔记
SC Day 38

复习 - Week 6 总结 (DeFi 合约模式 + Solana 基础)

### 一、Week 6 学习路径回顾

2026-05-08
第二阶段:框架实战
ReviewWeek6DeFiSolanaVaultStakingFlashLoanPDAAnchor

日期: 2026-05-08 方向: Solidity / Solana 阶段: 第二阶段:框架实战 标签: #Review #Week6 #DeFi #Solana #Vault #Staking #FlashLoan #PDA #Anchor


今日目标

类型内容
学习系统回顾 Week 6 所有核心知识点,建立知识连接
实操完成自测题,用架构图梳理 DeFi 合约模式之间的关系
产出Week 6 知识图谱 + 自测 Quiz + 交互关系图

核心概念

一、Week 6 学习路径回顾

Week 6 (Day 32-37) 学习路径:

Day 32: Vault (ERC4626)          ─── Solidity DeFi 模式
Day 33: Solana 架构总览           ─── 跳转到 Solana
Day 34: StakingRewards            ─── 回到 Solidity DeFi
Day 35: Anchor Hello World        ─── Solana 框架实战
Day 36: FlashLoan                 ─── Solidity DeFi 高阶
Day 37: Counter + PDA             ─── Solana 核心概念

交替学习节奏:
  Solidity (DeFi模式) ↔ Solana (架构+框架)
  → 好处: 交叉对比加深理解
  → 挑战: 需要在两种心智模型之间切换

二、DeFi 合约三大模式对比

本周学习了 DeFi 中最重要的三种合约模式。它们不是孤立的——大多数 DeFi 协议是这三种模式的组合。

模式总览表

维度Vault (ERC4626)StakingRewardsFlashLoan (EIP3156)
核心思想存入资产 → 获得 share → share 增值质押代币 → 随时间获得奖励单笔交易内借→用→还
标准ERC-4626Synthetix 模式(事实标准)EIP-3156
参考实现Yearn/Compound/AaveSynthetix/Convex/SushiAave/dYdX/Uniswap
用户操作deposit → withdrawstake → claim → withdrawflashLoan → callback → repay
收益来源池子中资产增值(策略收益)外部注入的奖励代币手续费(借款者支付)
数学核心shares = assets * totalShares / totalAssetsrewardPerToken 累加器balanceAfter >= balanceBefore + fee
代币机制铸造/销毁 share token无新代币(奖励是外部注入的)无代币操作
时间因素无(share 按比例兑换)有(奖励随时间线性累积)无(一笔交易内完成)
风险策略亏损 → share 贬值奖励耗尽 → 收益归零还不上 → 整笔 revert

数学公式对比

============= Vault =============
存入时: shares = depositAmount × totalShares / totalAssets
取出时: assets = sharesAmount × totalAssets / totalShares

关键: totalAssets 增加(策略赚钱)→ 每个 share 可兑换更多 assets

数值例子:
  初始: Alice deposit 100 USDC, 获得 100 shares (1:1)
  收益: 池子从 100 → 120 USDC (策略赚了 20)
  现在: Alice 的 100 shares = 100 × 120/100 = 120 USDC
  Bob deposit 60 USDC: shares = 60 × 100/120 = 50 shares
  池子: 180 USDC, 150 shares

============= StakingRewards =============
全局累加器: rewardPerToken += (rewardRate × Δt) / totalStaked
用户奖励: earned = staked × (rewardPerToken - userRewardPerTokenPaid)

关键: 每次用户操作时才更新该用户的奖励快照

数值例子:
  T0: Alice stake 80, Bob stake 20 (total=100)
  T0→T100: rewardRate = 1/sec → 100 tokens
    rewardPerToken = 0 + (1 × 100) / 100 = 1.0
    Alice earned = 80 × 1.0 = 80 tokens (80%)
    Bob earned = 20 × 1.0 = 20 tokens (20%)

============= FlashLoan =============
检查: balanceAfter >= balanceBefore + fee
fee = amount × feeRate (通常 0.05%-0.09%)

关键: 原子性保证 — 不满足条件 → 整笔交易 revert

数值例子:
  借出 1,000,000 USDC, feeRate = 0.09%
  fee = 1,000,000 × 0.0009 = 900 USDC
  必须归还: 1,000,900 USDC
  套利利润 > 900 USDC + gas → 才值得做

三种模式的组合关系

真实 DeFi 协议中的组合:

Yearn Finance:
  ┌─────────────────────────────────────┐
  │          Vault (ERC4626)            │  ← 用户存入
  │  ┌─────────────────────────┐        │
  │  │    Strategy             │        │
  │  │  ┌──────────────────┐   │        │
  │  │  │  StakingRewards  │   │        │  ← 策略: 质押赚取奖励
  │  │  └──────────────────┘   │        │
  │  │  ┌──────────────────┐   │        │
  │  │  │    FlashLoan     │   │        │  ← 策略: 用闪电贷杠杆
  │  │  └──────────────────┘   │        │
  │  └─────────────────────────┘        │
  └─────────────────────────────────────┘

Aave:
  ┌──────────────────────┐
  │  Vault (aToken)      │  ← 存款(类 ERC4626,aToken 自动增值)
  │  FlashLoan Pool      │  ← 闪电贷(池中资金可被闪电贷使用)
  │  StakingRewards      │  ← stkAAVE 安全模块质押奖励
  └──────────────────────┘

Compound:
  ┌──────────────────────┐
  │  Vault (cToken)      │  ← 存款 → 铸造 cToken(和 ERC4626 类似的 share 模型)
  │  StakingRewards      │  ← COMP 代币挖矿奖励(流动性激励)
  └──────────────────────┘

三、Solana 基础概念回顾

本周还学习了 Solana 的架构基础和 Anchor 框架。

Solana 核心概念图

Solana 核心概念关系:

                    Transaction
                   ┌──────────────┐
                   │ Instruction 1│ ← (programId, accounts[], data)
                   │ Instruction 2│
                   │ Instruction 3│
                   └──────┬───────┘
                          │
              ┌───────────┼───────────┐
              ▼           ▼           ▼
          Program 1    Program 2   System Program
          (代码)       (代码)      (创建账户)
              │           │
              ▼           ▼
         Data Account  Data Account
         (状态)        (状态)
              │
              ▼
            PDA
         (程序拥有的
          确定性地址)

Solana vs Ethereum 核心差异总结

维度EthereumSolanaPM 视角影响
账户模型合约 = 代码+状态Program(代码) + Account(状态) 分离Solana 状态可以并行读写 → 更高 TPS
Gas 模型动态 Gas Price + Gas Limit固定基础费 + Priority Fee (compute units)Solana gas 更便宜且更可预测
执行模型串行(每次一笔交易)并行(Sealevel,不冲突的交易并行执行)Solana 适合高频场景(DEX/游戏)
合约语言Solidity (专用)Rust (通用)Solana 开发门槛更高
状态存储合约 storage slots独立的 Account(需付 rent)Solana 需要预先规划 space
Token 标准ERC20/721(每个代币一个合约)SPL Token(单一 program,不同 mint)Solana 上发币不需要写合约
访问控制msg.sender / onlyOwnerPDA seeds + Signer 验证概念相似但实现完全不同

Anchor 框架三大宏回顾

// ===== 1. #[program]: 定义程序指令 =====
#[program]
pub mod my_program {
    use super::*;

    // 每个 pub fn 就是一个可调用的指令
    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        // 业务逻辑
        Ok(())
    }
}

// ===== 2. #[derive(Accounts)]: 定义指令需要的账户 =====
#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = user, space = 8 + 32)]
    pub data: Account<'info, MyData>,

    #[account(mut)]
    pub user: Signer<'info>,

    pub system_program: Program<'info, System>,
}
// Anchor 自动: 验证签名、验证 owner、反序列化数据

// ===== 3. #[account]: 定义数据结构 =====
#[account]
pub struct MyData {
    pub authority: Pubkey,  // 32 bytes
    pub value: u64,         // 8 bytes
}
// Anchor 自动: Borsh 序列化/反序列化 + 8 byte discriminator

PDA 核心知识点

PDA (Program Derived Address) 总结:

推导: address = SHA256(seeds, bump, program_id)
      必须不在 ed25519 曲线上

Bump:
  - 从 255 递减查找
  - 第一个有效值 = canonical bump
  - 始终使用 canonical bump

Seeds 设计:
  - 全局单例: ["config"]
  - 用户关联: ["user_data", user_pubkey]
  - 多维: ["position", pool, user]

Anchor 约束:
  init:  创建新 PDA 账户(seeds + bump + payer + space)
  mut:   修改已有 PDA(seeds + bump = account.bump)
  has_one: 字段值匹配验证

关键特性:
  ✓ 确定性: 同 seeds → 同地址
  ✓ 无私钥: 不可伪造
  ✓ 程序可签: invoke_signed()

四、DeFi 合约交互关系图

一个完整的 DeFi 生态中,各合约之间的调用关系:

用户视角的 DeFi 交互:

                    ┌─────────┐
                    │  User   │
                    └────┬────┘
                         │
         ┌───────────────┼───────────────┐
         ▼               ▼               ▼
    ┌─────────┐    ┌──────────┐    ┌──────────┐
    │  DEX    │    │ Lending  │    │  Yield   │
    │ (Swap)  │    │ (Borrow) │    │ (Farm)   │
    └────┬────┘    └────┬─────┘    └────┬─────┘
         │              │               │
    ┌────┴────┐    ┌────┴─────┐    ┌────┴─────┐
    │  AMM    │    │  Vault   │    │  Vault   │ ← ERC4626
    │  Pool   │    │ (aToken) │    │ (yToken) │
    └────┬────┘    └────┬─────┘    └────┬─────┘
         │              │               │
         │         ┌────┴──────┐   ┌────┴──────────┐
         │         │FlashLoan  │   │ StakingRewards │
         │         │  Pool     │   │  (策略收益)     │
         │         └───────────┘   └────────────────┘
         │
    ┌────┴──────────┐
    │  Price Oracle  │ ← Chainlink
    └───────────────┘

合约间调用链:
  用户 → Vault.deposit()
  Vault → Strategy.invest()
  Strategy → DEX.swap() (重新平衡)
  Strategy → Lending.supply() (赚取利息)
  Strategy → FlashLoan.flashLoan() (杠杆操作)
  Lending → Oracle.getPrice() (计算抵押率)

五、Solidity 合约安全模式回顾

本周三个合约中反复出现的安全模式:

1. Checks-Effects-Interactions (CEI)
   ✓ Vault.withdraw():  检查 shares → 销毁 shares → 转出 assets
   ✓ FlashLoan:         记录余额 → 转出资金 → 回调 → 检查余额
   ✓ StakingRewards:    更新奖励 → 修改余额 → 转出代币

2. ReentrancyGuard
   ✓ Vault: deposit/withdraw 都需要
   ✓ FlashLoan: flashLoan 函数需要
   ✓ Staking: stake/withdraw/getReward 需要

3. SafeMath / checked_*
   ✓ Solidity 0.8+ 默认溢出检查
   ✓ 但除法仍需注意: 先乘后除避免精度丢失
   ✓ Vault 的 share 计算: mulDiv 或 OpenZeppelin Math

4. Access Control
   ✓ Vault: owner 管理策略
   ✓ Staking: rewardDistributor 设置奖励率
   ✓ FlashLoan: 任何人可借(但必须还)

5. 精度处理
   ✓ Vault: 首次存款攻击防护(虚拟偏移量或最小存款)
   ✓ Staking: rewardPerToken 使用 1e18 精度
   ✓ FlashLoan: fee 计算避免除法截断

代码实战

Week 6 合约模式速查代码

将三大模式的核心函数放在一起,方便对比学习:

// =================== Vault (ERC4626) 核心 ===================

function deposit(uint256 assets) external returns (uint256 shares) {
    // 计算能获得多少 shares
    shares = totalSupply() == 0
        ? assets                                        // 首次存款 1:1
        : assets * totalSupply() / totalAssets();       // 按比例

    // 转入资产
    asset.transferFrom(msg.sender, address(this), assets);

    // 铸造 shares 给用户
    _mint(msg.sender, shares);
}

function withdraw(uint256 shares) external returns (uint256 assets) {
    // 计算能取出多少 assets
    assets = shares * totalAssets() / totalSupply();

    // 销毁用户的 shares
    _burn(msg.sender, shares);

    // 转出资产
    asset.transfer(msg.sender, assets);
}


// =================== StakingRewards 核心 ===================

// 全局累加器: 每个已质押 token 累计获得的奖励
function rewardPerToken() public view returns (uint256) {
    if (totalStaked == 0) return rewardPerTokenStored;

    return rewardPerTokenStored +
        (rewardRate * (block.timestamp - lastUpdateTime) * 1e18) / totalStaked;
}

// 用户已赚取的奖励
function earned(address account) public view returns (uint256) {
    return (staked[account] * (rewardPerToken() - userRewardPerTokenPaid[account])) / 1e18
        + rewards[account];
}

// 修饰符: 每次操作前更新奖励
modifier updateReward(address account) {
    rewardPerTokenStored = rewardPerToken();
    lastUpdateTime = block.timestamp;
    rewards[account] = earned(account);
    userRewardPerTokenPaid[account] = rewardPerTokenStored;
    _;
}

function stake(uint256 amount) external updateReward(msg.sender) {
    totalStaked += amount;
    staked[msg.sender] += amount;
    stakingToken.transferFrom(msg.sender, address(this), amount);
}


// =================== FlashLoan (EIP3156) 核心 ===================

function flashLoan(
    IERC3156FlashBorrower receiver,
    address token,
    uint256 amount,
    bytes calldata data
) external returns (bool) {
    uint256 balanceBefore = IERC20(token).balanceOf(address(this));
    uint256 fee = amount * feeRate / 10000;

    // 1. 转出资金给借款者
    IERC20(token).transfer(address(receiver), amount);

    // 2. 调用借款者的回调(借款者在这里使用资金并归还)
    require(
        receiver.onFlashLoan(msg.sender, token, amount, fee, data)
            == keccak256("ERC3156FlashBorrower.onFlashLoan"),
        "Invalid callback return"
    );

    // 3. 验证资金已归还 + 手续费
    uint256 balanceAfter = IERC20(token).balanceOf(address(this));
    require(balanceAfter >= balanceBefore + fee, "Repay failed");

    return true;
}

Solana/Anchor 模式速查代码

// =================== Anchor 程序结构 ===================

use anchor_lang::prelude::*;

declare_id!("...");

#[program]
pub mod my_program {
    use super::*;

    pub fn initialize(ctx: Context<Initialize>, param: u64) -> Result<()> {
        let account = &mut ctx.accounts.my_account;
        account.authority = ctx.accounts.user.key();
        account.value = param;
        account.bump = ctx.bumps.my_account;
        Ok(())
    }

    pub fn update(ctx: Context<Update>, new_value: u64) -> Result<()> {
        ctx.accounts.my_account.value = new_value;
        Ok(())
    }
}

// =================== 账户约束模板 ===================

// 创建 PDA 账户
#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(
        init,                                          // 创建
        payer = user,                                  // 付 rent
        space = 8 + 32 + 8 + 1,                       // discriminator + fields
        seeds = [b"my_account", user.key().as_ref()],  // PDA seeds
        bump                                           // canonical bump
    )]
    pub my_account: Account<'info, MyAccount>,

    #[account(mut)]
    pub user: Signer<'info>,

    pub system_program: Program<'info, System>,
}

// 修改 PDA 账户
#[derive(Accounts)]
pub struct Update<'info> {
    #[account(
        mut,
        seeds = [b"my_account", user.key().as_ref()],
        bump = my_account.bump,
        has_one = authority @ MyError::Unauthorized  // 权限检查
    )]
    pub my_account: Account<'info, MyAccount>,

    pub authority: Signer<'info>,  // = my_account.authority
}

// 数据结构
#[account]
pub struct MyAccount {
    pub authority: Pubkey,  // 32
    pub value: u64,         // 8
    pub bump: u8,           // 1
}

// 错误码
#[error_code]
pub enum MyError {
    #[msg("Unauthorized")]
    Unauthorized,
}

自测 Quiz

Part 1: DeFi 合约模式 (10 题)

Q1: Vault 的 share 计算 如果一个 Vault 中有 1000 USDC 和 800 shares,Alice 存入 250 USDC,她获得多少 shares?

<details> <summary>答案</summary>
shares = depositAmount × totalShares / totalAssets
       = 250 × 800 / 1000
       = 200 shares

验证: 存入后 Vault 有 1250 USDC, 1000 shares
  Alice 的 200 shares = 200/1000 × 1250 = 250 USDC ✓
</details>

Q2: StakingRewards 累加器 Alice 质押了 100 tokens,Bob 质押了 400 tokens。rewardRate = 10 tokens/sec。过了 50 秒后,Alice 能获得多少奖励?

<details> <summary>答案</summary>
totalStaked = 100 + 400 = 500
rewardPerToken = (10 × 50) / 500 = 1.0
Alice earned = 100 × 1.0 = 100 tokens
Bob earned = 400 × 1.0 = 400 tokens
总奖励 = 500 = 10 × 50 ✓ (符合)
Alice 占比 = 100/500 = 20% → 500 × 20% = 100 ✓
</details>

Q3: FlashLoan 盈亏计算 用 Aave 闪电贷借了 500,000 USDC (fee rate 0.09%),在 DEX A 买入 ETH,在 DEX B 卖出,获得 501,000 USDC。Gas 费 50 USDC。这笔套利赚了多少?

<details> <summary>答案</summary>
闪电贷手续费 = 500,000 × 0.0009 = 450 USDC
总成本 = 450 (手续费) + 50 (gas) = 500 USDC
总收入 = 501,000 - 500,000 = 1,000 USDC
净利润 = 1,000 - 500 = 500 USDC

如果 DEX B 只卖了 500,400 USDC:
  利润 = 400 - 500 = -100 → 亏损
  但闪电贷会 revert!因为 500,400 < 500,450 (本金+手续费)
  → 实际不会亏损,交易直接失败(只损失 gas)
</details>

Q4: 为什么 ERC4626 Vault 有首次存款攻击风险?

<details> <summary>答案</summary>
攻击过程:
1. Vault 为空,Alice 准备存入 10,000 USDC
2. 攻击者抢先存入 1 wei USDC,获得 1 wei shares
3. 攻击者直接往 Vault 转 10,000 USDC (不通过 deposit)
4. 现在: totalAssets = 10,000 + 1 wei, totalShares = 1 wei
5. Alice 存入 10,000 USDC:
   shares = 10,000 × 1 / 10,000 ≈ 1 (向下取整可能为 0!)
6. Alice 可能获得 0 shares → 损失全部资金

防护:
  a) 虚拟偏移量: 内部乘以 1e6 计算
  b) 初始注入: 部署时预存 dead shares
  c) 最小存款限制
</details>

Q5: StakingRewards 中,为什么要用 modifier updateReward 而不是在 view 函数中实时计算?

<details> <summary>答案</summary>
view 函数 earned() 确实实时计算,但只是"查看"。

modifier updateReward 的作用:
1. 将计算结果持久化到 storage(rewardPerTokenStored, rewards[user])
2. 这是因为 solidity 无法在 view 函数中写入 storage
3. 如果不持久化,每次质押/取出时都要从 T=0 重新累加 → gas 随时间线性增长

本质: 这是一个 "lazy evaluation" 模式
  → 只在用户交互时更新(O(1) 写操作)
  → 不需要后台定时任务遍历所有用户
</details>

Q6: FlashLoan 中的 onFlashLoan 回调为什么要返回特定的 keccak256 哈希?

<details> <summary>答案</summary>
return keccak256("ERC3156FlashBorrower.onFlashLoan");

目的: 防止"意外接收"攻击

场景: 如果没有返回值检查
  1. 攻击者部署一个没有 onFlashLoan 函数的合约
  2. Solidity 的 fallback 函数被调用,不 revert
  3. 资金被转到了一个不会归还的合约

有了返回值检查:
  → 借款者必须显式实现 onFlashLoan 并返回正确的值
  → 确保借款者"知道自己在做什么"
  → 类似于 ERC721 的 onERC721Received 检查
</details>

Part 2: Solana 基础 (8 题)

Q7: Solana 的 Account 模型中,以下哪个说法是正确的? A) Program 和 Data Account 存储在一起 B) Program 是可变的,可以修改自身代码 C) Data Account 的 owner 决定了谁能修改它的数据 D) 每个 Account 可以有多个 owner

<details> <summary>答案</summary>
C) Data Account 的 owner 决定了谁能修改它的数据

A ✗: 代码与数据分离,这是 Solana 的核心设计
B ✗: Program 部署后默认不可变(除非用 upgradeable loader)
C ✓: 只有 owner program 能修改 Account 的 data 字段
D ✗: 每个 Account 有且只有一个 owner
</details>

Q8: PDA 的 bump seed 是什么?为什么从 255 开始找?

<details> <summary>答案</summary>
Bump 是一个 0-255 的整数,附加到 seeds 中用于生成 PDA。

目的: 确保生成的地址不在 ed25519 曲线上

从 255 开始的原因:
1. 保证确定性: 所有客户端都从 255 开始 → 找到的第一个有效 bump 一致
2. 这个第一个有效 bump = "canonical bump"
3. 如果从 0 开始找,也能工作,但社区约定从 255 开始
4. 高 bump 值不在曲线上的概率约 50%,通常 1-2 次就找到

实际: 大多数情况下 bump = 255 或 254
</details>

Q9: 在 Anchor 中,以下代码的 space 应该是多少?

#[account]
pub struct UserProfile {
    pub name: String,       // 最大 32 字符
    pub level: u16,
    pub is_active: bool,
    pub wallet: Pubkey,
}
<details> <summary>答案</summary>
space = 8                    // Anchor discriminator
      + (4 + 32)            // String: 4 bytes length prefix + 32 bytes content
      + 2                   // u16
      + 1                   // bool
      + 32                  // Pubkey
      = 79 bytes

注意:
  - String 的 4 bytes 是长度前缀(Borsh 序列化格式)
  - 如果 name 可能超过 32 字符,需要增加分配
  - 宁可多分配一点,不够会导致序列化失败
</details>

Q10: 为什么 Solana 上 Counter 用 PDA 而不是让用户自己创建一个普通 Account?

<details> <summary>答案</summary>
1. 确定性寻址:
   PDA = f(seeds, programId) → 客户端可以直接计算地址
   普通 Account = 随机 keypair → 需要额外存储映射关系

2. 安全性:
   PDA 没有私钥 → 只有程序能通过 CPI 签名
   普通 Account 有私钥 → 谁持有私钥谁就能直接修改

3. 一致性:
   seeds = ["counter", user] → 每个用户恰好一个 counter
   普通 Account → 用户可以创建多个(如何判断哪个是"官方的"?)

4. 可组合性:
   其他程序可以通过 seeds 计算出 PDA → 直接读取数据
   普通 Account → 必须通过链下索引查找
</details>

Part 3: 跨链对比 (4 题)

Q11: 把 Solidity 的 Vault 迁移到 Solana/Anchor,最大的架构差异是什么?

<details> <summary>答案</summary>
Solidity Vault:
  - 一个合约 = 代码 + 所有状态
  - mapping(address => uint256) shares; ← 所有用户的 shares 在一个 slot 树中
  - totalAssets() 直接读自身余额

Solana Vault:
  - Program = 只有代码
  - 每个用户的 deposit → 一个独立的 PDA Account
  - Vault 全局状态 → 另一个 PDA
  - Token 余额 → PDA 拥有的 Token Account (又一个独立账户)

最大差异: 状态的拆分方式
  Solidity: 一个合约里 → 串行读写
  Solana: 多个 Account → 可以并行读写不同用户的账户

影响:
  - Solana 需要预先声明所有要读写的 Account(显式)
  - 但获得了并行执行的性能优势
  - 编程模型更复杂,但扩展性更好
</details>

Q12: Solana 上能实现 FlashLoan 吗?和 Ethereum 有什么区别?

<details> <summary>答案</summary>
可以!但实现方式不同。

Ethereum FlashLoan:
  → 利用 EVM 的 "内部调用" 机制
  → 合约 A 调用合约 B → B 使用资金 → 回到 A 检查余额
  → 在一个 message call 栈中完成

Solana FlashLoan:
  → 利用 Transaction 的 "多 Instruction" 特性
  → Instruction 1: borrow(amount) — 从池子转出
  → Instruction 2-N: 用户自定义操作(swap, liquidate, etc.)
  → Instruction N+1: repay(amount + fee) — 归还
  → 如果最后余额不够 → 整个 Transaction revert

区别:
  Ethereum: 回调模式(borrower 实现 onFlashLoan)
  Solana: 多指令模式(在 Transaction 中编排多个 Instruction)
  Solana 模式更灵活(不需要部署特殊的 borrower 合约)
</details>

Q13: 为什么说 Solana 的 PDA 是"可组合性"的关键?

<details> <summary>答案</summary>
可组合性 = 不同协议之间能互相调用和读取数据

PDA 使可组合性成为可能:
1. 任何程序只要知道 seeds → 就能计算出 PDA 地址 → 读取数据
2. 不需要链下索引、不需要预言机、不需要额外的注册表

例子: DEX 程序想读取 Lending 协议中用户的抵押率
  Ethereum: 调用 Lending 合约的 view 函数
  Solana: 计算 PDA 地址 → 直接从 Account 数据反序列化

差异:
  Ethereum 的可组合性: 通过合约调用(需要知道合约地址和 ABI)
  Solana 的可组合性: 通过 PDA 推导(只需知道 seeds 约定和数据结构)
  两者都很强,但 Solana 的方式更"去中心化"(不依赖合约代码的可调用性)
</details>

Q14: 如果你是 PM,在选择 Ethereum 还是 Solana 时,从架构角度有哪些考量?

<details> <summary>答案</summary>
选 Ethereum (及 L2) 的场景:
  - DeFi 蓝筹协议(TVL 大,安全优先)
  - 需要和大量现有协议组合(Uniswap/Aave/Compound)
  - 用户群体习惯 MetaMask + EVM 链
  - 合约逻辑复杂但交易频率不高

选 Solana 的场景:
  - 高频交易/订单簿 DEX(需要高 TPS 低延迟)
  - 游戏/NFT(需要低 gas 和快速确认)
  - 消费级应用(用户对 gas 敏感)
  - 需要并行处理大量用户操作

PM 决策框架:
  1. 目标用户在哪条链?→ 去用户在的地方
  2. 需要与哪些协议组合?→ 生态决定平台
  3. TPS 和延迟要求?→ 高频 → Solana
  4. 开发者资源?→ Solidity 人才多于 Rust/Anchor
  5. 安全优先级?→ EVM 工具链更成熟(审计/形式验证)
</details>

关键要点总结

Week 6 的三个核心收获

1. DeFi 合约不是孤立的
   → Vault + Staking + FlashLoan 是 DeFi 的三块基石
   → 理解它们的组合方式 = 理解 DeFi 协议的架构

2. Solana 和 Ethereum 是两种完全不同的编程范式
   → Ethereum: 合约中心,状态内聚,串行执行
   → Solana: 账户中心,状态分散,并行执行
   → 选择取决于产品需求,不是"哪个更好"

3. PDA 是 Solana 的灵魂
   → 类似于 Ethereum 的 mapping,但更强大(可签名、跨程序寻址)
   → 掌握 PDA = 掌握 Solana 编程的核心

下周预告 (Week 7)

Day 39: SimpleAMM — 恒定乘积公式实现    ← DeFi 核心中的核心
Day 40: SPL Token + ATA + Token 铸造     ← Solana Token 体系
Day 41: MultiSig Wallet                  ← 安全模式
Day 42: Governance + Timelock            ← DAO 治理
Day 43: Oracle Integration               ← 价格预言机
Day 44: Week 7 复习                      ← 总结

常见误区

误区: "学了 Solidity 就不需要学 Solana,反之亦然"

✗ 错误: 只精通一个就够了
✓ 正确: 两种模型的对比学习能加深对底层原理的理解

为什么要两个都学:
1. 面试: 很多公司会问"你了解 Solana 吗?和 EVM 有什么区别?"
2. 产品设计: 多链战略需要理解每条链的 trade-off
3. 心智模型: 对比才能真正理解"为什么这样设计"
   - 只看 Ethereum → 觉得"合约存状态"是理所当然的
   - 看了 Solana → 才理解"代码和数据分离"的另一种哲学

误区: "FlashLoan 只能用来攻击"

✗ 错误: FlashLoan 是黑客工具
✓ 正确: FlashLoan 是资本效率的极致工具

正当用途:
  - 套利: 维持市场价格一致性(市场有益)
  - 自清算: 避免被协议惩罚清算
  - 抵押品切换: 一步完成仓位调整
  - Yield 优化: 闪电贷杠杆提升收益

攻击只是滥用,不是本质用途
  就像菜刀不是因为有人拿来伤人就变成了武器

面试关联

Q: 如果让你从零设计一个 DeFi 借贷协议,你会用到本周学的哪些模式?

完整借贷协议的架构:

1. Vault (ERC4626) — 存款池
   → 用户存入 USDC → 获得 aUSDC (share token)
   → aUSDC 的价值随利息累积自动增长

2. StakingRewards — 流动性激励
   → 质押 aUSDC → 获得协议治理代币奖励
   → 吸引更多存款 → TVL 增长

3. FlashLoan — 资金利用率
   → 池中闲置资金可被闪电贷使用
   → 手续费收入分给存款者
   → 提高 APY → 吸引更多存款

4. Oracle — 价格数据
   → 计算抵押率和清算线
   → 不是本周内容,但必不可少

5. Liquidation — 清算机制
   → 结合 FlashLoan 实现无资本清算
   → 保护协议偿付能力

参考资源

资源说明
ERC-4626 标准Vault 标准规范
Synthetix StakingRewardsStaking 参考实现
EIP-3156 FlashLoanFlashLoan 标准
Solana CookbookSolana 开发参考
Anchor BookAnchor 框架文档
Solana vs Ethereum官方开发者资源