背景
最近比较无聊, 来研究下Web3的一些知识, 要做就做科学家!
批量交易:
预设并自动执行交易指令,轻松实现批量在DEX交易,提高了交易的效率和时效性,特别适用于快速执行大量交易的场景
我从一些线上工具网站上, 看到批量交易有下图的几个区域
https://slerf.tools/zh-cn/multi-swap/solana
- 私钥导入 和 用户信息查询
- 批量dex(Raydium,PumpFun,MoonShot)交易
- 交易参数设置
后文, 我们将围绕这几个功能来分析其功能实现. 但是我们需要了解到一点 "所有功能都是通过官方提供的API来实现的"
https://github.com/gagliardetto/solana-go
1. 私钥导入 & 基本信息查询
1.1 私钥导入
私钥导入解析其实是比较容易的一个功能.
solana-go
直接给出了实例, 通过privateKey
获取到公钥
// Load private key from base58:
{
privateKey, err := solana.PrivateKeyFromBase58("66cDvko73yAf8LYvFMM3r8vF5vJtkk7JKMgEKwkmBC86oHdq41C7i1a2vS3zE1yCcdLLk6VUatUb32ZzVjSBXtRs")
if err != nil {
panic(err)
}
fmt.Println("private key:", privateKey.String())
fmt.Println("public key:", privateKey.PublicKey().String())
}
// OR:
{
privateKey := solana.MustPrivateKeyFromBase58("66cDvko73yAf8LYvFMM3r8vF5vJtkk7JKMgEKwkmBC86oHdq41C7i1a2vS3zE1yCcdLLk6VUatUb32ZzVjSBXtRs")
_ = privateKey
}
1.2 基本信息查询
基本信息查询也就是余额查询. 我们可以通过官方提供的GetBalance
来实现.
这里需要注意的是, 查询出来的余额是lamports
, 它是solana链的最小货币单位.
Lamports与Sol的关系
- 1 Sol 等于 1,000,000,000 Lamports。
- 这种关系类似于比特币中的Satoshis与比特币的关系,其中1比特币等于100,000,000 Satoshis。
endpoint := rpc.MainNetBeta_RPC
client := rpc.New(endpoint)
pubKey := solana.MustPublicKeyFromBase58("7xLk17EQQ5KLDLDe44wCmupJKJjTGd8hs3eSVVhCx932")
out, err := client.GetBalance(
context.TODO(),
pubKey,
rpc.CommitmentFinalized,
)
if err != nil {
panic(err)
}
spew.Dump(out)
spew.Dump(out.Value) // total lamports on the account; 1 sol = 1000000000 lamports
var lamportsOnAccount = new(big.Float).SetUint64(uint64(out.Value))
// Convert lamports to sol:
var solBalance = new(big.Float).Quo(lamportsOnAccount, new(big.Float).SetUint64(solana.LAMPORTS_PER_SOL))
// WARNING: this is not a precise conversion.
fmt.Println("◎", solBalance.Text('f', 10))
官方示例还非常贴心的给出了转换Sol和Lamports的代码
var lamportsOnAccount = new(big.Float).SetUint64(uint64(out.Value))
// Convert lamports to sol:
var solBalance = new(big.Float).Quo(lamportsOnAccount, new(big.Float).SetUint64(solana.LAMPORTS_PER_SOL))
// WARNING: this is not a precise conversion.
fmt.Println("◎", solBalance.Text('f', 10))
1.3 批量查询
我们在工具页上可以看到, 它是支持多账号信息的余额查询的. 我们应该如何做呢?
func getMultipleBalances(client *rpc.Client, pKeys []string) {
pubKeys := make([]solana.PublicKey, len(pKeys))
for i, pKey := range pKeys {
pubKeys[i] = solana.MustPublicKeyFromBase58(pKey)
}
out, err := client.GetMultipleAccountsWithOpts(
context.TODO(),
pubKeys,
&rpc.GetMultipleAccountsOpts{
Encoding: solana.EncodingBase64Zstd,
Commitment: rpc.CommitmentFinalized,
},
)
if err != nil {
panic(err)
}
spew.Dump(out)
// 获取余额
// ...
}
2. 批量dex(Raydium,PumpFun,MoonShot)交易
这里我们只讨论Raydium.
2.1 什么是Dex?
当我看到Dex的时候,也是一脸蒙蔽的. 我在网上搜集了一些资料
在区块链和加密货币的领域中,DEX代表去中心化交易所(Decentralized Exchange)。去中心化交易所是一种允许用户直接在彼此之间进行加密货币交易的平台,而无需通过中心化的第三方机构(如传统的金融交易所)来进行交易和资金的托管。
那么Raydium是个交易所?
Raydium 是建立在 Solana 区块链上的 AMM 自动化做市商,利用 Serum 去中心化交易平台的订单簿,实现闪电般的快速交易,共享流动性,赚取挖矿收益等功能。
更多的未知关键词:
- AMM(Automated Market Maker) : 去中心化金融(DeFi)领域中常见的协议,用于在没有传统买家和卖家的情况下自动提供流动性。AMM允许数字资产在去中心化交易所(DEX)上通过预设的数学公式进行交易,而不是通过传统的订单簿。
- Serum : 一个基于 Solana 区块链的去中心化交易所(DEX),它由 Project Serum 团队开发,旨在提供低成本、高速度的交易体验。Serum 的独特之处在于它结合了中心化交易所的一些优点(如订单簿模型)与去中心化交易所的优点(如安全性和去中心化的托管),从而创造出一个高效且用户友好的交易平台。
2.2 批量交易设置
我们已经知道了Dex是一个类似于交易所的东西. 那么批量交易设置我认为可以把它当成一种购物行为.
- 把我手里的
A币
替换成B币
2.2.1 PriorityFee 优先费
当我点击此处时, 发现网站发送了getPriorityFeeEstimate
的请求.
2.2.1.1 什么是优先费
优先费 (Priority Fees):用于计算资源/交易打包成为瓶颈时,本质上类似于其他区块链网络提高 Gas 费的做法,具有全局属性。
2.2.1.2 为什么要使用优先费
当交易经过验证器时,验证器的一个关键阶段是对交易进行调度。验证器在经济上有动机优先调度那些与较高计算单元费用相关的交易,这保证了用户以最优方式使用资源。用户仍然可以执行没有优先费用的交易,但执行的保证较少。当区块被带有优先费用的交易填满时,验证器将会丢弃没有优先费用的交易。
也就是说, 谁的优先费高, 谁就优先交易, 类似GAS的东西.
2.2.1.3 我要如何使用优先费
在了解了优先费以后, 优先费对我们的交易至关重要, 它决定了我们交易的成功率.
我非常着急买入某个币, 但是因为fees用的是最便宜的或没用, 那么,只有等别人买完了,我才有机会.
记得很久以前有一句话 "GAS拉满,倾家荡产🐕"
2.2.2 目标代币地址
一般情况下, 我们都不会用科学家把它切换成主流的代笔, 而是会切换成一些奇奇怪怪的代币.
3. 交易参数设置
在交易参数这边, 我们可以看到2个比较陌生的词
滑点
- 滑点(Slippage)是金融交易中一个常见的概念,特别是在加密货币和其他金融市场的交易中。滑点指的是交易执行的价格与交易者期望或预定价格之间的差异。这种差异通常是由市场波动性或交易量不足导致的。
Jito MEV 小费
- ito MEV 防夹模式为您的交易分配专用“赛道”,基于 Solana 的技术,确保每个区块有三分之一的空间专用于 Jito 的操作。
- 通过固定位置的捆绑包,Jito MEV 功能可以提高上链的速度和成功率
- 可选择支付给矿工的“贿赂”费用,提高交易的优先级,费用越高,交易上链速度越快
4. 交易
- getMultipleAccounts
- getLatestBlockhash
- simulateTransaction
- getTransaction
- getBalance
- getAccountInfo
首先, 我们需要了解下这几个接口的作用.
4.1 getMultipleAccounts
package main
import (
"context"
"github.com/davecgh/go-spew/spew"
"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/rpc"
)
func main() {
endpoint := rpc.MainNetBeta_RPC
client := rpc.New(endpoint)
{
out, err := client.GetMultipleAccounts(
context.TODO(),
solana.MustPublicKeyFromBase58("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"), // serum token
solana.MustPublicKeyFromBase58("4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R"), // raydium token
)
if err != nil {
panic(err)
}
spew.Dump(out)
}
{
out, err := client.GetMultipleAccountsWithOpts(
context.TODO(),
[]solana.PublicKey{solana.MustPublicKeyFromBase58("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"), // serum token
solana.MustPublicKeyFromBase58("4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R"), // raydium token
},
&rpc.GetMultipleAccountsOpts{
Encoding: solana.EncodingBase64Zstd,
Commitment: rpc.CommitmentFinalized,
// You can get just a part of the account data by specify a DataSlice:
// DataSlice: &rpc.DataSlice{
// Offset: pointer.ToUint64(0),
// Length: pointer.ToUint64(1024),
// },
},
)
if err != nil {
panic(err)
}
spew.Dump(out)
}
}
我们看看别人发的数据
{
"method": "getMultipleAccounts",
"jsonrpc": "2.0",
"params": [
[
"38XamZUodnZc4ms8e53gZa45Kx9YkCi4fzR5SrnS4XYu"
],
{
"encoding": "base64",
"commitment": "confirmed"
}
],
"id": "51622a64-e984-4120-87d4-259a30d79df1"
}
未完待续