合约架构

概述

Zanbara 智能合约是基于 Solana 区块链和 Anchor 框架构建的永续合约交易系统核心组件。本文档详细介绍了合约的整体架构设计、技术选型和实现原理。

技术栈

Solana 区块链

Zanbara 选择 Solana 作为底层区块链平台,主要基于以下优势:

  • 高性能:支持 50,000+ TPS,满足高频交易需求

  • 低成本:交易费用远低于以太坊,适合频繁链上操作

  • 快速确认:400ms 出块时间,提供接近中心化交易所的体验

  • 并行执行:Sealevel 运行时支持并行交易处理,提升吞吐量

Anchor 框架

Anchor 是 Solana 智能合约开发的事实标准框架,提供:

  • 类型安全:强类型账户验证和序列化

  • 简化开发:宏驱动的开发体验,减少样板代码

  • 自动化安全:内置账户验证、权限检查等安全机制

  • IDL 生成:自动生成接口定义语言文件,便于前端集成

  • 测试工具:完整的测试框架和本地验证器支持

# Anchor.toml
[features]
seeds = false
skip-lint = false

[programs.localnet]
zanbara_dex = "BaTAwfL7msaQ2QGvN7EYqt2MKtjs6TGc1wzNN1BTP8F"

[programs.devnet]
zanbara_dex = "BaTAwfL7msaQ2QGvN7EYqt2MKtjs6TGc1wzNN1BTP8F"

[registry]
url = "https://api.apr.dev"

[provider]
cluster = "Devnet"
wallet = "~/.config/solana/id.json"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

程序架构

Program ID

Zanbara 智能合约的 Program ID:

declare_id!("BaTAwfL7msaQ2QGvN7EYqt2MKtjs6TGc1wzNN1BTP8F");

该地址在部署时确定,是合约在 Solana 网络上的唯一标识符。所有与合约的交互都通过此 Program ID 进行。

模块结构

Zanbara 合约采用模块化设计,将功能划分为多个独立模块:

pub mod state;          // 状态定义模块
pub mod instructions;   // 指令处理模块
pub mod errors;         // 错误定义模块
pub mod constants;      // 常量定义模块
pub mod oracle;         // 预言机集成模块
pub mod risk;           // 风险管理模块

state 模块

定义链上存储的所有数据结构:

  • UserAccount:用户账户,存储用户余额、抵押品和账户状态

  • Position:持仓记录,存储单个市场的持仓信息

  • Market:市场配置,存储交易对参数、费率和限制

instructions 模块

实现所有可调用的合约指令:

  • initialize_user:初始化用户账户

  • initialize_market:初始化交易市场(仅管理员)

  • deposit:存入抵押品

  • withdraw:提取资金

  • open_position:开仓或调整持仓

  • close_position:平仓

  • update_price:更新市场价格(从预言机)

  • liquidate:清算低保证金账户

oracle 模块

集成 Pyth Network 预言机:

  • 价格数据获取和验证

  • 时效性检查

  • 置信度评估

  • 价格格式转换

risk 模块

风险管理和保证金计算:

  • 初始保证金率计算

  • 维持保证金率计算

  • 清算价格计算

  • 杠杆倍数验证

程序入口点

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

    /// 初始化用户账户
    pub fn initialize_user(ctx: Context<InitializeUser>) -> Result<()> {
        instructions::initialize_user::handler(ctx)
    }

    /// 存入抵押品
    pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
        instructions::deposit::handler(ctx, amount)
    }

    /// 提取资金
    pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
        instructions::withdraw::handler(ctx, amount)
    }

    /// 开仓或调整持仓
    pub fn open_position(
        ctx: Context<OpenPosition>,
        market_index: u16,
        size: i64,
        is_long: bool,
    ) -> Result<()> {
        instructions::open_position::handler(ctx, market_index, size, is_long)
    }

    /// 平仓
    pub fn close_position(ctx: Context<ClosePosition>, market_index: u16) -> Result<()> {
        instructions::close_position::handler(ctx, market_index)
    }

    /// 初始化市场(仅管理员)
    pub fn initialize_market(
        ctx: Context<InitializeMarket>,
        market_index: u16,
        oracle: Pubkey,
    ) -> Result<()> {
        instructions::initialize_market::handler(ctx, market_index, oracle)
    }

    /// 更新市场价格
    pub fn update_price(ctx: Context<UpdatePrice>, market_index: u16) -> Result<()> {
        instructions::update_price::handler(ctx, market_index)
    }

    /// 清算低保证金持仓
    pub fn liquidate(ctx: Context<Liquidate>, market_index: u16) -> Result<()> {
        instructions::liquidate::handler(ctx, market_index)
    }
}

账户模型与 PDA

Program Derived Addresses (PDA)

