Skip to content
Merged
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
87 changes: 87 additions & 0 deletions solana/coinbasestableswapper/accounts_liquidity_pool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package coinbase_stable_swapper

import (
"bytes"
"crypto/ed25519"
"encoding/binary"
"fmt"

"github.com/mr-tron/base58"
)

// Minimum size without the dynamic supported_tokens vector
const (
LiquidityPoolAccountMinSize = (8 + // discriminator
32 + // operations_authority
32 + // pause_authority
32 + // fee_recipient
4 + // supported_tokens vector length (empty)
8 + // fee_rate
1 + // swaps_paused
1 + // liquidity_paused
1) // bump
)

type LiquidityPoolAccount struct {
OperationsAuthority ed25519.PublicKey
PauseAuthority ed25519.PublicKey
FeeRecipient ed25519.PublicKey
SupportedTokens []ed25519.PublicKey
FeeRate uint64
SwapsPaused bool
LiquidityPaused bool
Bump uint8
}

func (obj *LiquidityPoolAccount) Unmarshal(data []byte) error {
if len(data) < LiquidityPoolAccountMinSize {
return ErrInvalidAccountData
}

var offset int

var discriminator []byte
getDiscriminator(data, &discriminator, &offset)
if !bytes.Equal(discriminator, LiquidityPoolAccountDiscriminator) {
return ErrInvalidAccountData
}

getKey(data, &obj.OperationsAuthority, &offset)
getKey(data, &obj.PauseAuthority, &offset)
getKey(data, &obj.FeeRecipient, &offset)

// Read supported_tokens vector (4-byte length prefix + pubkeys)
vecLen := binary.LittleEndian.Uint32(data[offset:])
offset += 4

obj.SupportedTokens = make([]ed25519.PublicKey, vecLen)
for i := uint32(0); i < vecLen; i++ {
getKey(data, &obj.SupportedTokens[i], &offset)
}

getUint64(data, &obj.FeeRate, &offset)
getBool(data, &obj.SwapsPaused, &offset)
getBool(data, &obj.LiquidityPaused, &offset)
getUint8(data, &obj.Bump, &offset)

return nil
}

func (obj *LiquidityPoolAccount) String() string {
tokensList := make([]string, len(obj.SupportedTokens))
for i, t := range obj.SupportedTokens {
tokensList[i] = base58.Encode(t)
}

return fmt.Sprintf(
"LiquidityPool{operations_authority=%s,pause_authority=%s,fee_recipient=%s,supported_tokens=%v,fee_rate=%d,swaps_paused=%t,liquidity_paused=%t,bump=%d}",
base58.Encode(obj.OperationsAuthority),
base58.Encode(obj.PauseAuthority),
base58.Encode(obj.FeeRecipient),
tokensList,
obj.FeeRate,
obj.SwapsPaused,
obj.LiquidityPaused,
obj.Bump,
)
}
58 changes: 58 additions & 0 deletions solana/coinbasestableswapper/address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package coinbase_stable_swapper

import (
"crypto/ed25519"

"github.com/code-payments/ocp-server/solana"
)

var (
LiquidityPoolPrefix = []byte("liquidity_pool")
TokenVaultPrefix = []byte("token_vault")
VaultTokenAccountPrefix = []byte("vault_token_account")
AddressWhitelistPrefix = []byte("address_whitelist")
)

// GetPoolAddress returns the PDA for the liquidity pool
func GetPoolAddress() (ed25519.PublicKey, uint8, error) {
return solana.FindProgramAddressAndBump(
PROGRAM_ID,
LiquidityPoolPrefix,
)
}

type GetTokenVaultAddressArgs struct {
Pool ed25519.PublicKey
Mint ed25519.PublicKey
}

// GetTokenVaultAddress returns the PDA for a token vault
func GetTokenVaultAddress(args *GetTokenVaultAddressArgs) (ed25519.PublicKey, uint8, error) {
return solana.FindProgramAddressAndBump(
PROGRAM_ID,
TokenVaultPrefix,
args.Pool,
args.Mint,
)
}

type GetVaultTokenAccountAddressArgs struct {
Vault ed25519.PublicKey
}

// GetVaultTokenAccountAddress returns the PDA for a vault's token account
func GetVaultTokenAccountAddress(args *GetVaultTokenAccountAddressArgs) (ed25519.PublicKey, uint8, error) {
return solana.FindProgramAddressAndBump(
PROGRAM_ID,
VaultTokenAccountPrefix,
args.Vault,
)
}

// GetWhitelistAddress returns the PDA for the address whitelist
func GetWhitelistAddress() (ed25519.PublicKey, uint8, error) {
return solana.FindProgramAddressAndBump(
PROGRAM_ID,
AddressWhitelistPrefix,
)
}
139 changes: 139 additions & 0 deletions solana/coinbasestableswapper/instructions_swap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package coinbase_stable_swapper

import (
"crypto/ed25519"

"github.com/code-payments/ocp-server/solana"
)

const (
SwapInstructionArgsSize = (8 + // discriminator
8 + // amount_in
8) // min_amount_out
)

type SwapInstructionArgs struct {
AmountIn uint64
MinAmountOut uint64
}

