raw tx binary
This commit is contained in:
198
metaorapool.go
198
metaorapool.go
@@ -2,8 +2,10 @@ package pump_parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
agbinary "github.com/gagliardetto/binary"
|
||||
"github.com/gagliardetto/solana-go"
|
||||
@@ -20,6 +22,14 @@ type metaoraPoolSwapArgs struct {
|
||||
MinimumOutAmount uint64
|
||||
}
|
||||
|
||||
type metaoraPoolSwapEvent struct {
|
||||
InAmount uint64
|
||||
OutAmount uint64
|
||||
TradeFee uint64
|
||||
ProtocolFee uint64
|
||||
HostFee uint64
|
||||
}
|
||||
|
||||
var (
|
||||
meteoraVaultProgram = solana.MustPublicKeyFromBase58("24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi")
|
||||
meteoraVaultDepositDiscriminator = []byte{0xf2, 0x23, 0xc6, 0x89, 0x52, 0xe1, 0xf2, 0xb6}
|
||||
@@ -731,6 +741,7 @@ func metaoraPoolRemoveLiquidity(tx *Tx, instruction Instruction, innerInstructio
|
||||
}
|
||||
|
||||
func metaoraPoolSwap(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||
swapOffset := offset
|
||||
var args metaoraPoolSwapArgs
|
||||
if err := agbinary.NewBorshDecoder(instruction.Data[8:]).Decode(&args); err != nil {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed to decode meteora pools swap args: %w", err)
|
||||
@@ -886,10 +897,195 @@ func metaoraPoolSwap(tx *Tx, instruction Instruction, innerInstructions InnerIns
|
||||
EntryContract: entryContract,
|
||||
},
|
||||
}
|
||||
swaps[0].SetSwapAmountInfo(
|
||||
fixedSide := fixedSwapAmountSide(event, SwapModeExactIn)
|
||||
limitSide := oppositeSwapAmountSide(fixedSide)
|
||||
if fixedSide == SwapAmountSideUnknown || limitSide == SwapAmountSideUnknown {
|
||||
swaps[0].SetSwapAmountInfo(
|
||||
SwapModeExactIn,
|
||||
decimal.NewFromUint64(args.InAmount),
|
||||
decimal.NewFromUint64(args.MinimumOutAmount),
|
||||
)
|
||||
return swaps, offset, nil
|
||||
}
|
||||
|
||||
actualLimitAmount := swapAmountForSide(baseAmount, quoteAmount, limitSide)
|
||||
if swapEvent, ok := metaoraPoolSwapEventFromInstruction(instruction); ok {
|
||||
actualLimitAmount = decimal.NewFromUint64(swapEvent.OutAmount)
|
||||
} else if swapEvent, ok := metaoraPoolSwapEventForOffset(tx, swapOffset); ok {
|
||||
actualLimitAmount = decimal.NewFromUint64(swapEvent.OutAmount)
|
||||
}
|
||||
swaps[0].SetSwapAmountInfoDetailed(
|
||||
SwapModeExactIn,
|
||||
decimal.NewFromUint64(args.InAmount),
|
||||
fixedSide,
|
||||
swapMintForSide(baseMint, quoteMint, fixedSide),
|
||||
SwapLimitTypeMinOut,
|
||||
decimal.NewFromUint64(args.MinimumOutAmount),
|
||||
limitSide,
|
||||
swapMintForSide(baseMint, quoteMint, limitSide),
|
||||
actualLimitAmount,
|
||||
)
|
||||
return swaps, offset, nil
|
||||
}
|
||||
|
||||
func metaoraPoolSwapEventFromInstruction(instruction Instruction) (metaoraPoolSwapEvent, bool) {
|
||||
for _, event := range instruction.LogEvents {
|
||||
if swapEvent, ok := metaoraPoolDecodeSwapEventData(event); ok {
|
||||
return swapEvent, true
|
||||
}
|
||||
}
|
||||
return metaoraPoolSwapEvent{}, false
|
||||
}
|
||||
|
||||
func metaoraPoolSwapEventForOffset(tx *Tx, offset [2]uint) (metaoraPoolSwapEvent, bool) {
|
||||
if tx == nil || tx.rawTx == nil {
|
||||
return metaoraPoolSwapEvent{}, false
|
||||
}
|
||||
occurrence := metaoraPoolSwapInstructionOccurrence(tx.rawTx, offset)
|
||||
if occurrence == 0 {
|
||||
return metaoraPoolSwapEvent{}, false
|
||||
}
|
||||
return metaoraPoolSwapEventFromLogs(tx.rawTx.Meta.LogMessages, occurrence)
|
||||
}
|
||||
|
||||
func metaoraPoolSwapInstructionOccurrence(rawTx *RawTx, offset [2]uint) int {
|
||||
if rawTx == nil {
|
||||
return 0
|
||||
}
|
||||
accountList := rawTx.getAccountList()
|
||||
innerByOuter := make(map[int]InnerInstructions, len(rawTx.Meta.InnerInstructions))
|
||||
for _, inner := range rawTx.Meta.InnerInstructions {
|
||||
innerByOuter[inner.Index] = inner
|
||||
}
|
||||
|
||||
occurrence := 0
|
||||
for i, instruction := range rawTx.Transaction.Message.Instructions {
|
||||
if uint(i) == offset[0] && offset[1] == 0 {
|
||||
if metaoraPoolIsSwapInstruction(accountList, instruction) {
|
||||
return occurrence + 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
if metaoraPoolIsSwapInstruction(accountList, instruction) {
|
||||
occurrence++
|
||||
}
|
||||
|
||||
inner := innerByOuter[i]
|
||||
for j, instruction := range inner.Instructions {
|
||||
innerOffset := uint(j + 1)
|
||||
if uint(i) == offset[0] && offset[1] == innerOffset {
|
||||
if metaoraPoolIsSwapInstruction(accountList, instruction) {
|
||||
return occurrence + 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
if metaoraPoolIsSwapInstruction(accountList, instruction) {
|
||||
occurrence++
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func metaoraPoolIsSwapInstruction(accountList []solana.PublicKey, instruction Instruction) bool {
|
||||
if instruction.ProgramIDIndex < 0 || instruction.ProgramIDIndex >= len(accountList) {
|
||||
return false
|
||||
}
|
||||
if !accountList[instruction.ProgramIDIndex].Equals(metaoraPoolProgramID) {
|
||||
return false
|
||||
}
|
||||
return len(instruction.Data) >= 8 && bytes.Equal(instruction.Data[:8], metaoraPoolSwapDiscriminator[:])
|
||||
}
|
||||
|
||||
func metaoraPoolSwapEventFromLogs(logMessages []string, occurrence int) (metaoraPoolSwapEvent, bool) {
|
||||
if occurrence <= 0 {
|
||||
return metaoraPoolSwapEvent{}, false
|
||||
}
|
||||
|
||||
type frame struct {
|
||||
program string
|
||||
sawSwap bool
|
||||
}
|
||||
|
||||
targetProgram := metaoraPoolProgramID.String()
|
||||
var stack []frame
|
||||
seen := 0
|
||||
for _, logMessage := range logMessages {
|
||||
if program, ok := metaoraPoolLogInvokeProgram(logMessage); ok {
|
||||
stack = append(stack, frame{program: program})
|
||||
continue
|
||||
}
|
||||
|
||||
if len(stack) > 0 && stack[len(stack)-1].program == targetProgram {
|
||||
if logMessage == "Program log: Instruction: Swap" {
|
||||
stack[len(stack)-1].sawSwap = true
|
||||
continue
|
||||
}
|
||||
if stack[len(stack)-1].sawSwap && strings.HasPrefix(logMessage, "Program data: ") {
|
||||
event, ok := metaoraPoolDecodeSwapEventLog(strings.TrimSpace(strings.TrimPrefix(logMessage, "Program data: ")))
|
||||
if ok {
|
||||
seen++
|
||||
if seen == occurrence {
|
||||
return event, true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if program, ok := metaoraPoolLogFinishedProgram(logMessage); ok {
|
||||
for i := len(stack) - 1; i >= 0; i-- {
|
||||
if stack[i].program == program {
|
||||
stack = stack[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return metaoraPoolSwapEvent{}, false
|
||||
}
|
||||
|
||||
func metaoraPoolLogInvokeProgram(logMessage string) (string, bool) {
|
||||
if !strings.HasPrefix(logMessage, "Program ") || !strings.Contains(logMessage, " invoke [") {
|
||||
return "", false
|
||||
}
|
||||
rest := strings.TrimPrefix(logMessage, "Program ")
|
||||
program, _, ok := strings.Cut(rest, " ")
|
||||
return program, ok
|
||||
}
|
||||
|
||||
func metaoraPoolLogFinishedProgram(logMessage string) (string, bool) {
|
||||
if !strings.HasPrefix(logMessage, "Program ") {
|
||||
return "", false
|
||||
}
|
||||
if !strings.HasSuffix(logMessage, " success") && !strings.Contains(logMessage, " failed:") {
|
||||
return "", false
|
||||
}
|
||||
rest := strings.TrimPrefix(logMessage, "Program ")
|
||||
program, _, ok := strings.Cut(rest, " ")
|
||||
return program, ok
|
||||
}
|
||||
|
||||
func metaoraPoolDecodeSwapEventLog(encoded string) (metaoraPoolSwapEvent, bool) {
|
||||
data, err := base64.StdEncoding.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return metaoraPoolSwapEvent{}, false
|
||||
}
|
||||
return metaoraPoolDecodeSwapEventData(data)
|
||||
}
|
||||
|
||||
func metaoraPoolDecodeSwapEventData(data []byte) (metaoraPoolSwapEvent, bool) {
|
||||
if len(data) < 48 {
|
||||
return metaoraPoolSwapEvent{}, false
|
||||
}
|
||||
if !bytes.Equal(data[:8], metaoraPoolSwapEventDiscriminator[:]) {
|
||||
return metaoraPoolSwapEvent{}, false
|
||||
}
|
||||
body := data[8:]
|
||||
return metaoraPoolSwapEvent{
|
||||
InAmount: binary.LittleEndian.Uint64(body[0:8]),
|
||||
OutAmount: binary.LittleEndian.Uint64(body[8:16]),
|
||||
TradeFee: binary.LittleEndian.Uint64(body[16:24]),
|
||||
ProtocolFee: binary.LittleEndian.Uint64(body[24:32]),
|
||||
HostFee: binary.LittleEndian.Uint64(body[32:40]),
|
||||
}, true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user