Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/solana-go-sdk.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,55 @@ func main() {

```

# Transaction Parsing
```go

package main

import (
"context"
"fmt"
"github.com/blocto/solana-go-sdk/rpc"
"log"

"github.com/blocto/solana-go-sdk/client"
"github.com/blocto/solana-go-sdk/pkg/instructionparser"
)

func main() {
c := client.NewClient(rpc.MainnetRPCEndpoint)
//Transaction signature
txSig := "67LZFTjbfN1n5PKPeUkh5zDF7uiPEH6eMnK5VoQ42JgNe3tA4gatp3U2reAi5FkcrtfKHASiQLXy4vbhucUKpsug"

tx, err := c.GetTransaction(context.Background(), txSig)
if err != nil {
log.Fatal(err)
}

if tx == nil {
log.Fatal("Transaction not found")
}

fmt.Printf("Parsing %d instructions:\n", len(tx.Transaction.Message.Instructions))

for i, instr := range tx.Transaction.Message.Instructions {
programID := tx.Transaction.Message.Accounts[instr.ProgramIDIndex]

parsed, err := instructionparser.ParseInstruction(programID, instr.Data)
if err != nil {
fmt.Printf("Instruction %d: Failed to parse - %v\n", i, err)
continue
}

fmt.Printf("Instruction %d:\n", i)
fmt.Printf(" Program: %s\n", parsed.ProgramID)
fmt.Printf(" Type: %s\n", parsed.Type)
fmt.Printf(" Data: %+v\n", parsed.Data)
fmt.Println()
}
}

```
## Programing model & Program

There are some important tpyes in solana.
Expand Down
Empty file added example/instruction_main.go
Empty file.
21 changes: 21 additions & 0 deletions pkg/instructionparser/ata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package instructionparser

import "fmt"

type ATAParser struct{}

type ATAInstruction struct {
Type string
Data map[string]interface{}
}

func (p *ATAParser) Parse(data []byte) (interface{}, error) {
if len(data) == 0 {
return &ATAInstruction{
Type: "Create",
Data: map[string]interface{}{},
}, nil
}

return nil, fmt.Errorf("unknown ATA instruction")
}
62 changes: 62 additions & 0 deletions pkg/instructionparser/parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package instructionparser

import (
"fmt"

"github.com/blocto/solana-go-sdk/common"
)

type InstructionParser interface {
Parse(data []byte) (interface{}, error)
}

type ParsedInstruction struct {
ProgramID common.PublicKey
Type string
Data interface{}
}

func ParseInstruction(programID common.PublicKey, data []byte) (*ParsedInstruction, error) {
var parser InstructionParser
var instrType string

switch programID {
case common.TokenProgramID:
parser = &TokenParser{}
instrType = "Token"
case common.SystemProgramID:
parser = &SystemParser{}
instrType = "System"
case common.SPLAssociatedTokenAccountProgramID:
parser = &ATAParser{}
instrType = "AssociatedToken"
default:
return parseGeneric(programID, data)
}

parsed, err := parser.Parse(data)
if err != nil {
return nil, err
}

return &ParsedInstruction{
ProgramID: programID,
Type: instrType,
Data: parsed,
}, nil
}

func parseGeneric(programID common.PublicKey, data []byte) (*ParsedInstruction, error) {
if len(data) == 0 {
return nil, fmt.Errorf("empty instruction data")
}

return &ParsedInstruction{
ProgramID: programID,
Type: "Generic",
Data: map[string]interface{}{
"discriminator": data[0],
"raw_data": data,
},
}, nil
}
83 changes: 83 additions & 0 deletions pkg/instructionparser/system.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package instructionparser

import (
"encoding/binary"
"fmt"
)

type SystemParser struct{}

type SystemInstruction struct {
Type string
Data map[string]interface{}
}

func (p *SystemParser) Parse(data []byte) (interface{}, error) {
if len(data) < 4 {
return nil, fmt.Errorf("invalid system instruction data")
}

discriminator := binary.LittleEndian.Uint32(data[0:4])

switch discriminator {
case 0: // CreateAccount
return p.parseCreateAccount(data)
case 2: // Transfer
return p.parseTransfer(data)
case 3: // CreateAccountWithSeed
return p.parseCreateAccountWithSeed(data)
default:
return p.parseUnknown(data)
}
}

func (p *SystemParser) parseTransfer(data []byte) (*SystemInstruction, error) {
if len(data) < 12 {
return nil, fmt.Errorf("invalid transfer instruction data")
}

lamports := binary.LittleEndian.Uint64(data[4:12])

return &SystemInstruction{
Type: "Transfer",
Data: map[string]interface{}{
"lamports": lamports,
},
}, nil
}

func (p *SystemParser) parseCreateAccount(data []byte) (*SystemInstruction, error) {
if len(data) < 52 {
return nil, fmt.Errorf("invalid create account instruction data")
}

lamports := binary.LittleEndian.Uint64(data[4:12])
space := binary.LittleEndian.Uint64(data[12:20])

return &SystemInstruction{
Type: "CreateAccount",
Data: map[string]interface{}{
"lamports": lamports,
"space": space,
},
}, nil
}

func (p *SystemParser) parseCreateAccountWithSeed(data []byte) (*SystemInstruction, error) {
return &SystemInstruction{
Type: "CreateAccountWithSeed",
Data: map[string]interface{}{},
}, nil
}

func (p *SystemParser) parseUnknown(data []byte) (*SystemInstruction, error) {
discriminator := binary.LittleEndian.Uint32(data[0:4])

return &SystemInstruction{
Type: "Unknown",
Data: map[string]interface{}{
"discriminator": discriminator,
"raw_data": data,
},
}, nil
}
Loading