type SwapInstructionAccounts struct {
Pool ed25519.PublicKey
InVault ed25519.PublicKey
OutVault ed25519.PublicKey
InVaultTokenAccount ed25519.PublicKey
OutVaultTokenAccount ed25519.PublicKey
UserFromTokenAccount ed25519.PublicKey
ToTokenAccount ed25519.PublicKey
FeeRecipientTokenAccount ed25519.PublicKey
FeeRecipient ed25519.PublicKey
FromMint ed25519.PublicKey
ToMint ed25519.PublicKey
User ed25519.PublicKey
Whitelist ed25519.PublicKey
}

func NewSwapInstruction(
accounts *SwapInstructionAccounts,
args *SwapInstructionArgs,
) solana.Instruction {
var offset int

// Serialize instruction arguments
data := make([]byte, SwapInstructionArgsSize)

putDiscriminator(data, SwapInstructionDiscriminator, &offset)
putUint64(data, args.AmountIn, &offset)
putUint64(data, args.MinAmountOut, &offset)

return solana.Instruction{
Program: PROGRAM_ADDRESS,

// Instruction args
Data: data,

// Instruction accounts
Accounts: []solana.AccountMeta{
{
PublicKey: accounts.Pool,
IsWritable: false,
IsSigner: false,
},
{
PublicKey: accounts.InVault,
IsWritable: false,
IsSigner: false,
},
{
PublicKey: accounts.OutVault,
IsWritable: false,
IsSigner: false,
},
{
PublicKey: accounts.InVaultTokenAccount,
IsWritable: true,
IsSigner: false,
},
{
PublicKey: accounts.OutVaultTokenAccount,
IsWritable: true,
IsSigner: false,
},
{
PublicKey: accounts.UserFromTokenAccount,
IsWritable: true,
IsSigner: false,
},
{
PublicKey: accounts.ToTokenAccount,
IsWritable: true,
IsSigner: false,
},
{
PublicKey: accounts.FeeRecipientTokenAccount,
IsWritable: true,
IsSigner: false,
},
{
PublicKey: accounts.FeeRecipient,
IsWritable: false,
IsSigner: false,
},
{
PublicKey: accounts.FromMint,
IsWritable: false,
IsSigner: false,
},
{
PublicKey: accounts.ToMint,
IsWritable: false,
IsSigner: false,
},
{
PublicKey: accounts.User,
IsWritable: true,
IsSigner: true,
},
{
PublicKey: accounts.Whitelist,
IsWritable: false,
IsSigner: false,
},
{
PublicKey: SPL_TOKEN_PROGRAM_ID,
IsWritable: false,
IsSigner: false,
},
{
PublicKey: ASSOCIATED_TOKEN_PROGRAM_ID,
IsWritable: false,
IsSigner: false,
},
{
PublicKey: SYSTEM_PROGRAM_ID,
IsWritable: false,
IsSigner: false,
},
},
}
}
23 changes: 23 additions & 0 deletions solana/coinbasestableswapper/program.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package coinbase_stable_swapper

import (
"crypto/ed25519"
"errors"
)

var (
ErrInvalidProgram = errors.New("invalid program id")
ErrInvalidAccountData = errors.New("unexpected account data")
ErrInvalidInstructionData = errors.New("unexpected instruction data")
)

var (
PROGRAM_ADDRESS = mustBase58Decode("pqgqKahpG1y2wsgxFhzaAnkV1cL9vk8MSg9qm4q646F")
PROGRAM_ID = ed25519.PublicKey(PROGRAM_ADDRESS)
)

var (
SPL_TOKEN_PROGRAM_ID = ed25519.PublicKey(mustBase58Decode("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"))
ASSOCIATED_TOKEN_PROGRAM_ID = ed25519.PublicKey(mustBase58Decode("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"))
SYSTEM_PROGRAM_ID = ed25519.PublicKey(mustBase58Decode("11111111111111111111111111111111"))
)
13 changes: 13 additions & 0 deletions solana/coinbasestableswapper/types_account_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package coinbase_stable_swapper

// Account discriminators from the on-chain IDL
var (
// LiquidityPool discriminator: [66, 38, 17, 64, 188, 80, 68, 129]
LiquidityPoolAccountDiscriminator = []byte{66, 38, 17, 64, 188, 80, 68, 129}

// AddressWhitelist discriminator: [10, 102, 46, 176, 154, 249, 160, 48]
AddressWhitelistAccountDiscriminator = []byte{10, 102, 46, 176, 154, 249, 160, 48}

// TokenVault discriminator: [121, 7, 84, 254, 151, 228, 43, 144]
TokenVaultAccountDiscriminator = []byte{121, 7, 84, 254, 151, 228, 43, 144}
)
12 changes: 12 additions & 0 deletions solana/coinbasestableswapper/types_instruction_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package coinbase_stable_swapper

// Instruction discriminators from the on-chain IDL
var (
// swap discriminator: [248, 198, 158, 145, 225, 117, 135, 200]
SwapInstructionDiscriminator = []byte{248, 198, 158, 145, 225, 117, 135, 200}
)

func putDiscriminator(dst []byte, discriminator []byte, offset *int) {
copy(dst[*offset:], discriminator)
*offset += 8
}
Loading
Loading