|
| 1 | +# [优先费与快速上链](/2024/11/solana_priority_fee.md) |
| 2 | + |
| 3 | +如何调优先费让交易快速上链——从agave源码剖析为什么jito适合套利?什么时候用优先费什么时候用jito? |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +判断SOL网络拥堵的指标, SOL 域名解析商 sns.id 网页上右上角基本都是显示 `Congested network`, 从 solscan 等区块浏览器上看 sol TPS 普遍都有 4000+ 但 true TPS 真正属于用户交易的其实就只有几百 TPS,其余的 TPS 都是网络共识层投票等。 |
| 8 | + |
| 9 | +网络拥堵时,交易员/开发者/套利者等发出去的交易常常等很久都不上链,也听space上开发者抱怨交易很难上链或者优先费用卷的很高,如何利用**优先费用(priority fee)**或者**jito小费(jito tips)**机制提高交易成功率和上链速度呢? |
| 10 | + |
| 11 | +## 优先费用机制 |
| 12 | + |
| 13 | +1. 交易带 SetComputeUnitPrice 指令才启用 优先费机制 |
| 14 | +2. 交易带 SetComputeUnitPrice 但没 SetComputeUnitLimit 指令 默认按 20万 ComputeUnitLimit/每条指令 |
| 15 | +3. 交易同时带 CU price/limit 两个指令(不管指令顺序),不管交易成功或失败都收取 price*limit 的优先费用 |
| 16 | + |
| 17 | +ComputeUnit往后简称为CU, [sol官方文档 how-to-use-priority-fees](https://solana.com/developers/guides/advanced/how-to-use-priority-fees) 中有个[规则2的示例交易(solscan)](https://solscan.io/tx/5scDyuiiEbLxjLUww3APE9X7i8LE3H63unzonUwMG7s2htpoAGG17sgRsNAhR1zVs6NQAnZeRVemVbkAct5myi17) |
| 18 | + |
| 19 | +文档说 `set the Compute Unit Limit to 300 CUs while also adding a priority fee of 20000 micro-lamports` |
| 20 | + |
| 21 | +实际上这个交易忘了设置 CU limit 变成默认的 20万 CU limit 每条指令(最新验证者节点这个交易CU limit应该自动是300 可能过于古老了) 所以收取了每笔交易基本费用,注意CU price单位是micro lamports要乘以1e-6转换成SOL_lamports per CU的量纲 |
| 22 | + |
| 23 | +> 5000 + (20000*1e-6 * 200000) = 9000 lamports SOL = 0.000009SOL |
| 24 | +
|
| 25 | +交易费用=5000lamports基础费+账户租金费/开户费+优先费用, 优先费用总值的竞价排名决定了交易在验证者出块的优先级 |
| 26 | + |
| 27 | +源码在 agave(solana 2.0之后改名成 agave 项目继续维护了,原github地址不更新) 的 cost-model crate 中可以看到常量 DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT 就是 20万 |
| 28 | + |
| 29 | +```rust |
| 30 | + fn get_transaction_cost( |
| 31 | + transaction: &impl TransactionWithMeta, |
| 32 | + feature_set: &FeatureSet, |
| 33 | + ) -> (u64, u64, u64) { |
| 34 | + let mut programs_execution_costs = 0u64; |
| 35 | + let mut compute_unit_limit_is_set = false; |
| 36 | + let mut has_user_space_instructions = false; |
| 37 | + for (program_id, instruction) in transaction.program_instructions_iter() { |
| 38 | + let ix_execution_cost = |
| 39 | + if let Some(builtin_cost) = BUILTIN_INSTRUCTION_COSTS.get(program_id) { |
| 40 | + *builtin_cost |
| 41 | + } else { |
| 42 | + has_user_space_instructions = true; |
| 43 | + u64::from(DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT) |
| 44 | + }; |
| 45 | + programs_execution_costs = programs_execution_costs |
| 46 | + .saturating_add(ix_execution_cost) |
| 47 | + .min(u64::from(MAX_COMPUTE_UNIT_LIMIT)); |
| 48 | + if compute_budget::check_id(program_id) { |
| 49 | + if let Ok(ComputeBudgetInstruction::SetComputeUnitLimit(_)) = |
| 50 | + try_from_slice_unchecked(instruction.data) |
| 51 | +``` |
| 52 | + |
| 53 | +再看一个同时设置了CU price和limit的例子: <https://solscan.io/tx/BcBCS61y3GpYCHVzUZ3KK7v1M7PgYqNM8oyqx3oLq7XcxaYDW5SH1GQqvL5pkR91jypxh9sPDpztMby32oxEnre> |
| 54 | + |
| 55 | +agave 源码中 solana_compute_budget_program DEFAULT_COMPUTE_UNITS 是 150, 设置CU limit/price和转账都是150 |
| 56 | + |
| 57 | +上述交易CU limit设置了3000,交易有2个设置CU的指令和18次SOL转账的指令,加起来刚好是(2+18)*150=3000 CU |
| 58 | + |
| 59 | +> 10000*1e-6 * 3000 = 300 lamports |
| 60 | +
|
| 61 | +## CU limit 如何设置 |
| 62 | + |
| 63 | +**即便交易只用了300CU但设置了20万的CU limit**(如上述例子1的tx), 也是按照CU limit 20万收取优先费用 |
| 64 | + |
| 65 | +所以交易中尽可能设置更低的CU Limit,而且根据[helius 文档](https://www.helius.dev/blog/how-to-land-transactions-on-solana#compute-units) |
| 66 | +CULimit越低的交易上链的优先级更大 |
| 67 | + |
| 68 | +常见的交易指令中消耗的 CU |
| 69 | + |
| 70 | +- transfer/SetCU: 150 |
| 71 | +- 智能合约部署: 约2500-3000 |
| 72 | +- token transfer: 5000(不需要开户时) |
| 73 | +- create ATA account(token开户): 约30000 |
| 74 | +- raydium AMM swap: 约33000-40000 |
| 75 | +- jupiter swap: 约100000-400000 |
| 76 | + |
| 77 | +像jup swap这样不确定的CU消耗 推荐用simulate rpc模拟执行获取CU消耗 |
| 78 | + |
| 79 | +## 优先费用价格CU price设置算法 |
| 80 | + |
| 81 | +1. 交易预期利润的60%除以CU limit得到单价的套利贿赂矿工算法 |
| 82 | +2. Helius Priority Fee API 的推荐 |
| 83 | +3. rpc getRecentPrioritizationFees |
| 84 | +4. triton Improved Priority Fees API |
| 85 | +5. jupiter,raydium,metaora这样的产品也内置了优先费用的计算,无需开发者处理 |
| 86 | + |
| 87 | +原版 rpc 优先费 API 返回的是某个智能合约地址最近150区块交易中的最小值(at least one successfully landed) |
| 88 | + |
| 89 | +其实更推荐用 triton 增强型优先费API能查询到某智能合约最近大伙给的优先费的中位数 |
| 90 | + |
| 91 | +一般网络拥堵的时候我个人经验是 jupiter TURBO 等级的优先费用都要给到0.2u~0.4u不等 |
| 92 | + |
| 93 | +> **SOL跟EVM不同的是,EVM如果交易失败了 收取的是 gasPrice * gasUsed(实际消耗的CU)** |
| 94 | +
|
| 95 | +那么问题来了,对于一个预期利润是 10SOL 的套利交易,拿出了80%预期利润8SOL的优先费用贿赂矿工 |
| 96 | + |
| 97 | +但是交易因为滑点过大没抢单成功,**交易失败了也要支付8SOL的优先费用**,是非常昂贵的成本了 |
| 98 | + |
| 99 | +## 为套利者而生的jito |
| 100 | + |
| 101 | +jito的出现就解决了这个问题,jito的交易不需要设置优先费用CU price的指令(CU limit还是建议要) |
| 102 | + |
| 103 | +而是让交易最后加一个给jito 8个小费地址随机选一个转账的指令,为什么要随机选一个呢 |
| 104 | + |
| 105 | +如果大伙都往第一个小费地址打钱,8个地址随机选一个可以提高吞吐量同时有8个jito小费指令可并行执行 |
| 106 | + |
| 107 | +所以发送给jito的交易失败发生回滚的话,最后一条小费指令不会执行,也就损失5000lamports的基础交易费用 |
| 108 | + |
| 109 | +**bloXroute**好像是类似jito这样的产品,我没用过就不评价了 |
| 110 | + |
| 111 | +## 什么时候用jito什么时候用优先费 |
| 112 | + |
| 113 | +§ 适用于jito加速交易的业务 |
| 114 | +1. 套利交易 |
| 115 | +2. 狙击pump/raydium等开盘 |
| 116 | +3. 价格波动大的LP建仓 |
| 117 | +4. 失败率高允许重试有希望快点上链的业务 |
| 118 | + |
| 119 | +笔者有次LP建仓发交易就说价格波动导致tick滑点变动交易失败,连续失败4-5次 每次亏损0.4$的优先费用 如果用jito不断重试交易就不必亏这么多了 |
| 120 | + |
| 121 | +§ 适用于优先费用加速交易的业务 |
| 122 | +1. memecoin swap交易 |
| 123 | + |
| 124 | +§ 既不要jito也不要优先费的业务 |
| 125 | +1. 转账(钱包软件基本不给优先费) |
| 126 | +2. 智能合约部署 |
| 127 | +3. LP超出区间了移除流动性等不是很急的业务 |
| 128 | + |
| 129 | +智能合约部署我的经验是推荐用 aws免费节点+helius免费rpc 上传/部署智能合约 |
| 130 | + |
| 131 | +项目不急着上线失败就重试几次(急的话deploy可加优先费用的命令行参数),失败的话会出现一些 program buffer 占用资金,稍后 solana program close 关掉后就能回收SOL了 |
| 132 | + |
| 133 | +### 谁当leader就给谁发 |
| 134 | + |
| 135 | +由于SOL网络中当前epoch POS的leader顺序是确定的,也可以预测下个leader是jito的节点就给jito发交易,如果是helius/triton节点就用优先费给他们发 |
| 136 | + |
| 137 | +~~还有一种思路nonceAccount同时签名**两个交易一个发jito一个发helius**,一个成功另一个自然因为nonce无效而失败~~ |
| 138 | + |
| 139 | +## swqos机制加速上链 |
| 140 | + |
| 141 | +swqos简单说就是质押量越大的验证者节点,在下个区块出块中能提交给leader节点的交易数更多,所以走质押量更大的节点rpc上链更快 |
| 142 | + |
| 143 | +[听solayer在space说](https://x.com/Solana_zh/status/1856684090399117736) |
| 144 | +要把swqos加速上链的机制做成restaking奖励,用户质押给项目方更多SOL,项目的上链速度更快体验更好,项目方应该奖励给质押用户额外奖励这样的经济模型 |
| 145 | + |
| 146 | +swqos节点基本是被triton,helius这样的大户厂商垄断了,quicknode这样的知名厂商也没有,~~所以solayer能否打破这种垄断呢(支持华语区项目打破垄断)~~ |
| 147 | + |
| 148 | +由于triton服务不对外公开销售了,所以也就只能用helius付费的staked connection rpc消耗的额度是普通rpc的50倍 |
| 149 | + |
| 150 | +## 如何确认交易成功/交易重试 |
| 151 | + |
| 152 | +由于solana网络拥堵时,发出去的交易可能被rpc节点丢弃没有发成功给leader节点了,或者等很久很久才上链,因此重试策略推荐阅读 triton 这篇文章 <https://docs.triton.one/chains/solana/sending-txs> |
| 153 | + |
| 154 | +1. 计算下签名交易用的blockhash还有多久过期 |
| 155 | +1. ws订阅交易的signature |
| 156 | +2. 发送交易 |
| 157 | +3. 每隔一段时间获取交易状态,定时重发交易 |
| 158 | + |
| 159 | +直到http轮询交易成功或者ws推送交易成功 |
| 160 | + |
| 161 | +由于笔者做的是套利业务对时效性要求极高(行情过几秒后可能滑点巨大),所以我的做法是交易中插入一条超时5s指令 |
| 162 | + |
| 163 | +ws+http轮询交易状态超过5s就认为是超时了,这样交易很久之后才上链会因为超时而失败 |
0 commit comments