Skip to content

Commit

Permalink
Add support for muxed accounts in Horizon responses (#350)
Browse files Browse the repository at this point in the history
  • Loading branch information
tamirms authored Jun 29, 2021
1 parent 2e5931c commit 07a70c3
Show file tree
Hide file tree
Showing 21 changed files with 1,002 additions and 235 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ As this project is pre 1.0, breaking changes may happen for minor version bumps.

* Add opt-in support for [SEP23](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0023.md) M-strkeys for `MuxedAccount`s
* Add `getClaimableBalanceId()` method to `Transaction` class which returns the claimable balance id for a given operation.
* Add support for additional _muxed and _muxed_id optional fields in Horizon's JSON responses (available since Horizon 2.4, following what's described in SEP 23).

## 0.25.0

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.stellar.sdk.responses;

import com.google.common.collect.ImmutableList;
import com.google.common.reflect.TypeToken;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;

import java.lang.reflect.Type;
import java.util.List;

public class ImmutableListDeserializer implements JsonDeserializer<ImmutableList<?>> {

@Override
public ImmutableList<?> deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
@SuppressWarnings("unchecked")
final TypeToken<ImmutableList<?>> immutableListToken = (TypeToken<ImmutableList<?>>) TypeToken.of(type);
final TypeToken<? super ImmutableList<?>> listToken = immutableListToken.getSupertype(List.class);
final List<?> list = context.deserialize(json, listToken.getType());
return ImmutableList.copyOf(list);
}

}
43 changes: 43 additions & 0 deletions src/main/java/org/stellar/sdk/responses/MuxedAccount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.stellar.sdk.responses;

import com.google.common.base.Objects;

