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
78 changes: 43 additions & 35 deletions ocp/rpc/transaction/stateful_swap.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,6 @@ func (s *transactionServer) handleReserveStatefulSwap(
return handleStatefulSwapError(streamer, err)
}

if !common.IsCoreMint(fromMint) && !common.IsCoreMint(toMint) {
return handleStatefulSwapError(streamer, NewSwapDeniedError("swap must involve core mint"))
}

if bytes.Equal(fromMint.PublicKey().ToBytes(), toMint.PublicKey().ToBytes()) {
return handleStatefulSwapError(streamer, NewSwapValidationError("must swap between two different mints"))
}
Expand Down Expand Up @@ -284,29 +280,41 @@ func (s *transactionServer) handleReserveStatefulSwap(
return handleStatefulSwapError(streamer, NewSwapDeniedErrorf("funding source %s is not supported", initiateReserveSwapReq.FundingSource))
}

otherMint := fromMint
if common.IsCoreMint(otherMint) {
otherMint = toMint
}
isSell := !common.IsCoreMint(fromMint)
isBuy := !common.IsCoreMint(toMint)

var destinationCurrencyMetadataRecord *currency.MetadataRecord
var initializesMint bool
currencyMetadataRecord, err := s.data.GetCurrencyMetadata(ctx, otherMint.PublicKey().ToBase58())
if err == currency.ErrNotFound {
return handleStatefulSwapError(streamer, NewSwapValidationError("mint not found"))
} else if err != nil {
log.With(zap.Error(err)).Warn("failure getting destination timelock record")
return handleStatefulSwapError(streamer, err)
}
switch currencyMetadataRecord.State {
case currency.MetadataStateAvailable:
initializesMint = false
case currency.MetadataStateWaitingForInitialPurchase:
initializesMint = true
default:
return handleStatefulSwapError(streamer, NewSwapDeniedError("mint is being initialized"))
if isBuy {
destinationCurrencyMetadataRecord, err = s.data.GetCurrencyMetadata(ctx, toMint.PublicKey().ToBase58())
if err == currency.ErrNotFound {
return handleStatefulSwapError(streamer, NewSwapValidationError("mint not found"))
} else if err != nil {
log.With(zap.Error(err)).Warn("failure getting destination currency metadata record")
return handleStatefulSwapError(streamer, err)
}
switch destinationCurrencyMetadataRecord.State {
case currency.MetadataStateAvailable:
initializesMint = false
case currency.MetadataStateWaitingForInitialPurchase:
if !common.IsCoreMint(fromMint) {
return handleStatefulSwapError(streamer, NewSwapDeniedError("new currency can only be created from the core mint"))
}
initializesMint = true
default:
return handleStatefulSwapError(streamer, NewSwapDeniedError("mint is being initialized"))
}
}

if !initializesMint && !common.IsCoreMint(fromMint) {
if isSell {
sourceCurrencyMetadataRecord, err := s.data.GetCurrencyMetadata(ctx, fromMint.PublicKey().ToBase58())
if err == currency.ErrNotFound {
return handleStatefulSwapError(streamer, NewSwapValidationError("mint not found"))
} else if err != nil {
log.With(zap.Error(err)).Warn("failure getting source currency metadata record")
return handleStatefulSwapError(streamer, err)
}

liveReserveState, err := s.mintDataProvider.GetLiveReserveState(ctx, fromMint)
if err != nil {
log.With(zap.Error(err)).Warn("failure getting live reserve state")
Expand All @@ -317,7 +325,7 @@ func (s *transactionServer) handleReserveStatefulSwap(
CurrentSupplyInQuarks: liveReserveState.SupplyFromBonding,
SellAmountInQuarks: initiateReserveSwapReq.SwapAmount,
ValueMintDecimals: uint8(common.CoreMintDecimals),
SellFeeBps: currencyMetadataRecord.SellFeeBps,
SellFeeBps: sourceCurrencyMetadataRecord.SellFeeBps,
})
if estimatedFees == 0 {
return handleStatefulSwapError(streamer, NewSwapDeniedError("swap would not generate a sell fee"))
Expand Down Expand Up @@ -370,7 +378,7 @@ func (s *transactionServer) handleReserveStatefulSwap(
return handleStatefulSwapError(streamer, NewSwapValidationError("owner must be swap authority"))
}

if owner.PublicKey().ToBase58() != currencyMetadataRecord.CreatedBy {
if owner.PublicKey().ToBase58() != destinationCurrencyMetadataRecord.CreatedBy {
return handleStatefulSwapError(streamer, NewSwapDeniedError("only the currency creator can buy initial tokens"))
}

Expand All @@ -379,7 +387,7 @@ func (s *transactionServer) handleReserveStatefulSwap(
}

// The VM is not supported yet, so we need to work around GetVmConfigForMint
destinationVaultRecord, err := s.data.GetKey(ctx, currencyMetadataRecord.Authority)
destinationVaultRecord, err := s.data.GetKey(ctx, destinationCurrencyMetadataRecord.Authority)
if err != nil {
log.With(zap.Error(err)).Warn("failure getting destination vm authority vault record")
return handleStatefulSwapError(streamer, err)
Expand Down Expand Up @@ -450,31 +458,31 @@ func (s *transactionServer) handleReserveStatefulSwap(
initiateReserveSwapReq.FeeAmount,
selectedNonce,
)
} else if common.IsCoreMint(fromMint) {
swapHandler = NewReserveBuySwapHandler(
} else if isBuy && isSell {
swapHandler = NewReserveBuySellSwapHandler(
s.data,
owner,
swapAuthority,
fromMint,
toMint,
initiateReserveSwapReq.SwapAmount,
selectedNonce,
)
} else if common.IsCoreMint(toMint) {
swapHandler = NewReserveSellSwapHandler(
} else if isBuy {
swapHandler = NewReserveBuySwapHandler(
s.data,
owner,
swapAuthority,
fromMint,
toMint,
initiateReserveSwapReq.SwapAmount,
selectedNonce,
)
} else {
swapHandler = NewReserveBuySellSwapHandler(
swapHandler = NewReserveSellSwapHandler(
s.data,
owner,
swapAuthority,
fromMint,
toMint,
initiateReserveSwapReq.SwapAmount,
selectedNonce,
)
Expand Down Expand Up @@ -639,8 +647,8 @@ func (s *transactionServer) handleReserveStatefulSwap(
}

if initializesMint {
currencyMetadataRecord.State = currency.MetadataStateFundingAuthority
err = s.data.SaveCurrencyMetadata(ctx, currencyMetadataRecord)
destinationCurrencyMetadataRecord.State = currency.MetadataStateFundingAuthority
err = s.data.SaveCurrencyMetadata(ctx, destinationCurrencyMetadataRecord)
if err != nil {
log.With(zap.Error(err)).Warn("failure saving currency metadata record")
return err
Expand Down
23 changes: 5 additions & 18 deletions ocp/worker/swap/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"time"

"github.com/code-payments/ocp-server/metrics"
"github.com/code-payments/ocp-server/ocp/common"
"github.com/code-payments/ocp-server/ocp/data/swap"
)

Expand Down Expand Up @@ -52,23 +51,11 @@ func recordSwapCountEvent(ctx context.Context, state swap.State, count uint64) {
}

func recordSwapFinalizedEvent(ctx context.Context, swapRecord *swap.Record, quarksBought uint64) {
if !common.IsCoreMintUsdStableCoin() {
return
}

var usdMarketValue float64
if common.CoreMintAccount.PublicKey().ToBase58() == swapRecord.FromMint {
usdMarketValue = float64(swapRecord.SwapAmount) / float64(common.CoreMintQuarksPerUnit)
} else {
usdMarketValue = float64(quarksBought) / float64(common.CoreMintQuarksPerUnit)
}

metrics.RecordEvent(ctx, swapFinalizedEventName, map[string]interface{}{
"id": swapRecord.Id,
"from_mint": swapRecord.FromMint,
"to_mint": swapRecord.ToMint,
"quarks_sold": swapRecord.SwapAmount,
"quarks_bought": quarksBought,
"usd_market_value": usdMarketValue,
"id": swapRecord.Id,
"from_mint": swapRecord.FromMint,
"to_mint": swapRecord.ToMint,
"quarks_sold": swapRecord.SwapAmount,
"quarks_bought": quarksBought,
})
}
42 changes: 32 additions & 10 deletions ocp/worker/swap/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,12 +532,10 @@ func (p *runtime) maybeUpdateBalancesForFinalizedReserveSwap(ctx context.Context
if !common.IsCoreMintUsdStableCoin() {
return 0, false, errors.New("core mint is not a usd stable coin")
}
if !common.IsCoreMint(fromMint) && !common.IsCoreMint(toMint) {
return 0, false, errors.New("core mint must be involved in swap")
}

var destinationCurrencyMetadataRecord *currency.MetadataRecord
if !common.IsCoreMint(toMint) {
destinationCurrencyMetadataRecord, err := p.data.GetCurrencyMetadata(ctx, swapRecord.ToMint)
destinationCurrencyMetadataRecord, err = p.data.GetCurrencyMetadata(ctx, swapRecord.ToMint)
if err != nil {
return 0, false, err
}
Expand Down Expand Up @@ -599,8 +597,26 @@ func (p *runtime) maybeUpdateBalancesForFinalizedReserveSwap(ctx context.Context
nativeAmountWithoutFees = fundingIntentRecord.SendPublicPaymentMetadata.NativeAmount
usdMarketValueWithoutFees = fundingIntentRecord.SendPublicPaymentMetadata.UsdMarketValue

if common.IsCoreMint(toMint) {
usdMarketValue, err := currency_util.CalculateUsdMarketValueFromTokenAmount(ctx, p.data, p.exchangeRateStore, p.reserveStore, common.CoreMintAccount, uint64(deltaQuarksIntoOmnibus), time.Now())
if !common.IsCoreMint(fromMint) {
coreMintQuarksFromSell := uint64(deltaQuarksIntoOmnibus)
if !common.IsCoreMint(toMint) {
destinationCurrencyAccounts, err := common.GetLaunchpadCurrencyAccounts(destinationCurrencyMetadataRecord)
if err != nil {
return 0, false, err
}

coreMintQuarksIntoDestinationPool, err := transaction_util.GetDeltaQuarksFromTokenBalances(destinationCurrencyAccounts.VaultBase, tokenBalances)
if err != nil {
return 0, false, err
}
if coreMintQuarksIntoDestinationPool <= 0 {
return 0, false, errors.New("delta quarks into destination pool base vault is not positive")
}

coreMintQuarksFromSell = uint64(coreMintQuarksIntoDestinationPool)
}

usdMarketValue, err := currency_util.CalculateUsdMarketValueFromTokenAmount(ctx, p.data, p.exchangeRateStore, p.reserveStore, common.CoreMintAccount, coreMintQuarksFromSell, time.Now())
if err != nil {
return 0, false, err
}
Expand All @@ -610,11 +626,17 @@ func (p *runtime) maybeUpdateBalancesForFinalizedReserveSwap(ctx context.Context
big.NewFloat(0.99).SetPrec(128),
).Float64()

exchangeCurrency = currency_lib.USD
nativeAmountWithoutFees = usdMarketValueWithoutFees
// A sell settles into the core mint, so its native value is reported in USD. A
// cross-currency swap keeps the client's original exchange currency and
// native amount (discounted by the sell fee below); only its USD market
// value is reconciled to the realized core mint.
if common.IsCoreMint(toMint) {
exchangeCurrency = currency_lib.USD
nativeAmountWithoutFees = usdMarketValueWithoutFees
}

// Update funding intent record with actual USD market value for
// consistent USD cost basis
// Reconcile the source funding payment's cost basis to the core mint actually
// realized by the sell.
fundingIntentRecord.SendPublicPaymentMetadata.UsdMarketValue = usdMarketValueWithoutFees
err = p.data.SaveIntent(ctx, fundingIntentRecord)
if err != nil {
Expand Down
Loading