Skip to content

Commit

Permalink
Merge pull request #378 from stellar/374_change_trust_reponse_liquidi…
Browse files Browse the repository at this point in the history
…ty_asset

#374: fix npe on liquidity pool trustline in changetrustresponse

closes #374
  • Loading branch information
sreuland authored Nov 10, 2021
2 parents f0ccceb + 7534180 commit 80fc0e6
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 21 deletions.
36 changes: 31 additions & 5 deletions src/main/java/org/stellar/sdk/Asset.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ public abstract class Asset implements Comparable<Asset> {

/**
* Parses an asset string and returns the equivalent Asset instance.
* The asset string is expected to either be "native" or a string of the form "CODE:ISSUER"
* The asset string is expected to either be "native" or a string of Alpha4 or Alpha12
* asset code as "CODE:ISSUER"
*
* @param canonicalForm Canonical string representation of an asset
* @param canonicalForm Canonical string representation of an Alpha4 or Alpha12 asset
* @return Asset or throws IllegalArgumentException if not Alpha4 or Alpha12 asset code
*/
public static Asset create(String canonicalForm) {
if (canonicalForm.equals("native")) {
Expand All @@ -24,12 +26,36 @@ public static Asset create(String canonicalForm) {
return Asset.createNonNativeAsset(parts[0], parts[1]);
}

/**
* Creates Asset for Alpha4/Alpha5/Native
*
* @param type the type of asset can be 'native', 'alpha4', 'alpha12'
* @param code the asset code that conforms to type or null
* @param issuer the asset issuer the conforms to type or null
* @return
*/
public static Asset create(String type, String code, String issuer) {
return create(type, code, issuer, null);
}

/**
* Creates Asset for Alpha4/Alpha5/Native/LiquidityPool
*
* @param type the type of asset can be 'native', 'alpha4', 'alpha12' or 'liquidity_pool_shares'
* @param code the asset code that conforms to type or null
* @param issuer the asset issuer the conforms to type or null
* @param liquidityPoolID provided only if type is 'liquidity_pool_shares'
* @return Asset
*/
public static Asset create(String type, String code, String issuer, String liquidityPoolID) {
if (type.equals("native")) {
return new AssetTypeNative();
} else {
return Asset.createNonNativeAsset(code, issuer);
}
if (type.equals("liquidity_pool_shares")) {
return new AssetTypePoolShare(liquidityPoolID);
}

return Asset.createNonNativeAsset(code, issuer);
}

public static Asset create(ChangeTrustAsset.Wrapper wrapped) {
Expand Down Expand Up @@ -82,7 +108,7 @@ public static Asset fromXdr(org.stellar.sdk.xdr.Asset xdr) {
* <li><code>native</code></li>
* <li><code>credit_alphanum4</code></li>
* <li><code>credit_alphanum12</code></li>
* <li><code>pool_share</code></li>
* <li><code>liquidity_pool_shares</code></li>
* </ul>
*/
public abstract String getType();
Expand Down
70 changes: 70 additions & 0 deletions src/main/java/org/stellar/sdk/AssetTypePoolShare.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package org.stellar.sdk;

import com.google.common.base.Objects;

/**
* Represents Stellar liquidity pool share asset - <a href="https://developers.stellar.org/docs/glossary/liquidity-pool/#trustlines" target="_blank">lumens (XLM)</a>
* @see <a href="https://developers.stellar.org/docs/glossary/liquidity-pool/#trustlines" target="_blank">Assets</a>
*/
public final class AssetTypePoolShare extends Asset {

private final String poolId;

public AssetTypePoolShare(String poolId) {
this.poolId = poolId;
}

@Override
public String toString() {
return "liquidity_pool_shares";
}

@Override
public String getType() {
return "liquidity_pool_shares";
}

@Override
public boolean equals(Object object) {
if (object == null || !this.getClass().equals(object.getClass())) {
return false;
}

return (Objects.equal(((AssetTypePoolShare)object).getPoolId(), poolId));
}

@Override
public int hashCode() {
return Objects.hashCode(poolId);
}

@Override
public org.stellar.sdk.xdr.Asset toXdr() {
throw new UnsupportedOperationException("liquidity_pool_shares are not defined as Asset in XDR");
}

@Override
public int compareTo(Asset other) {
if (other == null || !this.getClass().equals(other.getClass())) {
return -1;
}

AssetTypePoolShare otherPoolShare = (AssetTypePoolShare)other;

if (poolId == null && otherPoolShare.getPoolId() == null) {
return 0;
}

if (poolId == null) {
return -1;
}

if (otherPoolShare.getPoolId() == null) {
return 1;
}

return poolId.compareTo(otherPoolShare.getPoolId());
}

public String getPoolId() { return poolId; }
}
24 changes: 15 additions & 9 deletions src/main/java/org/stellar/sdk/responses/AssetDeserializer.java
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
package org.stellar.sdk.responses;

import com.google.common.base.Function;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;

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

import java.lang.reflect.Type;

import static com.google.common.base.Optional.fromNullable;

class AssetDeserializer implements JsonDeserializer<Asset> {
@Override
public Asset deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (!json.isJsonObject()) {
// Probably a canonical string
return Asset.create(json.getAsString());
}
String type = json.getAsJsonObject().get("asset_type").getAsString();
if (type.equals("native")) {
return new AssetTypeNative();
} else {
String code = json.getAsJsonObject().get("asset_code").getAsString();
String issuer = json.getAsJsonObject().get("asset_issuer").getAsString();
return Asset.createNonNativeAsset(code, issuer);

return Asset.create(json.getAsJsonObject().get("asset_type").getAsString(),
fromNullable(json.getAsJsonObject().get("asset_code")).transform(ToString.FUNCTION).orNull(),
fromNullable(json.getAsJsonObject().get("asset_issuer")).transform(ToString.FUNCTION).orNull(),
fromNullable(json.getAsJsonObject().get("liquidity_pool_id")).transform(ToString.FUNCTION).orNull());
}

enum ToString implements Function<JsonElement, String> {
FUNCTION;
@Override
public String apply(JsonElement input) {
return input.getAsString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

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;

/**
Expand All @@ -30,6 +28,8 @@ public class ChangeTrustOperationResponse extends OperationResponse {
private String assetIssuer;
@SerializedName("limit")
private String limit;
@SerializedName("liquidity_pool_id")
private String liquidityPoolId;

public Optional<MuxedAccount> getTrustorMuxed() {
if (this.trustorMuxed == null || this.trustorMuxed.isEmpty()) {
Expand All @@ -51,10 +51,7 @@ public String getLimit() {
}

public Asset getAsset() {
if (assetType.equals("native")) {
return new AssetTypeNative();
} else {
return Asset.createNonNativeAsset(assetCode, assetIssuer);
}
return Asset.create(assetType, assetCode, assetIssuer, liquidityPoolId);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -366,13 +366,54 @@ public void testDeserializeChangeTrustOperation() {

ChangeTrustOperationResponse operation = (ChangeTrustOperationResponse) GsonSingleton.getInstance().fromJson(json, OperationResponse.class);

operation.getAsset();
assertEquals(operation.getTrustee(), "GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM");
assertEquals(operation.getTrustor(), "GDZ55LVXECRTW4G36EZPTHI4XIYS5JUC33TUS22UOETVFVOQ77JXWY4F");
assertEquals(operation.getLimit(), "922337203685.4775807");
assertEquals(operation.getAsset(), Asset.createNonNativeAsset("EUR", "GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM"));
assertFalse(operation.getTrustorMuxed().isPresent());
}

@Test
public void testDeserializeChangeTrustOperationLiquidityPoolShares() {
String json = "{\n" +
" \"_links\": {\n" +
" \"self\": {\n" +
" \"href\": \"//horizon-testnet.stellar.org/operations/3602970755207169\"\n" +
" },\n" +
" \"transaction\": {\n" +
" \"href\": \"//horizon-testnet.stellar.org/transactions/8d409a788543895843d269c3f97a2d6a2ebca6e9f8f9a7ae593457b5c0ba6644\"\n" +
" },\n" +
" \"effects\": {\n" +
" \"href\": \"//horizon-testnet.stellar.org/operations/3602970755207169/effects\"\n" +
" },\n" +
" \"succeeds\": {\n" +
" \"href\": \"//horizon-testnet.stellar.org/effects?order=desc\\u0026cursor=3602970755207169\"\n" +
" },\n" +
" \"precedes\": {\n" +
" \"href\": \"//horizon-testnet.stellar.org/effects?order=asc\\u0026cursor=3602970755207169\"\n" +
" }\n" +
" },\n" +
" \"id\": \"3602970755207169\",\n" +
" \"paging_token\": \"3602970755207169\",\n" +
" \"source_account\": \"GDZ55LVXECRTW4G36EZPTHI4XIYS5JUC33TUS22UOETVFVOQ77JXWY4F\",\n" +
" \"type\": \"change_trust\",\n" +
" \"type_i\": 6,\n" +
" \"asset_type\": \"liquidity_pool_shares\",\n" +
" \"liquidity_pool_id\": \"02449937ed825805b7a945bb6c027b53dfaf140983c1a1a64c42a81edd89b5e0\",\n" +
" \"limit\": \"5.0000000\",\n" +
" \"trustee\": \"GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM\",\n" +
" \"trustor\": \"GDZ55LVXECRTW4G36EZPTHI4XIYS5JUC33TUS22UOETVFVOQ77JXWY4F\"\n" +
" }";

ChangeTrustOperationResponse operation = (ChangeTrustOperationResponse) GsonSingleton.getInstance().fromJson(json, OperationResponse.class);

assertEquals(operation.getTrustee(), "GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM");
assertEquals(operation.getTrustor(), "GDZ55LVXECRTW4G36EZPTHI4XIYS5JUC33TUS22UOETVFVOQ77JXWY4F");
assertEquals(operation.getLimit(), "5.0000000");
assertEquals(((AssetTypePoolShare)operation.getAsset()).getPoolId(), "02449937ed825805b7a945bb6c027b53dfaf140983c1a1a64c42a81edd89b5e0");
}

@Test
public void testDeserializeMuxedChangeTrustOperation() {
String json = "{\n" +
Expand Down

0 comments on commit 80fc0e6

Please sign in to comment.