Skip to content

Commit

Permalink
[ANCHOR-355] Implement SEP-6 deposit (#1035)
Browse files Browse the repository at this point in the history
This implements the SEP-6 `GET /deposit` endpoint _mostly_ according to
the sequence diagram from the PR.

Where the implementation deviates is how `CUSTOMER_UPDATED` anchor
events are structured. This implementation sends out 1 event whenever a
SEP-12 customer is updated instead of fanning them out a
`TRANSACTION_STATUS_CHANGED` event for each ongoing transaction
submitted by the customer. This was done to keep the scope of this
change small, but we can consider whether to move this into the platform
later.
  • Loading branch information
philipliu authored Sep 8, 2023
1 parent 6442a92 commit 678b9e8
Show file tree
Hide file tree
Showing 55 changed files with 1,532 additions and 216 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.google.gson.annotations.SerializedName;
import lombok.*;
import org.stellar.anchor.api.platform.CustomerUpdatedResponse;
import org.stellar.anchor.api.platform.GetQuoteResponse;
import org.stellar.anchor.api.platform.GetTransactionResponse;

Expand All @@ -13,8 +14,7 @@
* Schema</a>
*/
@Builder
@Getter
@Setter
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AnchorEvent {
Expand All @@ -23,6 +23,7 @@ public class AnchorEvent {
String sep;
GetTransactionResponse transaction;
GetQuoteResponse quote;
CustomerUpdatedResponse customer;

public enum Type {
@SerializedName("transaction_created")
Expand All @@ -32,7 +33,9 @@ public enum Type {
@SerializedName("transaction_error")
TRANSACTION_ERROR("transaction_error"),
@SerializedName("quote_created")
QUOTE_CREATED("quote_created");
QUOTE_CREATED("quote_created"),
@SerializedName("customer_updated")
CUSTOMER_UPDATED("customer_updated");

public final String type;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.stellar.anchor.api.platform;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CustomerUpdatedResponse {
String id;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.google.gson.annotations.SerializedName;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
Expand Down Expand Up @@ -86,7 +87,23 @@ public class PlatformTransactionData {
Customers customers;
StellarId creator;

@SerializedName("required_info_message")
String requiredInfoMessage;

@SerializedName("required_info_updates")
List<String> requiredInfoUpdates;

@SerializedName("required_customer_info_message")
String requiredCustomerInfoMessage;

@SerializedName("required_customer_info_updates")
List<String> requiredCustomerInfoUpdates;

Map<String, InstructionField> instructions;

public enum Sep {
@SerializedName("6")
SEP_6(6),
@SuppressWarnings("unused")
@SerializedName("24")
SEP_24(24),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.stellar.anchor.api.sep.sep6;

import com.google.gson.annotations.SerializedName;
import lombok.Builder;
import lombok.Data;
import lombok.NonNull;

/**
* The request body of the GET /deposit endpoint.
*
* @see <a href="">GET /deposit</a>
*/
@Builder
@Data
public class GetDepositRequest {
/** The asset code of the asset to deposit. */
@NonNull
@SerializedName("asset_code")
String assetCode;

/** The Stellar account ID of the user to deposit to. */
@NonNull String account;

/** The memo type to use for the deposit. */
@SerializedName("memo_type")
String memoType;

/** The memo to use for the deposit. */
String memo;

/** Email address of depositor. Currently, ignored. */
@SerializedName("email_address")
String emailAddress;

/** Type of deposit. */
@NonNull String type;

/** Name of wallet to deposit to. Currently, ignored. */
@SerializedName("wallet_name")
String walletName;

/**
* Anchor should link to this when notifying the user that the transaction has completed.
* Currently, ignored
*/
@SerializedName("wallet_url")
String walletUrl;

/**
* Defaults to en if not specified or if the specified language is not supported. Currently,
* ignored.
*/
String lang;

/** The amount to deposit. */
@NonNull String amount;

/** The ISO 3166-1 alpha-3 code of the user's current address. */
@SerializedName("country_code")
String countryCode;

/**
* Whether the client supports receiving deposit transactions as a claimable balance. Currently,
* unsupported.
*/
@SerializedName("claimable_balances_supported")
Boolean claimableBalancesSupported;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.stellar.anchor.api.sep.sep6;

import lombok.Builder;
import lombok.Data;

/**
* The response to the GET /deposit endpoint.
*
* @see <a
* href="https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0006.md#response">GET
* /deposit response</a>
*/
@Builder
@Data
public class GetDepositResponse {
/**
* Terse but complete instructions for how to deposit the asset.
*
* <p>Anchor Platform does not support synchronous deposit flows, so this field will never contain
* real instructions.
*/
String how;

/** The anchor's ID for this deposit. */
String id;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package org.stellar.anchor.api.sep.sep6;

import com.google.gson.annotations.SerializedName;
import java.util.List;
import java.util.Map;
import lombok.Builder;
import lombok.Data;
import org.stellar.anchor.api.shared.InstructionField;
import org.stellar.anchor.api.shared.Refunds;

@Data
Expand Down Expand Up @@ -77,7 +80,14 @@ public class Sep6Transaction {
@SerializedName("required_info_message")
String requiredInfoMessage;

// TODO: use a more structured type
@SerializedName("required_info_updates")
String requiredInfoUpdates;
List<String> requiredInfoUpdates;

@SerializedName("required_customer_info_message")
String requiredCustomerInfoMessage;

@SerializedName("required_customer_info_updates")
List<String> requiredCustomerInfoUpdates;

Map<String, InstructionField> instructions;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.stellar.anchor.api.shared;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class InstructionField {
String value;
String description;
}
32 changes: 27 additions & 5 deletions core/src/main/java/org/stellar/anchor/sep12/Sep12Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@
import io.micrometer.core.instrument.Metrics;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jetbrains.annotations.NotNull;
import org.stellar.anchor.api.callback.*;
import org.stellar.anchor.api.event.AnchorEvent;
import org.stellar.anchor.api.exception.*;
import org.stellar.anchor.api.platform.CustomerUpdatedResponse;
import org.stellar.anchor.api.sep.sep12.*;
import org.stellar.anchor.asset.AssetService;
import org.stellar.anchor.auth.Sep10Jwt;
import org.stellar.anchor.event.EventService;
import org.stellar.anchor.util.Log;
import org.stellar.anchor.util.MemoHelper;
import org.stellar.sdk.xdr.MemoType;
Expand All @@ -31,7 +35,12 @@ public class Sep12Service {

private final Set<String> knownTypes;

public Sep12Service(CustomerIntegration customerIntegration, AssetService assetService) {
private final EventService.Session eventSession;

public Sep12Service(
CustomerIntegration customerIntegration,
AssetService assetService,
EventService eventService) {
this.customerIntegration = customerIntegration;
Stream<String> receiverTypes =
assetService.listAllAssets().stream()
Expand All @@ -41,7 +50,9 @@ public Sep12Service(CustomerIntegration customerIntegration, AssetService assetS
assetService.listAllAssets().stream()
.filter(x -> x.getSep31() != null)
.flatMap(x -> x.getSep31().getSep12().getSender().getTypes().keySet().stream());
knownTypes = Stream.concat(receiverTypes, senderTypes).collect(Collectors.toSet());
this.knownTypes = Stream.concat(receiverTypes, senderTypes).collect(Collectors.toSet());
this.eventSession =
eventService.createSession(this.getClass().getName(), EventService.EventQueue.TRANSACTION);

Log.info("Sep12Service initialized.");
}
Expand Down Expand Up @@ -69,11 +80,22 @@ public Sep12PutCustomerResponse putCustomer(Sep10Jwt token, Sep12PutCustomerRequ
if (request.getAccount() == null && token.getAccount() != null) {
request.setAccount(token.getAccount());
}
Sep12PutCustomerResponse response =
PutCustomerResponse.to(customerIntegration.putCustomer(PutCustomerRequest.from(request)));

PutCustomerResponse response =
customerIntegration.putCustomer(PutCustomerRequest.from(request));

// Only publish event if the customer was updated.
eventSession.publish(
AnchorEvent.builder()
.id(UUID.randomUUID().toString())
.sep("12")
.type(AnchorEvent.Type.CUSTOMER_UPDATED)
.customer(CustomerUpdatedResponse.builder().id(response.getId()).build())
.build());

// increment counter
sep12PutCustomerCounter.increment();
return response;
return PutCustomerResponse.to(response);
}

public void deleteCustomer(Sep10Jwt sep10Jwt, String account, String memo, String memoType)
Expand Down
Loading

0 comments on commit 678b9e8

Please sign in to comment.