Zanbara 使用 PDA 确保账户地址的确定性和安全性。PDA 是从 Program ID 和种子(seeds)派生的账户地址,无私钥,只能由程序本身控制。

优势

  1. 确定性:相同的种子始终生成相同的地址

  2. 安全性:无私钥意味着只有程序可以签名

  3. 简化查询:客户端可以计算地址,无需额外查询

种子设计

// constants.rs
pub const USER_ACCOUNT_SEED: &[u8] = b"user_account";
pub const POSITION_SEED: &[u8] = b"position";
pub const MARKET_SEED: &[u8] = b"market";
pub const VAULT_SEED: &[u8] = b"vault";

PDA 派生示例

// 用户账户 PDA
// seeds = [b"user_account", user_pubkey]
let (user_account_pda, user_account_bump) = Pubkey::find_program_address(
    &[USER_ACCOUNT_SEED, user.key().as_ref()],
    program_id,
);

// 持仓 PDA
// seeds = [b"position", user_pubkey, market_index]
let (position_pda, position_bump) = Pubkey::find_program_address(
    &[POSITION_SEED, user.key().as_ref(), &market_index.to_le_bytes()],
    program_id,
);

// 市场 PDA
// seeds = [b"market", market_index]
let (market_pda, market_bump) = Pubkey::find_program_address(
    &[MARKET_SEED, &market_index.to_le_bytes()],
    program_id,
);

账户关系图

┌─────────────────────────────────────────────────────────────┐
│                      Zanbara Program                         │
│               (BaTAwfL7msaQ2QGvN7...BTP8F)                  │
└─────────────────────────────────────────────────────────────┘

        ┌───────────────────┼───────────────────┐
        │                   │                   │
        ▼                   ▼                   ▼
┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│ Market (PDA) │    │ User Account │    │  Vault (PDA) │
│              │    │    (PDA)     │    │              │
│ - ETH-PERP   │    │              │    │ - Collateral │
│ - BTC-PERP   │    │ - Balance    │    │ - Pool       │
│ - SOL-PERP   │◄───┤ - Positions  │───►│              │
└──────────────┘    └──────────────┘    └──────────────┘
       │                    │
       │                    │
       └────────┬───────────┘


        ┌──────────────┐
        │Position (PDA)│
        │              │
        │ - Market     │
        │ - Size       │
        │ - Entry Price│
        │ - Collateral │
        └──────────────┘


        ┌──────────────┐
        │ Oracle Price │
        │  (Pyth)      │
        │              │
        │ - BTC/USD    │
        │ - ETH/USD    │
        │ - SOL/USD    │
        └──────────────┘

状态管理

账户数据持久化

Solana 采用账户模型(Account Model),所有数据存储在账户中。Zanbara 使用 Anchor 的 #[account] 宏自动处理序列化和反序列化。

#[account]
#[derive(Default)]
pub struct UserAccount {
    pub authority: Pubkey,        // 32 bytes
    pub collateral: u64,          // 8 bytes
    pub available_balance: u64,   // 8 bytes
    pub total_pnl: i64,           // 8 bytes
    pub active_positions: u16,    // 2 bytes
    pub cumulative_fees: u64,     // 8 bytes
    pub created_at: i64,          // 8 bytes
    pub last_updated: i64,        // 8 bytes
    pub bump: u8,                 // 1 byte
}

impl UserAccount {
    // 账户空间大小计算
    pub const LEN: usize = 8 +     // discriminator (Anchor 自动添加)
        32 +                       // authority
        8 +                        // collateral
        8 +                        // available_balance
        8 +                        // total_pnl
        2 +                        // active_positions
        8 +                        // cumulative_fees
        8 +                        // created_at
        8 +                        // last_updated
        1;                         // bump
}

账户空间租金

Solana 要求账户支付租金(rent)以保持数据存活。Zanbara 账户在创建时支付足额租金,确保账户永久免租金。

// 创建账户时计算所需租金
let rent = Rent::get()?;
let space = UserAccount::LEN;
let lamports = rent.minimum_balance(space);

并发控制

Solana 的并行执行模型允许不冲突的交易同时处理。Zanbara 通过账户设计确保:

  • 不同用户的操作可以并行执行(不同的 UserAccount)

  • 同一用户的不同市场持仓可以并行操作(不同的 Position)

  • 只有涉及相同账户的交易会串行执行

跨程序调用 (CPI)

System Program CPI

用于 SOL 转账:

use anchor_lang::system_program::{transfer, Transfer};

pub fn handler(ctx: Context<Deposit>, amount: u64) -> Result<()> {
    // 从用户钱包转账到用户账户 PDA
    transfer(
        CpiContext::new(
            ctx.accounts.system_program.to_account_info(),
            Transfer {
                from: ctx.accounts.user.to_account_info(),
                to: ctx.accounts.user_account.to_account_info(),
            },
        ),
        amount,
    )?;

    Ok(())
}

