Solidity开发者学习Solana的Anchor框架开发,需要注意的语法

  • Alan
  • 更新于 2024-09-22 22:18
  • 阅读 826

Solidity开发者在学习Solana的Anchor框架开发时,需要注意以下语法和概念上的差异。Anchor框架是Rust语言的Solana智能合约开发框架,旨在简化和标准化Solana程序(Program)的开发。它与Solidity在开发风格和范式上有较大不同

Solidity开发者在学习Solana的Anchor框架开发时,需要注意以下语法和概念上的差异。Anchor框架是Rust语言的Solana智能合约开发框架,旨在简化和标准化Solana程序(Program)的开发。它与Solidity在开发风格和范式上有较大不同,因此理解这些差异对于熟悉Solidity的开发者来说至关重要。

1. 账户模型 vs EVM模型

Solidity(EVM):

  • 基于以太坊虚拟机(EVM)的账户模型,每个账户都有自己的余额和存储空间。
  • 合约状态变量直接存储在合约的存储空间中,调用合约时可以直接访问这些状态变量。

Solana(Anchor):

  • 基于账户(Account)模型,每个账户都可以存储数据,合约逻辑(Program)不能直接存储状态。
  • 程序(Program)中的状态数据通常存储在单独的PDA(Program Derived Account)或其他账户中,调用程序时需要将这些账户作为参数传递。

注意

  • 在Solana中,所有数据的读写都依赖于账户,程序本身是无状态的。
  • 在调用时,需要显式地将相关账户传递给程序。

示例:

// Anchor 中的账户声明
#[account]
pub struct MyAccount {
    pub data: u64,  // 账户存储的数据
}

#[derive(Accounts)]
pub struct MyInstruction<'info> {
    #[account(mut)]
    pub my_account: Account<'info, MyAccount>,
}

2. 函数修饰符和访问控制

Solidity:

  • Solidity使用修饰符(如onlyOwnermodifier)来控制函数的访问权限。
  • 可以通过自定义修饰符来验证调用者身份和条件。

Anchor:

  • Anchor使用#[derive(Accounts)]和特定的属性标签(Attribute)来对函数调用进行访问控制和验证。
  • 访问控制通常通过在账户结构体(如ContextAccounts)中定义验证逻辑来实现。

注意

  • 在Anchor中,访问控制逻辑通常通过自定义的验证函数和检查账户条件实现,而不是像Solidity那样通过修饰符直接定义在函数上。

示例:

// Anchor 中的访问控制
#[program]
pub mod my_program {
    use super::*;

    // 定义一个公开的处理函数
    pub fn initialize(ctx: Context<Initialize>) -> ProgramResult {
        let my_account = &mut ctx.accounts.my_account;
        my_account.data = 42; // 初始化数据
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = user, space = 8 + 8)]
    pub my_account: Account<'info, MyAccount>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}

3. 数据结构和序列化

Solidity:

  • Solidity中数据结构(如结构体和数组)可以直接在合约中定义和使用,EVM会自动管理其内存布局和存储。
  • Solidity不需要显式地处理序列化和反序列化。

Anchor(Rust):

  • Anchor使用Rust的结构体定义数据结构,通常需要实现BorshSerializeBorshDeserialize trait来进行数据的序列化和反序列化。
  • 数据存储在账户中,访问时需要先从账户数据中反序列化为结构体,再进行操作。

注意

  • 在Anchor中,数据存储和读取时都需要显式地进行序列化和反序列化,这与Solidity中的状态变量读写方式不同。

示例:

use anchor_lang::prelude::*;
use anchor_lang::solana_program::entrypoint::ProgramResult;
use borsh::{BorshDeserialize, BorshSerialize};

#[derive(BorshSerialize, BorshDeserialize, Debug, Clone)]
pub struct MyData {
    pub value: u64,
}

#[account]
pub struct MyAccount {
    pub data: MyData,
}

4. 交易和上下文(Transaction and Context)

Solidity:

  • 在Solidity中,每个交易(transaction)对应一个状态更改操作,可以调用多个合约函数,所有上下文信息(如msg.sender)都内置在函数中。

Anchor:

  • Anchor中的每个程序调用通常对应一个特定的指令(instruction),所有需要的账户和数据都必须作为上下文(Context)传递给指令。
  • 函数的上下文通过ContextAccounts结构体来管理,调用时需要显式传递所有需要的账户。

注意

  • Anchor中的上下文传递更加明确,需要手动传递所有涉及的账户和数据,而Solidity中这些信息是隐式传递的。

示例:

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

    pub fn process(ctx: Context<MyInstruction>, new_value: u64) -> ProgramResult {
        let my_account = &mut ctx.accounts.my_account;
        my_account.data = new_value;  // 修改账户中的数据
        Ok(())
    }
}

