fix okx user
This commit is contained in:
483
rawtx.go
483
rawtx.go
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/gagliardetto/solana-go/rpc"
|
||||
"github.com/jackc/pgtype"
|
||||
"github.com/shopspring/decimal"
|
||||
pb "go.onsig.ai/onsig/yellowstone-proto"
|
||||
)
|
||||
|
||||
func (tx *RawTx) getAccountList() []solana.PublicKey {
|
||||
@@ -292,6 +293,198 @@ func InstructionsFromRpc(instructions []solana.CompiledInstruction) []Instructio
|
||||
return instrs
|
||||
}
|
||||
|
||||
func FromRpcTransactionWithMeta(tx rpc.TransactionWithMeta, blockTime *uint64, slot uint64, index int64) (*RawTx, error) {
|
||||
created := int64(0)
|
||||
if blockTime != nil {
|
||||
created = int64(*blockTime)
|
||||
}
|
||||
sTx := &RawTx{
|
||||
BlockTime: created,
|
||||
Slot: slot,
|
||||
IndexWithinBlock: index,
|
||||
Meta: Meta{
|
||||
Err: nil,
|
||||
Fee: 0,
|
||||
InnerInstructions: nil,
|
||||
LoadedAddresses: LoadedAddresses{},
|
||||
LogMessages: nil,
|
||||
PostBalances: nil,
|
||||
PostTokenBalances: nil,
|
||||
PreBalances: nil,
|
||||
PreTokenBalances: nil,
|
||||
Rewards: nil,
|
||||
},
|
||||
}
|
||||
|
||||
meta := tx.Meta
|
||||
yTx, _ := tx.GetTransaction()
|
||||
|
||||
if meta.Err != nil {
|
||||
e, _ := json.Marshal(meta.Err)
|
||||
sTx.Meta.Err = string(e)
|
||||
}
|
||||
sTx.Meta.Fee = meta.Fee
|
||||
//sTx.Meta.InnerInstructions = meta.InnerInstructions
|
||||
|
||||
for _, innerInstr := range meta.InnerInstructions {
|
||||
var instrs []Instruction
|
||||
for _, instr := range innerInstr.Instructions {
|
||||
instrs = append(instrs, Instruction{
|
||||
ProgramIDIndex: int(instr.ProgramIDIndex),
|
||||
Accounts: func() []int {
|
||||
var out []int
|
||||
for i := range instr.Accounts {
|
||||
out = append(out, int(instr.Accounts[i]))
|
||||
}
|
||||
return out
|
||||
}(),
|
||||
Data: instr.Data,
|
||||
StackHeight: newInt16(instr.StackHeight),
|
||||
})
|
||||
}
|
||||
sTx.Meta.InnerInstructions = append(sTx.Meta.InnerInstructions, InnerInstructions{
|
||||
Index: int(innerInstr.Index),
|
||||
Instructions: instrs,
|
||||
})
|
||||
}
|
||||
sTx.Meta.LogMessages = meta.LogMessages
|
||||
sTx.Meta.PostBalances = meta.PostBalances
|
||||
sTx.Meta.PreBalances = meta.PreBalances
|
||||
sTx.Meta.PostTokenBalances = convertTokenBalanceFromRpc(meta.PostTokenBalances)
|
||||
sTx.Meta.PreTokenBalances = convertTokenBalanceFromRpc(meta.PreTokenBalances)
|
||||
sTx.Meta.Rewards = nil
|
||||
sTx.Meta.LoadedAddresses.Readonly = meta.LoadedAddresses.ReadOnly
|
||||
sTx.Meta.LoadedAddresses.Writable = meta.LoadedAddresses.Writable
|
||||
|
||||
// copy signatures
|
||||
for i := range yTx.Signatures {
|
||||
sTx.Transaction.Signatures = append(sTx.Transaction.Signatures, yTx.Signatures[i])
|
||||
}
|
||||
// copy message
|
||||
sTx.Transaction.Message = Message{
|
||||
RecentBlockHash: yTx.Message.RecentBlockhash.String(),
|
||||
}
|
||||
// copy message.AccountKeys
|
||||
//stopAt := len(yTx.Message.AccountKeys) - sTx.Message.NumLookups()
|
||||
stopAt := len(yTx.Message.AccountKeys)
|
||||
for accIndex, acc := range yTx.Message.AccountKeys {
|
||||
sTx.Transaction.Message.AccountKeys = append(sTx.Transaction.Message.AccountKeys, acc)
|
||||
if accIndex == stopAt-1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
// copy message.Header
|
||||
sTx.Transaction.Message.Header = Header{
|
||||
NumRequiredSignatures: int(yTx.Message.Header.NumRequiredSignatures),
|
||||
NumReadonlySignedAccounts: int(yTx.Message.Header.NumReadonlySignedAccounts),
|
||||
NumReadonlyUnsignedAccounts: int(yTx.Message.Header.NumReadonlyUnsignedAccounts),
|
||||
}
|
||||
|
||||
// copy message.versioned
|
||||
if yTx.Message.IsVersioned() {
|
||||
sTx.Version = solana.MessageVersionV0
|
||||
} else {
|
||||
sTx.Version = solana.MessageVersionLegacy
|
||||
}
|
||||
|
||||
// copy address table lookups
|
||||
{
|
||||
tables := map[solana.PublicKey]solana.PublicKeySlice{}
|
||||
writable := meta.LoadedAddresses.Writable
|
||||
readonly := meta.LoadedAddresses.ReadOnly
|
||||
for _, addr := range yTx.Message.AddressTableLookups {
|
||||
sTx.Transaction.Message.AddressTableLookups = append(sTx.Transaction.Message.AddressTableLookups, solana.MessageAddressTableLookup{
|
||||
AccountKey: addr.AccountKey,
|
||||
WritableIndexes: addr.WritableIndexes,
|
||||
ReadonlyIndexes: addr.ReadonlyIndexes,
|
||||
})
|
||||
numTakeWritable := len(addr.WritableIndexes)
|
||||
numTakeReadonly := len(addr.ReadonlyIndexes)
|
||||
tableKey := addr.AccountKey
|
||||
{
|
||||
// now need to rebuild the address table taking into account the indexes, and put the keys into the tables
|
||||
maxIndex := 0
|
||||
for _, indexB := range addr.WritableIndexes {
|
||||
index := int(indexB)
|
||||
if index > maxIndex {
|
||||
maxIndex = index
|
||||
}
|
||||
}
|
||||
for _, indexB := range addr.ReadonlyIndexes {
|
||||
index := int(indexB)
|
||||
if index > maxIndex {
|
||||
maxIndex = index
|
||||
}
|
||||
}
|
||||
tables[tableKey] = make([]solana.PublicKey, maxIndex+1)
|
||||
}
|
||||
if numTakeWritable > 0 {
|
||||
writableForTable := writable[:numTakeWritable]
|
||||
for i, indexB := range addr.WritableIndexes {
|
||||
index := int(indexB)
|
||||
tables[tableKey][index] = writableForTable[i]
|
||||
}
|
||||
writable = writable[numTakeWritable:]
|
||||
}
|
||||
if numTakeReadonly > 0 {
|
||||
readableForTable := readonly[:numTakeReadonly]
|
||||
for i, indexB := range addr.ReadonlyIndexes {
|
||||
index := int(indexB)
|
||||
tables[tableKey][index] = readableForTable[i]
|
||||
}
|
||||
readonly = readonly[numTakeReadonly:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy instructions
|
||||
for _, instr := range yTx.Message.Instructions {
|
||||
sTx.Transaction.Message.Instructions = append(sTx.Transaction.Message.Instructions, Instruction{
|
||||
ProgramIDIndex: int(instr.ProgramIDIndex),
|
||||
Accounts: func() []int {
|
||||
var out []int
|
||||
for i := range instr.Accounts {
|
||||
out = append(out, int(instr.Accounts[i]))
|
||||
}
|
||||
return out
|
||||
}(),
|
||||
Data: instr.Data,
|
||||
})
|
||||
}
|
||||
|
||||
return sTx, nil
|
||||
}
|
||||
|
||||
func convertTokenBalanceFromRpc(tb []rpc.TokenBalance) []TokenBalance {
|
||||
var tokenBalances []TokenBalance = make([]TokenBalance, len(tb))
|
||||
for i, balance := range tb {
|
||||
var uiAmount = float64(0)
|
||||
if balance.UiTokenAmount.UiAmount != nil {
|
||||
uiAmount = *balance.UiTokenAmount.UiAmount
|
||||
}
|
||||
tokenBalances[i] = TokenBalance{
|
||||
AccountIndex: int(balance.AccountIndex),
|
||||
MintAccount: balance.Mint,
|
||||
OwnerAccount: balance.Owner,
|
||||
ProgramIDAccount: func() solana.PublicKey {
|
||||
if balance.ProgramId != nil {
|
||||
return *balance.ProgramId
|
||||
}
|
||||
return solana.PublicKey{}
|
||||
}(),
|
||||
UITokenAmount: UITokenAmount{
|
||||
Amount: balance.UiTokenAmount.Amount,
|
||||
Decimals: uint64(balance.UiTokenAmount.Decimals),
|
||||
UIAmount: uiAmount,
|
||||
UIAmountString: balance.UiTokenAmount.UiAmountString,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return tokenBalances
|
||||
|
||||
}
|
||||
|
||||
func InnerInstructionsFromRpc(instructions []rpc.InnerInstruction) []InnerInstructions {
|
||||
var innerInstructions []InnerInstructions = make([]InnerInstructions, len(instructions))
|
||||
for i, instruction := range instructions {
|
||||
@@ -374,6 +567,49 @@ func getAccountBalanceAfterTx(result *RawTx, accountIndex int) decimal.Decimal {
|
||||
return amount
|
||||
}
|
||||
|
||||
func tokenBalanceChange(result *RawTx, accountIndex int, tokenProgram, mint solana.PublicKey) (change decimal.Decimal, ataIndex int) {
|
||||
ataAccount, _, _ := solana.FindProgramAddress([][]byte{
|
||||
result.accountList[accountIndex][:],
|
||||
tokenProgram[:],
|
||||
mint[:],
|
||||
},
|
||||
solana.SPLAssociatedTokenAccountProgramID)
|
||||
|
||||
for i, account := range result.accountList {
|
||||
if account.Equals(ataAccount) {
|
||||
ataIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if ataIndex == 0 {
|
||||
return decimal.Zero, ataIndex
|
||||
}
|
||||
before := decimal.Zero
|
||||
after := decimal.Zero
|
||||
|
||||
for _, pre := range result.Meta.PreTokenBalances {
|
||||
if pre.AccountIndex == ataIndex {
|
||||
amount, err := decimal.NewFromString(pre.UITokenAmount.Amount)
|
||||
if err != nil {
|
||||
return decimal.Zero, ataIndex
|
||||
}
|
||||
before = amount
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, post := range result.Meta.PostTokenBalances {
|
||||
if post.AccountIndex == ataIndex {
|
||||
amount, err := decimal.NewFromString(post.UITokenAmount.Amount)
|
||||
if err != nil {
|
||||
return decimal.Zero, ataIndex
|
||||
}
|
||||
after = amount
|
||||
break
|
||||
}
|
||||
}
|
||||
return after.Sub(before).Abs(), ataIndex
|
||||
}
|
||||
|
||||
func GetTokenBalanceAfterTx(result *RawTx, accountIndex int, tokenProgram, mint solana.PublicKey) decimal.Decimal {
|
||||
ataAccount, _, _ := solana.FindProgramAddress([][]byte{
|
||||
result.accountList[accountIndex][:],
|
||||
@@ -445,3 +681,250 @@ func isAccountOwner(account, owner, mint solana.PublicKey) (bool, error) {
|
||||
}
|
||||
return account == ata, nil
|
||||
}
|
||||
|
||||
func ConvertYellowstoneGrpcTransactionToSolanaTransaction(y *pb.SubscribeUpdateTransaction, created int64) (*RawTx, error) {
|
||||
sTx := &RawTx{
|
||||
BlockTime: created,
|
||||
Slot: y.Slot,
|
||||
IndexWithinBlock: int64(y.Transaction.Index),
|
||||
Meta: Meta{
|
||||
Err: nil,
|
||||
Fee: 0,
|
||||
InnerInstructions: nil,
|
||||
LoadedAddresses: LoadedAddresses{},
|
||||
LogMessages: nil,
|
||||
PostBalances: nil,
|
||||
PostTokenBalances: nil,
|
||||
PreBalances: nil,
|
||||
PreTokenBalances: nil,
|
||||
Rewards: nil,
|
||||
},
|
||||
//Transaction: types.Transaction{
|
||||
// Message: types.Message{
|
||||
// AccountKeys: nil,
|
||||
// AddressTableLookups: nil,
|
||||
// Header: types.Header{},
|
||||
// Instructions: nil,
|
||||
// RecentBlockHash: "",
|
||||
// },
|
||||
// Signatures: nil,
|
||||
//},
|
||||
//Version: nil,
|
||||
}
|
||||
meta := y.Transaction.GetMeta()
|
||||
yTx := y.Transaction.Transaction
|
||||
|
||||
if meta.Err != nil && len(meta.Err.GetErr()) > 0 {
|
||||
// If the transaction has an error, we set the error in the Meta
|
||||
transError, err := DecodeTransactionError(meta.Err.GetErr())
|
||||
if err != nil {
|
||||
sTx.Meta.Err = err
|
||||
} else {
|
||||
sTx.Meta.Err = transError
|
||||
}
|
||||
// sTx.Meta.Err = meta.Err.GetErr()
|
||||
}
|
||||
sTx.Meta.Fee = meta.Fee
|
||||
//sTx.Meta.InnerInstructions = meta.InnerInstructions
|
||||
|
||||
for _, innerInstr := range meta.InnerInstructions {
|
||||
var instrs []Instruction
|
||||
for _, instr := range innerInstr.Instructions {
|
||||
instrs = append(instrs, Instruction{
|
||||
ProgramIDIndex: int(instr.ProgramIdIndex),
|
||||
Accounts: func() []int {
|
||||
var out []int
|
||||
for i := range instr.Accounts {
|
||||
out = append(out, int(instr.Accounts[i]))
|
||||
}
|
||||
return out
|
||||
}(),
|
||||
Data: instr.Data,
|
||||
StackHeight: newInt(instr.StackHeight),
|
||||
})
|
||||
}
|
||||
sTx.Meta.InnerInstructions = append(sTx.Meta.InnerInstructions, InnerInstructions{
|
||||
Index: int(innerInstr.Index),
|
||||
Instructions: instrs,
|
||||
})
|
||||
}
|
||||
sTx.Meta.LogMessages = meta.LogMessages
|
||||
sTx.Meta.PostBalances = meta.PostBalances
|
||||
sTx.Meta.PostTokenBalances = grpcTokenBalance(meta.PostTokenBalances)
|
||||
sTx.Meta.PreBalances = meta.PreBalances
|
||||
sTx.Meta.PreTokenBalances = grpcTokenBalance(meta.PreTokenBalances)
|
||||
sTx.Meta.Rewards = nil
|
||||
sTx.Meta.LoadedAddresses.Readonly = byteSlicesToKeySlices(meta.LoadedReadonlyAddresses)
|
||||
sTx.Meta.LoadedAddresses.Writable = byteSlicesToKeySlices(meta.LoadedWritableAddresses)
|
||||
|
||||
// copy signatures
|
||||
for i := range yTx.Signatures {
|
||||
sTx.Transaction.Signatures = append(sTx.Transaction.Signatures, solana.SignatureFromBytes(yTx.Signatures[i]))
|
||||
}
|
||||
// copy message
|
||||
sTx.Transaction.Message = Message{
|
||||
RecentBlockHash: solana.HashFromBytes(yTx.Message.RecentBlockhash).String(),
|
||||
}
|
||||
// copy message.AccountKeys
|
||||
//stopAt := len(yTx.Message.AccountKeys) - sTx.Message.NumLookups()
|
||||
stopAt := len(yTx.Message.AccountKeys)
|
||||
for accIndex, acc := range yTx.Message.AccountKeys {
|
||||
sTx.Transaction.Message.AccountKeys = append(sTx.Transaction.Message.AccountKeys, solana.PublicKeyFromBytes(acc))
|
||||
if accIndex == stopAt-1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
// copy message.Header
|
||||
sTx.Transaction.Message.Header = Header{
|
||||
NumRequiredSignatures: int(yTx.Message.Header.NumRequiredSignatures),
|
||||
NumReadonlySignedAccounts: int(yTx.Message.Header.NumReadonlySignedAccounts),
|
||||
NumReadonlyUnsignedAccounts: int(yTx.Message.Header.NumReadonlyUnsignedAccounts),
|
||||
}
|
||||
|
||||
// copy message.versioned
|
||||
if yTx.Message.Versioned {
|
||||
sTx.Version = solana.MessageVersionV0
|
||||
} else {
|
||||
sTx.Version = solana.MessageVersionLegacy
|
||||
}
|
||||
|
||||
// copy address table lookups
|
||||
{
|
||||
tables := map[solana.PublicKey]solana.PublicKeySlice{}
|
||||
writable := byteSlicesToKeySlices(meta.LoadedWritableAddresses)
|
||||
readonly := byteSlicesToKeySlices(meta.LoadedReadonlyAddresses)
|
||||
for _, addr := range yTx.Message.AddressTableLookups {
|
||||
sTx.Transaction.Message.AddressTableLookups = append(sTx.Transaction.Message.AddressTableLookups, solana.MessageAddressTableLookup{
|
||||
AccountKey: solana.PublicKeyFromBytes(addr.AccountKey),
|
||||
WritableIndexes: addr.WritableIndexes,
|
||||
ReadonlyIndexes: addr.ReadonlyIndexes,
|
||||
})
|
||||
numTakeWritable := len(addr.WritableIndexes)
|
||||
numTakeReadonly := len(addr.ReadonlyIndexes)
|
||||
tableKey := solana.PublicKeyFromBytes(addr.AccountKey)
|
||||
{
|
||||
// now need to rebuild the address table taking into account the indexes, and put the keys into the tables
|
||||
maxIndex := 0
|
||||
for _, indexB := range addr.WritableIndexes {
|
||||
index := int(indexB)
|
||||
if index > maxIndex {
|
||||
maxIndex = index
|
||||
}
|
||||
}
|
||||
for _, indexB := range addr.ReadonlyIndexes {
|
||||
index := int(indexB)
|
||||
if index > maxIndex {
|
||||
maxIndex = index
|
||||
}
|
||||
}
|
||||
tables[tableKey] = make([]solana.PublicKey, maxIndex+1)
|
||||
}
|
||||
if numTakeWritable > 0 {
|
||||
writableForTable := writable[:numTakeWritable]
|
||||
for i, indexB := range addr.WritableIndexes {
|
||||
index := int(indexB)
|
||||
tables[tableKey][index] = writableForTable[i]
|
||||
}
|
||||
writable = writable[numTakeWritable:]
|
||||
}
|
||||
if numTakeReadonly > 0 {
|
||||
readableForTable := readonly[:numTakeReadonly]
|
||||
for i, indexB := range addr.ReadonlyIndexes {
|
||||
index := int(indexB)
|
||||
tables[tableKey][index] = readableForTable[i]
|
||||
}
|
||||
readonly = readonly[numTakeReadonly:]
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// copy instructions
|
||||
for _, instr := range yTx.Message.Instructions {
|
||||
sTx.Transaction.Message.Instructions = append(sTx.Transaction.Message.Instructions, Instruction{
|
||||
ProgramIDIndex: int(instr.ProgramIdIndex),
|
||||
Accounts: func() []int {
|
||||
var out []int
|
||||
for i := range instr.Accounts {
|
||||
out = append(out, int(instr.Accounts[i]))
|
||||
}
|
||||
return out
|
||||
}(),
|
||||
Data: instr.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// resolve the lookups
|
||||
//{
|
||||
// if sTx.Transaction.Message.IsVersioned() {
|
||||
// // only versioned transactions have address table lookups
|
||||
// err := sTx.Transaction.Message.ResolveLookups()
|
||||
// if err != nil {
|
||||
// return sTx, fmt.Errorf("failed to resolve lookups: %w", err)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
return sTx, nil
|
||||
}
|
||||
|
||||
func newInt16(x uint16) *int {
|
||||
y := int(x)
|
||||
return &y
|
||||
}
|
||||
|
||||
func newInt(x *uint32) *int {
|
||||
if x == nil {
|
||||
return nil
|
||||
}
|
||||
y := int(*x)
|
||||
return &y
|
||||
}
|
||||
|
||||
func byteSlicesToKeySlices(keys [][]byte) []solana.PublicKey {
|
||||
var out []solana.PublicKey
|
||||
for _, key := range keys {
|
||||
var k solana.PublicKey
|
||||
copy(k[:], key)
|
||||
out = append(out, k)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func grpcTokenBalance(src []*pb.TokenBalance) []TokenBalance {
|
||||
out := make([]TokenBalance, len(src))
|
||||
for i, tb := range src {
|
||||
var (
|
||||
mintAccount solana.PublicKey
|
||||
ownerAccount solana.PublicKey
|
||||
programIDAccount solana.PublicKey
|
||||
)
|
||||
|
||||
if tb.Mint != "" {
|
||||
mintAccount, _ = solana.PublicKeyFromBase58(tb.Mint)
|
||||
}
|
||||
if tb.Owner != "" {
|
||||
ownerAccount, _ = solana.PublicKeyFromBase58(tb.Owner)
|
||||
}
|
||||
if tb.ProgramId != "" {
|
||||
programIDAccount, _ = solana.PublicKeyFromBase58(tb.ProgramId)
|
||||
}
|
||||
|
||||
out[i] = TokenBalance{
|
||||
AccountIndex: int(tb.AccountIndex),
|
||||
MintAccount: mintAccount,
|
||||
OwnerAccount: &ownerAccount,
|
||||
ProgramIDAccount: programIDAccount,
|
||||
Mint: tb.Mint,
|
||||
Owner: tb.Owner,
|
||||
ProgramID: tb.ProgramId,
|
||||
UITokenAmount: UITokenAmount{
|
||||
Amount: tb.UiTokenAmount.Amount,
|
||||
Decimals: uint64(tb.UiTokenAmount.Decimals),
|
||||
UIAmount: tb.UiTokenAmount.UiAmount,
|
||||
UIAmountString: tb.UiTokenAmount.UiAmountString,
|
||||
},
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user