Skip to content

Commit

Permalink
add parsers for OKX DEX AggregationRouterV2
Browse files Browse the repository at this point in the history
- add parser for Swap instruction
- add parser for CommissionSplProxySwap instruction
- merge program U6m2CDdhRg
- fixes #8
  • Loading branch information
0xjeffro committed Dec 10, 2024
1 parent 5392a6b commit 3c720f5
Show file tree
Hide file tree
Showing 12 changed files with 1,116 additions and 56 deletions.
768 changes: 768 additions & 0 deletions solana/data/OKXDEX_commissionSplProxySwap.json

Large diffs are not rendered by default.

File renamed without changes.
File renamed without changes.
118 changes: 71 additions & 47 deletions solana/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package solana

import (
"fmt"
"github.com/0xjeffro/tx-parser/solana/programs/OKXDEXAggregationRouterV2"
"github.com/0xjeffro/tx-parser/solana/programs/jupiterAggregatorV6"
"io/ioutil"
"os"
"testing"

"github.com/0xjeffro/tx-parser/solana/globals"
"github.com/0xjeffro/tx-parser/solana/programs/U6m2CDdhRg"
"github.com/0xjeffro/tx-parser/solana/programs/computeBudget"
"github.com/0xjeffro/tx-parser/solana/programs/jupiterDCA"
"github.com/0xjeffro/tx-parser/solana/programs/pumpfun"
Expand Down Expand Up @@ -329,52 +329,6 @@ func TestTokenProgramInitializeAccount(t *testing.T) {
}
}

func TestU6m2CDdhRgSwap(t *testing.T) {
byteValue, err := readJsonFile("data/U6m2CDdhRg_swap_0.json")
if err != nil {
t.Errorf("Error reading JSON file: %v", err)
}
results, _ := Parser(byteValue)
action := results[0].Actions[3]

if transferAction, ok := action.(*types.U6m2CDdhRgSwapAction); ok {
assert.Equal(t, transferAction.ProgramID, U6m2CDdhRg.Program)
assert.Equal(t, transferAction.ProgramName, U6m2CDdhRg.ProgramName)
assert.Equal(t, transferAction.InstructionName, "Unknown")
assert.Equal(t, transferAction.FromToken, "7atgF8KQo4wJrD5ATGX7t1V2zVvykPJbFfNeVf1icFv1")
assert.Equal(t, transferAction.FromTokenAmount, uint64(358800))
assert.Equal(t, transferAction.FromTokenDecimals, uint64(2))
assert.Equal(t, transferAction.ToToken, "ED5nyyWEzpPPiWimP8vYm7sD7TD3LAt3Q3gRTWHzPJBY")
assert.Equal(t, transferAction.ToTokenAmount, uint64(74619))
assert.Equal(t, transferAction.ToTokenDecimals, uint64(6))
} else {
t.Errorf("Error type assertion")
}
}

func TestU6m2CDdhRgSwap1(t *testing.T) {
byteValue, err := readJsonFile("data/U6m2CDdhRg_swap_1.json")
if err != nil {
t.Errorf("Error reading JSON file: %v", err)
}
results, _ := Parser(byteValue)
action := results[0].Actions[7]

if transferAction, ok := action.(*types.U6m2CDdhRgSwapAction); ok {
assert.Equal(t, transferAction.ProgramID, U6m2CDdhRg.Program)
assert.Equal(t, transferAction.ProgramName, U6m2CDdhRg.ProgramName)
assert.Equal(t, transferAction.InstructionName, "Unknown")
assert.Equal(t, transferAction.FromToken, "So11111111111111111111111111111111111111112")
assert.Equal(t, transferAction.FromTokenAmount, uint64(10000000000))
assert.Equal(t, transferAction.FromTokenDecimals, uint64(9))
assert.Equal(t, transferAction.ToToken, "KMnDBXcPXoz6oMJW5XG4tXdwSWpmWEP2RQM1Uujpump")
assert.Equal(t, transferAction.ToTokenAmount, uint64(998528432013))
assert.Equal(t, transferAction.ToTokenDecimals, uint64(6))
} else {
t.Errorf("Error type assertion")
}
}