#[derive(Accounts)]
pub struct MyInstruction<'info> {
    #[account(mut)]
    pub my_account: Account<'info, MyAccount>,  // 需要修改的账户
    pub user: Signer<'info>,  // 调用者(签名者)
}

5. 错误处理(Error Handling)

Solidity:

  • Solidity使用requireassertrevert进行错误处理。
  • 可以定义自定义错误码或消息,用于表示不同的错误情况。

Anchor:

  • Anchor使用Rust的Result类型进行错误处理,错误信息通过ProgramError或自定义的Error类型来返回。
  • Anchor中可以定义错误枚举来表示不同的错误,并与Result类型结合使用。

注意

  • Anchor的错误处理更加灵活和安全,但语法比Solidity更为复杂。

示例:

use anchor_lang::prelude::*;
use anchor_lang::solana_program::program_error::ProgramError;

#[error]
pub enum MyError {
    #[msg("The provided value is too small.")]
    ValueTooSmall,
}

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

    pub fn process(ctx: Context<MyInstruction>, new_value: u64) -> ProgramResult {
        if new_value < 10 {
            return Err(MyError::ValueTooSmall.into());
        }
        let my_account = &mut ctx.accounts.my_account;
        my_account.data = new_value;
        Ok(())
    }
}

6. 合约升级(Contract Upgrades)

Solidity:

  • Solidity的合约升级可以通过代理模式(Proxy)来实现,即通过一个代理合约指向实际合约的地址。
  • 升级时,只需要更改代理合约指向的逻辑合约地址。

Anchor:

  • Anchor通过管理程序状态的账户(state account)和数据迁移来实现合约升级。
  • 合约升级时,需要使用相应的工具(如anchor upgrade)并提供新的程序版本和数据结构的兼容性。

注意

  • Anchor的合约升级需要考虑数据结构的兼容性,必须确保新旧数据格式兼容或设计迁移逻辑。

示例:

// 假设原始的状态结构体
#[account]
pub struct OldState {
    pub data: u64,
}

// 新的状态结构体(可能增加了新字段)
#[account]
pub struct NewState {
    pub data: u64,
    pub new_field: String,
}

// 迁移逻辑:将旧状态转换为新状态
pub fn migrate(ctx: Context<Migrate>) -> ProgramResult {
    let old_state = &mut ctx.accounts.old_state;
    let new_state = &mut ctx.accounts.new_state;

    // 从旧状态复制数据到新状态
    new_state.data = old_state.data;
    new_state.new_field = String::from("default value");
    Ok(())
}

#[derive(Accounts)]
pub struct Migrate<'info> {
    #[account(mut)]
    pub old_state: Account<'info, OldState>,
    #[account(init, payer = user, space = 8 + 64)]
    pub new_state: Account<'info, NewState>,
    #[account(mut)]
    pub user: Signer<'info>,
}

7. 多账户操作(Multi-account Operations)

Solidity:

  • Solidity中多账户操作通常涉及多合约调用和委托授权(DelegateCall)。
  • 可以使用msg.sendermsg.value来管理跨账户操作。

Anchor:

  • Anchor中多账户操作通过在上下文中传递多个账户来实现,调用时所有相关账户都必须作为参数传递。
  • 可以通过条件检查确保所有账户满足特定的约束。

注意

  • Anchor中的多账户操作比Solidity更为复杂,但提供了更严格的账户验证和管理机制

示例:

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

    pub fn transfer_data(ctx: Context<TransferData>, amount: u64) -> ProgramResult {
        let from_account = &mut ctx.accounts.from_account;
        let to_account = &mut ctx.accounts.to_account;

        if from_account.data < amount {
            return Err(ProgramError::InsufficientFunds);
        }

        from_account.data -= amount;
        to_account.data += amount;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct TransferData<'info> {
    #[account(mut)]
    pub from_account: Account<'info, MyAccount>,
    #[account(mut)]
    pub to_account: Account<'info, MyAccount>,
    pub user: Signer<'info>,
}

总结

  1. 账户模型:Solana使用基于账户的模型,所有数据存储在账户中,而不是合约中。
  2. 访问控制:Anchor中访问控制通过账户结构体和上下文管理,而不是函数修饰符。
  3. 数据管理:Anchor需要显式地序列化和反序列化数据,而Solidity中数据管理较为自动化。
  4. 多账户操作:Anchor中所有涉及的账户都必须作为参数传递,而Solidity中账户和数据访问相对灵活。
  5. 合约升级:Anchor需要手动管理状态迁移和兼容性,而Solidity可以通过代理模式实现。

熟悉这些差异可以帮助Solidity开发者更快地适应Anchor框架的开发模式,从而更高效地构建和管理Solana程序。

点赞 1
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Alan
Alan
0x9cAD...0097
区块链BTC、ETH、BNB