SPL Token Program CPI

用于 USDC 等代币操作:

use anchor_spl::token::{self, Transfer, Token, TokenAccount};

pub fn transfer_collateral(
    ctx: &Context<SomeInstruction>,
    amount: u64,
) -> Result<()> {
    let cpi_accounts = Transfer {
        from: ctx.accounts.user_token_account.to_account_info(),
        to: ctx.accounts.vault_token_account.to_account_info(),
        authority: ctx.accounts.user.to_account_info(),
    };

    let cpi_program = ctx.accounts.token_program.to_account_info();
    let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);

    token::transfer(cpi_ctx, amount)?;
    Ok(())
}

Pyth Oracle CPI

读取价格数据:

use pyth_sdk_solana::state::SolanaPriceAccount;

pub fn get_oracle_price(oracle_account: &AccountInfo) -> Result<u64> {
    let price_feed = SolanaPriceAccount::account_info_to_feed(oracle_account)
        .map_err(|_| PerpError::InvalidOracle)?;

    let clock = Clock::get()?;
    let current_price = price_feed
        .get_price_no_older_than(clock.unix_timestamp, 60)
        .ok_or(PerpError::StalePriceData)?;

    Ok(current_price.price as u64)
}

混合架构设计

Zanbara 采用链下撮合 + 链上结算的混合架构,平衡性能和去中心化:

链下组件

撮合引擎(基于 PeakPoilt):

  • 高性能订单簿维护

  • 实时订单撮合

  • 价格发现

  • 成交记录生成

优势

  • 超低延迟(< 1ms)

  • 高吞吐量(100,000+ orders/s)

  • 复杂订单类型支持

  • 无链上交易费用

链上组件

智能合约

  • 用户资金托管

  • 持仓状态管理

  • 结算验证和执行

  • 清算处理

  • 预言机价格更新

优势

  • 资金安全由区块链保障

  • 透明可审计

  • 无需信任中心化撮合

  • 抗审查

协作流程

用户下单


链下撮合引擎

    ├─► 订单簿更新
    ├─► 价格发现
    └─► 生成成交


    批量结算


    链上合约

         ├─► 验证成交合法性
         ├─► 更新用户余额
         ├─► 更新持仓状态
         └─► 计算手续费

安全机制

权限验证

Anchor 提供强类型的账户验证:

#[derive(Accounts)]
pub struct Withdraw<'info> {
    #[account(
        mut,
        seeds = [USER_ACCOUNT_SEED, user.key().as_ref()],
        bump = user_account.bump,
        has_one = authority @ PerpError::Unauthorized  // 验证 authority 匹配
    )]
    pub user_account: Account<'info, UserAccount>,

    #[account(mut)]
    pub authority: Signer<'info>,  // 必须是签名者

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

数值溢出保护

使用 Rust 的 checked 算术运算:

pub fn deposit(&mut self, amount: u64) -> Result<()> {
    self.collateral = self.collateral
        .checked_add(amount)
        .ok_or(PerpError::ArithmeticOverflow)?;

    self.available_balance = self.available_balance
        .checked_add(amount)
        .ok_or(PerpError::ArithmeticOverflow)?;

    Ok(())
}

重入攻击防护

Solana 运行时自动防止重入攻击,因为交易是原子性的,且账户锁定机制确保同一账户不会被同一交易重复修改。

预言机验证

pub fn validate_oracle_price(
    oracle_price: &OraclePrice,
    max_age: i64,
    max_confidence_pct: u64,
) -> Result<()> {
    // 检查价格时效性
    require!(
        !oracle_price.is_stale(max_age)?,
        PerpError::StalePriceData
    );

    // 检查置信区间
    let max_conf = (oracle_price.price.abs() as u64)
        .checked_mul(max_confidence_pct)
        .and_then(|v| v.checked_div(10000))
        .ok_or(PerpError::ArithmeticOverflow)?;

    require!(
        oracle_price.conf <= max_conf,
        PerpError::InvalidOracle
    );

    Ok(())
}

升级策略

程序升级权限

Solana 程序可以设置升级权限(upgrade authority):

# 部署时指定升级权限
solana program deploy \
  --program-id <PROGRAM_ID> \
  --upgrade-authority <AUTHORITY_KEYPAIR> \
  target/deploy/zanbara_dex.so

# 转移升级权限到多签
solana program set-upgrade-authority \
  <PROGRAM_ID> \
  --new-upgrade-authority <MULTISIG_PUBKEY>

数据迁移

由于账户结构变更可能影响现有数据,升级时需考虑:

  1. 向后兼容:新字段添加到结构末尾

  2. 版本标识:账户包含版本号字段

  3. 迁移指令:提供数据迁移指令

