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
22 changes: 13 additions & 9 deletions pkg/jupiter/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ func (q *Quote) GetEstimatedSwapAmount() uint64 {
return q.estimatedSwapAmount
}

func (q *Quote) String() string {
return q.jsonString
}

// GetQuote gets an optimal route for performing a swap
func (c *Client) GetQuote(
ctx context.Context,
Expand Down Expand Up @@ -117,6 +121,7 @@ type SwapInstructions struct {
SetupInstructions []solana.Instruction
SwapInstruction solana.Instruction
CleanupInstruction *solana.Instruction
AddressLookupTables []string
}

// GetSwapInstructions gets the instructions to construct a transaction to sign
Expand All @@ -130,10 +135,6 @@ func (c *Client) GetSwapInstructions(
tracer := metrics.TraceMethodCall(ctx, metricsStructName, "GetSwapInstructions")
defer tracer.End()

if !quote.useLegacyInstructions {
return nil, errors.New("only legacy transactions are supported")
}

// todo: struct this
reqBody := fmt.Sprintf(
`{"quoteResponse": %s, "userPublicKey": "%s", "destinationTokenAccount": "%s", "prioritizationFeeLamports": "auto", "asLegacyTransaction": %v}`,
Expand Down Expand Up @@ -206,6 +207,8 @@ func (c *Client) GetSwapInstructions(
}
}

res.AddressLookupTables = jsonBody.AddressLookupTableAddresses

return &res, nil
}

Expand Down Expand Up @@ -258,9 +261,10 @@ type jsonInstruction struct {
}

type jsonSwapInstructions struct {
TokenLedgerInstruction *jsonInstruction `json:"tokenLedgerInstruction"`
ComputeBudgetInstructions []*jsonInstruction `json:"computeBudgetInstructions"`
SetupInstructions []*jsonInstruction `json:"setupInstructions"`
SwapInstruction *jsonInstruction `json:"swapInstruction"`
CleanupInstruction *jsonInstruction `json:"cleanupInstruction"`
TokenLedgerInstruction *jsonInstruction `json:"tokenLedgerInstruction"`
ComputeBudgetInstructions []*jsonInstruction `json:"computeBudgetInstructions"`
SetupInstructions []*jsonInstruction `json:"setupInstructions"`
SwapInstruction *jsonInstruction `json:"swapInstruction"`
CleanupInstruction *jsonInstruction `json:"cleanupInstruction"`
AddressLookupTableAddresses []string `json:"addressLookupTableAddresses"`
}
25 changes: 25 additions & 0 deletions pkg/solana/address_lookup_table.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package solana

import (
"bytes"
"crypto/ed25519"
)

type AddressLookupTable struct {
PublicKey ed25519.PublicKey
Addresses []ed25519.PublicKey
}

type SortableAddressLookupTables []AddressLookupTable

func (s SortableAddressLookupTables) Len() int {
return len(s)
}

func (s SortableAddressLookupTables) Less(i int, j int) bool {
return bytes.Compare(s[i].PublicKey, s[j].PublicKey) < 0
}

func (s SortableAddressLookupTables) Swap(i int, j int) {
s[i], s[j] = s[j], s[i]
}
62 changes: 62 additions & 0 deletions pkg/solana/addresslookuptable/accounts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package address_lookup_table

import (
"crypto/ed25519"
"errors"
"fmt"

"github.com/mr-tron/base58"

"github.com/code-payments/code-server/pkg/solana/binary"
)

var (
ErrInvalidAccountSize = errors.New("invalid address lookup table account size")
)

const optionSize = 1

const metadataSize = 56
const maxAddresses = 256

type AddressLookupTableAccount struct {
TypeIndex uint32
DeactivationSlot uint64
LastExtendedSlot uint64
LastExtendedSlotStartIndex uint8
Authority ed25519.PublicKey
Addresses []ed25519.PublicKey
}

func (obj *AddressLookupTableAccount) Unmarshal(data []byte) error {
var offset int

if len(data) < metadataSize {
return ErrInvalidAccountSize
}

binary.GetUint32(data[offset:], &obj.TypeIndex, &offset)
binary.GetUint64(data[offset:], &obj.DeactivationSlot, &offset)
binary.GetUint64(data[offset:], &obj.LastExtendedSlot, &offset)
binary.GetUint8(data[offset:], &obj.LastExtendedSlotStartIndex, &offset)
binary.GetOptionalKey32(data[offset:], &obj.Authority, &offset, optionSize)

fmt.Println(base58.Encode(obj.Authority))

offset = 56

addressBufferSize := len(data) - offset
addressCount := addressBufferSize / 32
if addressBufferSize%ed25519.PublicKeySize != 0 {
return ErrInvalidAccountSize
} else if addressCount > maxAddresses {
return ErrInvalidAccountSize
}

obj.Addresses = make([]ed25519.PublicKey, addressCount)
for i := 0; i < addressCount; i++ {
binary.GetKey32(data[offset:], &obj.Addresses[i], &offset)
}

return nil
}
29 changes: 17 additions & 12 deletions pkg/solana/binary/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ func PutKey32(dst []byte, src []byte, offset *int) {
*offset += ed25519.PublicKeySize
}

func PutOptionalKey32(dst []byte, src []byte, offset *int) {
func PutOptionalKey32(dst []byte, src []byte, offset *int, optionSize int) {
if len(src) > 0 {
dst[0] = 1
copy(dst[4:], src)
copy(dst[optionSize:], src)
}

*offset += 4 + ed25519.PublicKeySize
*offset += optionSize + ed25519.PublicKeySize
}

func PutUint64(dst []byte, v uint64, offset *int) {
Expand All @@ -29,12 +29,12 @@ func PutUint32(dst []byte, v uint32, offset *int) {
*offset += 4
}

func PutOptionalUint64(dst []byte, v *uint64, offset *int) {
func PutOptionalUint64(dst []byte, v *uint64, offset *int, optionSize int) {
if v != nil {
dst[0] = 1
binary.LittleEndian.PutUint64(dst[4:], *v)
binary.LittleEndian.PutUint64(dst[optionSize:], *v)
}
*offset += 4 + 8
*offset += optionSize + 8
}

func GetKey32(src []byte, dst *ed25519.PublicKey, offset *int) {
Expand All @@ -43,12 +43,12 @@ func GetKey32(src []byte, dst *ed25519.PublicKey, offset *int) {
*offset += ed25519.PublicKeySize
}

func GetOptionalKey32(src []byte, dst *ed25519.PublicKey, offset *int) {
func GetOptionalKey32(src []byte, dst *ed25519.PublicKey, offset *int, optionSize int) {
if src[0] == 1 {
*dst = make([]byte, ed25519.PublicKeySize)
copy(*dst, src[4:])
copy(*dst, src[optionSize:])
}
*offset += 4 + ed25519.PublicKeySize
*offset += optionSize + ed25519.PublicKeySize
}

func GetUint64(src []byte, dst *uint64, offset *int) {
Expand All @@ -61,10 +61,15 @@ func GetUint32(src []byte, dst *uint32, offset *int) {
*offset += 4
}

func GetOptionalUint64(src []byte, dst **uint64, offset *int) {
func GetUint8(src []byte, dst *uint8, offset *int) {
*dst = src[0]
*offset += 1
}

func GetOptionalUint64(src []byte, dst **uint64, offset *int, optionSize int) {
if src[0] == 1 {
val := binary.LittleEndian.Uint64(src[4:])
val := binary.LittleEndian.Uint64(src[optionSize:])
*dst = &val
}
*offset += 4 + 8
*offset += optionSize + 8
}
64 changes: 64 additions & 0 deletions pkg/solana/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ func (t *Transaction) Unmarshal(b []byte) error {
}

func (m Message) Marshal() []byte {
switch m.version {
case MessageVersionLegacy:
return m.marshalLegacy()
case MessageVersion0:
return m.marshalV0()
default:
panic("unsupported message version")
}
}

func (m Message) marshalLegacy() []byte {
b := bytes.NewBuffer(nil)

// Header
Expand Down Expand Up @@ -82,7 +93,60 @@ func (m Message) Marshal() []byte {
return b.Bytes()
}

func (m Message) marshalV0() []byte {
b := bytes.NewBuffer(nil)

// Version Number
_ = b.WriteByte(byte(m.version + 127))

// Header
_ = b.WriteByte(m.Header.NumSignatures)
_ = b.WriteByte(m.Header.NumReadonlySigned)
_ = b.WriteByte(m.Header.NumReadOnly)

// Accounts
_, _ = shortvec.EncodeLen(b, len(m.Accounts))
for _, a := range m.Accounts {
_, _ = b.Write(a)
}

// Recent Blockhash
_, _ = b.Write(m.RecentBlockhash[:])

// Instructions
_, _ = shortvec.EncodeLen(b, len(m.Instructions))
for _, i := range m.Instructions {
_ = b.WriteByte(i.ProgramIndex)

// Accounts
_, _ = shortvec.EncodeLen(b, len(i.Accounts))
_, _ = b.Write(i.Accounts)

// Data
_, _ = shortvec.EncodeLen(b, len(i.Data))
_, _ = b.Write(i.Data)
}

_, _ = shortvec.EncodeLen(b, len(m.AddressTableLookups))
for _, addressTableLookup := range m.AddressTableLookups {
_, _ = b.Write(addressTableLookup.PublicKey)

_, _ = shortvec.EncodeLen(b, len(addressTableLookup.WritableIndexes))
_, _ = b.Write(addressTableLookup.WritableIndexes)

_, _ = shortvec.EncodeLen(b, len(addressTableLookup.ReadonlyIndexes))
_, _ = b.Write(addressTableLookup.ReadonlyIndexes)
}

return b.Bytes()
}

func (m *Message) Unmarshal(b []byte) (err error) {
// todo: double check this is correct
if b[0] > 127 {
return errors.New("versioned messages not supported")
}

buf := bytes.NewBuffer(b)

// Header
Expand Down
14 changes: 8 additions & 6 deletions pkg/solana/token/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const AccountSize = 165
// Reference: https://github.com/solana-labs/solana-program-library/blob/8944f428fe693c3a4226bf766a79be9c75e8e520/token/program/src/state.rs#L214
const MultisigAccountSize = 355

const optionSize = 4

type Account struct {
// The mint associated with this account
Mint ed25519.PublicKey
Expand Down Expand Up @@ -49,12 +51,12 @@ func (a *Account) Marshal() []byte {
binary.PutKey32(b, a.Mint, &offset)
binary.PutKey32(b[offset:], a.Owner, &offset)
binary.PutUint64(b[offset:], a.Amount, &offset)
binary.PutOptionalKey32(b[offset:], a.Delegate, &offset)
binary.PutOptionalKey32(b[offset:], a.Delegate, &offset, optionSize)
b[offset] = byte(a.State)
offset++
binary.PutOptionalUint64(b[offset:], a.IsNative, &offset)
binary.PutOptionalUint64(b[offset:], a.IsNative, &offset, optionSize)
binary.PutUint64(b[offset:], a.DelegatedAmount, &offset)
binary.PutOptionalKey32(b[offset:], a.CloseAuthority, &offset)
binary.PutOptionalKey32(b[offset:], a.CloseAuthority, &offset, optionSize)

return b
}
Expand All @@ -68,12 +70,12 @@ func (a *Account) Unmarshal(b []byte) bool {
binary.GetKey32(b, &a.Mint, &offset)
binary.GetKey32(b[offset:], &a.Owner, &offset)
binary.GetUint64(b[offset:], &a.Amount, &offset)
binary.GetOptionalKey32(b[offset:], &a.Delegate, &offset)
binary.GetOptionalKey32(b[offset:], &a.Delegate, &offset, optionSize)
a.State = AccountState(b[offset])
offset++
binary.GetOptionalUint64(b[offset:], &a.IsNative, &offset)
binary.GetOptionalUint64(b[offset:], &a.IsNative, &offset, optionSize)
binary.GetUint64(b[offset:], &a.DelegatedAmount, &offset)
binary.GetOptionalKey32(b[offset:], &a.CloseAuthority, &offset)
binary.GetOptionalKey32(b[offset:], &a.CloseAuthority, &offset, optionSize)

return true
}
Loading