这次一定好好学 Solana (3) : 交易和费用

  • dgu
  • 发布于 2025-03-21 21:59
  • 阅读 544

这篇应该是正式写代码前最后的理论知识了,我这篇绝对比看官网文档效率高,官网像一个冰冷的机器,而我的是一个有温度的肉体Solana交易入门:从拼装参数到SVM执行我之前学过BTC、Sui和CKB的开发,感觉区块链交易这东西其实挺简单的。说白了,跟Web2调用接口没啥本质

这篇应该是正式写代码前最后的理论知识了, 我这篇绝对比看官网文档效率高, 官网像一个冰冷的机器, 而我的是一个有温度的肉体


Solana 交易入门:从拼装参数到 SVM 执行

我之前学过 BTC、Sui 和 CKB 的开发,感觉区块链交易这东西其实挺简单的。说白了,跟 Web2 调用接口没啥本质区别,就是拼装参数,扔给网络执行。Solana 也不例外,无论是用 SDK 的高级方法直接调用业务逻辑,还是手动拼装底层交易结构,最终都是为了生成一个符合规范的交易包。

下面我结合官方文档和自己的理解,梳理一下 Solana 交易的要点,顺便分享一个转账 SOL 的 JSON 示例,帮你快速入门。


交易的本质和执行流程

Solana 的交易(Transaction)本质上是一个数据包,提交后会在 Solana 虚拟机(SVM)中执行。执行者是当前 slot 的 Leader,执行完后广播给其他 Validator 验证。出于网络性能考虑,交易数据包大小被控制在 1232 字节以内,比 IPv4/IPv6 的 1500 字节 MTU 小得多,避免了分片或重组失败的风险。

一个 Transaction 可以包含多个指令(Instruction),这些指令按你在代码中添加的顺序依次执行,整个交易是原子性的——要么全成功,要么全失败,跟数据库事务或银行转账一个道理。


交易的组成:Instruction 是核心

每个 Instruction 是交易的基本单元,定义了:

  • 调用哪个程序(Program):通过 programIdIndex 指定。
  • 操作哪些账户:通过 accounts 指定账户索引。
  • 传递什么参数:通过 data 定义具体方法和参数。

用代码调用时,SDK 提供两种方式:

  1. 高级方式:直接调用封装好的方法,比如 SystemProgram.transfer,简单省事。
  2. 底层方式:手动拼装 Instruction,写起来复杂,但灵活性更高。

一个转账 SOL 的交易 JSON 示例

我们来看一个实际的转账交易 JSON(发送时是二进制形式,这里是人类可读的表示):

{
  "transaction": {
    "message": {
      "accountKeys": [
        "3z9vL1zjN6qyAFHhHQdWYRTFAcy69pJydkZmSFBKHg1R",
        "5snoUseZG8s8CDFHrXY2ZHaCrJYsW457piktDmhyb5Jd",
        "11111111111111111111111111111111"
      ],
      "header": {
        "numReadonlySignedAccounts": 0,
        "numReadonlyUnsignedAccounts": 1,
        "numRequiredSignatures": 1
      },
      "recentBlockhash": "DzfXchZJoLMG3cNftcf2sw7qatkkuwQf4xH15N5wkKAb",
      "instructions": [
        {
          "accounts": [0, 1],
          "programIdIndex": 2,
          "data": "3Bxs4NN8M2Yn4TLb"
        }
      ]
    },
    "signatures": [
      "5LrcE2f6uvydKRquEJ8xp19heGxSvqsVbcqUeFoiWbXe8JNip7ftPQNTAVPyTK7ijVdpkzmKKaAQR7MWMmujAhXD"
    ]
  }
}

这个交易是从账户 3z9v... 转账 100 lamports 到 5sno...,调用的是 SystemProgramTransfer 方法。下面逐一拆解。


交易结构详解

1. accountKeys

  • 作用:列出交易中所有涉及的账户公钥,包括发起者、被操作账户和程序账户。
  • 为什么提前定义:节省空间!后续 instructions 只需要用索引引用,避免重复写 32 字节的公钥。
  • 示例解析
    • accountKeys[0]: "3z9v..."(付款方)。
    • accountKeys[1]: "5sno..."(收款方)。
    • accountKeys[2]: "1111..."SystemProgram,程序也是账户!)。