#[account]
pub struct UserAccountV2 {
    pub version: u8,              // 新增版本字段
    pub authority: Pubkey,
    pub collateral: u64,
    // ... 现有字段
    pub new_field: u64,           // 新增字段
}

pub fn migrate_user_account(ctx: Context<MigrateAccount>) -> Result<()> {
    let account = &mut ctx.accounts.user_account;
    if account.version < 2 {
        // 执行迁移逻辑
        account.new_field = 0;
        account.version = 2;
    }
    Ok(())
}

不可变部署

对于完全审计的生产版本,可以撤销升级权限,使程序变为不可变:

solana program set-upgrade-authority \
  <PROGRAM_ID> \
  --new-upgrade-authority none

性能优化

计算单元优化

Solana 限制每笔交易的计算单元(Compute Units)。优化建议:

  1. 避免不必要的计算:缓存重复计算结果

  2. 使用高效算法:选择 O(1) 而非 O(n) 操作

  3. 减少账户加载:只加载必需的账户

  4. 批量操作:合并多个小操作为一个大操作

// 优化前:多次计算
let price1 = calculate_liquidation_price(...);
let price2 = calculate_liquidation_price(...);  // 重复计算

// 优化后:缓存结果
let price = calculate_liquidation_price(...);
// 重用 price

账户大小优化

减小账户大小可降低租金成本:

  • 使用紧凑的数据类型(u16 而非 u64)

  • 避免存储可计算的数据

  • 使用位标志(bit flags)代替多个布尔值

// 优化前
pub struct Market {
    pub is_active: bool,     // 1 byte
    pub is_paused: bool,     // 1 byte
    pub is_closed: bool,     // 1 byte
    // ... 3 bytes
}

// 优化后
pub struct Market {
    pub status: u8,          // 1 byte (0=active, 1=paused, 2=closed)
    // ... 节省 2 bytes
}

开发工具链

本地开发环境

# 安装 Anchor CLI
cargo install --git https://github.com/coral-xyz/anchor anchor-cli --locked

# 启动本地验证器
solana-test-validator

# 构建合约
anchor build

# 运行测试
anchor test

# 部署到本地
anchor deploy

IDL 生成

Anchor 自动生成 IDL(Interface Definition Language)文件:

{
  "version": "0.1.0",
  "name": "zanbara_dex",
  "instructions": [
    {
      "name": "initializeUser",
      "accounts": [
        { "name": "userAccount", "isMut": true, "isSigner": false },
        { "name": "user", "isMut": true, "isSigner": true },
        { "name": "systemProgram", "isMut": false, "isSigner": false }
      ],
      "args": []
    },
    {
      "name": "deposit",
      "accounts": [
        { "name": "userAccount", "isMut": true, "isSigner": false },
        { "name": "user", "isMut": true, "isSigner": true },
        { "name": "systemProgram", "isMut": false, "isSigner": false }
      ],
      "args": [
        { "name": "amount", "type": "u64" }
      ]
    }
  ],
  "accounts": [
    {
      "name": "UserAccount",
      "type": {
        "kind": "struct",
        "fields": [
          { "name": "authority", "type": "publicKey" },
          { "name": "collateral", "type": "u64" },
          { "name": "availableBalance", "type": "u64" }
        ]
      }
    }
  ]
}

前端集成

使用 Anchor TS 客户端:

import { Program, AnchorProvider, web3 } from '@coral-xyz/anchor';
import { Zanbara } from './idl/zanbara_dex';
import idl from './idl/zanbara_dex.json';

const provider = AnchorProvider.env();
const program = new Program<Zanbara>(idl as Zanbara, provider);

// 调用合约指令
const tx = await program.methods
  .deposit(new BN(1000000))
  .accounts({
    userAccount: userAccountPda,
    user: wallet.publicKey,
    systemProgram: web3.SystemProgram.programId,
  })
  .rpc();

相关资源

  • Solana 官方文档:https://docs.solana.com/

  • Anchor 框架文档:https://www.anchor-lang.com/

  • Pyth Network:https://pyth.network/developers

  • Solana Cookbook:https://solanacookbook.com/

  • Solana Program Library:https://spl.solana.com/

总结

Zanbara 智能合约架构体现了现代 DeFi 应用的最佳实践:

  • 安全第一:多层次权限验证和数值保护

  • 高性能:优化的计算和存储设计

  • 可扩展:模块化架构便于功能扩展

  • 开发者友好:强类型 + IDL + 完善的工具链

  • 混合架构:链下性能 + 链上安全

通过深入理解这些架构设计,您将能够有效地与 Zanbara 合约集成,或基于此架构构建自己的永续合约系统。

Last updated