Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ANCHOR-514] Populate sep38quote info into sep24txn #1194

Merged
merged 12 commits into from
Nov 27, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ public enum Sep38Context {
@SerializedName("sep6")
SEP6("sep6"),

@SerializedName("sep24")
SEP24("sep24"),

@SerializedName("sep31")
SEP31("sep31");

Expand Down
71 changes: 56 additions & 15 deletions core/src/main/java/org/stellar/anchor/sep24/Sep24Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import static org.stellar.anchor.api.sep.sep24.InfoResponse.FeeResponse;
import static org.stellar.anchor.event.EventService.EventQueue.TRANSACTION;
import static org.stellar.anchor.sep24.Sep24Helper.fromTxn;
import static org.stellar.anchor.sep24.Sep24Transaction.Kind.WITHDRAWAL;
import static org.stellar.anchor.sep24.Sep24Transaction.Kind.*;
import static org.stellar.anchor.util.Log.debug;
import static org.stellar.anchor.util.Log.debugF;
import static org.stellar.anchor.util.Log.info;
Expand All @@ -27,18 +27,9 @@
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.*;
import org.stellar.anchor.api.event.AnchorEvent;
import org.stellar.anchor.api.exception.AnchorException;
import org.stellar.anchor.api.exception.SepException;
import org.stellar.anchor.api.exception.SepNotAuthorizedException;
import org.stellar.anchor.api.exception.SepNotFoundException;
import org.stellar.anchor.api.exception.SepValidationException;
import org.stellar.anchor.api.exception.*;
import org.stellar.anchor.api.sep.AssetInfo;
import org.stellar.anchor.api.sep.sep24.GetTransactionRequest;
import org.stellar.anchor.api.sep.sep24.GetTransactionsRequest;
Expand All @@ -55,6 +46,8 @@
import org.stellar.anchor.config.CustodyConfig;
import org.stellar.anchor.config.Sep24Config;
import org.stellar.anchor.event.EventService;
import org.stellar.anchor.sep38.Sep38Quote;
import org.stellar.anchor.sep6.ExchangeAmountsCalculator;
import org.stellar.anchor.util.ConfigHelper;
import org.stellar.anchor.util.CustodyUtils;
import org.stellar.anchor.util.MetricConstants;
Expand All @@ -74,6 +67,7 @@ public class Sep24Service {
final InteractiveUrlConstructor interactiveUrlConstructor;
final MoreInfoUrlConstructor moreInfoUrlConstructor;
final CustodyConfig custodyConfig;
final ExchangeAmountsCalculator exchangeAmountsCalculator;

final Counter sep24TransactionRequestedCounter =
counter(MetricConstants.SEP24_TRANSACTION_REQUESTED);
Expand Down Expand Up @@ -103,7 +97,8 @@ public Sep24Service(
EventService eventService,
InteractiveUrlConstructor interactiveUrlConstructor,
MoreInfoUrlConstructor moreInfoUrlConstructor,
CustodyConfig custodyConfig) {
CustodyConfig custodyConfig,
ExchangeAmountsCalculator exchangeAmountsCalculator) {
debug("appConfig:", appConfig);
debug("sep24Config:", sep24Config);
this.appConfig = appConfig;
Expand All @@ -116,6 +111,7 @@ public Sep24Service(
this.interactiveUrlConstructor = interactiveUrlConstructor;
this.moreInfoUrlConstructor = moreInfoUrlConstructor;
this.custodyConfig = custodyConfig;
this.exchangeAmountsCalculator = exchangeAmountsCalculator;
info("Sep24Service initialized.");
}

Expand Down Expand Up @@ -250,6 +246,14 @@ public InteractiveTransactionResponse withdraw(
builder.refundMemoType(memoTypeString(memoType(refundMemo)));
}

String quoteId = withdrawRequest.get("quote_id");
if (quoteId != null) {
System.out.println(asset.getSep38AssetName() + 1231231);
AssetInfo buyAsset = assetService.getAssetByName(withdrawRequest.get("destination_asset"));
this.validatedAndPopulateQuote(
quoteId, asset, buyAsset, strAmount, builder, WITHDRAWAL.toString(), txnId);
}

Sep24Transaction txn = builder.build();
txnStore.save(txn);

Expand Down Expand Up @@ -340,7 +344,7 @@ public InteractiveTransactionResponse deposit(Sep10Jwt token, Map<String, String
}
}

// Verify that the asset code exists in our database, with withdraw enabled.
// Verify that the asset code exists in our database, with deposit enabled.
AssetInfo asset = assetService.getAsset(assetCode, assetIssuer);
if (asset == null || !asset.getDeposit().getEnabled() || !asset.getSep24Enabled()) {
infoF("invalid operation for asset {}", assetCode);
Expand Down Expand Up @@ -385,7 +389,7 @@ public InteractiveTransactionResponse deposit(Sep10Jwt token, Map<String, String
new Sep24TransactionBuilder(txnStore)
.transactionId(txnId)
.status(INCOMPLETE.toString())
.kind(Sep24Transaction.Kind.DEPOSIT.toString())
.kind(DEPOSIT.toString())
.assetCode(assetCode)
.assetIssuer(depositRequest.get("asset_issuer"))
.startedAt(Instant.now())
Expand All @@ -410,6 +414,13 @@ public InteractiveTransactionResponse deposit(Sep10Jwt token, Map<String, String
builder.memoType(memoTypeString(memoType(memo)));
}

String quoteId = depositRequest.get("quote_id");
if (quoteId != null) {
AssetInfo sellAsset = assetService.getAssetByName(depositRequest.get("source_asset"));
this.validatedAndPopulateQuote(
quoteId, sellAsset, asset, strAmount, builder, DEPOSIT.toString(), txnId);
}

Sep24Transaction txn = builder.build();
txnStore.save(txn);

Expand Down Expand Up @@ -557,4 +568,34 @@ public InfoResponse getInfo() {
sep24Config.getFeatures().getClaimableBalances()))
.build();
}

public void validatedAndPopulateQuote(
String quoteId,
AssetInfo sellAsset,
AssetInfo buyAsset,
String strAmount,
Sep24TransactionBuilder builder,
String kind,
String txnId)
throws AnchorException {
Sep38Quote quote =
exchangeAmountsCalculator.validateQuoteAgainstRequestInfo(
quoteId, sellAsset, buyAsset, strAmount);

debugF("Updating transaction ({}) with quote ({})", txnId, quoteId);
builder.quoteId(quoteId);
builder.amountExpected(quote.getSellAmount());
builder.amountIn(quote.getSellAmount());
builder.amountInAsset(quote.getSellAsset());
builder.amountOut(quote.getBuyAmount());
builder.amountOutAsset(quote.getBuyAsset());
builder.amountFee(quote.getFee().getTotal());
builder.amountFeeAsset(quote.getFee().getAsset());

if (kind.equals(DEPOSIT.toString())) {
builder.sourceAsset(quote.getSellAsset());
} else {
builder.destinationAsset(quote.getBuyAsset());
}
}
}
12 changes: 12 additions & 0 deletions core/src/main/java/org/stellar/anchor/sep24/Sep24Transaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,18 @@ public interface Sep24Transaction extends SepTransaction {

String getMessage();

String getQuoteId();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs would need to be updated to include quote_id under the SEP-24 transaction as well. https://developers.stellar.org/api/anchor-platform/resources/get-transaction


void setQuoteId(String quoteId);

String getSourceAsset();

void setSourceAsset(String sourceAsset);

String getDestinationAsset();

void setDestinationAsset(String sourceAsset);

enum Kind {
DEPOSIT("deposit"),
WITHDRAWAL("withdrawal");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,18 @@ public Sep24TransactionBuilder refundMemoType(String refundMemoType) {
return this;
}

public void quoteId(String quoteId) {
txn.setQuoteId(quoteId);
}

public void sourceAsset(String sourceAsset) {
txn.setSourceAsset(sourceAsset);
}

public void destinationAsset(String destinationAsset) {
txn.setDestinationAsset(destinationAsset);
}

public Sep24Transaction build() {
return txn;
}
Expand Down
7 changes: 3 additions & 4 deletions core/src/main/java/org/stellar/anchor/sep38/Sep38Service.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.stellar.anchor.sep38;

import static org.stellar.anchor.api.sep.sep38.Sep38Context.SEP31;
import static org.stellar.anchor.api.sep.sep38.Sep38Context.SEP6;
import static org.stellar.anchor.api.sep.sep38.Sep38Context.*;
import static org.stellar.anchor.api.sep.sep38.Sep38QuoteResponse.*;
import static org.stellar.anchor.event.EventService.EventQueue.TRANSACTION;
import static org.stellar.anchor.util.BeanHelper.updateField;
Expand Down Expand Up @@ -337,8 +336,8 @@ public Sep38QuoteResponse postQuote(Sep10Jwt token, Sep38PostQuoteRequest reques

// context
Sep38Context context = request.getContext();
if (context == null || !List.of(SEP6, SEP31).contains(context)) {
throw new BadRequestException("Unsupported context. Should be one of [sep6, sep31].");
if (context == null || !List.of(SEP6, SEP24, SEP31).contains(context)) {
throw new BadRequestException("Unsupported context. Should be one of [sep6, sep24, sep31].");
}

// Check SEP31 send limits
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,35 +30,63 @@ public class ExchangeAmountsCalculator {
*/
public Amounts calculateFromQuote(String quoteId, AssetInfo sellAsset, String sellAmount)
throws AnchorException {
Sep38Quote quote = validateQuoteAgainstRequestInfo(quoteId, sellAsset, null, sellAmount);
return Amounts.builder()
.amountIn(quote.getSellAmount())
.amountInAsset(quote.getSellAsset())
.amountOut(quote.getBuyAmount())
.amountOutAsset(quote.getBuyAsset())
.amountFee(quote.getFee().getTotal())
.amountFeeAsset(quote.getFee().getAsset())
.build();
}

/**
* validate the quote info against Sep6 or Sep24 or request info.
*
* @param quoteId The quote ID
* @param sellAsset The asset the user is selling. It can be null for sep24 deposit request
* @param buyAsset The amount the user is buying. It can be null for sep24 withdraw request
* @param sellAmount The amount the user is selling. It can be null for sep24 deposit and withdraw
* requests
* @return The quote
* @throws AnchorException if the quote is invalid
*/
public Sep38Quote validateQuoteAgainstRequestInfo(
String quoteId, AssetInfo sellAsset, AssetInfo buyAsset, String sellAmount)
throws AnchorException {
Sep38Quote quote = sep38QuoteStore.findByQuoteId(quoteId);
if (quote == null) {
throw new BadRequestException("Quote not found");
}
if (!amountEquals(sellAmount, quote.getSellAmount())) {

if (sellAsset != null && !sellAsset.getSep38AssetName().equals(quote.getSellAsset())) {
throw new BadRequestException(
String.format(
"amount(%s) does not match quote sell amount(%s)",
sellAmount, quote.getSellAmount()));
"source asset(%s) does not match quote sell asset(%s)",
sellAsset.getSep38AssetName(), quote.getSellAsset()));
}
if (!sellAsset.getSep38AssetName().equals(quote.getSellAsset())) {

if (buyAsset != null && !buyAsset.getSep38AssetName().equals(quote.getBuyAsset())) {
throw new BadRequestException(
String.format(
"source asset(%s) does not match quote sell asset(%s)",
sellAsset.getSep38AssetName(), quote.getSellAsset()));
"destination asset(%s) does not match quote buy asset(%s)",
buyAsset.getSep38AssetName(), quote.getBuyAsset()));
}

if (sellAmount != null && !amountEquals(sellAmount, quote.getSellAmount())) {
throw new BadRequestException(
String.format(
"amount(%s) does not match quote sell amount(%s)",
sellAmount, quote.getSellAmount()));
}

RateFee fee = quote.getFee();
if (fee == null) {
throw new SepValidationException("Quote is missing the 'fee' field");
}

return Amounts.builder()
.amountIn(quote.getSellAmount())
.amountInAsset(quote.getSellAsset())
.amountOut(quote.getBuyAmount())
.amountOutAsset(quote.getBuyAsset())
.amountFee(fee.getTotal())
.amountFeeAsset(fee.getAsset())
.build();
return quote;
}

/** Amounts calculated for an exchange request. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ public static GetTransactionResponse toGetTransactionResponse(
.memoType(txn.getMemoType())
.refundMemo(txn.getRefundMemo())
.refundMemoType(txn.getRefundMemoType())
.quoteId(txn.getQuoteId())
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,7 @@ public class PojoSep24Transaction implements Sep24Transaction {
String message;
String refundMemo;
String refundMemoType;
String quoteId;
String sourceAsset;
String destinationAsset;
}
Loading
Loading