2. header

  • 作用:定义 accountKeys 中账户的角色和属性,分为四类:

    • 签名账户:发起交易,需要私钥签名。
    • 非签名账户:被操作的对象,比如收款账户。
    • 可写账户:状态会改变,比如转账时的发送方和接收方。
    • 只读账户:只读不改,比如程序账户或配置账户。
  • 排序规则accountKeys 的顺序是固定的:先签名后非签名,先可写后只读。

  • 示例解析

    • numRequiredSignatures = 1:前 1 个账户(3z9v...)需要签名。
    • numReadonlySignedAccounts = 0:签名账户中没有只读的,3z9v... 是可写的。
    • numReadonlyUnsignedAccounts = 1:非签名账户(5sno...1111...)中,最后 1 个(1111...)是只读的。
    • 结果:
    • 3z9v...:签名 + 可写(付款方)。
    • 5sno...:无签名 + 可写(收款方)。
    • 1111...:无签名 + 只读(SystemProgram)。
  • 一个比较极限的例子

    • 假设 accountKeys = [A, B, C, D, E, F, G, H, L, M](10 个账户):
    • num_required_signatures = 5[A, B, C, D, E] 需要签名。
    • num_readonly_signed_accounts = 2:签名账户中后 2 个(D, E)是只读,[A, B, C] 是可写。
    • num_readonly_unsigned_accounts = 2:非签名账户 [F, G, H, L, M] 中后 2 个(L, M)是只读,[F, G, H] 是可写。
    • 最终角色:
    • A, B, C:签名 + 可写。
    • D, E:签名 + 只读。
    • F, G, H:无签名 + 可写。
    • L, M:无签名 + 只读。

3. recentBlockhash

  • 作用
    • 防止重放攻击:确保交易只能在短时间内有效。
    • 交易时效性:引用最近的区块哈希,过期(约 150 个槽位,1-2 分钟)后无效。
    • 签名验证:包含在 message 中,签名者对其签名。
    • 交易排序:帮助网络按时间顺序处理。
  • 来源:通过 SDK 的 getLatestBlockhash 从 RPC 获取(高层方法如 sendAndConfirmTransaction 会自动处理)。
  • 示例"DzfXchZJoLMG3cNftcf2sw7qatkkuwQf4xH15N5wkKAb" 是某个已确认区块的哈希。

4. instructions

  • 作用:定义具体的操作指令。
  • 字段解析
    • programIdIndex:指向 accountKeys 中的程序账户,决定调用哪个程序。
    • 示例:2 -> accountKeys[2] -> SystemProgram
    • accounts:指向 accountKeys 的索引,指定操作哪些账户,顺序由程序定义。
    • 示例:[0, 1] -> 操作 3z9v...(付款方)和 5sno...(收款方)。
    • data:指定调用程序的哪个方法和参数。
    • 示例:"3Bxs4NN8M2Yn4TLb" 解码为 [2, 0, 0, 0, 100, 0, 0, 0]
      • [2, 0, 0, 0]Transfer 指令(ID = 2)。
      • [100, 0, 0, 0, 0, 0, 0, 0]:转账金额 100 lamports。
  • 执行逻辑SystemProgramTransferaccounts[0] 转账 100 lamports 到 accounts[1]

5. signatures

  • 作用:签名账户对 message 的签名,证明交易授权。
  • 示例:只有一个签名,对应 accountKeys[0]3z9v...)。

交易费用(简单提一下)

  • 基本费用(Base Fee):固定费用,一半销毁,一半给 Validator。
  • 优先费用(Prioritization Fee):可选,100% 给 Validator,提升交易优先级。
  • 创建账户费用:如果接收方没有账户,会自动创建,需支付最小免租余额(rent-exempt)。

具体如何计算暂不深入,以后有需要再研究


总结:交易其实不复杂

Solana 的交易跟其他区块链类似,就是拼装参数扔给网络执行。核心是理解 accountKeysheaderinstructions 的关系:

  • accountKeys 定义所有账户。
  • header 指定账户角色。
  • instructions 描述具体操作。

用 SDK 时,高级方法省心,底层拼装灵活,选哪种看需求。希望这篇笔记能帮你快速上手 Solana 交易开发,有问题欢迎留言讨论!


  • 原创
  • 学分: 21
  • 分类: Solana
  • 标签:
点赞 1
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
dgu
dgu
江湖只有他的大名,没有他的介绍。