231 lines
4.9 KiB
Go
231 lines
4.9 KiB
Go
package pump_parser
|
|
|
|
import (
|
|
"encoding/json"
|
|
|
|
"github.com/gagliardetto/solana-go"
|
|
"github.com/shopspring/decimal"
|
|
)
|
|
|
|
var maxSlippageBps = decimal.NewFromInt(10000)
|
|
|
|
func normalizeSlippageBps(value decimal.Decimal) decimal.Decimal {
|
|
//if value.IsNegative() {
|
|
// return decimal.Zero
|
|
//}
|
|
//if value.GreaterThan(maxSlippageBps) {
|
|
// return maxSlippageBps
|
|
//}
|
|
return value
|
|
}
|
|
|
|
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 {
|
|
var value decimal.Decimal
|
|
switch limitType {
|
|
case SwapLimitTypeMinOut:
|
|
if !actualAmount.IsPositive() {
|
|
if !limitAmount.IsPositive() {
|
|
value = maxSlippageBps
|
|
break
|
|
}
|
|
value = maxSlippageBps.Neg()
|
|
break
|
|
}
|
|
if !limitAmount.IsPositive() {
|
|
value = maxSlippageBps
|
|
break
|
|
}
|
|
value = actualAmount.Sub(limitAmount).Mul(maxSlippageBps).Div(actualAmount)
|
|
case SwapLimitTypeMaxIn:
|
|
if !limitAmount.IsPositive() {
|
|
if !actualAmount.IsPositive() {
|
|
value = maxSlippageBps
|
|
break
|
|
}
|
|
value = maxSlippageBps.Neg()
|
|
break
|
|
}
|
|
value = limitAmount.Sub(actualAmount).Mul(maxSlippageBps).Div(limitAmount)
|
|
default:
|
|
value = decimal.Zero
|
|
}
|
|
return normalizeSlippageBps(value)
|
|
}
|
|
|
|
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,
|
|
)
|
|
}
|