public class MuxedAccount {
private final String muxedAddress;
private final String unmuxedAddress;
private final Long id;

public MuxedAccount(String muxedAddress, String unmuxedAddress, Long id) {
this.muxedAddress = muxedAddress;
this.unmuxedAddress = unmuxedAddress;
this.id = id;
}

@Override
public String toString() {
return muxedAddress;
}

public Long getId() {
return id;
}

public String getUnmuxedAddress() {
return unmuxedAddress;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
return (getClass() == o.getClass()) && Objects.equal(muxedAddress, ((MuxedAccount)o).muxedAddress)
&& Objects.equal(unmuxedAddress, ((MuxedAccount)o).unmuxedAddress)
&& Objects.equal(id, ((MuxedAccount)o).id);
}

@Override
public int hashCode() {
return Objects.hashCode(muxedAddress, unmuxedAddress, id);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.stellar.sdk.responses;

import com.google.common.collect.ImmutableList;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
Expand All @@ -23,6 +24,7 @@ public OperationResponse deserialize(JsonElement json, Type typeOfT, JsonDeseria
.registerTypeAdapter(Asset.class, new AssetDeserializer())
.registerTypeAdapter(Predicate.class, new PredicateDeserializer())
.registerTypeAdapter(TransactionResponse.class, new TransactionDeserializer())
.registerTypeAdapter(ImmutableList.class, new ImmutableListDeserializer())
.create();

int type = json.getAsJsonObject().get("type_i").getAsInt();
Expand Down
120 changes: 50 additions & 70 deletions src/main/java/org/stellar/sdk/responses/TransactionResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,86 +17,66 @@
*/
public class TransactionResponse extends Response implements Pageable {
@SerializedName("hash")
private final String hash;
private String hash;
@SerializedName("ledger")
private final Long ledger;
private Long ledger;
@SerializedName("created_at")
private final String createdAt;
private String createdAt;
@SerializedName("source_account")
private final String sourceAccount;
private String sourceAccount;
@SerializedName("fee_account")
private final String feeAccount;
private String feeAccount;
@SerializedName("successful")
private final Boolean successful;
private Boolean successful;
@SerializedName("paging_token")
private final String pagingToken;
private String pagingToken;
@SerializedName("source_account_sequence")
private final Long sourceAccountSequence;
private Long sourceAccountSequence;
@SerializedName("max_fee")
private final Long maxFee;
private Long maxFee;
@SerializedName("fee_charged")
private final Long feeCharged;
private Long feeCharged;
@SerializedName("operation_count")
private final Integer operationCount;
private Integer operationCount;
@SerializedName("envelope_xdr")
private final String envelopeXdr;
private String envelopeXdr;
@SerializedName("result_xdr")
private final String resultXdr;
private String resultXdr;
@SerializedName("result_meta_xdr")
private final String resultMetaXdr;
private String resultMetaXdr;
@SerializedName("signatures")
private final List<String> signatures;
private List<String> signatures;
@SerializedName("fee_bump_transaction")
private final FeeBumpTransaction feeBumpTransaction;
private FeeBumpTransaction feeBumpTransaction;
@SerializedName("inner_transaction")
private final InnerTransaction innerTransaction;
private InnerTransaction innerTransaction;
@SerializedName("account_muxed")
private String accountMuxed;
@SerializedName("account_muxed_id")
private Long accountMuxedId;
@SerializedName("fee_account_muxed")
private String feeAccountMuxed;
@SerializedName("fee_account_muxed_id")
private Long feeAccountMuxedId;
@SerializedName("_links")
private final Links links;
private Links links;

// GSON won't serialize `transient` variables automatically. We need this behaviour
// because Memo is an abstract class and GSON tries to instantiate it.
private transient Memo memo;

TransactionResponse(
String hash,
Long ledger,
String createdAt,
String sourceAccount,
String feeAccount,
Boolean successful,
String pagingToken,
Long sourceAccountSequence,
Long maxFee,
Long feeCharged,
Integer operationCount,
String envelopeXdr,
String resultXdr,
String resultMetaXdr,
Memo memo,
List<String> signatures,
FeeBumpTransaction feeBumpTransaction,
InnerTransaction innerTransaction,
Links links
) {
this.hash = hash;
this.ledger = ledger;
this.createdAt = createdAt;
this.sourceAccount = sourceAccount;
this.feeAccount = feeAccount;
this.successful = successful;
this.pagingToken = pagingToken;
this.sourceAccountSequence = sourceAccountSequence;
this.maxFee = maxFee;
this.feeCharged = feeCharged;
this.operationCount = operationCount;
this.envelopeXdr = envelopeXdr;
this.resultXdr = resultXdr;
this.resultMetaXdr = resultMetaXdr;
this.memo = memo;
this.signatures = signatures;
this.feeBumpTransaction = feeBumpTransaction;
this.innerTransaction = innerTransaction;
this.links = links;
public Optional<MuxedAccount> getSourceAccountMuxed() {
if (this.accountMuxed == null || this.accountMuxed.isEmpty()) {
return Optional.absent();
}
return Optional.of(new MuxedAccount(this.accountMuxed, this.sourceAccount, this.accountMuxedId));
}

public Optional<MuxedAccount> getFeeAccountMuxed() {
if (this.feeAccountMuxed == null || this.feeAccountMuxed.isEmpty()) {
return Optional.absent();
}
return Optional.of(new MuxedAccount(this.feeAccountMuxed, this.feeAccount, this.feeAccountMuxedId));
}

public String getHash() {
Expand Down Expand Up @@ -190,9 +170,9 @@ public Links getLinks() {
*/
public static class FeeBumpTransaction {
@SerializedName("hash")
private final String hash;
private String hash;
@SerializedName("signatures")
private final List<String> signatures;
private List<String> signatures;

FeeBumpTransaction(String hash, List<String> signatures) {
this.hash = hash;
Expand All @@ -216,11 +196,11 @@ public List<String> getSignatures() {
*/
public static class InnerTransaction {
@SerializedName("hash")
private final String hash;
private String hash;
@SerializedName("signatures")
private final List<String> signatures;
private List<String> signatures;
@SerializedName("max_fee")
private final Long maxFee;
private Long maxFee;


InnerTransaction(String hash, List<String> signatures, Long maxFee) {
Expand Down Expand Up @@ -248,19 +228,19 @@ public Long getMaxFee() {
*/
public static class Links {
@SerializedName("account")
private final Link account;
private Link account;
@SerializedName("effects")
private final Link effects;
private Link effects;
@SerializedName("ledger")
private final Link ledger;
private Link ledger;
@SerializedName("operations")
private final Link operations;
private Link operations;
@SerializedName("precedes")
private final Link precedes;
private Link precedes;
@SerializedName("self")
private final Link self;
private Link self;
@SerializedName("succeeds")
private final Link succeeds;
private Link succeeds;

Links(Link account, Link effects, Link ledger, Link operations, Link self, Link precedes, Link succeeds) {
this.account = account;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package org.stellar.sdk.responses.effects;

import com.google.common.base.Optional;
import com.google.gson.annotations.SerializedName;

import org.stellar.sdk.responses.Link;
import org.stellar.sdk.responses.MuxedAccount;
import org.stellar.sdk.responses.Pageable;
import org.stellar.sdk.responses.Response;

Expand All @@ -14,15 +16,19 @@
*/
public abstract class EffectResponse extends Response implements Pageable {
@SerializedName("id")
protected String id;
private String id;
@SerializedName("account")
protected String account;
private String account;
@SerializedName("account_muxed")
private String accountMuxed;
@SerializedName("account_muxed_id")
private Long accountMuxedId;
@SerializedName("type")
protected String type;
private String type;
@SerializedName("created_at")
protected String createdAt;
private String createdAt;
@SerializedName("paging_token")
protected String pagingToken;
private String pagingToken;
@SerializedName("_links")
private Links links;

Expand All @@ -34,6 +40,13 @@ public String getAccount() {
return account;
}

public Optional<MuxedAccount> getAccountMuxed() {
if (this.accountMuxed == null || this.accountMuxed.isEmpty()) {
return Optional.absent();
}
return Optional.of(new MuxedAccount(this.accountMuxed, this.account, this.accountMuxedId));
}

/**
* <p>Returns effect type. Possible types:</p>
* <ul>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package org.stellar.sdk.responses.effects;

import com.google.common.base.Optional;
import com.google.gson.annotations.SerializedName;

import org.stellar.sdk.Asset;
import org.stellar.sdk.AssetTypeNative;
import org.stellar.sdk.responses.MuxedAccount;

/**
* Represents trade effect response.
Expand All @@ -13,39 +15,38 @@
*/
public class TradeEffectResponse extends EffectResponse {
@SerializedName("seller")
protected final String seller;
private String seller;
@SerializedName("seller_muxed")
private String sellerMuxed;
@SerializedName("seller_muxed_id")
private Long sellerMuxedId;

@SerializedName("offer_id")
protected final Long offerId;
private Long offerId;

@SerializedName("sold_amount")
protected final String soldAmount;
private String soldAmount;
@SerializedName("sold_asset_type")
protected final String soldAssetType;
private String soldAssetType;
@SerializedName("sold_asset_code")
protected final String soldAssetCode;
private String soldAssetCode;
@SerializedName("sold_asset_issuer")
protected final String soldAssetIssuer;
private String soldAssetIssuer;

@SerializedName("bought_amount")
protected final String boughtAmount;
private String boughtAmount;
@SerializedName("bought_asset_type")
protected final String boughtAssetType;
private String boughtAssetType;
@SerializedName("bought_asset_code")
protected final String boughtAssetCode;
private String boughtAssetCode;
@SerializedName("bought_asset_issuer")
protected final String boughtAssetIssuer;
private String boughtAssetIssuer;

TradeEffectResponse(String seller, Long offerId, String soldAmount, String soldAssetType, String soldAssetCode, String soldAssetIssuer, String boughtAmount, String boughtAssetType, String boughtAssetCode, String boughtAssetIssuer) {
this.seller = seller;
this.offerId = offerId;
this.soldAmount = soldAmount;
this.soldAssetType = soldAssetType;
this.soldAssetCode = soldAssetCode;
this.soldAssetIssuer = soldAssetIssuer;
this.boughtAmount = boughtAmount;
this.boughtAssetType = boughtAssetType;
this.boughtAssetCode = boughtAssetCode;
this.boughtAssetIssuer = boughtAssetIssuer;
public Optional<MuxedAccount> getSellerMuxed() {
if (this.sellerMuxed == null || this.sellerMuxed.isEmpty()) {
return Optional.absent();
}
return Optional.of(new MuxedAccount(this.sellerMuxed, this.seller, this.sellerMuxedId));
}

public String getSeller() {
Expand Down
Loading

0 comments on commit 07a70c3

Please sign in to comment.