214 lines
4.6 KiB
Go
214 lines
4.6 KiB
Go
|
|
package pump_parser
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
|
||
|
|
"github.com/gagliardetto/solana-go"
|
||
|
|
"github.com/shopspring/decimal"
|
||
|
|
)
|
||
|
|
|
||
|
|
var maxSlippageBps = decimal.NewFromInt(10000)
|
||
|
|
|
||
|
|
type SwapMode uint8
|
||
|
|
type SwapAmountSide uint8
|
||
|
|
type SwapLimitType uint8
|
||
|
|
|
||
|
|
const (
|
||
|
|
SwapModeUnknown SwapMode = iota
|
||
|
|
SwapModeExactIn
|
||
|
|
SwapModeExactOut
|
||
|
|
)
|
||
|
|
|
||
|
|
const (
|
||
|
|
SwapAmountSideUnknown SwapAmountSide = iota
|
||
|
|
SwapAmountSideBase
|
||
|
|
SwapAmountSideQuote
|
||
|
|
)
|
||
|
|
|
||
|
|
const (
|
||
|
|
SwapLimitTypeUnknown SwapLimitType = iota
|
||
|
|
SwapLimitTypeMinOut
|
||
|
|
SwapLimitTypeMaxIn
|
||
|
|
)
|
||
|
|
|
||
|
|
func (m SwapMode) String() string {
|
||
|
|
switch m {
|
||
|
|
case SwapModeExactIn:
|
||
|
|
return "exact_in"
|
||
|
|
case SwapModeExactOut:
|
||
|
|
return "exact_out"
|
||
|
|
default:
|
||
|
|
return ""
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (m SwapMode) MarshalJSON() ([]byte, error) {
|
||
|
|
return json.Marshal(m.String())
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s SwapAmountSide) String() string {
|
||
|
|
switch s {
|
||
|
|
case SwapAmountSideBase:
|
||
|
|
return "base"
|
||
|
|
case SwapAmountSideQuote:
|
||
|
|
return "quote"
|
||
|
|
default:
|
||
|
|
return ""
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s SwapAmountSide) MarshalJSON() ([]byte, error) {
|
||
|
|
return json.Marshal(s.String())
|
||
|
|
}
|
||
|
|
|
||
|
|
func (t SwapLimitType) String() string {
|
||
|
|
switch t {
|
||
|
|
case SwapLimitTypeMinOut:
|
||
|
|
return "min_out"
|
||
|
|
case SwapLimitTypeMaxIn:
|
||
|
|
return "max_in"
|
||
|
|
default:
|
||
|
|
return ""
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (t SwapLimitType) MarshalJSON() ([]byte, error) {
|
||
|
|
return json.Marshal(t.String())
|
||
|
|
}
|
||
|
|
|
||
|
|
func swapAmountForSide(baseAmount, quoteAmount decimal.Decimal, side SwapAmountSide) decimal.Decimal {
|
||
|
|
switch side {
|
||
|
|
case SwapAmountSideBase:
|
||
|
|
return baseAmount
|
||
|
|
case SwapAmountSideQuote:
|
||
|
|
return quoteAmount
|
||
|
|
default:
|
||
|
|
return decimal.Zero
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func swapMintForSide(baseMint, quoteMint solana.PublicKey, side SwapAmountSide) solana.PublicKey {
|
||
|
|
switch side {
|
||
|
|
case SwapAmountSideBase:
|
||
|
|
return baseMint
|
||
|
|
case SwapAmountSideQuote:
|
||
|
|
return quoteMint
|
||
|
|
default:
|
||
|
|
return solana.PublicKey{}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func oppositeSwapAmountSide(side SwapAmountSide) SwapAmountSide {
|
||
|
|
switch side {
|
||
|
|
case SwapAmountSideBase:
|
||
|
|
return SwapAmountSideQuote
|
||
|
|
case SwapAmountSideQuote:
|
||
|
|
return SwapAmountSideBase
|
||
|
|
default:
|
||
|
|
return SwapAmountSideUnknown
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func fixedSwapAmountSide(event string, swapMode SwapMode) SwapAmountSide {
|
||
|
|
switch swapMode {
|
||
|
|
case SwapModeExactIn:
|
||
|
|
switch event {
|
||
|
|
case TxEventBuy:
|
||
|
|
return SwapAmountSideQuote
|
||
|
|
case TxEventSell:
|
||
|
|
return SwapAmountSideBase
|
||
|
|
}
|
||
|
|
case SwapModeExactOut:
|
||
|
|
switch event {
|
||
|
|
case TxEventBuy:
|
||
|
|
return SwapAmountSideBase
|
||
|
|
case TxEventSell:
|
||
|
|
return SwapAmountSideQuote
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return SwapAmountSideUnknown
|
||
|
|
}
|
||
|
|
|
||
|
|
func limitSwapAmountType(swapMode SwapMode) SwapLimitType {
|
||
|
|
switch swapMode {
|
||
|
|
case SwapModeExactIn:
|
||
|
|
return SwapLimitTypeMinOut
|
||
|
|
case SwapModeExactOut:
|
||
|
|
return SwapLimitTypeMaxIn
|
||
|
|
default:
|
||
|
|
return SwapLimitTypeUnknown
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func calculateLimitSlippageBps(limitType SwapLimitType, limitAmount, actualAmount decimal.Decimal) decimal.Decimal {
|
||
|
|
switch limitType {
|
||
|
|
case SwapLimitTypeMinOut:
|
||
|
|
if !actualAmount.IsPositive() {
|
||
|
|
if !limitAmount.IsPositive() {
|
||
|
|
return maxSlippageBps
|
||
|
|
}
|
||
|
|
return maxSlippageBps.Neg()
|
||
|
|
}
|
||
|
|
if !limitAmount.IsPositive() {
|
||
|
|
return maxSlippageBps
|
||
|
|
}
|
||
|
|
return actualAmount.Sub(limitAmount).Mul(maxSlippageBps).Div(actualAmount)
|
||
|
|
case SwapLimitTypeMaxIn:
|
||
|
|
if !limitAmount.IsPositive() {
|
||
|
|
if !actualAmount.IsPositive() {
|
||
|
|
return maxSlippageBps
|
||
|
|
}
|
||
|
|
return maxSlippageBps.Neg()
|
||
|
|
}
|
||
|
|
return limitAmount.Sub(actualAmount).Mul(maxSlippageBps).Div(limitAmount)
|
||
|
|
default:
|
||
|
|
return decimal.Zero
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *Swap) SetSwapAmountInfoDetailed(
|
||
|
|
swapMode SwapMode,
|
||
|
|
fixedAmount decimal.Decimal,
|
||
|
|
fixedSide SwapAmountSide,
|
||
|
|
fixedMint solana.PublicKey,
|
||
|
|
limitType SwapLimitType,
|
||
|
|
limitAmount decimal.Decimal,
|
||
|
|
limitSide SwapAmountSide,
|
||
|
|
limitMint solana.PublicKey,
|
||
|
|
actualLimitAmount decimal.Decimal,
|
||
|
|
) {
|
||
|
|
s.SwapMode = swapMode
|
||
|
|
s.FixedAmount = fixedAmount
|
||
|
|
s.FixedAmountSide = fixedSide
|
||
|
|
s.FixedMint = fixedMint
|
||
|
|
s.LimitAmountType = limitType
|
||
|
|
s.LimitAmount = limitAmount
|
||
|
|
s.LimitAmountSide = limitSide
|
||
|
|
s.LimitMint = limitMint
|
||
|
|
s.ActualLimitAmount = actualLimitAmount
|
||
|
|
s.ActualLimitAmountSide = limitSide
|
||
|
|
s.SlippageBps = calculateLimitSlippageBps(limitType, limitAmount, actualLimitAmount)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *Swap) SetSwapAmountInfo(swapMode SwapMode, fixedAmount, limitAmount decimal.Decimal) {
|
||
|
|
fixedSide := fixedSwapAmountSide(s.Event, swapMode)
|
||
|
|
if fixedSide == SwapAmountSideUnknown {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
limitType := limitSwapAmountType(swapMode)
|
||
|
|
limitSide := oppositeSwapAmountSide(fixedSide)
|
||
|
|
actualLimitAmount := swapAmountForSide(s.BaseAmount, s.QuoteAmount, limitSide)
|
||
|
|
s.SetSwapAmountInfoDetailed(
|
||
|
|
swapMode,
|
||
|
|
fixedAmount,
|
||
|
|
fixedSide,
|
||
|
|
swapMintForSide(s.BaseMint, s.QuoteMint, fixedSide),
|
||
|
|
limitType,
|
||
|
|
limitAmount,
|
||
|
|
limitSide,
|
||
|
|
swapMintForSide(s.BaseMint, s.QuoteMint, limitSide),
|
||
|
|
actualLimitAmount,
|
||
|
|
)
|
||
|
|
}
|