func TestJupiterDcaOpenDcaV2_0(t *testing.T) {
byteValue, err := readJsonFile("data/jupiterDca_openDcaV2_0.json")
if err != nil {
Expand Down Expand Up @@ -671,3 +625,73 @@ func TestJupiterAggregatorV6Route_2(t *testing.T) {
assert.Equal(t, swapAction.ToTokenDecimals, uint64(9))
}
}

func TestOKXDEXCommissionSplProxySwap_1(t *testing.T) {
byteValue, err := readJsonFile("data/OKXDEX_commissionSplProxySwap.json")
if err != nil {
t.Errorf("Error reading JSON file: %v", err)
}
results, _ := Parser(byteValue)
action := results[0].Actions[3]

if swapAction, ok := action.(*OKXDEXAggregationRouterV2.CommissionSplProxySwapAction); ok {
assert.Equal(t, swapAction.ProgramID, OKXDEXAggregationRouterV2.Program)
assert.Equal(t, swapAction.ProgramName, OKXDEXAggregationRouterV2.ProgramName)
assert.Equal(t, swapAction.InstructionName, "CommissionSplProxySwap")
assert.Equal(t, swapAction.Who, "GKj54MVFppoYsxPU9jMn7CMniVRAuveFadyTtFhn1vXy")
assert.Equal(t, swapAction.FromToken, "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
assert.Equal(t, swapAction.FromTokenAmount, uint64(250000000))
assert.Equal(t, swapAction.FromTokenDecimals, uint64(6))
assert.Equal(t, swapAction.ToToken, "DKu9kykSfbN5LBfFXtNNDPaX35o4Fv6vJ9FKk7pZpump")
assert.Equal(t, swapAction.ToTokenAmount, uint64(6373264828))
assert.Equal(t, swapAction.ToTokenDecimals, uint64(6))
} else {
t.Errorf("Error type assertion")
}
}

func TestOKXDEXSwap(t *testing.T) {
byteValue, err := readJsonFile("data/OKXDEX_swap_0.json")
if err != nil {
t.Errorf("Error reading JSON file: %v", err)
}
results, _ := Parser(byteValue)
action := results[0].Actions[3]

if transferAction, ok := action.(*OKXDEXAggregationRouterV2.SwapAction); ok {
assert.Equal(t, transferAction.ProgramID, OKXDEXAggregationRouterV2.Program)
assert.Equal(t, transferAction.ProgramName, OKXDEXAggregationRouterV2.ProgramName)
assert.Equal(t, transferAction.InstructionName, "Swap")
assert.Equal(t, transferAction.FromToken, "7atgF8KQo4wJrD5ATGX7t1V2zVvykPJbFfNeVf1icFv1")
assert.Equal(t, transferAction.FromTokenAmount, uint64(358800))
assert.Equal(t, transferAction.FromTokenDecimals, uint64(2))
assert.Equal(t, transferAction.ToToken, "ED5nyyWEzpPPiWimP8vYm7sD7TD3LAt3Q3gRTWHzPJBY")
assert.Equal(t, transferAction.ToTokenAmount, uint64(74619))
assert.Equal(t, transferAction.ToTokenDecimals, uint64(6))
} else {
t.Errorf("Error type assertion")
}
}

func TestOKXDEXSwap_1(t *testing.T) {
byteValue, err := readJsonFile("data/OKXDEX_swap_1.json")
if err != nil {
t.Errorf("Error reading JSON file: %v", err)
}
results, _ := Parser(byteValue)
action := results[0].Actions[7]

if transferAction, ok := action.(*OKXDEXAggregationRouterV2.SwapAction); ok {
assert.Equal(t, transferAction.ProgramID, OKXDEXAggregationRouterV2.Program)
assert.Equal(t, transferAction.ProgramName, OKXDEXAggregationRouterV2.ProgramName)
assert.Equal(t, transferAction.InstructionName, "Swap")
assert.Equal(t, transferAction.FromToken, "So11111111111111111111111111111111111111112")
assert.Equal(t, transferAction.FromTokenAmount, uint64(10000000000))
assert.Equal(t, transferAction.FromTokenDecimals, uint64(9))
assert.Equal(t, transferAction.ToToken, "KMnDBXcPXoz6oMJW5XG4tXdwSWpmWEP2RQM1Uujpump")
assert.Equal(t, transferAction.ToTokenAmount, uint64(998528432013))
assert.Equal(t, transferAction.ToTokenDecimals, uint64(6))
} else {
t.Errorf("Error type assertion")
}
}
7 changes: 7 additions & 0 deletions solana/programs/OKXDEXAggregationRouterV2/meta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package OKXDEXAggregationRouterV2

const Program = "6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma"
const ProgramName = "OKX DEX: Aggregation Router V2"

var CommissionSplProxySwapDiscriminator = [8]uint8{96, 67, 12, 151, 129, 164, 18, 71}
var SwapDiscriminator = [8]uint8{65, 75, 63, 76, 235, 91, 91, 136}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package parsers

import (
"fmt"
"github.com/0xjeffro/tx-parser/solana/globals"
"github.com/0xjeffro/tx-parser/solana/programs/OKXDEXAggregationRouterV2"
"github.com/0xjeffro/tx-parser/solana/programs/systemProgram"
SystemProgramParsers "github.com/0xjeffro/tx-parser/solana/programs/systemProgram/parsers"
"github.com/0xjeffro/tx-parser/solana/programs/tokenProgram"
TokenProgramParsers "github.com/0xjeffro/tx-parser/solana/programs/tokenProgram/parsers"
"github.com/0xjeffro/tx-parser/solana/types"
)

func CommissionSplProxySwapParser(result *types.ParsedResult, instruction types.Instruction) (*OKXDEXAggregationRouterV2.CommissionSplProxySwapAction, error) {

var who string
var fromToken, toToken string = globals.WSOL, globals.WSOL
var fromTokenDecimals, toTokenDecimals uint64 = globals.SOLDecimals, globals.SOLDecimals
var fromTokenAmount, toTokenAmount uint64

who = result.AccountList[instruction.Accounts[0]]
fromToken = result.AccountList[instruction.Accounts[3]]
toToken = result.AccountList[instruction.Accounts[4]]

var fromTokenAccount, toTokenAccount string
fromTokenAccount = result.AccountList[instruction.Accounts[1]]
toTokenAccount = result.AccountList[instruction.Accounts[2]]

// get index of this instruction
var instructionIndex int
for idx, instr := range result.RawTx.Transaction.Message.Instructions {
if result.AccountList[instr.ProgramIDIndex] == OKXDEXAggregationRouterV2.Program && instr.Data == instruction.Data {
instructionIndex = idx
break
}
}

// get all innerInstructions for this instruction
var innerInstructions []types.Instruction
for _, innerInstruction := range result.RawTx.Meta.InnerInstructions {
if innerInstruction.Index == instructionIndex {
innerInstructions = innerInstruction.Instructions
break
}
}

for _, instr := range innerInstructions {
programId := result.AccountList[instr.ProgramIDIndex]
switch programId {
case systemProgram.Program:
parsedData, err := SystemProgramParsers.InstructionRouter(result, instr)
if err != nil {
continue
}
switch p := parsedData.(type) {
case *types.SystemProgramTransferAction:
if p.From == fromTokenAccount {
fromTokenAmount += p.Lamports
}
if p.To == toTokenAccount {
toTokenAmount += p.Lamports
}
}
case tokenProgram.Program:
parsedData, err := TokenProgramParsers.InstructionRouter(result, instr)
if err != nil {
continue
}
switch p := parsedData.(type) {
case *types.TokenProgramTransferAction:
if p.From == fromTokenAccount {
fromTokenAmount += p.Amount
}
if p.To == toTokenAccount {
toTokenAmount += p.Amount
}
case *types.TokenProgramTransferCheckedAction:
if p.From == fromTokenAccount {
fromTokenAmount += p.Amount
}
if p.To == toTokenAccount {
toTokenAmount += p.Amount
}
}
default:
continue
}
}

var tokenBalances []types.TokenBalance
tokenBalances = append(tokenBalances, result.RawTx.Meta.PreTokenBalances...)
tokenBalances = append(tokenBalances, result.RawTx.Meta.PostTokenBalances...)

for _, tokenBalance := range tokenBalances {
account := result.AccountList[tokenBalance.AccountIndex]
if account == fromTokenAccount {
fromToken = tokenBalance.Mint
fromTokenDecimals = tokenBalance.UITokenAmount.Decimals
} else if account == toTokenAccount {
toToken = tokenBalance.Mint
toTokenDecimals = tokenBalance.UITokenAmount.Decimals
}
}

fmt.Println("fromTokenAmount: ", fromTokenAmount)
fmt.Println("toTokenAmount: ", toTokenAmount)

action := OKXDEXAggregationRouterV2.CommissionSplProxySwapAction{
BaseAction: types.BaseAction{
ProgramID: OKXDEXAggregationRouterV2.Program,
ProgramName: OKXDEXAggregationRouterV2.ProgramName,
InstructionName: "CommissionSplProxySwap",
},
Who: who,
ToToken: toToken,
FromToken: fromToken,
ToTokenAmount: toTokenAmount,
FromTokenAmount: fromTokenAmount,
FromTokenDecimals: fromTokenDecimals,
ToTokenDecimals: toTokenDecimals,
}

return &action, nil
}
31 changes: 31 additions & 0 deletions solana/programs/OKXDEXAggregationRouterV2/parsers/index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package parsers

import (
"github.com/0xjeffro/tx-parser/solana/programs/OKXDEXAggregationRouterV2"
"github.com/0xjeffro/tx-parser/solana/types"
"github.com/mr-tron/base58"
)

func InstructionRouter(result *types.ParsedResult, instruction types.Instruction) (types.Action, error) {
data := instruction.Data
decode, err := base58.Decode(data)
if err != nil {
return nil, err
}
discriminator := *(*[8]byte)(decode[:8])

switch discriminator {
case OKXDEXAggregationRouterV2.CommissionSplProxySwapDiscriminator:
return CommissionSplProxySwapParser(result, instruction)
case OKXDEXAggregationRouterV2.SwapDiscriminator:
return SwapParser(result, instruction, decode)
default:
return types.UnknownAction{
BaseAction: types.BaseAction{
ProgramID: result.AccountList[instruction.ProgramIDIndex],
ProgramName: OKXDEXAggregationRouterV2.ProgramName,
InstructionName: "Unknown",
},
}, nil
}
}
70 changes: 70 additions & 0 deletions solana/programs/OKXDEXAggregationRouterV2/parsers/swap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package parsers

import (
"encoding/binary"
"github.com/0xjeffro/tx-parser/solana/programs/OKXDEXAggregationRouterV2"
"github.com/0xjeffro/tx-parser/solana/types"
"strconv"
)

func SwapParser(result *types.ParsedResult, instruction types.Instruction, decodedData []byte) (*OKXDEXAggregationRouterV2.SwapAction, error) {
who := result.AccountList[instruction.Accounts[0]]
fromToken := result.AccountList[instruction.Accounts[3]]
toToken := result.AccountList[instruction.Accounts[4]]
fromTokenAmount := binary.LittleEndian.Uint64(decodedData[8:16])
toTokenAmount := uint64(0)

preTokenBalances := result.RawTx.Meta.PreTokenBalances
postTokenBalances := result.RawTx.Meta.PostTokenBalances

var preToTokenAmount, postToTokenAmount uint64
var fromTokenDecimals, toTokenDecimals uint64
for _, b := range preTokenBalances {
if b.Mint == toToken && b.Owner == who {
var err error
preToTokenAmount, err = strconv.ParseUint(b.UITokenAmount.Amount, 10, 64)
if err != nil {
return nil, err
}
}
if b.Mint == fromToken {
fromTokenDecimals = b.UITokenAmount.Decimals
}
if b.Mint == toToken {
toTokenDecimals = b.UITokenAmount.Decimals
}
}
for _, b := range postTokenBalances {
if b.Mint == toToken && b.Owner == who {
var err error
postToTokenAmount, err = strconv.ParseUint(b.UITokenAmount.Amount, 10, 64)
if err != nil {
return nil, err
}
break
}
if b.Mint == fromToken {
fromTokenDecimals = b.UITokenAmount.Decimals
}
if b.Mint == toToken {
toTokenDecimals = b.UITokenAmount.Decimals
}
}
toTokenAmount = postToTokenAmount - preToTokenAmount

action := OKXDEXAggregationRouterV2.SwapAction{
BaseAction: types.BaseAction{
ProgramID: OKXDEXAggregationRouterV2.Program,
ProgramName: OKXDEXAggregationRouterV2.ProgramName,
InstructionName: "Swap",
},
Who: who,
FromToken: fromToken,
ToToken: toToken,
FromTokenAmount: fromTokenAmount,
ToTokenAmount: toTokenAmount,
FromTokenDecimals: fromTokenDecimals,
ToTokenDecimals: toTokenDecimals,
}
return &action, nil
}
Loading

0 comments on commit 3c720f5

Please sign in to comment.